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