| Index: chrome/browser/renderer_host/audio_renderer_host.cc
|
| ===================================================================
|
| --- chrome/browser/renderer_host/audio_renderer_host.cc (revision 75488)
|
| +++ chrome/browser/renderer_host/audio_renderer_host.cc (working copy)
|
| @@ -1,480 +0,0 @@
|
| -// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/browser/renderer_host/audio_renderer_host.h"
|
| -
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/process.h"
|
| -#include "base/shared_memory.h"
|
| -#include "chrome/browser/renderer_host/audio_sync_reader.h"
|
| -#include "chrome/common/render_messages.h"
|
| -#include "chrome/common/render_messages_params.h"
|
| -#include "ipc/ipc_logging.h"
|
| -
|
| -// The minimum number of samples in a hardware packet.
|
| -// This value is selected so that we can handle down to 5khz sample rate.
|
| -static const int kMinSamplesPerHardwarePacket = 1024;
|
| -
|
| -// The maximum number of samples in a hardware packet.
|
| -// This value is selected so that we can handle up to 192khz sample rate.
|
| -static const int kMaxSamplesPerHardwarePacket = 64 * 1024;
|
| -
|
| -// This constant governs the hardware audio buffer size, this value should be
|
| -// chosen carefully.
|
| -// This value is selected so that we have 8192 samples for 48khz streams.
|
| -static const int kMillisecondsPerHardwarePacket = 170;
|
| -
|
| -static uint32 SelectSamplesPerPacket(AudioParameters params) {
|
| - // Select the number of samples that can provide at least
|
| - // |kMillisecondsPerHardwarePacket| worth of audio data.
|
| - int samples = kMinSamplesPerHardwarePacket;
|
| - while (samples <= kMaxSamplesPerHardwarePacket &&
|
| - samples * base::Time::kMillisecondsPerSecond <
|
| - params.sample_rate * kMillisecondsPerHardwarePacket) {
|
| - samples *= 2;
|
| - }
|
| - return samples;
|
| -}
|
| -
|
| -AudioRendererHost::AudioEntry::AudioEntry()
|
| - : render_view_id(0),
|
| - stream_id(0),
|
| - pending_buffer_request(false),
|
| - pending_close(false) {
|
| -}
|
| -
|
| -AudioRendererHost::AudioEntry::~AudioEntry() {}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -// AudioRendererHost implementations.
|
| -AudioRendererHost::AudioRendererHost() {
|
| -}
|
| -
|
| -AudioRendererHost::~AudioRendererHost() {
|
| - DCHECK(audio_entries_.empty());
|
| -}
|
| -
|
| -void AudioRendererHost::OnChannelClosing() {
|
| - BrowserMessageFilter::OnChannelClosing();
|
| -
|
| - // Since the IPC channel is gone, close all requested audio streams.
|
| - DeleteEntries();
|
| -}
|
| -
|
| -void AudioRendererHost::OnDestruct() const {
|
| - BrowserThread::DeleteOnIOThread::Destruct(this);
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -// media::AudioOutputController::EventHandler implementations.
|
| -void AudioRendererHost::OnCreated(media::AudioOutputController* controller) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - NewRunnableMethod(
|
| - this,
|
| - &AudioRendererHost::DoCompleteCreation,
|
| - make_scoped_refptr(controller)));
|
| -}
|
| -
|
| -void AudioRendererHost::OnPlaying(media::AudioOutputController* controller) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - NewRunnableMethod(
|
| - this,
|
| - &AudioRendererHost::DoSendPlayingMessage,
|
| - make_scoped_refptr(controller)));
|
| -}
|
| -
|
| -void AudioRendererHost::OnPaused(media::AudioOutputController* controller) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - NewRunnableMethod(
|
| - this,
|
| - &AudioRendererHost::DoSendPausedMessage,
|
| - make_scoped_refptr(controller)));
|
| -}
|
| -
|
| -void AudioRendererHost::OnError(media::AudioOutputController* controller,
|
| - int error_code) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - NewRunnableMethod(this,
|
| - &AudioRendererHost::DoHandleError,
|
| - make_scoped_refptr(controller),
|
| - error_code));
|
| -}
|
| -
|
| -void AudioRendererHost::OnMoreData(media::AudioOutputController* controller,
|
| - AudioBuffersState buffers_state) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - NewRunnableMethod(this,
|
| - &AudioRendererHost::DoRequestMoreData,
|
| - make_scoped_refptr(controller),
|
| - buffers_state));
|
| -}
|
| -
|
| -void AudioRendererHost::DoCompleteCreation(
|
| - media::AudioOutputController* controller) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupByController(controller);
|
| - if (!entry)
|
| - return;
|
| -
|
| - if (!peer_handle()) {
|
| - NOTREACHED() << "Renderer process handle is invalid.";
|
| - DeleteEntryOnError(entry);
|
| - return;
|
| - }
|
| -
|
| - // Once the audio stream is created then complete the creation process by
|
| - // mapping shared memory and sharing with the renderer process.
|
| - base::SharedMemoryHandle foreign_memory_handle;
|
| - if (!entry->shared_memory.ShareToProcess(peer_handle(),
|
| - &foreign_memory_handle)) {
|
| - // If we failed to map and share the shared memory then close the audio
|
| - // stream and send an error message.
|
| - DeleteEntryOnError(entry);
|
| - return;
|
| - }
|
| -
|
| - if (entry->controller->LowLatencyMode()) {
|
| - AudioSyncReader* reader =
|
| - static_cast<AudioSyncReader*>(entry->reader.get());
|
| -
|
| -#if defined(OS_WIN)
|
| - base::SyncSocket::Handle foreign_socket_handle;
|
| -#else
|
| - base::FileDescriptor foreign_socket_handle;
|
| -#endif
|
| -
|
| - // If we failed to prepare the sync socket for the renderer then we fail
|
| - // the construction of audio stream.
|
| - if (!reader->PrepareForeignSocketHandle(peer_handle(),
|
| - &foreign_socket_handle)) {
|
| - DeleteEntryOnError(entry);
|
| - return;
|
| - }
|
| -
|
| - Send(new ViewMsg_NotifyLowLatencyAudioStreamCreated(
|
| - entry->render_view_id, entry->stream_id, foreign_memory_handle,
|
| - foreign_socket_handle, entry->shared_memory.created_size()));
|
| - return;
|
| - }
|
| -
|
| - // The normal audio stream has created, send a message to the renderer
|
| - // process.
|
| - Send(new ViewMsg_NotifyAudioStreamCreated(
|
| - entry->render_view_id, entry->stream_id, foreign_memory_handle,
|
| - entry->shared_memory.created_size()));
|
| -}
|
| -
|
| -void AudioRendererHost::DoSendPlayingMessage(
|
| - media::AudioOutputController* controller) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupByController(controller);
|
| - if (!entry)
|
| - return;
|
| -
|
| - ViewMsg_AudioStreamState_Params params;
|
| - params.state = ViewMsg_AudioStreamState_Params::kPlaying;
|
| - Send(new ViewMsg_NotifyAudioStreamStateChanged(
|
| - entry->render_view_id, entry->stream_id, params));
|
| -}
|
| -
|
| -void AudioRendererHost::DoSendPausedMessage(
|
| - media::AudioOutputController* controller) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupByController(controller);
|
| - if (!entry)
|
| - return;
|
| -
|
| - ViewMsg_AudioStreamState_Params params;
|
| - params.state = ViewMsg_AudioStreamState_Params::kPaused;
|
| - Send(new ViewMsg_NotifyAudioStreamStateChanged(
|
| - entry->render_view_id, entry->stream_id, params));
|
| -}
|
| -
|
| -void AudioRendererHost::DoRequestMoreData(
|
| - media::AudioOutputController* controller,
|
| - AudioBuffersState buffers_state) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - // If we already have a pending request then return.
|
| - AudioEntry* entry = LookupByController(controller);
|
| - if (!entry || entry->pending_buffer_request)
|
| - return;
|
| -
|
| - DCHECK(!entry->controller->LowLatencyMode());
|
| - entry->pending_buffer_request = true;
|
| - Send(new ViewMsg_RequestAudioPacket(
|
| - entry->render_view_id, entry->stream_id, buffers_state));
|
| -}
|
| -
|
| -void AudioRendererHost::DoHandleError(media::AudioOutputController* controller,
|
| - int error_code) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupByController(controller);
|
| - if (!entry)
|
| - return;
|
| -
|
| - DeleteEntryOnError(entry);
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -// IPC Messages handler
|
| -bool AudioRendererHost::OnMessageReceived(const IPC::Message& message,
|
| - bool* message_was_ok) {
|
| - bool handled = true;
|
| - IPC_BEGIN_MESSAGE_MAP_EX(AudioRendererHost, message, *message_was_ok)
|
| - IPC_MESSAGE_HANDLER(ViewHostMsg_CreateAudioStream, OnCreateStream)
|
| - IPC_MESSAGE_HANDLER(ViewHostMsg_PlayAudioStream, OnPlayStream)
|
| - IPC_MESSAGE_HANDLER(ViewHostMsg_PauseAudioStream, OnPauseStream)
|
| - IPC_MESSAGE_HANDLER(ViewHostMsg_FlushAudioStream, OnFlushStream)
|
| - IPC_MESSAGE_HANDLER(ViewHostMsg_CloseAudioStream, OnCloseStream)
|
| - IPC_MESSAGE_HANDLER(ViewHostMsg_NotifyAudioPacketReady, OnNotifyPacketReady)
|
| - IPC_MESSAGE_HANDLER(ViewHostMsg_GetAudioVolume, OnGetVolume)
|
| - IPC_MESSAGE_HANDLER(ViewHostMsg_SetAudioVolume, OnSetVolume)
|
| - IPC_MESSAGE_UNHANDLED(handled = false)
|
| - IPC_END_MESSAGE_MAP_EX()
|
| -
|
| - return handled;
|
| -}
|
| -
|
| -void AudioRendererHost::OnCreateStream(
|
| - const IPC::Message& msg, int stream_id,
|
| - const ViewHostMsg_Audio_CreateStream_Params& params, bool low_latency) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| - DCHECK(LookupById(msg.routing_id(), stream_id) == NULL);
|
| -
|
| - AudioParameters audio_params(params.params);
|
| -
|
| - // Select the hardware packet size if not specified.
|
| - if (!audio_params.samples_per_packet) {
|
| - audio_params.samples_per_packet = SelectSamplesPerPacket(audio_params);
|
| - }
|
| - uint32 packet_size = audio_params.GetPacketSize();
|
| -
|
| - scoped_ptr<AudioEntry> entry(new AudioEntry());
|
| - // Create the shared memory and share with the renderer process.
|
| - if (!entry->shared_memory.CreateAndMapAnonymous(packet_size)) {
|
| - // If creation of shared memory failed then send an error message.
|
| - SendErrorMessage(msg.routing_id(), stream_id);
|
| - return;
|
| - }
|
| -
|
| - if (low_latency) {
|
| - // If this is the low latency mode, we need to construct a SyncReader first.
|
| - scoped_ptr<AudioSyncReader> reader(
|
| - new AudioSyncReader(&entry->shared_memory));
|
| -
|
| - // Then try to initialize the sync reader.
|
| - if (!reader->Init()) {
|
| - SendErrorMessage(msg.routing_id(), stream_id);
|
| - return;
|
| - }
|
| -
|
| - // If we have successfully created the SyncReader then assign it to the
|
| - // entry and construct an AudioOutputController.
|
| - entry->reader.reset(reader.release());
|
| - entry->controller =
|
| - media::AudioOutputController::CreateLowLatency(this, audio_params,
|
| - entry->reader.get());
|
| - } else {
|
| - // The choice of buffer capacity is based on experiment.
|
| - entry->controller =
|
| - media::AudioOutputController::Create(this, audio_params,
|
| - 3 * packet_size);
|
| - }
|
| -
|
| - if (!entry->controller) {
|
| - SendErrorMessage(msg.routing_id(), stream_id);
|
| - return;
|
| - }
|
| -
|
| - // If we have created the controller successfully create a entry and add it
|
| - // to the map.
|
| - entry->render_view_id = msg.routing_id();
|
| - entry->stream_id = stream_id;
|
| -
|
| - audio_entries_.insert(std::make_pair(
|
| - AudioEntryId(msg.routing_id(), stream_id),
|
| - entry.release()));
|
| -}
|
| -
|
| -void AudioRendererHost::OnPlayStream(const IPC::Message& msg, int stream_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
|
| - if (!entry) {
|
| - SendErrorMessage(msg.routing_id(), stream_id);
|
| - return;
|
| - }
|
| -
|
| - entry->controller->Play();
|
| -}
|
| -
|
| -void AudioRendererHost::OnPauseStream(const IPC::Message& msg, int stream_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
|
| - if (!entry) {
|
| - SendErrorMessage(msg.routing_id(), stream_id);
|
| - return;
|
| - }
|
| -
|
| - entry->controller->Pause();
|
| -}
|
| -
|
| -void AudioRendererHost::OnFlushStream(const IPC::Message& msg, int stream_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
|
| - if (!entry) {
|
| - SendErrorMessage(msg.routing_id(), stream_id);
|
| - return;
|
| - }
|
| -
|
| - entry->controller->Flush();
|
| -}
|
| -
|
| -void AudioRendererHost::OnCloseStream(const IPC::Message& msg, int stream_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
|
| -
|
| - if (entry)
|
| - CloseAndDeleteStream(entry);
|
| -}
|
| -
|
| -void AudioRendererHost::OnSetVolume(const IPC::Message& msg, int stream_id,
|
| - double volume) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
|
| - if (!entry) {
|
| - SendErrorMessage(msg.routing_id(), stream_id);
|
| - return;
|
| - }
|
| -
|
| - // Make sure the volume is valid.
|
| - if (volume < 0 || volume > 1.0)
|
| - return;
|
| - entry->controller->SetVolume(volume);
|
| -}
|
| -
|
| -void AudioRendererHost::OnGetVolume(const IPC::Message& msg, int stream_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| - NOTREACHED() << "This message shouldn't be received";
|
| -}
|
| -
|
| -void AudioRendererHost::OnNotifyPacketReady(
|
| - const IPC::Message& msg, int stream_id, uint32 packet_size) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
|
| - if (!entry) {
|
| - SendErrorMessage(msg.routing_id(), stream_id);
|
| - return;
|
| - }
|
| -
|
| - DCHECK(!entry->controller->LowLatencyMode());
|
| - CHECK(packet_size <= entry->shared_memory.created_size());
|
| -
|
| - if (!entry->pending_buffer_request) {
|
| - NOTREACHED() << "Buffer received but no such pending request";
|
| - }
|
| - entry->pending_buffer_request = false;
|
| -
|
| - // Enqueue the data to media::AudioOutputController.
|
| - entry->controller->EnqueueData(
|
| - reinterpret_cast<uint8*>(entry->shared_memory.memory()),
|
| - packet_size);
|
| -}
|
| -
|
| -void AudioRendererHost::SendErrorMessage(int32 render_view_id,
|
| - int32 stream_id) {
|
| - ViewMsg_AudioStreamState_Params state;
|
| - state.state = ViewMsg_AudioStreamState_Params::kError;
|
| - Send(new ViewMsg_NotifyAudioStreamStateChanged(
|
| - render_view_id, stream_id, state));
|
| -}
|
| -
|
| -void AudioRendererHost::DeleteEntries() {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - for (AudioEntryMap::iterator i = audio_entries_.begin();
|
| - i != audio_entries_.end(); ++i) {
|
| - CloseAndDeleteStream(i->second);
|
| - }
|
| -}
|
| -
|
| -void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
|
| - if (!entry->pending_close) {
|
| - entry->controller->Close(
|
| - NewRunnableMethod(this, &AudioRendererHost::OnStreamClosed, entry));
|
| - entry->pending_close = true;
|
| - }
|
| -}
|
| -
|
| -void AudioRendererHost::OnStreamClosed(AudioEntry* entry) {
|
| - // Delete the entry after we've closed the stream.
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO, FROM_HERE,
|
| - NewRunnableMethod(this, &AudioRendererHost::DeleteEntry, entry));
|
| -}
|
| -
|
| -void AudioRendererHost::DeleteEntry(AudioEntry* entry) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - // Delete the entry when this method goes out of scope.
|
| - scoped_ptr<AudioEntry> entry_deleter(entry);
|
| -
|
| - // Erase the entry from the map.
|
| - audio_entries_.erase(
|
| - AudioEntryId(entry->render_view_id, entry->stream_id));
|
| -}
|
| -
|
| -void AudioRendererHost::DeleteEntryOnError(AudioEntry* entry) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - // Sends the error message first before we close the stream because
|
| - // |entry| is destroyed in DeleteEntry().
|
| - SendErrorMessage(entry->render_view_id, entry->stream_id);
|
| - CloseAndDeleteStream(entry);
|
| -}
|
| -
|
| -AudioRendererHost::AudioEntry* AudioRendererHost::LookupById(
|
| - int route_id, int stream_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - AudioEntryMap::iterator i = audio_entries_.find(
|
| - AudioEntryId(route_id, stream_id));
|
| - if (i != audio_entries_.end() && !i->second->pending_close)
|
| - return i->second;
|
| - return NULL;
|
| -}
|
| -
|
| -AudioRendererHost::AudioEntry* AudioRendererHost::LookupByController(
|
| - media::AudioOutputController* controller) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - // Iterate the map of entries.
|
| - // TODO(hclam): Implement a faster look up method.
|
| - for (AudioEntryMap::iterator i = audio_entries_.begin();
|
| - i != audio_entries_.end(); ++i) {
|
| - if (!i->second->pending_close && controller == i->second->controller.get())
|
| - return i->second;
|
| - }
|
| - return NULL;
|
| -}
|
|
|