audio_message_filter.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/renderer/media/audio_message_filter.h" 6 7#include "base/bind.h" 8#include "base/message_loop_proxy.h" 9#include "content/common/media/audio_messages.h" 10#include "content/renderer/render_thread_impl.h" 11#include "ipc/ipc_logging.h" 12 13namespace content { 14 15namespace { 16const int kStreamIDNotSet = -1; 17} 18 19class AudioMessageFilter::AudioOutputIPCImpl 20 : public NON_EXPORTED_BASE(media::AudioOutputIPC) { 21 public: 22 AudioOutputIPCImpl(const scoped_refptr<AudioMessageFilter>& filter, 23 int render_view_id); 24 virtual ~AudioOutputIPCImpl(); 25 26 // media::AudioOutputIPC implementation. 27 virtual void CreateStream(media::AudioOutputIPCDelegate* delegate, 28 const media::AudioParameters& params) OVERRIDE; 29 virtual void PlayStream() OVERRIDE; 30 virtual void PauseStream() OVERRIDE; 31 virtual void CloseStream() OVERRIDE; 32 virtual void SetVolume(double volume) OVERRIDE; 33 34 private: 35 const scoped_refptr<AudioMessageFilter> filter_; 36 const int render_view_id_; 37 int stream_id_; 38}; 39 40AudioMessageFilter* AudioMessageFilter::g_filter = NULL; 41 42AudioMessageFilter::AudioMessageFilter( 43 const scoped_refptr<base::MessageLoopProxy>& io_message_loop) 44 : channel_(NULL), 45 audio_hardware_config_(NULL), 46 io_message_loop_(io_message_loop) { 47 DCHECK(!g_filter); 48 g_filter = this; 49} 50 51AudioMessageFilter::~AudioMessageFilter() { 52 DCHECK_EQ(g_filter, this); 53 g_filter = NULL; 54} 55 56// static 57AudioMessageFilter* AudioMessageFilter::Get() { 58 return g_filter; 59} 60 61AudioMessageFilter::AudioOutputIPCImpl::AudioOutputIPCImpl( 62 const scoped_refptr<AudioMessageFilter>& filter, int render_view_id) 63 : filter_(filter), 64 render_view_id_(render_view_id), 65 stream_id_(kStreamIDNotSet) {} 66 67AudioMessageFilter::AudioOutputIPCImpl::~AudioOutputIPCImpl() {} 68 69scoped_ptr<media::AudioOutputIPC> AudioMessageFilter::CreateAudioOutputIPC( 70 int render_view_id) { 71 DCHECK_GT(render_view_id, 0); 72 return scoped_ptr<media::AudioOutputIPC>( 73 new AudioOutputIPCImpl(this, render_view_id)); 74} 75 76void AudioMessageFilter::AudioOutputIPCImpl::CreateStream( 77 media::AudioOutputIPCDelegate* delegate, 78 const media::AudioParameters& params) { 79 DCHECK(filter_->io_message_loop_->BelongsToCurrentThread()); 80 DCHECK(delegate); 81 DCHECK_EQ(stream_id_, kStreamIDNotSet); 82 stream_id_ = filter_->delegates_.Add(delegate); 83 filter_->Send(new AudioHostMsg_CreateStream( 84 stream_id_, render_view_id_, params)); 85} 86 87void AudioMessageFilter::AudioOutputIPCImpl::PlayStream() { 88 DCHECK_NE(stream_id_, kStreamIDNotSet); 89 filter_->Send(new AudioHostMsg_PlayStream(stream_id_)); 90} 91 92void AudioMessageFilter::AudioOutputIPCImpl::PauseStream() { 93 DCHECK_NE(stream_id_, kStreamIDNotSet); 94 filter_->Send(new AudioHostMsg_PauseStream(stream_id_)); 95} 96 97void AudioMessageFilter::AudioOutputIPCImpl::CloseStream() { 98 DCHECK(filter_->io_message_loop_->BelongsToCurrentThread()); 99 DCHECK_NE(stream_id_, kStreamIDNotSet); 100 filter_->Send(new AudioHostMsg_CloseStream(stream_id_)); 101 filter_->delegates_.Remove(stream_id_); 102 stream_id_ = kStreamIDNotSet; 103} 104 105void AudioMessageFilter::AudioOutputIPCImpl::SetVolume(double volume) { 106 DCHECK_NE(stream_id_, kStreamIDNotSet); 107 filter_->Send(new AudioHostMsg_SetVolume(stream_id_, volume)); 108} 109 110void AudioMessageFilter::Send(IPC::Message* message) { 111 DCHECK(io_message_loop_->BelongsToCurrentThread()); 112 if (!channel_) { 113 delete message; 114 } else { 115 channel_->Send(message); 116 } 117} 118 119bool AudioMessageFilter::OnMessageReceived(const IPC::Message& message) { 120 DCHECK(io_message_loop_->BelongsToCurrentThread()); 121 bool handled = true; 122 IPC_BEGIN_MESSAGE_MAP(AudioMessageFilter, message) 123 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated, OnStreamCreated) 124 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged, OnStreamStateChanged) 125 IPC_MESSAGE_HANDLER(AudioMsg_NotifyDeviceChanged, OnOutputDeviceChanged) 126 IPC_MESSAGE_UNHANDLED(handled = false) 127 IPC_END_MESSAGE_MAP() 128 return handled; 129} 130 131void AudioMessageFilter::OnFilterAdded(IPC::Channel* channel) { 132 DCHECK(io_message_loop_->BelongsToCurrentThread()); 133 channel_ = channel; 134} 135 136void AudioMessageFilter::OnFilterRemoved() { 137 DCHECK(io_message_loop_->BelongsToCurrentThread()); 138 139 // Once removed, a filter will not be used again. At this time all 140 // delegates must be notified so they release their reference. 141 OnChannelClosing(); 142} 143 144void AudioMessageFilter::OnChannelClosing() { 145 DCHECK(io_message_loop_->BelongsToCurrentThread()); 146 channel_ = NULL; 147 148 DLOG_IF(WARNING, !delegates_.IsEmpty()) 149 << "Not all audio devices have been closed."; 150 151 IDMap<media::AudioOutputIPCDelegate>::iterator it(&delegates_); 152 while (!it.IsAtEnd()) { 153 it.GetCurrentValue()->OnIPCClosed(); 154 delegates_.Remove(it.GetCurrentKey()); 155 it.Advance(); 156 } 157} 158 159void AudioMessageFilter::OnStreamCreated( 160 int stream_id, 161 base::SharedMemoryHandle handle, 162#if defined(OS_WIN) 163 base::SyncSocket::Handle socket_handle, 164#else 165 base::FileDescriptor socket_descriptor, 166#endif 167 uint32 length) { 168 DCHECK(io_message_loop_->BelongsToCurrentThread()); 169 170#if !defined(OS_WIN) 171 base::SyncSocket::Handle socket_handle = socket_descriptor.fd; 172#endif 173 174 media::AudioOutputIPCDelegate* delegate = delegates_.Lookup(stream_id); 175 if (!delegate) { 176 DLOG(WARNING) << "Got OnStreamCreated() event for a non-existent or removed" 177 << " audio renderer. (stream_id=" << stream_id << ")."; 178 base::SharedMemory::CloseHandle(handle); 179 base::SyncSocket socket(socket_handle); 180 return; 181 } 182 delegate->OnStreamCreated(handle, socket_handle, length); 183} 184 185void AudioMessageFilter::OnStreamStateChanged( 186 int stream_id, media::AudioOutputIPCDelegate::State state) { 187 DCHECK(io_message_loop_->BelongsToCurrentThread()); 188 media::AudioOutputIPCDelegate* delegate = delegates_.Lookup(stream_id); 189 if (!delegate) { 190 DLOG(WARNING) << "Got OnStreamStateChanged() event for a non-existent or" 191 << " removed audio renderer. State: " << state; 192 return; 193 } 194 delegate->OnStateChanged(state); 195} 196 197void AudioMessageFilter::OnOutputDeviceChanged(int stream_id, 198 int new_buffer_size, 199 int new_sample_rate) { 200 DCHECK(io_message_loop_->BelongsToCurrentThread()); 201 base::AutoLock auto_lock(lock_); 202 203 // Ignore the message if an audio hardware config hasn't been created; this 204 // can occur if the renderer is using the high latency audio path. 205 // TODO(dalecurtis): After http://crbug.com/173435 is fixed, convert to CHECK. 206 if (!audio_hardware_config_) 207 return; 208 209 // TODO(crogers): fix OnOutputDeviceChanged() to pass AudioParameters. 210 media::ChannelLayout channel_layout = 211 audio_hardware_config_->GetOutputChannelLayout(); 212 int channels = audio_hardware_config_->GetOutputChannels(); 213 214 media::AudioParameters output_params; 215 output_params.Reset( 216 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 217 channel_layout, 218 channels, 219 0, 220 new_sample_rate, 221 16, 222 new_buffer_size); 223 224 audio_hardware_config_->UpdateOutputConfig(output_params); 225} 226 227void AudioMessageFilter::SetAudioHardwareConfig( 228 media::AudioHardwareConfig* config) { 229 base::AutoLock auto_lock(lock_); 230 audio_hardware_config_ = config; 231} 232 233} // namespace content 234