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
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/logging.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/metrics/histogram_functions.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "build/build_config.h"
19 #include "media/audio/audio_device_description.h"
20 #include "media/base/audio_renderer_mixer.h"
21 #include "media/base/audio_renderer_mixer_input.h"
22 #include "third_party/blink/public/web/modules/media/audio/web_audio_device_factory.h"
23 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
24
25 namespace {
26
27 // Calculate mixer output parameters based on mixer input parameters and
28 // hardware parameters for audio output.
GetMixerOutputParams(const media::AudioParameters & input_params,const media::AudioParameters & hardware_params,media::AudioLatency::LatencyType latency)29 media::AudioParameters GetMixerOutputParams(
30 const media::AudioParameters& input_params,
31 const media::AudioParameters& hardware_params,
32 media::AudioLatency::LatencyType latency) {
33 // For a compressed bitstream, no audio post processing is allowed, hence the
34 // output parameters should be the same as input parameters.
35 if (input_params.IsBitstreamFormat())
36 return input_params;
37
38 int output_sample_rate, preferred_output_buffer_size;
39 if (!hardware_params.IsValid() ||
40 hardware_params.format() == media::AudioParameters::AUDIO_FAKE) {
41 // With fake or invalid hardware params, don't waste cycles on resampling.
42 output_sample_rate = input_params.sample_rate();
43 preferred_output_buffer_size = 0; // Let media::AudioLatency() choose.
44 } else if (media::AudioLatency::IsResamplingPassthroughSupported(latency)) {
45 // Certain platforms don't require us to resample to a single rate for low
46 // latency, so again, don't waste cycles on resampling.
47 output_sample_rate = input_params.sample_rate();
48
49 // For playback, prefer the input params buffer size unless the hardware
50 // needs something even larger (say for Bluetooth devices).
51 if (latency == media::AudioLatency::LATENCY_PLAYBACK) {
52 preferred_output_buffer_size =
53 std::max(input_params.frames_per_buffer(),
54 hardware_params.frames_per_buffer());
55 } else {
56 preferred_output_buffer_size = hardware_params.frames_per_buffer();
57 }
58 } else {
59 // Otherwise, always resample and rebuffer to the hardware parameters.
60 output_sample_rate = hardware_params.sample_rate();
61 preferred_output_buffer_size = hardware_params.frames_per_buffer();
62 }
63
64 int output_buffer_size = 0;
65
66 // Adjust output buffer size according to the latency requirement.
67 switch (latency) {
68 case media::AudioLatency::LATENCY_INTERACTIVE:
69 output_buffer_size = media::AudioLatency::GetInteractiveBufferSize(
70 hardware_params.frames_per_buffer());
71 break;
72 case media::AudioLatency::LATENCY_RTC:
73 output_buffer_size = media::AudioLatency::GetRtcBufferSize(
74 output_sample_rate, preferred_output_buffer_size);
75 break;
76 case media::AudioLatency::LATENCY_PLAYBACK:
77 output_buffer_size = media::AudioLatency::GetHighLatencyBufferSize(
78 output_sample_rate, preferred_output_buffer_size);
79 break;
80 case media::AudioLatency::LATENCY_EXACT_MS:
81 // TODO(olka): add support when WebAudio requires it.
82 default:
83 NOTREACHED();
84 }
85
86 DCHECK_NE(output_buffer_size, 0);
87
88 media::AudioParameters params(input_params.format(),
89 input_params.channel_layout(),
90 output_sample_rate, output_buffer_size);
91
92 // Use the actual channel count when the channel layout is "DISCRETE".
93 if (input_params.channel_layout() == media::CHANNEL_LAYOUT_DISCRETE)
94 params.set_channels_for_discrete(input_params.channels());
95
96 // Specify the effects info the passed to the browser side.
97 params.set_effects(input_params.effects());
98
99 // Specify the latency info to be passed to the browser side.
100 params.set_latency_tag(latency);
101 return params;
102 }
103
104 } // namespace
105
106 namespace blink {
107
AudioRendererMixerManager(CreateSinkCB create_sink_cb)108 AudioRendererMixerManager::AudioRendererMixerManager(
109 CreateSinkCB create_sink_cb)
110 : create_sink_cb_(std::move(create_sink_cb)) {
111 DCHECK(create_sink_cb_);
112 }
113
~AudioRendererMixerManager()114 AudioRendererMixerManager::~AudioRendererMixerManager() {
115 // References to AudioRendererMixers may be owned by garbage collected
116 // objects. During process shutdown they may be leaked, so, transitively,
117 // |mixers_| may leak (i.e., may be non-empty at this time) as well.
118 }
119
120 // static
GetInstance()121 AudioRendererMixerManager& AudioRendererMixerManager::GetInstance() {
122 DEFINE_THREAD_SAFE_STATIC_LOCAL(
123 AudioRendererMixerManager, instance,
124 (base::BindRepeating(&WebAudioDeviceFactory::NewAudioRendererMixerSink)));
125 return instance;
126 }
127
128 scoped_refptr<media::AudioRendererMixerInput>
CreateInput(const blink::LocalFrameToken & source_frame_token,const base::UnguessableToken & session_id,const std::string & device_id,media::AudioLatency::LatencyType latency)129 AudioRendererMixerManager::CreateInput(
130 const blink::LocalFrameToken& source_frame_token,
131 const base::UnguessableToken& session_id,
132 const std::string& device_id,
133 media::AudioLatency::LatencyType latency) {
134 // AudioRendererMixerManager lives on the renderer thread and is destroyed on
135 // renderer thread destruction, so it's safe to pass its pointer to a mixer
136 // input.
137 //
138 // TODO(olka, grunell): |session_id| is always empty, delete since
139 // NewAudioRenderingMixingStrategy didn't ship, https://crbug.com/870836.
140 DCHECK(session_id.is_empty());
141 return base::MakeRefCounted<media::AudioRendererMixerInput>(
142 this, source_frame_token.value(), device_id, latency);
143 }
144
GetMixer(const blink::LocalFrameToken & source_frame_token,const media::AudioParameters & input_params,media::AudioLatency::LatencyType latency,const media::OutputDeviceInfo & sink_info,scoped_refptr<media::AudioRendererSink> sink)145 media::AudioRendererMixer* AudioRendererMixerManager::GetMixer(
146 const blink::LocalFrameToken& source_frame_token,
147 const media::AudioParameters& input_params,
148 media::AudioLatency::LatencyType latency,
149 const media::OutputDeviceInfo& sink_info,
150 scoped_refptr<media::AudioRendererSink> sink) {
151 // Ownership of the sink must be given to GetMixer().
152 DCHECK(sink->HasOneRef());
153 DCHECK_EQ(sink_info.device_status(), media::OUTPUT_DEVICE_STATUS_OK);
154
155 const MixerKey key(source_frame_token, input_params, latency,
156 sink_info.device_id());
157 base::AutoLock auto_lock(mixers_lock_);
158
159 auto it = mixers_.find(key);
160 if (it != mixers_.end()) {
161 auto new_count = ++it->second.ref_count;
162 CHECK(new_count != std::numeric_limits<decltype(new_count)>::max());
163
164 DVLOG(1) << "Reusing mixer: " << it->second.mixer;
165
166 // Sink will now be released unused, but still must be stopped.
167 //
168 // TODO(dalecurtis): Is it worth caching this sink instead for a future
169 // GetSink() call? We should experiment with a few top sites. We can't just
170 // drop in AudioRendererSinkCache here since it doesn't reuse sinks once
171 // they've been vended externally to the class.
172 sink->Stop();
173
174 return it->second.mixer;
175 }
176
177 const media::AudioParameters& mixer_output_params =
178 GetMixerOutputParams(input_params, sink_info.output_params(), latency);
179 media::AudioRendererMixer* mixer =
180 new media::AudioRendererMixer(mixer_output_params, std::move(sink));
181 mixers_[key] = {mixer, 1};
182 DVLOG(1) << __func__ << " mixer: " << mixer << " latency: " << latency
183 << "\n input: " << input_params.AsHumanReadableString()
184 << "\noutput: " << mixer_output_params.AsHumanReadableString();
185 return mixer;
186 }
187
GetSink(const blink::LocalFrameToken & source_frame_token,const std::string & device_id)188 scoped_refptr<media::AudioRendererSink> AudioRendererMixerManager::GetSink(
189 const blink::LocalFrameToken& source_frame_token,
190 const std::string& device_id) {
191 return create_sink_cb_.Run(
192 source_frame_token,
193 media::AudioSinkParameters(base::UnguessableToken(), device_id));
194 }
195
GetMixer(const base::UnguessableToken & source_frame_token,const media::AudioParameters & input_params,media::AudioLatency::LatencyType latency,const media::OutputDeviceInfo & sink_info,scoped_refptr<media::AudioRendererSink> sink)196 media::AudioRendererMixer* AudioRendererMixerManager::GetMixer(
197 const base::UnguessableToken& source_frame_token,
198 const media::AudioParameters& input_params,
199 media::AudioLatency::LatencyType latency,
200 const media::OutputDeviceInfo& sink_info,
201 scoped_refptr<media::AudioRendererSink> sink) {
202 // Ownership of the sink must be given to GetMixer().
203 DCHECK(sink->HasOneRef());
204 // Forward to the strongly typed version. We move the |sink| as GetMixer
205 // expects to be the sole owner at this point.
206 DCHECK(source_frame_token);
207 return GetMixer(blink::LocalFrameToken(source_frame_token), input_params,
208 latency, sink_info, std::move(sink));
209 }
210
ReturnMixer(media::AudioRendererMixer * mixer)211 void AudioRendererMixerManager::ReturnMixer(media::AudioRendererMixer* mixer) {
212 base::AutoLock auto_lock(mixers_lock_);
213 auto it = std::find_if(
214 mixers_.begin(), mixers_.end(),
215 [mixer](const std::pair<MixerKey, AudioRendererMixerReference>& val) {
216 return val.second.mixer == mixer;
217 });
218 DCHECK(it != mixers_.end());
219
220 // Only remove the mixer if AudioRendererMixerManager is the last owner.
221 it->second.ref_count--;
222 if (it->second.ref_count == 0) {
223 delete it->second.mixer;
224 mixers_.erase(it);
225 }
226 }
227
GetSink(const base::UnguessableToken & source_frame_token,const std::string & device_id)228 scoped_refptr<media::AudioRendererSink> AudioRendererMixerManager::GetSink(
229 const base::UnguessableToken& source_frame_token,
230 const std::string& device_id) {
231 // Forward to the strongly typed version.
232 DCHECK(source_frame_token);
233 return GetSink(blink::LocalFrameToken(source_frame_token), device_id);
234 }
235
MixerKey(const blink::LocalFrameToken & source_frame_token,const media::AudioParameters & params,media::AudioLatency::LatencyType latency,const std::string & device_id)236 AudioRendererMixerManager::MixerKey::MixerKey(
237 const blink::LocalFrameToken& source_frame_token,
238 const media::AudioParameters& params,
239 media::AudioLatency::LatencyType latency,
240 const std::string& device_id)
241 : source_frame_token(source_frame_token),
242 params(params),
243 latency(latency),
244 device_id(device_id) {}
245
246 AudioRendererMixerManager::MixerKey::MixerKey(const MixerKey& other) = default;
247
248 AudioRendererMixerManager::MixerKey::~MixerKey() = default;
249
250 } // namespace blink
251