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