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