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