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