Chromium Code Reviews| Index: content/renderer/media/audio_renderer_impl.cc |
| =================================================================== |
| --- content/renderer/media/audio_renderer_impl.cc (revision 90349) |
| +++ 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,53 @@ |
| 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); |
| + DCHECK(socket_handle); |
| +#else |
| + DCHECK_GE(handle.fd, 0); |
| + DCHECK_GE(socket_handle, 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 +308,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 +356,7 @@ |
| void AudioRendererImpl::NotifyPacketReadyTask() { |
| DCHECK(MessageLoop::current() == io_loop_); |
| + DCHECK_EQ(kHighLatency, latency_type_); |
| base::AutoLock auto_lock(lock_); |
| if (stopped_) |
| @@ -346,3 +410,38 @@ |
| 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))) { |
| + if (bytes == media::AudioOutputController::kPauseMark) { |
|
brettw
2011/07/06 22:13:23
Be consistent about {} for single-line conditional
|
| + 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 */); |
| + } |
| +} |