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 "media/audio/audio_output_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 "media/audio/audio_output_controller.h" | 11 #include "media/audio/audio_output_controller.h" |
12 #include "media/audio/audio_util.h" | 12 #include "media/audio/audio_util.h" |
13 | 13 |
14 using media::AudioRendererSink; | 14 namespace media { |
15 | 15 |
16 // Takes care of invoking the render callback on the audio thread. | 16 // Takes care of invoking the render callback on the audio thread. |
17 // 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 |
18 // OnStreamCreated(). | 18 // OnStreamCreated(). |
19 class AudioDevice::AudioThreadCallback | 19 class AudioOutputDevice::AudioThreadCallback |
20 : public AudioDeviceThread::Callback { | 20 : public AudioDeviceThread::Callback { |
21 public: | 21 public: |
22 AudioThreadCallback(const media::AudioParameters& audio_parameters, | 22 AudioThreadCallback(const AudioParameters& audio_parameters, |
23 base::SharedMemoryHandle memory, | 23 base::SharedMemoryHandle memory, |
24 int memory_length, | 24 int memory_length, |
25 AudioRendererSink::RenderCallback* render_callback); | 25 AudioRendererSink::RenderCallback* render_callback); |
26 virtual ~AudioThreadCallback(); | 26 virtual ~AudioThreadCallback(); |
27 | 27 |
28 virtual void MapSharedMemory() OVERRIDE; | 28 virtual void MapSharedMemory() OVERRIDE; |
29 | 29 |
30 // Called whenever we receive notifications about pending data. | 30 // Called whenever we receive notifications about pending data. |
31 virtual void Process(int pending_data) OVERRIDE; | 31 virtual void Process(int pending_data) OVERRIDE; |
32 | 32 |
33 private: | 33 private: |
34 AudioRendererSink::RenderCallback* render_callback_; | 34 AudioRendererSink::RenderCallback* render_callback_; |
35 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | 35 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
36 }; | 36 }; |
37 | 37 |
38 AudioDevice::AudioDevice( | 38 AudioOutputDevice::AudioOutputDevice( |
39 media::AudioOutputIPC* ipc, | 39 AudioOutputIPC* ipc, |
40 const scoped_refptr<base::MessageLoopProxy>& io_loop) | 40 const scoped_refptr<base::MessageLoopProxy>& io_loop) |
41 : ScopedLoopObserver(io_loop), | 41 : ScopedLoopObserver(io_loop), |
42 callback_(NULL), | 42 callback_(NULL), |
43 ipc_(ipc), | 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 CHECK(ipc_); | 47 CHECK(ipc_); |
48 } | 48 } |
49 | 49 |
50 void AudioDevice::Initialize(const media::AudioParameters& params, | 50 void AudioOutputDevice::Initialize(const AudioParameters& params, |
51 RenderCallback* callback) { | 51 RenderCallback* callback) { |
52 CHECK_EQ(0, stream_id_) << | 52 CHECK_EQ(0, stream_id_) << |
53 "AudioDevice::Initialize() must be called before Start()"; | 53 "AudioOutputDevice::Initialize() must be called before Start()"; |
54 | 54 |
55 CHECK(!callback_); // Calling Initialize() twice? | 55 CHECK(!callback_); // Calling Initialize() twice? |
56 | 56 |
57 audio_parameters_ = params; | 57 audio_parameters_ = params; |
58 callback_ = callback; | 58 callback_ = callback; |
59 } | 59 } |
60 | 60 |
61 AudioDevice::~AudioDevice() { | 61 AudioOutputDevice::~AudioOutputDevice() { |
62 // The current design requires that the user calls Stop() before deleting | 62 // The current design requires that the user calls Stop() before deleting |
63 // this class. | 63 // this class. |
64 CHECK_EQ(0, stream_id_); | 64 CHECK_EQ(0, stream_id_); |
65 } | 65 } |
66 | 66 |
67 void AudioDevice::Start() { | 67 void AudioOutputDevice::Start() { |
68 DCHECK(callback_) << "Initialize hasn't been called"; | 68 DCHECK(callback_) << "Initialize hasn't been called"; |
69 message_loop()->PostTask(FROM_HERE, | 69 message_loop()->PostTask(FROM_HERE, |
70 base::Bind(&AudioDevice::CreateStreamOnIOThread, this, | 70 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, |
71 audio_parameters_)); | 71 audio_parameters_)); |
72 } | 72 } |
73 | 73 |
74 void AudioDevice::Stop() { | 74 void AudioOutputDevice::Stop() { |
75 { | 75 { |
76 base::AutoLock auto_lock(audio_thread_lock_); | 76 base::AutoLock auto_lock(audio_thread_lock_); |
77 audio_thread_.Stop(MessageLoop::current()); | 77 audio_thread_.Stop(MessageLoop::current()); |
78 } | 78 } |
79 | 79 |
80 message_loop()->PostTask(FROM_HERE, | 80 message_loop()->PostTask(FROM_HERE, |
81 base::Bind(&AudioDevice::ShutDownOnIOThread, this)); | 81 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this)); |
82 } | 82 } |
83 | 83 |
84 void AudioDevice::Play() { | 84 void AudioOutputDevice::Play() { |
85 message_loop()->PostTask(FROM_HERE, | 85 message_loop()->PostTask(FROM_HERE, |
86 base::Bind(&AudioDevice::PlayOnIOThread, this)); | 86 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); |
87 } | 87 } |
88 | 88 |
89 void AudioDevice::Pause(bool flush) { | 89 void AudioOutputDevice::Pause(bool flush) { |
90 message_loop()->PostTask(FROM_HERE, | 90 message_loop()->PostTask(FROM_HERE, |
91 base::Bind(&AudioDevice::PauseOnIOThread, this, flush)); | 91 base::Bind(&AudioOutputDevice::PauseOnIOThread, this, flush)); |
92 } | 92 } |
93 | 93 |
94 bool AudioDevice::SetVolume(double volume) { | 94 bool AudioOutputDevice::SetVolume(double volume) { |
95 if (volume < 0 || volume > 1.0) | 95 if (volume < 0 || volume > 1.0) |
96 return false; | 96 return false; |
97 | 97 |
98 if (!message_loop()->PostTask(FROM_HERE, | 98 if (!message_loop()->PostTask(FROM_HERE, |
99 base::Bind(&AudioDevice::SetVolumeOnIOThread, this, volume))) { | 99 base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) { |
100 return false; | 100 return false; |
101 } | 101 } |
102 | 102 |
103 return true; | 103 return true; |
104 } | 104 } |
105 | 105 |
106 void AudioDevice::CreateStreamOnIOThread(const media::AudioParameters& params) { | 106 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) { |
107 DCHECK(message_loop()->BelongsToCurrentThread()); | 107 DCHECK(message_loop()->BelongsToCurrentThread()); |
108 // Make sure we don't create the stream more than once. | 108 // Make sure we don't create the stream more than once. |
109 DCHECK_EQ(0, stream_id_); | 109 DCHECK_EQ(0, stream_id_); |
110 if (stream_id_) | 110 if (stream_id_) |
111 return; | 111 return; |
112 | 112 |
113 stream_id_ = ipc_->AddDelegate(this); | 113 stream_id_ = ipc_->AddDelegate(this); |
114 ipc_->CreateStream(stream_id_, params); | 114 ipc_->CreateStream(stream_id_, params); |
115 } | 115 } |
116 | 116 |
117 void AudioDevice::PlayOnIOThread() { | 117 void AudioOutputDevice::PlayOnIOThread() { |
118 DCHECK(message_loop()->BelongsToCurrentThread()); | 118 DCHECK(message_loop()->BelongsToCurrentThread()); |
119 if (stream_id_ && is_started_) | 119 if (stream_id_ && is_started_) |
120 ipc_->PlayStream(stream_id_); | 120 ipc_->PlayStream(stream_id_); |
121 else | 121 else |
122 play_on_start_ = true; | 122 play_on_start_ = true; |
123 } | 123 } |
124 | 124 |
125 void AudioDevice::PauseOnIOThread(bool flush) { | 125 void AudioOutputDevice::PauseOnIOThread(bool flush) { |
126 DCHECK(message_loop()->BelongsToCurrentThread()); | 126 DCHECK(message_loop()->BelongsToCurrentThread()); |
127 if (stream_id_ && is_started_) { | 127 if (stream_id_ && is_started_) { |
128 ipc_->PauseStream(stream_id_); | 128 ipc_->PauseStream(stream_id_); |
129 if (flush) | 129 if (flush) |
130 ipc_->FlushStream(stream_id_); | 130 ipc_->FlushStream(stream_id_); |
131 } else { | 131 } else { |
132 // 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 |
133 // the stream is first starting. | 133 // the stream is first starting. |
134 play_on_start_ = false; | 134 play_on_start_ = false; |
135 } | 135 } |
136 } | 136 } |
137 | 137 |
138 void AudioDevice::ShutDownOnIOThread() { | 138 void AudioOutputDevice::ShutDownOnIOThread() { |
139 DCHECK(message_loop()->BelongsToCurrentThread()); | 139 DCHECK(message_loop()->BelongsToCurrentThread()); |
140 | 140 |
141 // Make sure we don't call shutdown more than once. | 141 // Make sure we don't call shutdown more than once. |
142 if (stream_id_) { | 142 if (stream_id_) { |
143 is_started_ = false; | 143 is_started_ = false; |
144 | 144 |
145 ipc_->CloseStream(stream_id_); | 145 ipc_->CloseStream(stream_id_); |
146 ipc_->RemoveDelegate(stream_id_); | 146 ipc_->RemoveDelegate(stream_id_); |
147 stream_id_ = 0; | 147 stream_id_ = 0; |
148 } | 148 } |
149 | 149 |
150 // 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 |
151 // 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 |
152 // get the OnStreamCreated callback. To handle that corner case, we call | 152 // get the OnStreamCreated callback. To handle that corner case, we call |
153 // Stop(). In most cases, the thread will already be stopped. | 153 // Stop(). In most cases, the thread will already be stopped. |
154 // 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 |
155 // 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 |
156 // and can't not rely on the main thread existing either. | 156 // and can't not rely on the main thread existing either. |
157 base::ThreadRestrictions::ScopedAllowIO allow_io; | 157 base::ThreadRestrictions::ScopedAllowIO allow_io; |
158 audio_thread_.Stop(NULL); | 158 audio_thread_.Stop(NULL); |
159 audio_callback_.reset(); | 159 audio_callback_.reset(); |
160 } | 160 } |
161 | 161 |
162 void AudioDevice::SetVolumeOnIOThread(double volume) { | 162 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { |
163 DCHECK(message_loop()->BelongsToCurrentThread()); | 163 DCHECK(message_loop()->BelongsToCurrentThread()); |
164 if (stream_id_) | 164 if (stream_id_) |
165 ipc_->SetVolume(stream_id_, volume); | 165 ipc_->SetVolume(stream_id_, volume); |
166 } | 166 } |
167 | 167 |
168 void AudioDevice::OnStateChanged(media::AudioOutputIPCDelegate::State state) { | 168 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) { |
169 DCHECK(message_loop()->BelongsToCurrentThread()); | 169 DCHECK(message_loop()->BelongsToCurrentThread()); |
170 | 170 |
171 // Do nothing if the stream has been closed. | 171 // Do nothing if the stream has been closed. |
172 if (!stream_id_) | 172 if (!stream_id_) |
173 return; | 173 return; |
174 | 174 |
175 if (state == media::AudioOutputIPCDelegate::kError) { | 175 if (state == AudioOutputIPCDelegate::kError) { |
176 DLOG(WARNING) << "AudioDevice::OnStateChanged(kError)"; | 176 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)"; |
177 // Don't dereference the callback object if the audio thread | 177 // Don't dereference the callback object if the audio thread |
178 // is stopped or stopping. That could mean that the callback | 178 // is stopped or stopping. That could mean that the callback |
179 // object has been deleted. | 179 // object has been deleted. |
180 // TODO(tommi): Add an explicit contract for clearing the callback | 180 // TODO(tommi): Add an explicit contract for clearing the callback |
181 // object. Possibly require calling Initialize again or provide | 181 // object. Possibly require calling Initialize again or provide |
182 // a callback object via Start() and clear it in Stop(). | 182 // a callback object via Start() and clear it in Stop(). |
183 if (!audio_thread_.IsStopped()) | 183 if (!audio_thread_.IsStopped()) |
184 callback_->OnRenderError(); | 184 callback_->OnRenderError(); |
185 } | 185 } |
186 } | 186 } |
187 | 187 |
188 void AudioDevice::OnStreamCreated( | 188 void AudioOutputDevice::OnStreamCreated( |
189 base::SharedMemoryHandle handle, | 189 base::SharedMemoryHandle handle, |
190 base::SyncSocket::Handle socket_handle, | 190 base::SyncSocket::Handle socket_handle, |
191 int length) { | 191 int length) { |
192 DCHECK(message_loop()->BelongsToCurrentThread()); | 192 DCHECK(message_loop()->BelongsToCurrentThread()); |
193 DCHECK_GE(length, audio_parameters_.GetBytesPerBuffer()); | 193 DCHECK_GE(length, audio_parameters_.GetBytesPerBuffer()); |
194 #if defined(OS_WIN) | 194 #if defined(OS_WIN) |
195 DCHECK(handle); | 195 DCHECK(handle); |
196 DCHECK(socket_handle); | 196 DCHECK(socket_handle); |
197 #else | 197 #else |
198 DCHECK_GE(handle.fd, 0); | 198 DCHECK_GE(handle.fd, 0); |
199 DCHECK_GE(socket_handle, 0); | 199 DCHECK_GE(socket_handle, 0); |
200 #endif | 200 #endif |
201 | 201 |
202 // We should only get this callback if stream_id_ is valid. If it is not, | 202 // We should only get this callback if stream_id_ is valid. If it is not, |
203 // the IPC layer should have closed the shared memory and socket handles | 203 // the IPC layer should have closed the shared memory and socket handles |
204 // for us and not invoked the callback. The basic assertion is that when | 204 // for us and not invoked the callback. The basic assertion is that when |
205 // stream_id_ is 0 the AudioDevice instance is not registered as a delegate | 205 // stream_id_ is 0 the AudioOutputDevice instance is not registered as a |
206 // and hence it should not receive callbacks. | 206 // delegate and hence it should not receive callbacks. |
207 DCHECK(stream_id_); | 207 DCHECK(stream_id_); |
208 | 208 |
209 base::AutoLock auto_lock(audio_thread_lock_); | 209 base::AutoLock auto_lock(audio_thread_lock_); |
210 | 210 |
211 DCHECK(audio_thread_.IsStopped()); | 211 DCHECK(audio_thread_.IsStopped()); |
212 audio_callback_.reset(new AudioDevice::AudioThreadCallback(audio_parameters_, | 212 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( |
213 handle, length, callback_)); | 213 audio_parameters_, handle, length, callback_)); |
214 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioDevice"); | 214 audio_thread_.Start(audio_callback_.get(), socket_handle, |
| 215 "AudioOutputDevice"); |
215 | 216 |
216 // 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 |
217 // multiple times before OnStreamCreated() gets called. | 218 // multiple times before OnStreamCreated() gets called. |
218 is_started_ = true; | 219 is_started_ = true; |
219 if (play_on_start_) | 220 if (play_on_start_) |
220 PlayOnIOThread(); | 221 PlayOnIOThread(); |
221 } | 222 } |
222 | 223 |
223 void AudioDevice::OnIPCClosed() { | 224 void AudioOutputDevice::OnIPCClosed() { |
224 ipc_ = NULL; | 225 ipc_ = NULL; |
225 } | 226 } |
226 | 227 |
227 void AudioDevice::WillDestroyCurrentMessageLoop() { | 228 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { |
228 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"; |
229 ShutDownOnIOThread(); | 230 ShutDownOnIOThread(); |
230 } | 231 } |
231 | 232 |
232 // AudioDevice::AudioThreadCallback | 233 // AudioOutputDevice::AudioThreadCallback |
233 | 234 |
234 AudioDevice::AudioThreadCallback::AudioThreadCallback( | 235 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( |
235 const media::AudioParameters& audio_parameters, | 236 const AudioParameters& audio_parameters, |
236 base::SharedMemoryHandle memory, | 237 base::SharedMemoryHandle memory, |
237 int memory_length, | 238 int memory_length, |
238 media::AudioRendererSink::RenderCallback* render_callback) | 239 AudioRendererSink::RenderCallback* render_callback) |
239 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length), | 240 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length), |
240 render_callback_(render_callback) { | 241 render_callback_(render_callback) { |
241 } | 242 } |
242 | 243 |
243 AudioDevice::AudioThreadCallback::~AudioThreadCallback() { | 244 AudioOutputDevice::AudioThreadCallback::~AudioThreadCallback() { |
244 } | 245 } |
245 | 246 |
246 void AudioDevice::AudioThreadCallback::MapSharedMemory() { | 247 void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() { |
247 shared_memory_.Map(media::TotalSharedMemorySizeInBytes(memory_length_)); | 248 shared_memory_.Map(TotalSharedMemorySizeInBytes(memory_length_)); |
248 } | 249 } |
249 | 250 |
250 // Called whenever we receive notifications about pending data. | 251 // Called whenever we receive notifications about pending data. |
251 void AudioDevice::AudioThreadCallback::Process(int pending_data) { | 252 void AudioOutputDevice::AudioThreadCallback::Process(int pending_data) { |
252 if (pending_data == media::AudioOutputController::kPauseMark) { | 253 if (pending_data == AudioOutputController::kPauseMark) { |
253 memset(shared_memory_.memory(), 0, memory_length_); | 254 memset(shared_memory_.memory(), 0, memory_length_); |
254 media::SetActualDataSizeInBytes(&shared_memory_, memory_length_, 0); | 255 SetActualDataSizeInBytes(&shared_memory_, memory_length_, 0); |
255 return; | 256 return; |
256 } | 257 } |
257 | 258 |
258 // Convert the number of pending bytes in the render buffer | 259 // Convert the number of pending bytes in the render buffer |
259 // into milliseconds. | 260 // into milliseconds. |
260 int audio_delay_milliseconds = pending_data / bytes_per_ms_; | 261 int audio_delay_milliseconds = pending_data / bytes_per_ms_; |
261 | 262 |
262 TRACE_EVENT0("audio", "AudioDevice::FireRenderCallback"); | 263 TRACE_EVENT0("audio", "AudioOutputDevice::FireRenderCallback"); |
263 | 264 |
264 // Update the audio-delay measurement then ask client to render audio. | 265 // Update the audio-delay measurement then ask client to render audio. |
265 size_t num_frames = render_callback_->Render(audio_data_, | 266 size_t num_frames = render_callback_->Render(audio_data_, |
266 audio_parameters_.frames_per_buffer(), audio_delay_milliseconds); | 267 audio_parameters_.frames_per_buffer(), audio_delay_milliseconds); |
267 | 268 |
268 // Interleave, scale, and clip to int. | 269 // Interleave, scale, and clip to int. |
269 // 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 |
270 // conversions that happen in the <audio> and WebRTC scenarios. | 271 // conversions that happen in the <audio> and WebRTC scenarios. |
271 media::InterleaveFloatToInt(audio_data_, shared_memory_.memory(), | 272 InterleaveFloatToInt(audio_data_, shared_memory_.memory(), |
272 num_frames, audio_parameters_.bits_per_sample() / 8); | 273 num_frames, audio_parameters_.bits_per_sample() / 8); |
273 | 274 |
274 // Let the host know we are done. | 275 // Let the host know we are done. |
275 media::SetActualDataSizeInBytes(&shared_memory_, memory_length_, | 276 SetActualDataSizeInBytes(&shared_memory_, memory_length_, |
276 num_frames * audio_parameters_.GetBytesPerFrame()); | 277 num_frames * audio_parameters_.GetBytesPerFrame()); |
277 } | 278 } |
| 279 |
| 280 } // namespace media. |
OLD | NEW |