Skip to content

Commit

Permalink
Convert <audio> pipeline to use async device info requests.
Browse files Browse the repository at this point in the history
This is part 4/4 CLs to move the <audio>/<video> elements off of
a synchronous API that can lead to renderer hangs and premature
audio renderer errors.

This changes the AudioRendererMixerPool API to require an
AudioRendererSink and OutputDeviceInfo when providing a mixer.
AudioRendererMixerInputs are subsequently changed to use the new
API.

Likewise AudioRendererImpl also now uses the asynchronous API. To
simplify the async process, AudioRendererMixerInputs will only setup
correctly when OutputDeviceInfo has been requested ahead of time,
since that's the pattern that AudioRendererImpl will use.

This also moves the NullAudioSink setup from WebAudioSourceProvider
over to the AudioRendererImpl. This causes WebAudio to be disconnected
from the element, but if audio isn't work anyways, it shouldn't matter.

BUG=905506
TEST=updated tests, compiles, runs.
R=olka

Change-Id: I4edf89bb1e20cc91191a6eb97a0e38b6aeba68f8
Reviewed-on: https://chromium-review.googlesource.com/c/1347795
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Jesse Doherty <jwd@chromium.org>
Reviewed-by: Olga Sharonova <olka@chromium.org>
Reviewed-by: Chrome Cunningham <chcunningham@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612526}
  • Loading branch information
dalecurtis authored and Commit Bot committed Nov 30, 2018
1 parent aedea6f commit 41607b5
Show file tree
Hide file tree
Showing 20 changed files with 460 additions and 481 deletions.
12 changes: 6 additions & 6 deletions content/renderer/media/audio/audio_device_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,9 @@ scoped_refptr<media::SwitchableAudioRendererSink> NewMixableSink(
DCHECK(render_thread) << "RenderThreadImpl is not instantiated, or "
<< "GetOutputDeviceInfo() is called on a wrong thread ";
DCHECK(!params.processing_id.has_value());
return scoped_refptr<media::AudioRendererMixerInput>(
render_thread->GetAudioRendererMixerManager()->CreateInput(
render_frame_id, params.session_id, params.device_id,
AudioDeviceFactory::GetSourceLatencyType(source_type)));
return render_thread->GetAudioRendererMixerManager()->CreateInput(
render_frame_id, params.session_id, params.device_id,
AudioDeviceFactory::GetSourceLatencyType(source_type));
}

} // namespace
Expand Down Expand Up @@ -110,8 +109,9 @@ scoped_refptr<media::AudioRendererSink>
AudioDeviceFactory::NewAudioRendererMixerSink(
int render_frame_id,
const media::AudioSinkParameters& params) {
return NewFinalAudioRendererSink(render_frame_id, params,
GetDefaultAuthTimeout());
// AudioRendererMixer sinks are always used asynchronously and thus can
// operate without a timeout value.
return NewFinalAudioRendererSink(render_frame_id, params, base::TimeDelta());
}

// static
Expand Down
8 changes: 5 additions & 3 deletions content/renderer/media/audio/audio_device_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ class CONTENT_EXPORT AudioDeviceFactory {
static media::AudioLatency::LatencyType GetSourceLatencyType(
SourceType source);

// Creates a sink for AudioRendererMixer.
// |render_frame_id| refers to the RenderFrame containing the entity
// producing the audio.
// Creates a sink for AudioRendererMixer. |render_frame_id| refers to the
// RenderFrame containing the entity producing the audio. Note: These sinks do
// not support the blocking GetOutputDeviceInfo() API and instead clients are
// required to use the GetOutputDeviceInfoAsync() API. As such they are
// configured with no authorization timeout value.
static scoped_refptr<media::AudioRendererSink> NewAudioRendererMixerSink(
int render_frame_id,
const media::AudioSinkParameters& params);
Expand Down
62 changes: 28 additions & 34 deletions content/renderer/media/audio/audio_renderer_mixer_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -147,31 +147,35 @@ std::unique_ptr<AudioRendererMixerManager> AudioRendererMixerManager::Create() {
base::BindRepeating(&AudioDeviceFactory::NewAudioRendererMixerSink)));
}

