Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(520)

Side by Side Diff: media/audio/audio_output_device.cc

Issue 10834033: Move AudioDevice and AudioInputDevice to media. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed comments and fixed a few lint issues Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/audio/audio_output_device.h ('k') | media/audio/audio_output_device_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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.
OLDNEW
« no previous file with comments | « media/audio/audio_output_device.h ('k') | media/audio/audio_output_device_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698