diff options
Diffstat (limited to 'chrome/browser/renderer_host/audio_renderer_host.cc')
-rw-r--r-- | chrome/browser/renderer_host/audio_renderer_host.cc | 275 |
1 files changed, 149 insertions, 126 deletions
diff --git a/chrome/browser/renderer_host/audio_renderer_host.cc b/chrome/browser/renderer_host/audio_renderer_host.cc index 1a35be3..f07d8e5 100644 --- a/chrome/browser/renderer_host/audio_renderer_host.cc +++ b/chrome/browser/renderer_host/audio_renderer_host.cc @@ -32,6 +32,17 @@ void RecordProcessTime(base::TimeDelta latency) { histogram.AddTime(latency); } +// This constant governs the hardware audio buffer size, this value should be +// choosen carefully and is platform specific. +const int kSamplesPerHardwarePacket = 8192; + +const size_t kMegabytes = 1024 * 1024; + +// The following parameters limit the request buffer and packet size from the +// renderer to avoid renderer from requesting too much memory. +const size_t kMaxDecodedPacketSize = 2 * kMegabytes; +const size_t kMaxBufferCapacity = 5 * kMegabytes; + } // namespace //----------------------------------------------------------------------------- @@ -43,17 +54,20 @@ AudioRendererHost::IPCAudioSource::IPCAudioSource( int route_id, int stream_id, AudioOutputStream* stream, - size_t packet_size) + size_t hardware_packet_size, + size_t decoded_packet_size, + size_t buffer_capacity) : host_(host), process_id_(process_id), route_id_(route_id), stream_id_(stream_id), stream_(stream), - packet_size_(packet_size), + hardware_packet_size_(hardware_packet_size), + decoded_packet_size_(decoded_packet_size), + buffer_capacity_(buffer_capacity), state_(AudioOutputStream::STATE_CREATED), - stop_providing_packets_(false), - packet_read_event_(false, false), - last_packet_size_(0) { + push_source_(hardware_packet_size), + outstanding_request_(false) { } AudioRendererHost::IPCAudioSource::~IPCAudioSource() { @@ -71,34 +85,60 @@ AudioRendererHost::IPCAudioSource* int channels, int sample_rate, char bits_per_sample, - size_t packet_size) { + size_t decoded_packet_size, + size_t buffer_capacity) { + // Perform come preliminary checks on the parameters. + // Make sure the renderer didn't ask for too much memory. + if (buffer_capacity > kMaxBufferCapacity || + decoded_packet_size > kMaxDecodedPacketSize) + return NULL; + + // Make sure the packet size and buffer capacity parameters are valid. + if (buffer_capacity < decoded_packet_size) + return NULL; + // Create the stream in the first place. AudioOutputStream* stream = AudioManager::GetAudioManager()->MakeAudioStream( format, channels, sample_rate, bits_per_sample); - if (stream && !stream->Open(packet_size)) { + + size_t hardware_packet_size = kSamplesPerHardwarePacket * channels * + bits_per_sample / 8; + if (stream && !stream->Open(hardware_packet_size)) { stream->Close(); stream = NULL; } if (stream) { IPCAudioSource* source = new IPCAudioSource( - host, process_id, route_id, stream_id, stream, packet_size); + host, + process_id, + route_id, + stream_id, + stream, + hardware_packet_size, + decoded_packet_size, + buffer_capacity); // If we can open the stream, proceed with sharing the shared memory. base::SharedMemoryHandle foreign_memory_handle; // Try to create, map and share the memory for the renderer process. // If they all succeeded then send a message to renderer to indicate // success. - if (source->shared_memory_.Create(L"", false, false, packet_size) && - source->shared_memory_.Map(packet_size) && + if (source->shared_memory_.Create(L"", + false, + false, + decoded_packet_size) && + source->shared_memory_.Map(decoded_packet_size) && source->shared_memory_.ShareToProcess(process_handle, &foreign_memory_handle)) { host->Send(new ViewMsg_NotifyAudioStreamCreated( - route_id, stream_id, foreign_memory_handle, packet_size)); + route_id, stream_id, foreign_memory_handle, decoded_packet_size)); + + // Also request the first packet to kick start the pre-rolling. + source->StartBuffering(); return source; } - source->Close(); delete source; } @@ -108,38 +148,47 @@ AudioRendererHost::IPCAudioSource* } void AudioRendererHost::IPCAudioSource::Start() { - // Only perform the start logic if this source has just created. - if (!stream_ || state_ != AudioOutputStream::STATE_CREATED) + // We can start from created or paused state. + if (!stream_ || + (state_ != AudioOutputStream::STATE_CREATED && + state_ != AudioOutputStream::STATE_PAUSED)) return; - // We don't start the stream immediately but prefetch some initial buffers - // so as to fill all internal buffers of the AudioOutputStream. The number - // of buffers to prefetch can be determined by - // AudioOutputStream::GetNumBuffers(). - if (stream_->GetNumBuffers()) { - // If the audio output stream does have internal buffer(s), request a - // packet from renderer and start the prefetching. - host_->Send(new ViewMsg_RequestAudioPacket(route_id_, stream_id_)); - } else { - // If the audio output stream does not use any internal buffers, we are - // safe to start it here. - state_ = AudioOutputStream::STATE_STARTED; - stream_->Start(this); - host_->Send(new ViewMsg_NotifyAudioStreamStateChanged( - route_id_, stream_id_, AudioOutputStream::STATE_STARTED, 0)); - } + stream_->Start(this); + + // Update the state and notify renderer. + state_ = AudioOutputStream::STATE_STARTED; + host_->Send(new ViewMsg_NotifyAudioStreamStateChanged( + route_id_, stream_id_, state_, 0)); } -void AudioRendererHost::IPCAudioSource::Close() { - // We need to wake up all waiting audio thread before calling stop. - StopWaitingForPacket(); +void AudioRendererHost::IPCAudioSource::Pause() { + // We can pause from started state. + if (!stream_ || + state_ != AudioOutputStream::STATE_STARTED) + return; + + // TODO(hclam): use stop to simulate pause, make sure the AudioOutpusStream + // can be started again after stop. + stream_->Stop(); + // Update the state and notify renderer. + state_ = AudioOutputStream::STATE_PAUSED; + host_->Send(new ViewMsg_NotifyAudioStreamStateChanged( + route_id_, stream_id_, state_, 0)); +} + +void AudioRendererHost::IPCAudioSource::Close() { if (!stream_) return; + stream_->Stop(); stream_->Close(); // After stream is closed it is destroyed, so don't keep a reference to it. stream_ = NULL; + + // Update the current state. + state_ = AudioOutputStream::STATE_STOPPED; } void AudioRendererHost::IPCAudioSource::SetVolume(double left, double right) { @@ -164,62 +213,16 @@ void AudioRendererHost::IPCAudioSource::GetVolume() { size_t AudioRendererHost::IPCAudioSource::OnMoreData(AudioOutputStream* stream, void* dest, size_t max_size) { -#ifdef IPC_MESSAGE_LOG_ENABLED - base::Time tick_start = base::Time::Now(); -#endif + size_t size = push_source_.OnMoreData(stream, dest, max_size); { AutoLock auto_lock(lock_); - // If we are ever stopped, don't ask for more audio packet from the - // renderer. - if (stop_providing_packets_) - return 0; + SubmitPacketRequest(&auto_lock); } - - // If we have an initial packet, use it immediately only in IO thread. - // There's a case when IO thread is blocked and audio hardware thread can - // reach here to consume initial packets. - if (MessageLoop::current() == host_->io_loop()) { - if (!initial_buffers_.empty()) { - uint8* initial_packet = initial_buffers_.front().first; - size_t initial_packet_size = initial_buffers_.front().second; - initial_buffers_.pop_front(); - size_t copied = - SafeCopyBuffer(dest, max_size, initial_packet, initial_packet_size); - delete [] initial_packet; - return copied; - } - NOTREACHED(); - } - - // We reach here because we ran out of initial packets, we need to ask the - // renderer to give us more. In this case we have to wait until the renderer - // gives us packet so we can't sleep on IO thread. - DCHECK(MessageLoop::current() != host_->io_loop()); - - // Send an IPC message to request audio packet from renderer and wait on the - // audio hardware thread. - host_->Send(new ViewMsg_RequestAudioPacket(route_id_, stream_id_)); - packet_read_event_.Wait(); - - size_t last_packet_size = 0; - { - AutoLock auto_lock(lock_); - last_packet_size = last_packet_size_; - } - - size_t copied = SafeCopyBuffer(dest, max_size, - shared_memory_.memory(), last_packet_size); -#ifdef IPC_MESSAGE_LOG_ENABLED - // The logging to round trip latency doesn't have dependency on IPC logging. - // But it's good we use IPC logging to trigger logging of total latency. - if (IPC::Logging::current()->Enabled()) - RecordRoundTripLatency(base::Time::Now() - tick_start); -#endif - return copied; + return size; } void AudioRendererHost::IPCAudioSource::OnClose(AudioOutputStream* stream) { - StopWaitingForPacket(); + push_source_.OnClose(stream); } void AudioRendererHost::IPCAudioSource::OnError(AudioOutputStream* stream, @@ -230,55 +233,60 @@ void AudioRendererHost::IPCAudioSource::OnError(AudioOutputStream* stream, host_->DestroySource(this); } -void AudioRendererHost::IPCAudioSource::NotifyPacketReady(size_t packet_size) { - if (packet_size > packet_size_) { - // If reported size is greater than capacity of the shared memory, close the - // stream. - host_->SendErrorMessage(route_id_, stream_id_, 0); - // We don't need to do packet_read_event_.Signal() here because the - // contained stream should be closed by the following call and OnClose will - // be received. - host_->DestroySource(this); - return; +void AudioRendererHost::IPCAudioSource::NotifyPacketReady( + size_t decoded_packet_size) { + bool ok = true; + { + AutoLock auto_lock(lock_); + outstanding_request_ = false; + // If reported size is greater than capacity of the shared memory, we have + // an error. + if (decoded_packet_size <= decoded_packet_size_) { + for (size_t i = 0; i < decoded_packet_size; i += hardware_packet_size_) { + size_t size = std::min(decoded_packet_size - i, hardware_packet_size_); + ok &= push_source_.Write( + static_cast<char*>(shared_memory_.memory()) + i, size); + if (!ok) + break; + } + + // Submit packet request if we have written something. + if (ok) + SubmitPacketRequest(&auto_lock); + } } - if (state_ == AudioOutputStream::STATE_CREATED) { - // If we are in a created state, that means we are performing prefetching. - uint8* packet = new uint8[packet_size]; - memcpy(packet, shared_memory_.memory(), packet_size); - initial_buffers_.push_back(std::make_pair(packet, packet_size)); - // If there's not enough initial packets prepared, ask more. - if (initial_buffers_.size() < stream_->GetNumBuffers()) { - host_->Send(new ViewMsg_RequestAudioPacket(route_id_, stream_id_)); - } else { - state_ = AudioOutputStream::STATE_STARTED; - stream_->Start(this); - host_->Send(new ViewMsg_NotifyAudioStreamStateChanged( - route_id_, stream_id_, AudioOutputStream::STATE_STARTED, 0)); - } - } else { - AutoLock auto_lock(lock_); - last_packet_size_ = packet_size; - packet_read_event_.Signal(); + // We have received a data packet but we didn't finish writing to push source. + // There's error an error and we should stop. + if (!ok) { + NOTREACHED(); + } +} + +void AudioRendererHost::IPCAudioSource::SubmitPacketRequest_Locked() { + lock_.AssertAcquired(); + // Submit a new request when these two conditions are fulfilled: + // 1. No outstanding request + // 2. There's space for data of the new request. + if (!outstanding_request_ && + (push_source_.UnProcessedBytes() + decoded_packet_size_ <= + buffer_capacity_)) { + outstanding_request_ = true; + host_->Send(new ViewMsg_RequestAudioPacket(route_id_, stream_id_)); } } -void AudioRendererHost::IPCAudioSource::StopWaitingForPacket() { - AutoLock auto_lock(lock_); - stop_providing_packets_ = true; - last_packet_size_ = 0; - packet_read_event_.Signal(); +void AudioRendererHost::IPCAudioSource::SubmitPacketRequest(AutoLock* alock) { + if (alock) { + SubmitPacketRequest_Locked(); + } else { + AutoLock auto_lock(lock_); + SubmitPacketRequest_Locked(); + } } -size_t AudioRendererHost::IPCAudioSource::SafeCopyBuffer( - void* dest, size_t dest_size, const void* src, size_t src_size) { - if (src_size > dest_size) { - host_->SendErrorMessage(route_id_, stream_id_, 0); - host_->DestroySource(this); - return 0; - } - memcpy(dest, src, src_size); - return src_size; +void AudioRendererHost::IPCAudioSource::StartBuffering() { + SubmitPacketRequest(NULL); } //----------------------------------------------------------------------------- @@ -333,6 +341,7 @@ bool AudioRendererHost::OnMessageReceived(const IPC::Message& message, IPC_BEGIN_MESSAGE_MAP_EX(AudioRendererHost, message, *message_was_ok) IPC_MESSAGE_HANDLER(ViewHostMsg_CreateAudioStream, OnCreateStream) IPC_MESSAGE_HANDLER(ViewHostMsg_StartAudioStream, OnStartStream) + IPC_MESSAGE_HANDLER(ViewHostMsg_PauseAudioStream, OnPauseStream) IPC_MESSAGE_HANDLER(ViewHostMsg_CloseAudioStream, OnCloseStream) IPC_MESSAGE_HANDLER(ViewHostMsg_NotifyAudioPacketReady, OnNotifyPacketReady) IPC_MESSAGE_HANDLER(ViewHostMsg_GetAudioVolume, OnGetVolume) @@ -347,6 +356,7 @@ bool AudioRendererHost::IsAudioRendererHostMessage( switch (message.type()) { case ViewHostMsg_CreateAudioStream::ID: case ViewHostMsg_StartAudioStream::ID: + case ViewHostMsg_PauseAudioStream::ID: case ViewHostMsg_CloseAudioStream::ID: case ViewHostMsg_NotifyAudioPacketReady::ID: case ViewHostMsg_GetAudioVolume::ID: @@ -374,13 +384,16 @@ void AudioRendererHost::OnCreateStream( params.channels, params.sample_rate, params.bits_per_sample, - params.packet_size); + params.packet_size, + params.buffer_capacity); // If we have created the source successfully, adds it to the map. if (source) { sources_.insert( std::make_pair( SourceID(source->route_id(), source->stream_id()), source)); + } else { + SendErrorMessage(msg.routing_id(), stream_id, 0); } } @@ -394,6 +407,16 @@ void AudioRendererHost::OnStartStream(const IPC::Message& msg, int stream_id) { } } +void AudioRendererHost::OnPauseStream(const IPC::Message& msg, int stream_id) { + DCHECK(MessageLoop::current() == io_loop_); + IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); + if (source) { + source->Pause(); + } else { + SendErrorMessage(msg.routing_id(), stream_id, 0); + } +} + void AudioRendererHost::OnCloseStream(const IPC::Message& msg, int stream_id) { DCHECK(MessageLoop::current() == io_loop_); IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); |