| Index: content/renderer/media/audio_renderer_impl.cc
|
| ===================================================================
|
| --- content/renderer/media/audio_renderer_impl.cc (revision 91508)
|
| +++ content/renderer/media/audio_renderer_impl.cc (working copy)
|
| @@ -6,24 +6,20 @@
|
|
|
| #include <math.h>
|
|
|
| +#include "base/command_line.h"
|
| +#include "content/common/content_switches.h"
|
| #include "content/common/media/audio_messages.h"
|
| #include "content/renderer/render_thread.h"
|
| #include "content/renderer/render_view.h"
|
| +#include "media/audio/audio_output_controller.h"
|
| #include "media/base/filter_host.h"
|
|
|
| -namespace {
|
| +// Static variable that says what code path we are using -- low or high
|
| +// latency. Made separate variable so we don't have to go to command line
|
| +// for every DCHECK().
|
| +AudioRendererImpl::LatencyType AudioRendererImpl::latency_type_ =
|
| + AudioRendererImpl::kUninitializedLatency;
|
|
|
| -// We will try to fill 200 ms worth of audio samples in each packet. A round
|
| -// trip latency for IPC messages are typically 10 ms, this should give us
|
| -// plenty of time to avoid clicks.
|
| -const int kMillisecondsPerPacket = 200;
|
| -
|
| -// We have at most 3 packets in browser, i.e. 600 ms. This is a reasonable
|
| -// amount to avoid clicks.
|
| -const int kPacketsInBuffer = 3;
|
| -
|
| -} // namespace
|
| -
|
| AudioRendererImpl::AudioRendererImpl(AudioMessageFilter* filter)
|
| : AudioRendererBase(),
|
| bytes_per_second_(0),
|
| @@ -37,11 +33,28 @@
|
| prerolling_(false),
|
| preroll_bytes_(0) {
|
| DCHECK(io_loop_);
|
| + // Figure out if we are planning to use high or low latency code path.
|
| + // We are initializing only one variable and double initialization is Ok,
|
| + // so there would not be any issues caused by CPU memory model.
|
| + if (latency_type_ == kUninitializedLatency) {
|
| + if (CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kLowLatencyAudio)) {
|
| + latency_type_ = kLowLatency;
|
| + } else {
|
| + latency_type_ = kHighLatency;
|
| + }
|
| + }
|
| }
|
|
|
| AudioRendererImpl::~AudioRendererImpl() {
|
| }
|
|
|
| +// static
|
| +void AudioRendererImpl::set_latency_type(LatencyType latency_type) {
|
| + DCHECK_EQ(kUninitializedLatency, latency_type_);
|
| + latency_type_ = latency_type;
|
| +}
|
| +
|
| base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) {
|
| if (bytes_per_second_) {
|
| return base::TimeDelta::FromMicroseconds(
|
| @@ -71,8 +84,21 @@
|
| // task to clean up.
|
| io_loop_->PostTask(FROM_HERE,
|
| NewRunnableMethod(this, &AudioRendererImpl::DestroyTask));
|
| +
|
| + if (audio_thread_.get()) {
|
| + socket_->Close();
|
| + audio_thread_->Join();
|
| + }
|
| }
|
|
|
| +void AudioRendererImpl::NotifyDataAvailableIfNecessary() {
|
| + if (latency_type_ == kHighLatency) {
|
| + // Post a task to render thread to notify a packet reception.
|
| + io_loop_->PostTask(FROM_HERE,
|
| + NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
|
| + }
|
| +}
|
| +
|
| void AudioRendererImpl::ConsumeAudioSamples(
|
| scoped_refptr<media::Buffer> buffer_in) {
|
| base::AutoLock auto_lock(lock_);
|
| @@ -84,13 +110,11 @@
|
| // Use the base class to queue the buffer.
|
| AudioRendererBase::ConsumeAudioSamples(buffer_in);
|
|
|
| - // Post a task to render thread to notify a packet reception.
|
| - io_loop_->PostTask(FROM_HERE,
|
| - NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
|
| + NotifyDataAvailableIfNecessary();
|
| }
|
|
|
| void AudioRendererImpl::SetPlaybackRate(float rate) {
|
| - DCHECK(rate >= 0.0f);
|
| + DCHECK_LE(0.0f, rate);
|
|
|
| base::AutoLock auto_lock(lock_);
|
| // Handle the case where we stopped due to |io_loop_| dying.
|
| @@ -115,9 +139,7 @@
|
| // If we are playing, give a kick to try fulfilling the packet request as
|
| // the previous packet request may be stalled by a pause.
|
| if (rate > 0.0f) {
|
| - io_loop_->PostTask(
|
| - FROM_HERE,
|
| - NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
|
| + NotifyDataAvailableIfNecessary();
|
| }
|
| }
|
|
|
| @@ -170,6 +192,7 @@
|
| void AudioRendererImpl::OnCreated(base::SharedMemoryHandle handle,
|
| uint32 length) {
|
| DCHECK(MessageLoop::current() == io_loop_);
|
| + DCHECK_EQ(kHighLatency, latency_type_);
|
|
|
| base::AutoLock auto_lock(lock_);
|
| if (stopped_)
|
| @@ -180,15 +203,51 @@
|
| shared_memory_size_ = length;
|
| }
|
|
|
| -void AudioRendererImpl::OnLowLatencyCreated(base::SharedMemoryHandle,
|
| - base::SyncSocket::Handle, uint32) {
|
| - // AudioRenderer should not have a low-latency audio channel.
|
| - NOTREACHED();
|
| +void AudioRendererImpl::CreateSocket(base::SyncSocket::Handle socket_handle) {
|
| + DCHECK_EQ(kLowLatency, latency_type_);
|
| +#if defined(OS_WIN)
|
| + DCHECK(socket_handle);
|
| +#else
|
| + DCHECK_GE(socket_handle, 0);
|
| +#endif
|
| + socket_.reset(new base::SyncSocket(socket_handle));
|
| }
|
|
|
| +void AudioRendererImpl::CreateAudioThread() {
|
| + DCHECK_EQ(kLowLatency, latency_type_);
|
| + audio_thread_.reset(
|
| + new base::DelegateSimpleThread(this, "renderer_audio_thread"));
|
| + audio_thread_->Start();
|
| +}
|
| +
|
| +void AudioRendererImpl::OnLowLatencyCreated(
|
| + base::SharedMemoryHandle handle,
|
| + base::SyncSocket::Handle socket_handle,
|
| + uint32 length) {
|
| + DCHECK(MessageLoop::current() == io_loop_);
|
| + DCHECK_EQ(kLowLatency, latency_type_);
|
| +#if defined(OS_WIN)
|
| + DCHECK(handle);
|
| +#else
|
| + DCHECK_GE(handle.fd, 0);
|
| +#endif
|
| + DCHECK_NE(0u, length);
|
| +
|
| + base::AutoLock auto_lock(lock_);
|
| + if (stopped_)
|
| + return;
|
| +
|
| + shared_memory_.reset(new base::SharedMemory(handle, false));
|
| + shared_memory_->Map(length);
|
| + shared_memory_size_ = length;
|
| +
|
| + CreateSocket(socket_handle);
|
| + CreateAudioThread();
|
| +}
|
| +
|
| void AudioRendererImpl::OnRequestPacket(AudioBuffersState buffers_state) {
|
| DCHECK(MessageLoop::current() == io_loop_);
|
| -
|
| + DCHECK_EQ(kHighLatency, latency_type_);
|
| {
|
| base::AutoLock auto_lock(lock_);
|
| DCHECK(!pending_request_);
|
| @@ -247,8 +306,10 @@
|
| // Let the browser choose packet size.
|
| params_to_send.samples_per_packet = 0;
|
|
|
| - filter_->Send(new AudioHostMsg_CreateStream(
|
| - 0, stream_id_, params_to_send, false));
|
| + filter_->Send(new AudioHostMsg_CreateStream(0,
|
| + stream_id_,
|
| + params_to_send,
|
| + latency_type_ == kLowLatency));
|
| }
|
|
|
| void AudioRendererImpl::PlayTask() {
|
| @@ -293,6 +354,7 @@
|
|
|
| void AudioRendererImpl::NotifyPacketReadyTask() {
|
| DCHECK(MessageLoop::current() == io_loop_);
|
| + DCHECK_EQ(kHighLatency, latency_type_);
|
|
|
| base::AutoLock auto_lock(lock_);
|
| if (stopped_)
|
| @@ -346,3 +408,36 @@
|
| stopped_ = true;
|
| DestroyTask();
|
| }
|
| +
|
| +// Our audio thread runs here. We receive requests for more data and send it
|
| +// on this thread.
|
| +void AudioRendererImpl::Run() {
|
| + audio_thread_->SetThreadPriority(base::kThreadPriority_RealtimeAudio);
|
| +
|
| + int bytes;
|
| + while (sizeof(bytes) == socket_->Receive(&bytes, sizeof(bytes))) {
|
| +LOG(ERROR) << "+++ bytes: " << bytes;
|
| + if (bytes == media::AudioOutputController::kPauseMark)
|
| + continue;
|
| + else if (bytes < 0)
|
| + break;
|
| + base::AutoLock auto_lock(lock_);
|
| + if (stopped_)
|
| + break;
|
| + float playback_rate = GetPlaybackRate();
|
| + if (playback_rate <= 0.0f)
|
| + continue;
|
| + DCHECK(shared_memory_.get());
|
| + base::TimeDelta request_delay = ConvertToDuration(bytes);
|
| + // We need to adjust the delay according to playback rate.
|
| + if (playback_rate != 1.0f) {
|
| + request_delay = base::TimeDelta::FromMicroseconds(
|
| + static_cast<int64>(ceil(request_delay.InMicroseconds() *
|
| + playback_rate)));
|
| + }
|
| + FillBuffer(static_cast<uint8*>(shared_memory_->memory()),
|
| + shared_memory_size_,
|
| + request_delay,
|
| + true /* buffers empty */);
|
| + }
|
| +}
|
|
|