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