// Copyright (c) 2012 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 "content/browser/renderer_host/media/audio_renderer_host.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/memory/shared_memory.h" #include "base/metrics/histogram.h" #include "base/process/process.h" #include "content/browser/browser_main_loop.h" #include "content/browser/media/capture/audio_mirroring_manager.h" #include "content/browser/media/media_internals.h" #include "content/browser/renderer_host/media/audio_input_device_manager.h" #include "content/browser/renderer_host/media/audio_sync_reader.h" #include "content/browser/renderer_host/media/media_stream_manager.h" #include "content/common/media/audio_messages.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/media_observer.h" #include "content/public/common/content_switches.h" #include "media/audio/audio_manager_base.h" #include "media/base/audio_bus.h" #include "media/base/limits.h" using media::AudioBus; using media::AudioManager; namespace content { class AudioRendererHost::AudioEntry : public media::AudioOutputController::EventHandler { public: AudioEntry(AudioRendererHost* host, int stream_id, int render_view_id, int render_frame_id, const media::AudioParameters& params, const std::string& output_device_id, scoped_ptr shared_memory, scoped_ptr reader); virtual ~AudioEntry(); int stream_id() const { return stream_id_; } int render_view_id() const { return render_view_id_; } int render_frame_id() const { return render_frame_id_; } media::AudioOutputController* controller() const { return controller_.get(); } base::SharedMemory* shared_memory() { return shared_memory_.get(); } media::AudioOutputController::SyncReader* reader() const { return reader_.get(); } bool playing() const { return playing_; } void set_playing(bool playing) { playing_ = playing; } private: // media::AudioOutputController::EventHandler implementation. virtual void OnCreated() OVERRIDE; virtual void OnPlaying() OVERRIDE; virtual void OnPaused() OVERRIDE; virtual void OnError() OVERRIDE; virtual void OnDeviceChange(int new_buffer_size, int new_sample_rate) OVERRIDE; AudioRendererHost* const host_; const int stream_id_; // The routing ID of the source render view/frame. const int render_view_id_; const int render_frame_id_; // Shared memory for transmission of the audio data. Used by |reader_|. const scoped_ptr shared_memory_; // The synchronous reader to be used by |controller_|. const scoped_ptr reader_; // The AudioOutputController that manages the audio stream. const scoped_refptr controller_; bool playing_; }; AudioRendererHost::AudioEntry::AudioEntry( AudioRendererHost* host, int stream_id, int render_view_id, int render_frame_id, const media::AudioParameters& params, const std::string& output_device_id, scoped_ptr shared_memory, scoped_ptr reader) : host_(host), stream_id_(stream_id), render_view_id_(render_view_id), render_frame_id_(render_frame_id), shared_memory_(shared_memory.Pass()), reader_(reader.Pass()), controller_(media::AudioOutputController::Create(host->audio_manager_, this, params, output_device_id, reader_.get())), playing_(false) { DCHECK(controller_.get()); } AudioRendererHost::AudioEntry::~AudioEntry() {} /////////////////////////////////////////////////////////////////////////////// // AudioRendererHost implementations. AudioRendererHost::AudioRendererHost( int render_process_id, media::AudioManager* audio_manager, AudioMirroringManager* mirroring_manager, MediaInternals* media_internals, MediaStreamManager* media_stream_manager) : BrowserMessageFilter(AudioMsgStart), render_process_id_(render_process_id), audio_manager_(audio_manager), mirroring_manager_(mirroring_manager), audio_log_(media_internals->CreateAudioLog( media::AudioLogFactory::AUDIO_OUTPUT_CONTROLLER)), media_stream_manager_(media_stream_manager), num_playing_streams_(0) { DCHECK(audio_manager_); DCHECK(media_stream_manager_); } AudioRendererHost::~AudioRendererHost() { DCHECK(audio_entries_.empty()); } void AudioRendererHost::GetOutputControllers( int render_view_id, const RenderViewHost::GetAudioOutputControllersCallback& callback) const { BrowserThread::PostTaskAndReplyWithResult( BrowserThread::IO, FROM_HERE, base::Bind(&AudioRendererHost::DoGetOutputControllers, this, render_view_id), callback); } void AudioRendererHost::OnChannelClosing() { // Since the IPC channel is gone, close all requested audio streams. while (!audio_entries_.empty()) { // Note: OnCloseStream() removes the entries from audio_entries_. OnCloseStream(audio_entries_.begin()->first); } } void AudioRendererHost::OnDestruct() const { BrowserThread::DeleteOnIOThread::Destruct(this); } void AudioRendererHost::AudioEntry::OnCreated() { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&AudioRendererHost::DoCompleteCreation, host_, stream_id_)); } void AudioRendererHost::AudioEntry::OnPlaying() { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged, host_, stream_id_, true)); } void AudioRendererHost::AudioEntry::OnPaused() { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged, host_, stream_id_, false)); } void AudioRendererHost::AudioEntry::OnError() { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&AudioRendererHost::ReportErrorAndClose, host_, stream_id_)); } void AudioRendererHost::AudioEntry::OnDeviceChange(int new_buffer_size, int new_sample_rate) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(base::IgnoreResult(&AudioRendererHost::Send), host_, new AudioMsg_NotifyDeviceChanged( stream_id_, new_buffer_size, new_sample_rate))); } void AudioRendererHost::DoCompleteCreation(int stream_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!PeerHandle()) { DLOG(WARNING) << "Renderer process handle is invalid."; ReportErrorAndClose(stream_id); return; } AudioEntry* const entry = LookupById(stream_id); if (!entry) { ReportErrorAndClose(stream_id); 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(PeerHandle(), &foreign_memory_handle)) { // If we failed to map and share the shared memory then close the audio // stream and send an error message. ReportErrorAndClose(entry->stream_id()); return; } AudioSyncReader* reader = static_cast(entry->reader()); #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(PeerHandle(), &foreign_socket_handle)) { ReportErrorAndClose(entry->stream_id()); return; } Send(new AudioMsg_NotifyStreamCreated( entry->stream_id(), foreign_memory_handle, foreign_socket_handle, entry->shared_memory()->requested_size())); } void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id, bool is_playing) { DCHECK_CURRENTLY_ON(BrowserThread::IO); AudioEntry* const entry = LookupById(stream_id); if (!entry) return; Send(new AudioMsg_NotifyStreamStateChanged( stream_id, is_playing ? media::AudioOutputIPCDelegate::kPlaying : media::AudioOutputIPCDelegate::kPaused)); MediaObserver* const media_observer = GetContentClient()->browser()->GetMediaObserver(); if (media_observer) { if (is_playing) { media_observer->OnAudioStreamPlaying( render_process_id_, entry->render_frame_id(), entry->stream_id(), base::Bind(&media::AudioOutputController::ReadCurrentPowerAndClip, entry->controller())); if (!entry->playing()) { entry->set_playing(true); base::AtomicRefCountInc(&num_playing_streams_); } } else { media_observer->OnAudioStreamStopped(render_process_id_, entry->render_frame_id(), entry->stream_id()); if (entry->playing()) { entry->set_playing(false); base::AtomicRefCountDec(&num_playing_streams_); } } } } RenderViewHost::AudioOutputControllerList AudioRendererHost::DoGetOutputControllers(int render_view_id) const { DCHECK_CURRENTLY_ON(BrowserThread::IO); RenderViewHost::AudioOutputControllerList controllers; AudioEntryMap::const_iterator it = audio_entries_.begin(); for (; it != audio_entries_.end(); ++it) { AudioEntry* entry = it->second; if (entry->render_view_id() == render_view_id) controllers.push_back(entry->controller()); } return controllers; } /////////////////////////////////////////////////////////////////////////////// // IPC Messages handler bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message) IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream) IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream) IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void AudioRendererHost::OnCreateStream( int stream_id, int render_view_id, int render_frame_id, int session_id, const media::AudioParameters& params) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DVLOG(1) << "AudioRendererHost@" << this << "::OnCreateStream(stream_id=" << stream_id << ", render_view_id=" << render_view_id << ", session_id=" << session_id << ")"; DCHECK_GT(render_view_id, 0); DCHECK_GT(render_frame_id, 0); // media::AudioParameters is validated in the deserializer. if (LookupById(stream_id) != NULL) { SendErrorMessage(stream_id); return; } // Initialize the |output_device_id| to an empty string which indicates that // the default device should be used. If a StreamDeviceInfo instance was found // though, then we use the matched output device. std::string output_device_id; const StreamDeviceInfo* info = media_stream_manager_-> audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); if (info) output_device_id = info->device.matched_output_device_id; // Create the shared memory and share with the renderer process. // For synchronized I/O (if input_channels > 0) then we allocate // extra memory after the output data for the input data. uint32 shared_memory_size = AudioBus::CalculateMemorySize(params); scoped_ptr shared_memory(new base::SharedMemory()); if (!shared_memory->CreateAndMapAnonymous(shared_memory_size)) { SendErrorMessage(stream_id); return; } scoped_ptr reader( new AudioSyncReader(shared_memory.get(), params)); if (!reader->Init()) { SendErrorMessage(stream_id); return; } MediaObserver* const media_observer = GetContentClient()->browser()->GetMediaObserver(); if (media_observer) media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id); scoped_ptr entry(new AudioEntry( this, stream_id, render_view_id, render_frame_id, params, output_device_id, shared_memory.Pass(), reader.PassAs())); if (mirroring_manager_) { mirroring_manager_->AddDiverter( render_process_id_, entry->render_view_id(), entry->controller()); } audio_entries_.insert(std::make_pair(stream_id, entry.release())); audio_log_->OnCreated(stream_id, params, output_device_id); } void AudioRendererHost::OnPlayStream(int stream_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); AudioEntry* entry = LookupById(stream_id); if (!entry) { SendErrorMessage(stream_id); return; } entry->controller()->Play(); audio_log_->OnStarted(stream_id); } void AudioRendererHost::OnPauseStream(int stream_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); AudioEntry* entry = LookupById(stream_id); if (!entry) { SendErrorMessage(stream_id); return; } entry->controller()->Pause(); audio_log_->OnStopped(stream_id); } void AudioRendererHost::OnSetVolume(int stream_id, double volume) { DCHECK_CURRENTLY_ON(BrowserThread::IO); AudioEntry* entry = LookupById(stream_id); if (!entry) { SendErrorMessage(stream_id); return; } // Make sure the volume is valid. if (volume < 0 || volume > 1.0) return; entry->controller()->SetVolume(volume); audio_log_->OnSetVolume(stream_id, volume); } void AudioRendererHost::SendErrorMessage(int stream_id) { Send(new AudioMsg_NotifyStreamStateChanged( stream_id, media::AudioOutputIPCDelegate::kError)); } void AudioRendererHost::OnCloseStream(int stream_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // Prevent oustanding callbacks from attempting to close/delete the same // AudioEntry twice. AudioEntryMap::iterator i = audio_entries_.find(stream_id); if (i == audio_entries_.end()) return; scoped_ptr entry(i->second); audio_entries_.erase(i); media::AudioOutputController* const controller = entry->controller(); if (mirroring_manager_) { mirroring_manager_->RemoveDiverter( render_process_id_, entry->render_view_id(), controller); } controller->Close( base::Bind(&AudioRendererHost::DeleteEntry, this, base::Passed(&entry))); audio_log_->OnClosed(stream_id); } void AudioRendererHost::DeleteEntry(scoped_ptr entry) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // At this point, make the final "say" in audio playback state. MediaObserver* const media_observer = GetContentClient()->browser()->GetMediaObserver(); if (media_observer) { media_observer->OnAudioStreamStopped(render_process_id_, entry->render_frame_id(), entry->stream_id()); if (entry->playing()) base::AtomicRefCountDec(&num_playing_streams_); } } void AudioRendererHost::ReportErrorAndClose(int stream_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // Make sure this isn't a stray callback executing after the stream has been // closed, so error notifications aren't sent after clients believe the stream // is closed. if (!LookupById(stream_id)) return; SendErrorMessage(stream_id); audio_log_->OnError(stream_id); OnCloseStream(stream_id); } AudioRendererHost::AudioEntry* AudioRendererHost::LookupById(int stream_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); AudioEntryMap::const_iterator i = audio_entries_.find(stream_id); return i != audio_entries_.end() ? i->second : NULL; } bool AudioRendererHost::HasActiveAudio() { return !base::AtomicRefCountIsZero(&num_playing_streams_); } } // namespace content