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" |
12 #include "build/build_config.h" | 13 #include "build/build_config.h" |
13 #include "content/renderer/media/audio_renderer_sink_cache.h" | 14 #include "content/renderer/media/audio_renderer_sink_cache.h" |
14 #include "media/audio/audio_device_description.h" | 15 #include "media/audio/audio_device_description.h" |
15 #include "media/base/audio_hardware_config.h" | 16 #include "media/base/audio_hardware_config.h" |
16 #include "media/base/audio_renderer_mixer.h" | 17 #include "media/base/audio_renderer_mixer.h" |
17 #include "media/base/audio_renderer_mixer_input.h" | 18 #include "media/base/audio_renderer_mixer_input.h" |
18 | 19 |
20 namespace { | |
21 // calculate mixer output parameters based on input parameters and audio | |
22 // hardware configuration. | |
23 media::AudioParameters GetMixerOutputParams( | |
24 const media::AudioParameters& input_params, | |
25 const media::AudioParameters& hardware_params, | |
26 media::AudioLatency::LatencyType latency) { | |
27 int output_sample_rate = input_params.sample_rate(); | |
28 bool valid_not_fake_hardware_params = | |
29 hardware_params.format() != media::AudioParameters::AUDIO_FAKE && | |
30 hardware_params.IsValid(); | |
31 int preferred_high_latency_output_bufffer_size = 0; | |
32 | |
33 #if !defined(OS_CHROMEOS) | |
34 // On ChromeOS as well as when a fake device is used, we can rely on the | |
35 // playback device to handle resampling, so don't waste cycles on it here. | |
36 // On other systems if hardware parameters are valid and the device is not | |
37 // fake, resample to hadrware sample rate. Otherwise, pass the input one and | |
38 // let the browser side handle automatic fallback. | |
39 if (valid_not_fake_hardware_params) { | |
40 output_sample_rate = hardware_params.sample_rate(); | |
41 preferred_high_latency_output_bufffer_size = | |
42 hardware_params.frames_per_buffer(); | |
43 } | |
44 #endif | |
45 | |
46 int output_buffer_size = input_params.frames_per_buffer(); | |
o1ka
2016/06/23 16:36:15
Note that here we rely on the current situation wh
chcunningham
2016/06/27 23:12:24
You bring up an interesting point - now I'm confus
o1ka
2016/06/28 13:04:57
Unfortunately I did not let you know: I changed it
chcunningham
2016/06/28 18:43:49
Ack - my bad. This method looks good in the new PS
DaleCurtis
2016/06/28 21:44:07
I don't think it matters either way; the size is "
| |
47 | |
48 if (output_sample_rate != input_params.sample_rate()) { | |
chcunningham
2016/06/27 23:12:25
Why do we only go down this path when in/out sampl
| |
49 // Adjust output buffer size according to the latency requirement. | |
50 switch (latency) { | |
51 case media::AudioLatency::LATENCY_EXACT_MS: | |
o1ka
2016/06/21 15:16:40
It's not used right now, but we'll need it for htt
| |
52 // Keep the provided buffer duration. | |
53 output_buffer_size = input_params.GetBufferDuration().InMicroseconds() * | |
54 output_sample_rate / | |
55 base::Time::kMicrosecondsPerSecond; | |
56 break; | |
57 case media::AudioLatency::LATENCY_INTERACTIVE: | |
58 // WebAudio should provide correct callback size in frames; it does not | |
59 // depend on the sample rate. | |
60 DCHECK_EQ(output_buffer_size, | |
61 media::AudioLatency::GetInteractiveBufferSize( | |
o1ka
2016/06/21 15:16:41
Also not sure about this check, probably should be
chcunningham
2016/06/22 04:34:07
I think this would be wrong for android, where Get
o1ka
2016/06/23 16:36:15
Currently WebAudio sets the buffer size in AudioDe
chcunningham
2016/06/27 23:12:25
I think this gets at my comment on line 46 above -
o1ka
2016/06/28 13:04:57
Agreed.
| |
62 hardware_params.frames_per_buffer())); | |
63 break; | |
64 case media::AudioLatency::LATENCY_RTC: | |
65 output_buffer_size = media::AudioLatency::GetRtcBufferSize( | |
66 output_sample_rate, valid_not_fake_hardware_params | |
67 ? hardware_params.frames_per_buffer() | |
68 : 0); | |
69 break; | |
70 case media::AudioLatency::LATENCY_PLAYBACK: | |
chcunningham
2016/06/22 04:34:07
Could you combine this case with the default? Woul
| |
71 output_buffer_size = media::AudioLatency::GetHighLatencyBufferSize( | |
72 output_sample_rate, preferred_high_latency_output_bufffer_size); | |
73 break; | |
74 default: | |
75 DCHECK(false); | |
chcunningham
2016/06/22 04:34:07
If you don't combine with LATENCY_PLAYBACK, this s
o1ka
2016/06/23 16:36:15
I would prefer to not combine, because it may be t
chcunningham
2016/06/27 23:12:25
Sounds good.
| |
76 } | |
77 } | |
78 | |
79 // Force to 16-bit output for now since we know that works everywhere; | |
80 // ChromeOS does not support other bit depths. | |
81 return media::AudioParameters(input_params.format(), | |
82 input_params.channel_layout(), | |
83 output_sample_rate, 16, output_buffer_size); | |
84 } | |
85 | |
86 } // namespace | |
87 | |
19 namespace content { | 88 namespace content { |
20 | 89 |
21 AudioRendererMixerManager::AudioRendererMixerManager( | 90 AudioRendererMixerManager::AudioRendererMixerManager( |
22 std::unique_ptr<AudioRendererSinkCache> sink_cache) | 91 std::unique_ptr<AudioRendererSinkCache> sink_cache) |
23 : sink_cache_(std::move(sink_cache)) { | 92 : sink_cache_(std::move(sink_cache)) { |
24 DCHECK(sink_cache_); | 93 DCHECK(sink_cache_); |
25 } | 94 } |
26 | 95 |
27 AudioRendererMixerManager::~AudioRendererMixerManager() { | 96 AudioRendererMixerManager::~AudioRendererMixerManager() { |
28 // References to AudioRendererMixers may be owned by garbage collected | 97 // References to AudioRendererMixers may be owned by garbage collected |
29 // objects. During process shutdown they may be leaked, so, transitively, | 98 // 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. | 99 // |mixers_| may leak (i.e., may be non-empty at this time) as well. |
31 } | 100 } |
32 | 101 |
33 // static | 102 // static |
34 std::unique_ptr<AudioRendererMixerManager> AudioRendererMixerManager::Create() { | 103 std::unique_ptr<AudioRendererMixerManager> AudioRendererMixerManager::Create() { |
35 return base::WrapUnique( | 104 return base::WrapUnique( |
36 new AudioRendererMixerManager(AudioRendererSinkCache::Create())); | 105 new AudioRendererMixerManager(AudioRendererSinkCache::Create())); |
37 } | 106 } |
38 | 107 |
39 media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput( | 108 media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput( |
40 int source_render_frame_id, | 109 int source_render_frame_id, |
41 int session_id, | 110 int session_id, |
42 const std::string& device_id, | 111 const std::string& device_id, |
43 const url::Origin& security_origin) { | 112 const url::Origin& security_origin, |
113 media::AudioLatency::LatencyType latency) { | |
chcunningham
2016/06/22 04:34:07
Has the spec settled on latency coming in through
o1ka
2016/06/23 16:36:15
Yes, it is passed in AudioContextOptions as a cons
chcunningham
2016/06/27 23:12:25
Acknowledged.
| |
44 // AudioRendererMixerManager lives on the renderer thread and is destroyed on | 114 // 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 | 115 // renderer thread destruction, so it's safe to pass its pointer to a mixer |
46 // input. | 116 // input. |
47 return new media::AudioRendererMixerInput( | 117 return new media::AudioRendererMixerInput( |
48 this, source_render_frame_id, | 118 this, source_render_frame_id, |
49 media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, | 119 media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, |
50 device_id) | 120 device_id) |
51 ? GetOutputDeviceInfo(source_render_frame_id, session_id, device_id, | 121 ? GetOutputDeviceInfo(source_render_frame_id, session_id, device_id, |
52 security_origin) | 122 security_origin) |
53 .device_id() | 123 .device_id() |
54 : device_id, | 124 : device_id, |
55 security_origin); | 125 security_origin, latency); |
56 } | 126 } |
57 | 127 |
58 media::AudioRendererMixer* AudioRendererMixerManager::GetMixer( | 128 media::AudioRendererMixer* AudioRendererMixerManager::GetMixer( |
59 int source_render_frame_id, | 129 int source_render_frame_id, |
60 const media::AudioParameters& params, | 130 const media::AudioParameters& input_params, |
131 media::AudioLatency::LatencyType latency, | |
61 const std::string& device_id, | 132 const std::string& device_id, |
62 const url::Origin& security_origin, | 133 const url::Origin& security_origin, |
63 media::OutputDeviceStatus* device_status) { | 134 media::OutputDeviceStatus* device_status) { |
64 // Effects are not passed through to output creation, so ensure none are set. | 135 // Effects are not passed through to output creation, so ensure none are set. |
65 DCHECK_EQ(params.effects(), media::AudioParameters::NO_EFFECTS); | 136 DCHECK_EQ(input_params.effects(), media::AudioParameters::NO_EFFECTS); |
66 | 137 |
67 const MixerKey key(source_render_frame_id, params, device_id, | 138 const MixerKey key(source_render_frame_id, input_params, latency, device_id, |
68 security_origin); | 139 security_origin); |
69 base::AutoLock auto_lock(mixers_lock_); | 140 base::AutoLock auto_lock(mixers_lock_); |
70 | 141 |
71 AudioRendererMixerMap::iterator it = mixers_.find(key); | 142 AudioRendererMixerMap::iterator it = mixers_.find(key); |
72 if (it != mixers_.end()) { | 143 if (it != mixers_.end()) { |
73 if (device_status) | 144 if (device_status) |
74 *device_status = media::OUTPUT_DEVICE_STATUS_OK; | 145 *device_status = media::OUTPUT_DEVICE_STATUS_OK; |
75 | 146 |
76 it->second.ref_count++; | 147 it->second.ref_count++; |
148 DVLOG(1) << "Reusing mixer: " << it->second.mixer; | |
77 return it->second.mixer; | 149 return it->second.mixer; |
78 } | 150 } |
79 | 151 |
80 scoped_refptr<media::AudioRendererSink> sink = | 152 scoped_refptr<media::AudioRendererSink> sink = |
81 sink_cache_->GetSink(source_render_frame_id, device_id, security_origin); | 153 sink_cache_->GetSink(source_render_frame_id, device_id, security_origin); |
82 | 154 |
83 const media::OutputDeviceInfo& device_info = sink->GetOutputDeviceInfo(); | 155 const media::OutputDeviceInfo& device_info = sink->GetOutputDeviceInfo(); |
84 if (device_status) | 156 if (device_status) |
85 *device_status = device_info.device_status(); | 157 *device_status = device_info.device_status(); |
86 if (device_info.device_status() != media::OUTPUT_DEVICE_STATUS_OK) { | 158 if (device_info.device_status() != media::OUTPUT_DEVICE_STATUS_OK) { |
87 sink_cache_->ReleaseSink(sink.get()); | 159 sink_cache_->ReleaseSink(sink.get()); |
88 sink->Stop(); | 160 sink->Stop(); |
89 return nullptr; | 161 return nullptr; |
90 } | 162 } |
91 | 163 |
92 // On ChromeOS as well as when a fake device is used, we can rely on the | 164 const media::AudioParameters& mixer_output_params = |
93 // playback device to handle resampling, so don't waste cycles on it here. | 165 GetMixerOutputParams(input_params, device_info.output_params(), latency); |
94 int sample_rate = params.sample_rate(); | |
95 int buffer_size = | |
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 = | 166 media::AudioRendererMixer* mixer = |
120 new media::AudioRendererMixer(output_params, sink); | 167 new media::AudioRendererMixer(mixer_output_params, sink); |
121 AudioRendererMixerReference mixer_reference = {mixer, 1, sink.get()}; | 168 AudioRendererMixerReference mixer_reference = {mixer, 1, sink.get()}; |
122 mixers_[key] = mixer_reference; | 169 mixers_[key] = mixer_reference; |
170 DVLOG(1) << "GetMixer: mixer " << mixer << " latency " << latency | |
chcunningham
2016/06/22 04:34:07
nit: use __FUNCTION__.
nit: can you format this l
o1ka
2016/06/23 16:36:15
Done.
| |
171 << "\n input " << input_params.AsHumanReadableString() << "\noutput " | |
172 << mixer_output_params.AsHumanReadableString(); | |
123 return mixer; | 173 return mixer; |
124 } | 174 } |
125 | 175 |
126 void AudioRendererMixerManager::ReturnMixer( | 176 void AudioRendererMixerManager::ReturnMixer( |
127 int source_render_frame_id, | 177 const media::AudioRendererMixer* mixer) { |
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_); | 178 base::AutoLock auto_lock(mixers_lock_); |
134 | 179 AudioRendererMixerMap::iterator it = std::find_if( |
135 AudioRendererMixerMap::iterator it = mixers_.find(key); | 180 mixers_.begin(), mixers_.end(), |
181 [mixer](const std::pair<MixerKey, AudioRendererMixerReference>& val) { | |
182 return val.second.mixer == mixer; | |
183 }); | |
136 DCHECK(it != mixers_.end()); | 184 DCHECK(it != mixers_.end()); |
137 | 185 |
138 // Only remove the mixer if AudioRendererMixerManager is the last owner. | 186 // Only remove the mixer if AudioRendererMixerManager is the last owner. |
139 it->second.ref_count--; | 187 it->second.ref_count--; |
140 if (it->second.ref_count == 0) { | 188 if (it->second.ref_count == 0) { |
141 // The mixer will be deleted now, so release the sink. | 189 // The mixer will be deleted now, so release the sink. |
142 sink_cache_->ReleaseSink(it->second.sink_ptr); | 190 sink_cache_->ReleaseSink(it->second.sink_ptr); |
143 delete it->second.mixer; | 191 delete it->second.mixer; |
144 mixers_.erase(it); | 192 mixers_.erase(it); |
145 } | 193 } |
146 } | 194 } |
147 | 195 |
148 media::OutputDeviceInfo AudioRendererMixerManager::GetOutputDeviceInfo( | 196 media::OutputDeviceInfo AudioRendererMixerManager::GetOutputDeviceInfo( |
149 int source_render_frame_id, | 197 int source_render_frame_id, |
150 int session_id, | 198 int session_id, |
151 const std::string& device_id, | 199 const std::string& device_id, |
152 const url::Origin& security_origin) { | 200 const url::Origin& security_origin) { |
153 return sink_cache_->GetSinkInfo(source_render_frame_id, session_id, device_id, | 201 return sink_cache_->GetSinkInfo(source_render_frame_id, session_id, device_id, |
154 security_origin); | 202 security_origin); |
155 } | 203 } |
156 | 204 |
157 AudioRendererMixerManager::MixerKey::MixerKey( | 205 AudioRendererMixerManager::MixerKey::MixerKey( |
158 int source_render_frame_id, | 206 int source_render_frame_id, |
159 const media::AudioParameters& params, | 207 const media::AudioParameters& params, |
208 media::AudioLatency::LatencyType latency, | |
160 const std::string& device_id, | 209 const std::string& device_id, |
161 const url::Origin& security_origin) | 210 const url::Origin& security_origin) |
162 : source_render_frame_id(source_render_frame_id), | 211 : source_render_frame_id(source_render_frame_id), |
163 params(params), | 212 params(params), |
213 latency(latency), | |
164 device_id(device_id), | 214 device_id(device_id), |
165 security_origin(security_origin) {} | 215 security_origin(security_origin) {} |
166 | 216 |
167 AudioRendererMixerManager::MixerKey::MixerKey(const MixerKey& other) = default; | 217 AudioRendererMixerManager::MixerKey::MixerKey(const MixerKey& other) = default; |
168 | 218 |
169 } // namespace content | 219 } // namespace content |
OLD | NEW |