OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/media/audio_renderer_mixer_manager.h" | 5 #include "content/renderer/media/audio_renderer_mixer_manager.h" |
6 | 6 |
| 7 #include <algorithm> |
7 #include <string> | 8 #include <string> |
8 | 9 |
9 #include "base/bind.h" | 10 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
11 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 13 #include "base/metrics/histogram_macros.h" |
| 14 #include "base/metrics/sparse_histogram.h" |
12 #include "build/build_config.h" | 15 #include "build/build_config.h" |
13 #include "content/renderer/media/audio_renderer_sink_cache.h" | 16 #include "content/renderer/media/audio_renderer_sink_cache.h" |
14 #include "media/audio/audio_device_description.h" | 17 #include "media/audio/audio_device_description.h" |
15 #include "media/base/audio_hardware_config.h" | 18 #include "media/base/audio_hardware_config.h" |
16 #include "media/base/audio_renderer_mixer.h" | 19 #include "media/base/audio_renderer_mixer.h" |
17 #include "media/base/audio_renderer_mixer_input.h" | 20 #include "media/base/audio_renderer_mixer_input.h" |
18 | 21 |
| 22 namespace { |
| 23 // Calculate mixer output parameters based on mixer input parameters and |
| 24 // hardware parameters for audio output. |
| 25 media::AudioParameters GetMixerOutputParams( |
| 26 const media::AudioParameters& input_params, |
| 27 const media::AudioParameters& hardware_params, |
| 28 media::AudioLatency::LatencyType latency) { |
| 29 int output_sample_rate = input_params.sample_rate(); |
| 30 bool valid_not_fake_hardware_params = |
| 31 hardware_params.format() != media::AudioParameters::AUDIO_FAKE && |
| 32 hardware_params.IsValid(); |
| 33 int preferred_high_latency_output_buffer_size = 0; |
| 34 |
| 35 #if !defined(OS_CHROMEOS) |
| 36 // On ChromeOS as well as when a fake device is used, we can rely on the |
| 37 // playback device to handle resampling, so don't waste cycles on it here. |
| 38 // On other systems if hardware parameters are valid and the device is not |
| 39 // fake, resample to hardware sample rate. Otherwise, pass the input one and |
| 40 // let the browser side handle automatic fallback. |
| 41 if (valid_not_fake_hardware_params) { |
| 42 output_sample_rate = hardware_params.sample_rate(); |
| 43 preferred_high_latency_output_buffer_size = |
| 44 hardware_params.frames_per_buffer(); |
| 45 } |
| 46 #endif |
| 47 |
| 48 int output_buffer_size = 0; |
| 49 |
| 50 // Adjust output buffer size according to the latency requirement. |
| 51 switch (latency) { |
| 52 case media::AudioLatency::LATENCY_INTERACTIVE: |
| 53 output_buffer_size = media::AudioLatency::GetInteractiveBufferSize( |
| 54 hardware_params.frames_per_buffer()); |
| 55 break; |
| 56 case media::AudioLatency::LATENCY_RTC: |
| 57 output_buffer_size = media::AudioLatency::GetRtcBufferSize( |
| 58 output_sample_rate, valid_not_fake_hardware_params |
| 59 ? hardware_params.frames_per_buffer() |
| 60 : 0); |
| 61 break; |
| 62 case media::AudioLatency::LATENCY_PLAYBACK: |
| 63 output_buffer_size = media::AudioLatency::GetHighLatencyBufferSize( |
| 64 output_sample_rate, preferred_high_latency_output_buffer_size); |
| 65 break; |
| 66 case media::AudioLatency::LATENCY_EXACT_MS: |
| 67 // TODO(olka): add support when WebAudio requires it. |
| 68 default: |
| 69 NOTREACHED(); |
| 70 } |
| 71 |
| 72 DCHECK_NE(output_buffer_size, 0); |
| 73 |
| 74 // Force to 16-bit output for now since we know that works everywhere; |
| 75 // ChromeOS does not support other bit depths. |
| 76 return media::AudioParameters(input_params.format(), |
| 77 input_params.channel_layout(), |
| 78 output_sample_rate, 16, output_buffer_size); |
| 79 } |
| 80 |
| 81 void LogMixerUmaHistogram(media::AudioLatency::LatencyType latency, int value) { |
| 82 switch (latency) { |
| 83 case media::AudioLatency::LATENCY_EXACT_MS: |
| 84 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 85 "Media.Audio.Render.AudioInputsPerMixer.LatencyExact", value, 1, 20, |
| 86 21); |
| 87 return; |
| 88 case media::AudioLatency::LATENCY_INTERACTIVE: |
| 89 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 90 "Media.Audio.Render.AudioInputsPerMixer.LatencyInteractive", value, 1, |
| 91 20, 21); |
| 92 return; |
| 93 case media::AudioLatency::LATENCY_RTC: |
| 94 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 95 "Media.Audio.Render.AudioInputsPerMixer.LatencyRtc", value, 1, 20, |
| 96 21); |
| 97 return; |
| 98 case media::AudioLatency::LATENCY_PLAYBACK: |
| 99 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 100 "Media.Audio.Render.AudioInputsPerMixer.LatencyPlayback", value, 1, |
| 101 20, 21); |
| 102 return; |
| 103 default: |
| 104 NOTREACHED(); |
| 105 } |
| 106 } |
| 107 |
| 108 } // namespace |
| 109 |
19 namespace content { | 110 namespace content { |
20 | 111 |
21 AudioRendererMixerManager::AudioRendererMixerManager( | 112 AudioRendererMixerManager::AudioRendererMixerManager( |
22 std::unique_ptr<AudioRendererSinkCache> sink_cache) | 113 std::unique_ptr<AudioRendererSinkCache> sink_cache) |
23 : sink_cache_(std::move(sink_cache)) { | 114 : sink_cache_(std::move(sink_cache)) { |
24 DCHECK(sink_cache_); | 115 DCHECK(sink_cache_); |
25 } | 116 } |
26 | 117 |
27 AudioRendererMixerManager::~AudioRendererMixerManager() { | 118 AudioRendererMixerManager::~AudioRendererMixerManager() { |
28 // References to AudioRendererMixers may be owned by garbage collected | 119 // References to AudioRendererMixers may be owned by garbage collected |
29 // objects. During process shutdown they may be leaked, so, transitively, | 120 // objects. During process shutdown they may be leaked, so, transitively, |
30 // |mixers_| may leak (i.e., may be non-empty at this time) as well. | 121 // |mixers_| may leak (i.e., may be non-empty at this time) as well. |
31 } | 122 } |
32 | 123 |
33 // static | 124 // static |
34 std::unique_ptr<AudioRendererMixerManager> AudioRendererMixerManager::Create() { | 125 std::unique_ptr<AudioRendererMixerManager> AudioRendererMixerManager::Create() { |
35 return base::WrapUnique( | 126 return base::WrapUnique( |
36 new AudioRendererMixerManager(AudioRendererSinkCache::Create())); | 127 new AudioRendererMixerManager(AudioRendererSinkCache::Create())); |
37 } | 128 } |
38 | 129 |
39 media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput( | 130 media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput( |
40 int source_render_frame_id, | 131 int source_render_frame_id, |
41 int session_id, | 132 int session_id, |
42 const std::string& device_id, | 133 const std::string& device_id, |
43 const url::Origin& security_origin) { | 134 const url::Origin& security_origin, |
| 135 media::AudioLatency::LatencyType latency) { |
44 // AudioRendererMixerManager lives on the renderer thread and is destroyed on | 136 // AudioRendererMixerManager lives on the renderer thread and is destroyed on |
45 // renderer thread destruction, so it's safe to pass its pointer to a mixer | 137 // renderer thread destruction, so it's safe to pass its pointer to a mixer |
46 // input. | 138 // input. |
47 return new media::AudioRendererMixerInput( | 139 return new media::AudioRendererMixerInput( |
48 this, source_render_frame_id, | 140 this, source_render_frame_id, |
49 media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, | 141 media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, |
50 device_id) | 142 device_id) |
51 ? GetOutputDeviceInfo(source_render_frame_id, session_id, device_id, | 143 ? GetOutputDeviceInfo(source_render_frame_id, session_id, device_id, |
52 security_origin) | 144 security_origin) |
53 .device_id() | 145 .device_id() |
54 : device_id, | 146 : device_id, |
55 security_origin); | 147 security_origin, latency); |
56 } | 148 } |
57 | 149 |
58 media::AudioRendererMixer* AudioRendererMixerManager::GetMixer( | 150 media::AudioRendererMixer* AudioRendererMixerManager::GetMixer( |
59 int source_render_frame_id, | 151 int source_render_frame_id, |
60 const media::AudioParameters& params, | 152 const media::AudioParameters& input_params, |
| 153 media::AudioLatency::LatencyType latency, |
61 const std::string& device_id, | 154 const std::string& device_id, |
62 const url::Origin& security_origin, | 155 const url::Origin& security_origin, |
63 media::OutputDeviceStatus* device_status) { | 156 media::OutputDeviceStatus* device_status) { |
64 // Effects are not passed through to output creation, so ensure none are set. | 157 // Effects are not passed through to output creation, so ensure none are set. |
65 DCHECK_EQ(params.effects(), media::AudioParameters::NO_EFFECTS); | 158 DCHECK_EQ(input_params.effects(), media::AudioParameters::NO_EFFECTS); |
66 | 159 |
67 const MixerKey key(source_render_frame_id, params, device_id, | 160 const MixerKey key(source_render_frame_id, input_params, latency, device_id, |
68 security_origin); | 161 security_origin); |
69 base::AutoLock auto_lock(mixers_lock_); | 162 base::AutoLock auto_lock(mixers_lock_); |
70 | 163 |
| 164 // Update latency map when the mixer is requested, i.e. there is an attempt to |
| 165 // mix and output audio with a given latency. This is opposite to |
| 166 // CreateInput() which creates a sink which is probably never used for output. |
| 167 if (!latency_map_[latency]) { |
| 168 latency_map_[latency] = 1; |
| 169 // Log the updated latency map. This can't be done once in the end of the |
| 170 // renderer lifetime, because the destructor is usually not called. So, |
| 171 // we'll have a sort of exponential scale here, with a smaller subset |
| 172 // logged both on its own and as a part of any larger subset. |
| 173 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.Render.AudioMixing.LatencyMap", |
| 174 latency_map_.to_ulong()); |
| 175 } |
| 176 |
71 AudioRendererMixerMap::iterator it = mixers_.find(key); | 177 AudioRendererMixerMap::iterator it = mixers_.find(key); |
72 if (it != mixers_.end()) { | 178 if (it != mixers_.end()) { |
73 if (device_status) | 179 if (device_status) |
74 *device_status = media::OUTPUT_DEVICE_STATUS_OK; | 180 *device_status = media::OUTPUT_DEVICE_STATUS_OK; |
75 | 181 |
76 it->second.ref_count++; | 182 it->second.ref_count++; |
| 183 DVLOG(1) << "Reusing mixer: " << it->second.mixer; |
77 return it->second.mixer; | 184 return it->second.mixer; |
78 } | 185 } |
79 | 186 |
80 scoped_refptr<media::AudioRendererSink> sink = | 187 scoped_refptr<media::AudioRendererSink> sink = |
81 sink_cache_->GetSink(source_render_frame_id, device_id, security_origin); | 188 sink_cache_->GetSink(source_render_frame_id, device_id, security_origin); |
82 | 189 |
83 const media::OutputDeviceInfo& device_info = sink->GetOutputDeviceInfo(); | 190 const media::OutputDeviceInfo& device_info = sink->GetOutputDeviceInfo(); |
84 if (device_status) | 191 if (device_status) |
85 *device_status = device_info.device_status(); | 192 *device_status = device_info.device_status(); |
86 if (device_info.device_status() != media::OUTPUT_DEVICE_STATUS_OK) { | 193 if (device_info.device_status() != media::OUTPUT_DEVICE_STATUS_OK) { |
87 sink_cache_->ReleaseSink(sink.get()); | 194 sink_cache_->ReleaseSink(sink.get()); |
88 sink->Stop(); | 195 sink->Stop(); |
89 return nullptr; | 196 return nullptr; |
90 } | 197 } |
91 | 198 |
92 // On ChromeOS as well as when a fake device is used, we can rely on the | 199 const media::AudioParameters& mixer_output_params = |
93 // playback device to handle resampling, so don't waste cycles on it here. | 200 GetMixerOutputParams(input_params, device_info.output_params(), latency); |
94 int sample_rate = params.sample_rate(); | 201 media::AudioRendererMixer* mixer = new media::AudioRendererMixer( |
95 int buffer_size = | 202 mixer_output_params, sink, base::Bind(LogMixerUmaHistogram, latency)); |
96 media::AudioHardwareConfig::GetHighLatencyBufferSize(sample_rate, 0); | |
97 | |
98 #if !defined(OS_CHROMEOS) | |
99 const media::AudioParameters& hardware_params = device_info.output_params(); | |
100 | |
101 // If we have valid, non-fake hardware parameters, use them. Otherwise, pass | |
102 // on the input params and let the browser side handle automatic fallback. | |
103 if (hardware_params.format() != media::AudioParameters::AUDIO_FAKE && | |
104 hardware_params.IsValid()) { | |
105 sample_rate = hardware_params.sample_rate(); | |
106 buffer_size = media::AudioHardwareConfig::GetHighLatencyBufferSize( | |
107 sample_rate, hardware_params.frames_per_buffer()); | |
108 } | |
109 #endif | |
110 | |
111 // Create output parameters based on the audio hardware configuration for | |
112 // passing on to the output sink. Force to 16-bit output for now since we | |
113 // know that works everywhere; ChromeOS does not support other bit depths. | |
114 media::AudioParameters output_params( | |
115 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, params.channel_layout(), | |
116 sample_rate, 16, buffer_size); | |
117 DCHECK(output_params.IsValid()); | |
118 | |
119 media::AudioRendererMixer* mixer = | |
120 new media::AudioRendererMixer(output_params, sink); | |
121 AudioRendererMixerReference mixer_reference = {mixer, 1, sink.get()}; | 203 AudioRendererMixerReference mixer_reference = {mixer, 1, sink.get()}; |
122 mixers_[key] = mixer_reference; | 204 mixers_[key] = mixer_reference; |
| 205 DVLOG(1) << __FUNCTION__ << " mixer: " << mixer << " latency: " << latency |
| 206 << "\n input: " << input_params.AsHumanReadableString() |
| 207 << "\noutput: " << mixer_output_params.AsHumanReadableString(); |
123 return mixer; | 208 return mixer; |
124 } | 209 } |
125 | 210 |
126 void AudioRendererMixerManager::ReturnMixer( | 211 void AudioRendererMixerManager::ReturnMixer(media::AudioRendererMixer* mixer) { |
127 int source_render_frame_id, | |
128 const media::AudioParameters& params, | |
129 const std::string& device_id, | |
130 const url::Origin& security_origin) { | |
131 const MixerKey key(source_render_frame_id, params, device_id, | |
132 security_origin); | |
133 base::AutoLock auto_lock(mixers_lock_); | 212 base::AutoLock auto_lock(mixers_lock_); |
134 | 213 AudioRendererMixerMap::iterator it = std::find_if( |
135 AudioRendererMixerMap::iterator it = mixers_.find(key); | 214 mixers_.begin(), mixers_.end(), |
| 215 [mixer](const std::pair<MixerKey, AudioRendererMixerReference>& val) { |
| 216 return val.second.mixer == mixer; |
| 217 }); |
136 DCHECK(it != mixers_.end()); | 218 DCHECK(it != mixers_.end()); |
137 | 219 |
138 // Only remove the mixer if AudioRendererMixerManager is the last owner. | 220 // Only remove the mixer if AudioRendererMixerManager is the last owner. |
139 it->second.ref_count--; | 221 it->second.ref_count--; |
140 if (it->second.ref_count == 0) { | 222 if (it->second.ref_count == 0) { |
141 // The mixer will be deleted now, so release the sink. | 223 // The mixer will be deleted now, so release the sink. |
142 sink_cache_->ReleaseSink(it->second.sink_ptr); | 224 sink_cache_->ReleaseSink(it->second.sink_ptr); |
143 delete it->second.mixer; | 225 delete it->second.mixer; |
144 mixers_.erase(it); | 226 mixers_.erase(it); |
145 } | 227 } |
146 } | 228 } |
147 | 229 |
148 media::OutputDeviceInfo AudioRendererMixerManager::GetOutputDeviceInfo( | 230 media::OutputDeviceInfo AudioRendererMixerManager::GetOutputDeviceInfo( |
149 int source_render_frame_id, | 231 int source_render_frame_id, |
150 int session_id, | 232 int session_id, |
151 const std::string& device_id, | 233 const std::string& device_id, |
152 const url::Origin& security_origin) { | 234 const url::Origin& security_origin) { |
153 return sink_cache_->GetSinkInfo(source_render_frame_id, session_id, device_id, | 235 return sink_cache_->GetSinkInfo(source_render_frame_id, session_id, device_id, |
154 security_origin); | 236 security_origin); |
155 } | 237 } |
156 | 238 |
157 AudioRendererMixerManager::MixerKey::MixerKey( | 239 AudioRendererMixerManager::MixerKey::MixerKey( |
158 int source_render_frame_id, | 240 int source_render_frame_id, |
159 const media::AudioParameters& params, | 241 const media::AudioParameters& params, |
| 242 media::AudioLatency::LatencyType latency, |
160 const std::string& device_id, | 243 const std::string& device_id, |
161 const url::Origin& security_origin) | 244 const url::Origin& security_origin) |
162 : source_render_frame_id(source_render_frame_id), | 245 : source_render_frame_id(source_render_frame_id), |
163 params(params), | 246 params(params), |
| 247 latency(latency), |
164 device_id(device_id), | 248 device_id(device_id), |
165 security_origin(security_origin) {} | 249 security_origin(security_origin) {} |
166 | 250 |
167 AudioRendererMixerManager::MixerKey::MixerKey(const MixerKey& other) = default; | 251 AudioRendererMixerManager::MixerKey::MixerKey(const MixerKey& other) = default; |
168 | 252 |
169 } // namespace content | 253 } // namespace content |
OLD | NEW |