| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/media/audio_device.h" | 5 #include "content/renderer/media/audio_device.h" |
| 6 | 6 |
| 7 #include "base/debug/trace_event.h" | 7 #include "base/debug/trace_event.h" |
| 8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 9 #include "base/threading/thread_restrictions.h" | 9 #include "base/threading/thread_restrictions.h" |
| 10 #include "base/time.h" | 10 #include "base/time.h" |
| 11 #include "content/common/media/audio_messages.h" | |
| 12 #include "content/common/view_messages.h" | |
| 13 #include "media/audio/audio_output_controller.h" | 11 #include "media/audio/audio_output_controller.h" |
| 14 #include "media/audio/audio_util.h" | 12 #include "media/audio/audio_util.h" |
| 15 | 13 |
| 16 using media::AudioRendererSink; | 14 using media::AudioRendererSink; |
| 17 | 15 |
| 18 // Takes care of invoking the render callback on the audio thread. | 16 // Takes care of invoking the render callback on the audio thread. |
| 19 // An instance of this class is created for each capture stream in | 17 // An instance of this class is created for each capture stream in |
| 20 // OnStreamCreated(). | 18 // OnStreamCreated(). |
| 21 class AudioDevice::AudioThreadCallback | 19 class AudioDevice::AudioThreadCallback |
| 22 : public AudioDeviceThread::Callback { | 20 : public AudioDeviceThread::Callback { |
| 23 public: | 21 public: |
| 24 AudioThreadCallback(const media::AudioParameters& audio_parameters, | 22 AudioThreadCallback(const media::AudioParameters& audio_parameters, |
| 25 base::SharedMemoryHandle memory, | 23 base::SharedMemoryHandle memory, |
| 26 int memory_length, | 24 int memory_length, |
| 27 AudioRendererSink::RenderCallback* render_callback); | 25 AudioRendererSink::RenderCallback* render_callback); |
| 28 virtual ~AudioThreadCallback(); | 26 virtual ~AudioThreadCallback(); |
| 29 | 27 |
| 30 virtual void MapSharedMemory() OVERRIDE; | 28 virtual void MapSharedMemory() OVERRIDE; |
| 31 | 29 |
| 32 // Called whenever we receive notifications about pending data. | 30 // Called whenever we receive notifications about pending data. |
| 33 virtual void Process(int pending_data) OVERRIDE; | 31 virtual void Process(int pending_data) OVERRIDE; |
| 34 | 32 |
| 35 private: | 33 private: |
| 36 AudioRendererSink::RenderCallback* render_callback_; | 34 AudioRendererSink::RenderCallback* render_callback_; |
| 37 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | 35 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
| 38 }; | 36 }; |
| 39 | 37 |
| 40 AudioDevice::AudioDevice( | 38 AudioDevice::AudioDevice( |
| 39 media::AudioOutputIPC* ipc, |
| 41 const scoped_refptr<base::MessageLoopProxy>& io_loop) | 40 const scoped_refptr<base::MessageLoopProxy>& io_loop) |
| 42 : ScopedLoopObserver(io_loop), | 41 : ScopedLoopObserver(io_loop), |
| 43 callback_(NULL), | 42 callback_(NULL), |
| 43 ipc_(ipc), |
| 44 stream_id_(0), | 44 stream_id_(0), |
| 45 play_on_start_(true), | 45 play_on_start_(true), |
| 46 is_started_(false) { | 46 is_started_(false) { |
| 47 // Use the filter instance already created on the main render thread. | 47 CHECK(ipc_); |
| 48 CHECK(AudioMessageFilter::Get()) << "Invalid audio message filter."; | |
| 49 filter_ = AudioMessageFilter::Get(); | |
| 50 } | 48 } |
| 51 | 49 |
| 52 void AudioDevice::Initialize(const media::AudioParameters& params, | 50 void AudioDevice::Initialize(const media::AudioParameters& params, |
| 53 RenderCallback* callback) { | 51 RenderCallback* callback) { |
| 54 CHECK_EQ(0, stream_id_) << | 52 CHECK_EQ(0, stream_id_) << |
| 55 "AudioDevice::Initialize() must be called before Start()"; | 53 "AudioDevice::Initialize() must be called before Start()"; |
| 56 | 54 |
| 57 CHECK(!callback_); // Calling Initialize() twice? | 55 CHECK(!callback_); // Calling Initialize() twice? |
| 58 | 56 |
| 59 audio_parameters_ = params; | 57 audio_parameters_ = params; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 return true; | 103 return true; |
| 106 } | 104 } |
| 107 | 105 |
| 108 void AudioDevice::CreateStreamOnIOThread(const media::AudioParameters& params) { | 106 void AudioDevice::CreateStreamOnIOThread(const media::AudioParameters& params) { |
| 109 DCHECK(message_loop()->BelongsToCurrentThread()); | 107 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 110 // Make sure we don't create the stream more than once. | 108 // Make sure we don't create the stream more than once. |
| 111 DCHECK_EQ(0, stream_id_); | 109 DCHECK_EQ(0, stream_id_); |
| 112 if (stream_id_) | 110 if (stream_id_) |
| 113 return; | 111 return; |
| 114 | 112 |
| 115 stream_id_ = filter_->AddDelegate(this); | 113 stream_id_ = ipc_->AddDelegate(this); |
| 116 Send(new AudioHostMsg_CreateStream(stream_id_, params)); | 114 ipc_->CreateStream(stream_id_, params); |
| 117 } | 115 } |
| 118 | 116 |
| 119 void AudioDevice::PlayOnIOThread() { | 117 void AudioDevice::PlayOnIOThread() { |
| 120 DCHECK(message_loop()->BelongsToCurrentThread()); | 118 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 121 if (stream_id_ && is_started_) | 119 if (stream_id_ && is_started_) |
| 122 Send(new AudioHostMsg_PlayStream(stream_id_)); | 120 ipc_->PlayStream(stream_id_); |
| 123 else | 121 else |
| 124 play_on_start_ = true; | 122 play_on_start_ = true; |
| 125 } | 123 } |
| 126 | 124 |
| 127 void AudioDevice::PauseOnIOThread(bool flush) { | 125 void AudioDevice::PauseOnIOThread(bool flush) { |
| 128 DCHECK(message_loop()->BelongsToCurrentThread()); | 126 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 129 if (stream_id_ && is_started_) { | 127 if (stream_id_ && is_started_) { |
| 130 Send(new AudioHostMsg_PauseStream(stream_id_)); | 128 ipc_->PauseStream(stream_id_); |
| 131 if (flush) | 129 if (flush) |
| 132 Send(new AudioHostMsg_FlushStream(stream_id_)); | 130 ipc_->FlushStream(stream_id_); |
| 133 } else { | 131 } else { |
| 134 // Note that |flush| isn't relevant here since this is the case where | 132 // Note that |flush| isn't relevant here since this is the case where |
| 135 // the stream is first starting. | 133 // the stream is first starting. |
| 136 play_on_start_ = false; | 134 play_on_start_ = false; |
| 137 } | 135 } |
| 138 } | 136 } |
| 139 | 137 |
| 140 void AudioDevice::ShutDownOnIOThread() { | 138 void AudioDevice::ShutDownOnIOThread() { |
| 141 DCHECK(message_loop()->BelongsToCurrentThread()); | 139 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 142 | 140 |
| 143 // Make sure we don't call shutdown more than once. | 141 // Make sure we don't call shutdown more than once. |
| 144 if (stream_id_) { | 142 if (stream_id_) { |
| 145 is_started_ = false; | 143 is_started_ = false; |
| 146 | 144 |
| 147 filter_->RemoveDelegate(stream_id_); | 145 ipc_->RemoveDelegate(stream_id_); |
| 148 Send(new AudioHostMsg_CloseStream(stream_id_)); | 146 ipc_->CloseStream(stream_id_); |
| 149 stream_id_ = 0; | 147 stream_id_ = 0; |
| 150 } | 148 } |
| 151 | 149 |
| 152 // We can run into an issue where ShutDownOnIOThread is called right after | 150 // We can run into an issue where ShutDownOnIOThread is called right after |
| 153 // OnStreamCreated is called in cases where Start/Stop are called before we | 151 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 154 // get the OnStreamCreated callback. To handle that corner case, we call | 152 // get the OnStreamCreated callback. To handle that corner case, we call |
| 155 // Stop(). In most cases, the thread will already be stopped. | 153 // Stop(). In most cases, the thread will already be stopped. |
| 156 // Another situation is when the IO thread goes away before Stop() is called | 154 // Another situation is when the IO thread goes away before Stop() is called |
| 157 // in which case, we cannot use the message loop to close the thread handle | 155 // in which case, we cannot use the message loop to close the thread handle |
| 158 // and can't not rely on the main thread existing either. | 156 // and can't not rely on the main thread existing either. |
| 159 base::ThreadRestrictions::ScopedAllowIO allow_io; | 157 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 160 audio_thread_.Stop(NULL); | 158 audio_thread_.Stop(NULL); |
| 161 audio_callback_.reset(); | 159 audio_callback_.reset(); |
| 162 } | 160 } |
| 163 | 161 |
| 164 void AudioDevice::SetVolumeOnIOThread(double volume) { | 162 void AudioDevice::SetVolumeOnIOThread(double volume) { |
| 165 DCHECK(message_loop()->BelongsToCurrentThread()); | 163 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 166 if (stream_id_) | 164 if (stream_id_) |
| 167 Send(new AudioHostMsg_SetVolume(stream_id_, volume)); | 165 ipc_->SetVolume(stream_id_, volume); |
| 168 } | 166 } |
| 169 | 167 |
| 170 void AudioDevice::OnStateChanged(AudioStreamState state) { | 168 void AudioDevice::OnStateChanged(media::AudioOutputIPCDelegate::State state) { |
| 171 DCHECK(message_loop()->BelongsToCurrentThread()); | 169 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 172 | 170 |
| 173 // Do nothing if the stream has been closed. | 171 // Do nothing if the stream has been closed. |
| 174 if (!stream_id_) | 172 if (!stream_id_) |
| 175 return; | 173 return; |
| 176 | 174 |
| 177 if (state == kAudioStreamError) { | 175 if (state == media::AudioOutputIPCDelegate::kError) { |
| 178 DLOG(WARNING) << "AudioDevice::OnStateChanged(kError)"; | 176 DLOG(WARNING) << "AudioDevice::OnStateChanged(kError)"; |
| 179 // Don't dereference the callback object if the audio thread | 177 // Don't dereference the callback object if the audio thread |
| 180 // is stopped or stopping. That could mean that the callback | 178 // is stopped or stopping. That could mean that the callback |
| 181 // object has been deleted. | 179 // object has been deleted. |
| 182 // TODO(tommi): Add an explicit contract for clearing the callback | 180 // TODO(tommi): Add an explicit contract for clearing the callback |
| 183 // object. Possibly require calling Initialize again or provide | 181 // object. Possibly require calling Initialize again or provide |
| 184 // a callback object via Start() and clear it in Stop(). | 182 // a callback object via Start() and clear it in Stop(). |
| 185 if (!audio_thread_.IsStopped()) | 183 if (!audio_thread_.IsStopped()) |
| 186 callback_->OnRenderError(); | 184 callback_->OnRenderError(); |
| 187 } | 185 } |
| 188 } | 186 } |
| 189 | 187 |
| 190 void AudioDevice::OnStreamCreated( | 188 void AudioDevice::OnStreamCreated( |
| 191 base::SharedMemoryHandle handle, | 189 base::SharedMemoryHandle handle, |
| 192 base::SyncSocket::Handle socket_handle, | 190 base::SyncSocket::Handle socket_handle, |
| 193 uint32 length) { | 191 int length) { |
| 194 DCHECK(message_loop()->BelongsToCurrentThread()); | 192 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 195 // TODO(vrk): Remove cast when |length| is int instead of uint32. | 193 DCHECK_GE(length, audio_parameters_.GetBytesPerBuffer()); |
| 196 DCHECK_GE(length, static_cast<uint32>(audio_parameters_.GetBytesPerBuffer())); | |
| 197 #if defined(OS_WIN) | 194 #if defined(OS_WIN) |
| 198 DCHECK(handle); | 195 DCHECK(handle); |
| 199 DCHECK(socket_handle); | 196 DCHECK(socket_handle); |
| 200 #else | 197 #else |
| 201 DCHECK_GE(handle.fd, 0); | 198 DCHECK_GE(handle.fd, 0); |
| 202 DCHECK_GE(socket_handle, 0); | 199 DCHECK_GE(socket_handle, 0); |
| 203 #endif | 200 #endif |
| 204 | 201 |
| 205 base::AutoLock auto_lock(audio_thread_lock_); | 202 base::AutoLock auto_lock(audio_thread_lock_); |
| 206 | 203 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 217 handle, length, callback_)); | 214 handle, length, callback_)); |
| 218 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioDevice"); | 215 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioDevice"); |
| 219 | 216 |
| 220 // We handle the case where Play() and/or Pause() may have been called | 217 // We handle the case where Play() and/or Pause() may have been called |
| 221 // multiple times before OnStreamCreated() gets called. | 218 // multiple times before OnStreamCreated() gets called. |
| 222 is_started_ = true; | 219 is_started_ = true; |
| 223 if (play_on_start_) | 220 if (play_on_start_) |
| 224 PlayOnIOThread(); | 221 PlayOnIOThread(); |
| 225 } | 222 } |
| 226 | 223 |
| 227 void AudioDevice::Send(IPC::Message* message) { | 224 void AudioDevice::OnIPCClosed() { |
| 228 filter_->Send(message); | 225 ipc_ = NULL; |
| 229 } | 226 } |
| 230 | 227 |
| 231 void AudioDevice::WillDestroyCurrentMessageLoop() { | 228 void AudioDevice::WillDestroyCurrentMessageLoop() { |
| 232 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; | 229 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; |
| 233 ShutDownOnIOThread(); | 230 ShutDownOnIOThread(); |
| 234 } | 231 } |
| 235 | 232 |
| 236 // AudioDevice::AudioThreadCallback | 233 // AudioDevice::AudioThreadCallback |
| 237 | 234 |
| 238 AudioDevice::AudioThreadCallback::AudioThreadCallback( | 235 AudioDevice::AudioThreadCallback::AudioThreadCallback( |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 272 // Interleave, scale, and clip to int. | 269 // Interleave, scale, and clip to int. |
| 273 // TODO(crogers/vrk): Figure out a way to avoid the float -> int -> float | 270 // TODO(crogers/vrk): Figure out a way to avoid the float -> int -> float |
| 274 // conversions that happen in the <audio> and WebRTC scenarios. | 271 // conversions that happen in the <audio> and WebRTC scenarios. |
| 275 media::InterleaveFloatToInt(audio_data_, shared_memory_.memory(), | 272 media::InterleaveFloatToInt(audio_data_, shared_memory_.memory(), |
| 276 num_frames, audio_parameters_.bits_per_sample() / 8); | 273 num_frames, audio_parameters_.bits_per_sample() / 8); |
| 277 | 274 |
| 278 // Let the host know we are done. | 275 // Let the host know we are done. |
| 279 media::SetActualDataSizeInBytes(&shared_memory_, memory_length_, | 276 media::SetActualDataSizeInBytes(&shared_memory_, memory_length_, |
| 280 num_frames * audio_parameters_.GetBytesPerFrame()); | 277 num_frames * audio_parameters_.GetBytesPerFrame()); |
| 281 } | 278 } |
| OLD | NEW |