Commit d6514607 authored by Dale Curtis's avatar Dale Curtis Committed by Commit Bot

Add AudioRendererSink::GetOutputDeviceInfoAsync() API.

This is part 1/4 CLs to move the <audio>/<video> elements off of
a synchronous API that can lead to renderer hangs and premature
audio renderer errors.

BUG=905506
TEST=updated tests, compiles.

Change-Id: I9b26ed5ed8faea2f691b3a415dbdc79e18f5125a
Reviewed-on: https://chromium-review.googlesource.com/c/1347588
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarOlga Sharonova <olka@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612330}
parent 0b9928ad
......@@ -15,6 +15,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
#include "base/timer/timer.h"
#include "base/trace_event/trace_event.h"
......@@ -22,6 +23,7 @@
#include "media/audio/audio_device_description.h"
#include "media/audio/audio_output_controller.h"
#include "media/audio/audio_output_device_thread_callback.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/limits.h"
namespace media {
......@@ -64,6 +66,17 @@ void AudioOutputDevice::InitializeOnIOThread(const AudioParameters& params,
}
AudioOutputDevice::~AudioOutputDevice() {
{
// Abort any pending callbacks. Technically we don't need to acquire the
// lock here since ther eshould be no other calls outstanding, but because
// we've used the GUARDED_BY compiler syntax, we'll get an error without it.
base::AutoLock auto_lock(device_info_lock_);
if (pending_device_info_cb_) {
std::move(pending_device_info_cb_)
.Run(OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL));
}
}
#if DCHECK_IS_ON()
// Make sure we've stopped the stream properly before destructing |this|.
DCHECK(audio_thread_lock_.Try());
......@@ -97,7 +110,6 @@ void AudioOutputDevice::Stop() {
audio_thread_.reset();
stopping_hack_ = true;
}
io_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AudioOutputDevice::ShutDownOnIOThread, this));
}
......@@ -128,13 +140,29 @@ bool AudioOutputDevice::SetVolume(double volume) {
OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() {
TRACE_EVENT0("audio", "AudioOutputDevice::GetOutputDeviceInfo");
DCHECK(!io_task_runner_->BelongsToCurrentThread());
did_receive_auth_.Wait();
return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice(
session_id_, device_id_)
? matched_device_id_
: device_id_,
device_status_, output_params_);
return GetOutputDeviceInfo_Signaled();
}
void AudioOutputDevice::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
{
// Hold the lock while checking the signal and setting the pending callback
// to avoid racing with authorization completion on the IO thread.
base::AutoLock auto_lock(device_info_lock_);
if (!did_receive_auth_.IsSignaled()) {
DCHECK(!pending_device_info_cb_);
pending_device_info_cb_ = BindToCurrentLoop(std::move(info_cb));
return;
}
}
// Always post to avoid the caller being reentrant. Local testing shows even
// on a powerful desktop, we haven't received device authorization by this
// point when AOD construction and GetOutputDeviceInfoAsync() happen back to
// back (which is the most common use case).
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(info_cb), GetOutputDeviceInfo_Signaled()));
}
bool AudioOutputDevice::IsOptimizedForHardwareParameters() {
......@@ -307,7 +335,7 @@ void AudioOutputDevice::OnDeviceAuthorized(
<< ", device_id: " << device_id_
<< ", matched_device_id: " << matched_device_id_;
did_receive_auth_.Signal();
OnAuthSignal();
}
} else {
TRACE_EVENT1("audio", "AudioOutputDevice not authorized", "auth status",
......@@ -378,8 +406,39 @@ void AudioOutputDevice::OnIPCClosed() {
ipc_.reset();
state_ = IDLE;
// Signal to unblock any blocked threads waiting for parameters
OnAuthSignal();
}
OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo_Signaled() {
DCHECK(did_receive_auth_.IsSignaled());
return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice(
session_id_, device_id_)
? matched_device_id_
: device_id_,
device_status_, output_params_);
}
void AudioOutputDevice::OnAuthSignal() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
// This lock is held while signaling to avoid any thread safety issues while
// GetOutputDeviceInfoAsync() may be checking the signal and modifying the
// |pending_device_info_cb_| on another thread.
//
// We might be able to get away with signaling outside of the lock, but this
// requires more careful construction for anyone checking the signal and
// using the result to set or get the pending callback value. The failure
// mode is also more subtle, callbacks will be lost versus a thread hang which
// is more easily detectable in the production population.
base::AutoLock auto_lock(device_info_lock_);
// Signal to unblock any blocked threads waiting for parameters.
did_receive_auth_.Signal();
// The callback is always posted by way media::BindToCurrentLoop() usage upon
// receipt, so this is safe to run under the lock.
if (pending_device_info_cb_)
std::move(pending_device_info_cb_).Run(GetOutputDeviceInfo_Signaled());
}
void AudioOutputDevice::NotifyRenderCallbackOfError() {
......
......@@ -110,6 +110,7 @@ class MEDIA_EXPORT AudioOutputDevice : public AudioRendererSink,
void Pause() override;
bool SetVolume(double volume) override;
OutputDeviceInfo GetOutputDeviceInfo() override;
void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
bool IsOptimizedForHardwareParameters() override;
bool CurrentThreadIsRenderingThread() override;
......@@ -171,6 +172,9 @@ class MEDIA_EXPORT AudioOutputDevice : public AudioRendererSink,
void NotifyRenderCallbackOfError();
OutputDeviceInfo GetOutputDeviceInfo_Signaled();
void OnAuthSignal();
const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
AudioParameters audio_parameters_;
......@@ -226,6 +230,14 @@ class MEDIA_EXPORT AudioOutputDevice : public AudioRendererSink,
const base::TimeDelta auth_timeout_;
std::unique_ptr<base::OneShotTimer> auth_timeout_action_;
// Pending callback for OutputDeviceInfo if it has not been received by the
// time a call to GetGetOutputDeviceInfoAsync() is called.
//
// Lock for use ONLY with |pending_device_info_cb_| and |did_receive_auth_|,
// if you add more usage of this lock ensure you have not added a deadlock.
base::Lock device_info_lock_;
OutputDeviceInfoCB pending_device_info_cb_ GUARDED_BY(device_info_lock_);
DISALLOW_COPY_AND_ASSIGN(AudioOutputDevice);
};
......
......@@ -99,9 +99,11 @@ class AudioOutputDeviceTest : public testing::Test {
void StartAudioDevice();
void CallOnStreamCreated();
void StopAudioDevice();
void CreateDevice(const std::string& device_id);
void CreateDevice(const std::string& device_id,
base::TimeDelta timeout = kAuthTimeout);
void SetDevice(const std::string& device_id);
void CheckDeviceStatus(OutputDeviceStatus device_status);
MOCK_METHOD1(OnDeviceInfoReceived, void(OutputDeviceInfo));
protected:
base::test::ScopedTaskEnvironment task_env_{
......@@ -134,7 +136,8 @@ AudioOutputDeviceTest::~AudioOutputDeviceTest() {
audio_device_ = nullptr;
}
void AudioOutputDeviceTest::CreateDevice(const std::string& device_id) {
void AudioOutputDeviceTest::CreateDevice(const std::string& device_id,
base::TimeDelta timeout) {
// Make sure the previous device is properly cleaned up.
if (audio_device_)
StopAudioDevice();
......@@ -142,7 +145,7 @@ void AudioOutputDeviceTest::CreateDevice(const std::string& device_id) {
audio_output_ipc_ = new NiceMock<MockAudioOutputIPC>();
audio_device_ = new AudioOutputDevice(
base::WrapUnique(audio_output_ipc_), task_env_.GetMainThreadTaskRunner(),
AudioSinkParameters(0, device_id), kAuthTimeout);
AudioSinkParameters(0, device_id), timeout);
}
void AudioOutputDeviceTest::SetDevice(const std::string& device_id) {
......@@ -163,11 +166,6 @@ void AudioOutputDeviceTest::SetDevice(const std::string& device_id) {
&callback_);
}
void AudioOutputDeviceTest::CheckDeviceStatus(OutputDeviceStatus status) {
DCHECK(!task_env_.GetMainThreadTaskRunner()->BelongsToCurrentThread());
EXPECT_EQ(status, audio_device_->GetOutputDeviceInfo().device_status());
}
void AudioOutputDeviceTest::ReceiveAuthorization(OutputDeviceStatus status) {
device_status_ = status;
if (device_status_ != OUTPUT_DEVICE_STATUS_OK)
......@@ -333,6 +331,57 @@ TEST_F(AudioOutputDeviceTest, AuthorizationTimedOut) {
task_env_.FastForwardBy(base::TimeDelta());
}
TEST_F(AudioOutputDeviceTest, GetOutputDeviceInfoAsync_Error) {
CreateDevice(kUnauthorizedDeviceId, base::TimeDelta());
EXPECT_CALL(*audio_output_ipc_,
RequestDeviceAuthorization(audio_device_.get(), 0,
kUnauthorizedDeviceId));
audio_device_->RequestDeviceAuthorization();
audio_device_->GetOutputDeviceInfoAsync(base::BindOnce(
&AudioOutputDeviceTest::OnDeviceInfoReceived, base::Unretained(this)));
task_env_.FastForwardBy(base::TimeDelta());
OutputDeviceInfo info;
constexpr auto kExpectedStatus = OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED;
EXPECT_CALL(*this, OnDeviceInfoReceived(_))
.WillOnce(testing::SaveArg<0>(&info));
ReceiveAuthorization(kExpectedStatus);
task_env_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(kExpectedStatus, info.device_status());
EXPECT_EQ(kUnauthorizedDeviceId, info.device_id());
EXPECT_TRUE(
AudioParameters::UnavailableDeviceParams().Equals(info.output_params()));
audio_device_->Stop();
task_env_.FastForwardBy(base::TimeDelta());
}
TEST_F(AudioOutputDeviceTest, GetOutputDeviceInfoAsync_Okay) {
CreateDevice(kDefaultDeviceId, base::TimeDelta());
EXPECT_CALL(
*audio_output_ipc_,
RequestDeviceAuthorization(audio_device_.get(), 0, kDefaultDeviceId));
audio_device_->RequestDeviceAuthorization();
audio_device_->GetOutputDeviceInfoAsync(base::BindOnce(
&AudioOutputDeviceTest::OnDeviceInfoReceived, base::Unretained(this)));
task_env_.FastForwardBy(base::TimeDelta());
OutputDeviceInfo info;
constexpr auto kExpectedStatus = OUTPUT_DEVICE_STATUS_OK;
EXPECT_CALL(*this, OnDeviceInfoReceived(_))
.WillOnce(testing::SaveArg<0>(&info));
ReceiveAuthorization(kExpectedStatus);
task_env_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(kExpectedStatus, info.device_status());
EXPECT_EQ(kDefaultDeviceId, info.device_id());
EXPECT_TRUE(default_audio_parameters_.Equals(info.output_params()));
audio_device_->Stop();
task_env_.FastForwardBy(base::TimeDelta());
}
namespace {
// This struct collects useful stuff without doing anything magical. It is used
......
......@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "media/audio/audio_manager.h"
#include "media/base/audio_timestamp_helper.h"
......@@ -77,7 +78,13 @@ bool AudioOutputStreamSink::SetVolume(double volume) {
}
OutputDeviceInfo AudioOutputStreamSink::GetOutputDeviceInfo() {
return OutputDeviceInfo();
return OutputDeviceInfo(OUTPUT_DEVICE_STATUS_OK);
}
void AudioOutputStreamSink::GetOutputDeviceInfoAsync(
OutputDeviceInfoCB info_cb) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(info_cb), GetOutputDeviceInfo()));
}
bool AudioOutputStreamSink::IsOptimizedForHardwareParameters() {
......
......@@ -42,6 +42,7 @@ class MEDIA_EXPORT AudioOutputStreamSink
void Play() override;
bool SetVolume(double volume) override;
OutputDeviceInfo GetOutputDeviceInfo() override;
void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
bool IsOptimizedForHardwareParameters() override;
bool CurrentThreadIsRenderingThread() override;
......
......@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/simple_thread.h"
#include "media/base/audio_hash.h"
......@@ -47,27 +48,27 @@ class ClocklessAudioSinkThread : public base::DelegateSimpleThread::Delegate {
}
private:
// Call Render() repeatedly, keeping track of the rendering time.
// Call Render() repeatedly, keeping track of the rendering time.
void Run() override {
base::TimeTicks start;
while (!stop_event_->IsSignaled()) {
const int frames_received = callback_->Render(
base::TimeDelta(), base::TimeTicks::Now(), 0, audio_bus_.get());
DCHECK_GE(frames_received, 0);
if (audio_hash_)
audio_hash_->Update(audio_bus_.get(), frames_received);
if (!frames_received) {
// No data received, so let other threads run to provide data.
base::PlatformThread::YieldCurrentThread();
} else if (start.is_null()) {
// First time we processed some audio, so record the starting time.
start = base::TimeTicks::Now();
} else {
// Keep track of the last time data was rendered.
playback_time_ = base::TimeTicks::Now() - start;
}
}
}
base::TimeTicks start;
while (!stop_event_->IsSignaled()) {
const int frames_received = callback_->Render(
base::TimeDelta(), base::TimeTicks::Now(), 0, audio_bus_.get());
DCHECK_GE(frames_received, 0);
if (audio_hash_)
audio_hash_->Update(audio_bus_.get(), frames_received);
if (!frames_received) {
// No data received, so let other threads run to provide data.
base::PlatformThread::YieldCurrentThread();
} else if (start.is_null()) {
// First time we processed some audio, so record the starting time.
start = base::TimeTicks::Now();
} else {
// Keep track of the last time data was rendered.
playback_time_ = base::TimeTicks::Now() - start;
}
}
}
AudioRendererSink::RenderCallback* callback_;
std::unique_ptr<AudioBus> audio_bus_;
......@@ -135,6 +136,11 @@ OutputDeviceInfo ClocklessAudioSink::GetOutputDeviceInfo() {
return device_info_;
}
void ClocklessAudioSink::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(info_cb), device_info_));
}
bool ClocklessAudioSink::IsOptimizedForHardwareParameters() {
return is_optimized_for_hw_params_;
}
......
......@@ -31,6 +31,7 @@ class MEDIA_EXPORT ClocklessAudioSink : public AudioRendererSink {
void Play() override;
bool SetVolume(double volume) override;
OutputDeviceInfo GetOutputDeviceInfo() override;
void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
bool IsOptimizedForHardwareParameters() override;
bool CurrentThreadIsRenderingThread() override;
......
......@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "media/base/audio_hash.h"
#include "media/base/fake_audio_worker.h"
......@@ -79,6 +80,11 @@ OutputDeviceInfo NullAudioSink::GetOutputDeviceInfo() {
return OutputDeviceInfo(OUTPUT_DEVICE_STATUS_OK);
}
void NullAudioSink::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(info_cb), GetOutputDeviceInfo()));
}
bool NullAudioSink::IsOptimizedForHardwareParameters() {
return false;
}
......
......@@ -34,6 +34,7 @@ class MEDIA_EXPORT NullAudioSink : public SwitchableAudioRendererSink {
void Play() override;
bool SetVolume(double volume) override;
OutputDeviceInfo GetOutputDeviceInfo() override;
void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
bool IsOptimizedForHardwareParameters() override;
bool CurrentThreadIsRenderingThread() override;
void SwitchOutputDevice(const std::string& device_id,
......
......@@ -21,14 +21,9 @@ AudioRendererMixerInput::AudioRendererMixerInput(
const std::string& device_id,
AudioLatency::LatencyType latency)
: mixer_pool_(mixer_pool),
started_(false),
playing_(false),
volume_(1.0f),
owner_id_(owner_id),
device_id_(device_id),
latency_(latency),
mixer_(nullptr),
callback_(nullptr),
error_cb_(base::Bind(&AudioRendererMixerInput::OnRenderError,
base::Unretained(this))) {
DCHECK(mixer_pool_);
......@@ -112,6 +107,12 @@ OutputDeviceInfo AudioRendererMixerInput::GetOutputDeviceInfo() {
owner_id_, 0 /* session_id */, device_id_);
}
void AudioRendererMixerInput::GetOutputDeviceInfoAsync(
OutputDeviceInfoCB info_cb) {
// TODO(dalecurtis): Implement this for https://crbug.com/905506.
NOTREACHED();
}
bool AudioRendererMixerInput::IsOptimizedForHardwareParameters() {
return true;
}
......@@ -173,8 +174,8 @@ double AudioRendererMixerInput::ProvideInput(AudioBus* audio_bus,
// AudioConverter expects unfilled frames to be zeroed.
if (frames_filled < audio_bus->frames()) {
audio_bus->ZeroFramesPartial(
frames_filled, audio_bus->frames() - frames_filled);
audio_bus->ZeroFramesPartial(frames_filled,
audio_bus->frames() - frames_filled);
}
// We're reading |volume_| from the audio device thread and must avoid racing
......
......@@ -47,6 +47,8 @@ class MEDIA_EXPORT AudioRendererMixerInput
void Pause() override;
bool SetVolume(double volume) override;
OutputDeviceInfo GetOutputDeviceInfo() override;
void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
bool IsOptimizedForHardwareParameters() override;
void Initialize(const AudioParameters& params,
AudioRendererSink::RenderCallback* renderer) override;
......@@ -74,9 +76,9 @@ class MEDIA_EXPORT AudioRendererMixerInput
// SetVolume().
base::Lock volume_lock_;
bool started_;
bool playing_;
double volume_ GUARDED_BY(volume_lock_);
bool started_ = false;
bool playing_ = false;
double volume_ GUARDED_BY(volume_lock_) = 1.0;
// AudioConverter::InputCallback implementation.
double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
......@@ -90,10 +92,10 @@ class MEDIA_EXPORT AudioRendererMixerInput
// AudioRendererMixer obtained from mixer pool during Initialize(),
// guaranteed to live (at least) until it is returned to the pool.
AudioRendererMixer* mixer_;
AudioRendererMixer* mixer_ = nullptr;
// Source of audio data which is provided to the mixer.
AudioRendererSink::RenderCallback* callback_;
AudioRendererSink::RenderCallback* callback_ = nullptr;
// Error callback for handing to AudioRendererMixer.
const base::Closure error_cb_;
......
......@@ -66,12 +66,29 @@ class AudioRendererSink
virtual bool SetVolume(double volume) = 0;
// Returns current output device information. If the information is not
// available yet, this method may block until it becomes available.
// If the sink is not associated with any output device, |device_status| of
// OutputDeviceInfo should be set to OUTPUT_DEVICE_STATUS_ERROR_INTERNAL.
// Must never be called on the IO thread.
// available yet, this method may block until it becomes available. If the
// sink is not associated with any output device, |device_status| of
// OutputDeviceInfo should be set to OUTPUT_DEVICE_STATUS_ERROR_INTERNAL. Must
// never be called on the IO thread.
//
// Note: Prefer to use GetOutputDeviceInfoAsync instead if possible.
virtual OutputDeviceInfo GetOutputDeviceInfo() = 0;
// Same as the above, but does not block and will execute |info_cb| when the
// OutputDeviceInfo is available. Callback will be executed on the calling
// thread. Prefer this function to the synchronous version, it does not have a
// timeout so will result in less spurious timeout errors.
//
// |info_cb| will always be posted (I.e., executed after this function
// returns), even if OutputDeviceInfo is already available.
//
// Upon destruction if OutputDeviceInfo is still not available, |info_cb| will
// be posted with OUTPUT_DEVICE_STATUS_ERROR_INTERNAL. Note: Because |info_cb|
// is posted it will execute after destruction, so clients must handle
// cancellation of the callback if needed.
using OutputDeviceInfoCB = base::OnceCallback<void(OutputDeviceInfo)>;
virtual void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) = 0;
// Returns |true| if a source with hardware parameters is preferable.
virtual bool IsOptimizedForHardwareParameters() = 0;
......
......@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/sequenced_task_runner_handle.h"
namespace media {
......@@ -69,6 +70,12 @@ OutputDeviceInfo FakeAudioRendererSink::GetOutputDeviceInfo() {
return output_device_info_;
}
void FakeAudioRendererSink::GetOutputDeviceInfoAsync(
OutputDeviceInfoCB info_cb) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(info_cb), output_device_info_));
}
bool FakeAudioRendererSink::IsOptimizedForHardwareParameters() {
return is_optimized_for_hw_params_;
}
......
......@@ -39,6 +39,7 @@ class FakeAudioRendererSink : public AudioRendererSink {
void Play() override;
bool SetVolume(double volume) override;
OutputDeviceInfo GetOutputDeviceInfo() override;
void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
bool IsOptimizedForHardwareParameters() override;
bool CurrentThreadIsRenderingThread() override;
......
......@@ -4,6 +4,9 @@
#include "media/base/mock_audio_renderer_sink.h"
#include "base/bind.h"
#include "base/threading/sequenced_task_runner_handle.h"
namespace media {
MockAudioRendererSink::MockAudioRendererSink()
: MockAudioRendererSink(OUTPUT_DEVICE_STATUS_OK) {}
......@@ -45,6 +48,12 @@ OutputDeviceInfo MockAudioRendererSink::GetOutputDeviceInfo() {
return output_device_info_;
}
void MockAudioRendererSink::GetOutputDeviceInfoAsync(
OutputDeviceInfoCB info_cb) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(info_cb), output_device_info_));
}
bool MockAudioRendererSink::IsOptimizedForHardwareParameters() {
return false;
}
......
......@@ -32,6 +32,7 @@ class MockAudioRendererSink : public SwitchableAudioRendererSink {
MOCK_METHOD0(CurrentThreadIsRenderingThread, bool());
OutputDeviceInfo GetOutputDeviceInfo() override;
void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
bool IsOptimizedForHardwareParameters() override;
......
......@@ -16,6 +16,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_annotations.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/audio/null_audio_sink.h"
#include "media/base/audio_timestamp_helper.h"
......@@ -266,6 +267,20 @@ OutputDeviceInfo WebAudioSourceProviderImpl::GetOutputDeviceInfo() {
: OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND);
}
void WebAudioSourceProviderImpl::GetOutputDeviceInfoAsync(
OutputDeviceInfoCB info_cb) {
base::AutoLock auto_lock(sink_lock_);
if (sink_) {
sink_->GetOutputDeviceInfoAsync(std::move(info_cb));
return;
}
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(info_cb),
OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND)));
}
bool WebAudioSourceProviderImpl::IsOptimizedForHardwareParameters() {
base::AutoLock auto_lock(sink_lock_);
return client_ ? false : true;
......
......@@ -67,6 +67,7 @@ class MEDIA_BLINK_EXPORT WebAudioSourceProviderImpl
void Pause() override;
bool SetVolume(double volume) override;
OutputDeviceInfo GetOutputDeviceInfo() override;
void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
bool IsOptimizedForHardwareParameters() override;
bool CurrentThreadIsRenderingThread() override;
void SwitchOutputDevice(const std::string& device_id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment