Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(513)

Unified Diff: content/renderer/media/audio_renderer_mixer_manager.cc

Issue 2067863003: Mixing audio with different latency requirements (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: android test fix Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/audio_renderer_mixer_manager.cc
diff --git a/content/renderer/media/audio_renderer_mixer_manager.cc b/content/renderer/media/audio_renderer_mixer_manager.cc
index d03e615b66ed55d18cf71de6e970b1366f099656..30f66fb377147e097e9e18905b2fab34c4cc5744 100644
--- a/content/renderer/media/audio_renderer_mixer_manager.cc
+++ b/content/renderer/media/audio_renderer_mixer_manager.cc
@@ -4,11 +4,14 @@
#include "content/renderer/media/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 "content/renderer/media/audio_renderer_sink_cache.h"
#include "media/audio/audio_device_description.h"
@@ -16,6 +19,94 @@
#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.
+ return media::AudioParameters(input_params.format(),
+ input_params.channel_layout(),
+ output_sample_rate, 16, output_buffer_size);
+}
+
+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 content {
AudioRendererMixerManager::AudioRendererMixerManager(
@@ -40,7 +131,8 @@ media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput(
int source_render_frame_id,
int session_id,
const std::string& device_id,
- const url::Origin& security_origin) {
+ 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.
@@ -52,28 +144,43 @@ media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput(
security_origin)
.device_id()
: device_id,
- security_origin);
+ security_origin, latency);
}
media::AudioRendererMixer* AudioRendererMixerManager::GetMixer(
int source_render_frame_id,
- const media::AudioParameters& params,
+ 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(params.effects(), media::AudioParameters::NO_EFFECTS);
+ DCHECK_EQ(input_params.effects(), media::AudioParameters::NO_EFFECTS);
- const MixerKey key(source_render_frame_id, params, device_id,
+ 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;
}
@@ -89,50 +196,25 @@ media::AudioRendererMixer* AudioRendererMixerManager::GetMixer(
return nullptr;
}
- // 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.
- int sample_rate = params.sample_rate();
- int buffer_size =
- media::AudioHardwareConfig::GetHighLatencyBufferSize(sample_rate, 0);
-
-#if !defined(OS_CHROMEOS)
- const media::AudioParameters& hardware_params = device_info.output_params();
-
- // If we have valid, non-fake hardware parameters, use them. Otherwise, pass
- // on the input params and let the browser side handle automatic fallback.
- if (hardware_params.format() != media::AudioParameters::AUDIO_FAKE &&
- hardware_params.IsValid()) {
- sample_rate = hardware_params.sample_rate();
- buffer_size = media::AudioHardwareConfig::GetHighLatencyBufferSize(
- sample_rate, hardware_params.frames_per_buffer());
- }
-#endif
-
- // Create output parameters based on the audio hardware configuration for
- // passing on to the output sink. Force to 16-bit output for now since we
- // know that works everywhere; ChromeOS does not support other bit depths.
- media::AudioParameters output_params(
- media::AudioParameters::AUDIO_PCM_LOW_LATENCY, params.channel_layout(),
- sample_rate, 16, buffer_size);
- DCHECK(output_params.IsValid());
-
- media::AudioRendererMixer* mixer =
- new media::AudioRendererMixer(output_params, sink);
+ 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) << __FUNCTION__ << " mixer: " << mixer << " latency: " << latency
+ << "\n input: " << input_params.AsHumanReadableString()
+ << "\noutput: " << mixer_output_params.AsHumanReadableString();
return mixer;
}
-void AudioRendererMixerManager::ReturnMixer(
- int source_render_frame_id,
- const media::AudioParameters& params,
- const std::string& device_id,
- const url::Origin& security_origin) {
- const MixerKey key(source_render_frame_id, params, device_id,
- security_origin);
+void AudioRendererMixerManager::ReturnMixer(media::AudioRendererMixer* mixer) {
base::AutoLock auto_lock(mixers_lock_);
-
- AudioRendererMixerMap::iterator it = mixers_.find(key);
+ 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.
@@ -157,10 +239,12 @@ media::OutputDeviceInfo AudioRendererMixerManager::GetOutputDeviceInfo(
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) {}
« no previous file with comments | « content/renderer/media/audio_renderer_mixer_manager.h ('k') | content/renderer/media/audio_renderer_mixer_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698