media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput(
scoped_refptr<media::AudioRendererMixerInput>
AudioRendererMixerManager::CreateInput(
int source_render_frame_id,
int session_id,
const std::string& device_id,
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)
.device_id()
: device_id,
latency);
//
// TODO(olka, grunell): |session_id| is always zero, delete since
// NewAudioRenderingMixingStrategy didn't ship, https://crbug.com/870836.
DCHECK_EQ(session_id, 0);
return base::MakeRefCounted<media::AudioRendererMixerInput>(
this, source_render_frame_id, device_id, latency);
}

media::AudioRendererMixer* AudioRendererMixerManager::GetMixer(
int source_render_frame_id,
const media::AudioParameters& input_params,
media::AudioLatency::LatencyType latency,
const std::string& device_id,
media::OutputDeviceStatus* device_status) {
const MixerKey key(source_render_frame_id, input_params, latency, device_id);
const media::OutputDeviceInfo& sink_info,
scoped_refptr<media::AudioRendererSink> sink) {
// Ownership of the sink must be given to GetMixer().
DCHECK(sink->HasOneRef());
DCHECK_EQ(sink_info.device_status(), media::OUTPUT_DEVICE_STATUS_OK);

const MixerKey key(source_render_frame_id, input_params, latency,
sink_info.device_id());
base::AutoLock auto_lock(mixers_lock_);

// Update latency map when the mixer is requested, i.e. there is an attempt to
Expand All @@ -189,27 +193,22 @@ media::AudioRendererMixer* AudioRendererMixerManager::GetMixer(

auto 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 = create_sink_cb_.Run(
source_render_frame_id, media::AudioSinkParameters(0, device_id));

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 will now be released unused, but still must be stopped.
//
// TODO(dalecurtis): Is it worth caching this sink instead for a future
// GetSink() call? We should experiment with a few top sites. We can't just
// drop in AudioRendererSinkCache here since it doesn't reuse sinks once
// they've been vended externally to the class.
sink->Stop();
return nullptr;

return it->second.mixer;
}

const media::AudioParameters& mixer_output_params =
GetMixerOutputParams(input_params, device_info.output_params(), latency);
GetMixerOutputParams(input_params, sink_info.output_params(), latency);
media::AudioRendererMixer* mixer = new media::AudioRendererMixer(
mixer_output_params, std::move(sink),
base::BindRepeating(LogMixerUmaHistogram, latency));
Expand Down Expand Up @@ -237,16 +236,11 @@ void AudioRendererMixerManager::ReturnMixer(media::AudioRendererMixer* mixer) {
}
}

media::OutputDeviceInfo AudioRendererMixerManager::GetOutputDeviceInfo(
scoped_refptr<media::AudioRendererSink> AudioRendererMixerManager::GetSink(
int source_render_frame_id,
int session_id,
const std::string& device_id) {
auto sink =
create_sink_cb_.Run(source_render_frame_id,
media::AudioSinkParameters(session_id, device_id));
auto info = sink->GetOutputDeviceInfo();
sink->Stop();
return info;
return create_sink_cb_.Run(source_render_frame_id,
media::AudioSinkParameters(0, device_id));
}

AudioRendererMixerManager::MixerKey::MixerKey(
Expand Down
9 changes: 4 additions & 5 deletions content/renderer/media/audio/audio_renderer_mixer_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class CONTENT_EXPORT AudioRendererMixerManager
// device to use. If |device_id| is empty and |session_id| is nonzero,
// output device associated with the opened input device designated by
// |session_id| is used. Otherwise, |session_id| is ignored.
media::AudioRendererMixerInput* CreateInput(
scoped_refptr<media::AudioRendererMixerInput> CreateInput(
int source_render_frame_id,
int session_id,
const std::string& device_id,
Expand All @@ -65,14 +65,13 @@ class CONTENT_EXPORT AudioRendererMixerManager
int source_render_frame_id,
const media::AudioParameters& input_params,
media::AudioLatency::LatencyType latency,
const std::string& device_id,
media::OutputDeviceStatus* device_status) final;
const media::OutputDeviceInfo& sink_info,
scoped_refptr<media::AudioRendererSink> sink) final;

void ReturnMixer(media::AudioRendererMixer* mixer) final;

media::OutputDeviceInfo GetOutputDeviceInfo(
scoped_refptr<media::AudioRendererSink> GetSink(
int source_render_frame_id,
int session_id,
const std::string& device_id) final;

protected:
Expand Down
Loading

0 comments on commit 41607b5

Please sign in to comment.