OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // THREAD SAFETY | 5 // THREAD SAFETY |
6 // | 6 // |
7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: | 7 // AlsaPcmOutputStream object is *not* thread-safe -- we assume that |
8 // | |
9 // client thread - creates the object and calls the public APIs. | 8 // client thread - creates the object and calls the public APIs. |
10 // message loop thread - executes all the internal tasks including querying | 9 // message loop thread - executes all the internal tasks including querying |
11 // the data source for more data, writing to the alsa device, and closing | 10 // the data source for more data, writing to the alsa device, and closing |
12 // the alsa device. It does *not* handle opening the device though. | 11 // the alsa device. |
13 // | 12 // is actually the same thread. |
14 // The class is designed so that most operations that read/modify the object's | |
15 // internal state are done on the message loop thread. The exception is data | |
16 // conatined in the |shared_data_| structure. Data in this structure needs to | |
17 // be accessed by both threads, and should only be accessed when the | |
18 // |shared_data_.lock_| is held. | |
19 // | |
20 // All member variables that are not in |shared_data_| are created/destroyed on | |
21 // the |message_loop_|. This allows safe access to them from any task posted to | |
22 // |message_loop_|. The values in |shared_data_| are considered to be read-only | |
23 // signals by tasks posted to |message_loop_| (see the SEMANTICS of | |
24 // |shared_data_| section below). Because of these two constraints, tasks can, | |
25 // and must, be coded to be safe in the face of a changing |shared_data_|. | |
26 // | |
27 // | |
28 // SEMANTICS OF |shared_data_| | |
29 // | |
30 // Though |shared_data_| is accessable by both threads, the code has been | |
31 // structured so that all mutations to |shared_data_| are only done in the | |
32 // client thread. The message loop thread only ever reads the shared data. | |
33 // | |
34 // This reduces the need for critical sections because the public API code can | |
35 // assume that no mutations occur to the |shared_data_| between queries. | |
36 // | |
37 // On the message loop side, tasks have been coded such that they can | |
38 // operate safely regardless of when state changes happen to |shared_data_|. | |
39 // Code that is sensitive to the timing of state changes are delegated to the | |
40 // |shared_data_| object so they can executed while holding | |
41 // |shared_data_.lock_|. | |
42 // | 13 // |
43 // | 14 // |
44 // SEMANTICS OF CloseTask() | 15 // SEMANTICS OF CloseTask() |
45 // | 16 // |
46 // The CloseTask() is responsible for cleaning up any resources that were | 17 // The CloseTask() is responsible for cleaning up any resources that were |
47 // acquired after a successful Open(). After a CloseTask() has executed, | 18 // acquired after a successful Open(). CloseTask() would revoke any |
48 // scheduling of reads should stop. Currently scheduled tasks may run, but | 19 // scheduled outstanding runnable methods. |
49 // they should not attempt to access any of the internal structures beyond | |
50 // querying the |stop_stream_| flag and no-oping themselves. This will | |
51 // guarantee that eventually no more tasks will be posted into the message | |
52 // loop, and the AlsaPcmOutputStream will be able to delete itself. | |
53 // | 20 // |
54 // | 21 // |
55 // SEMANTICS OF ERROR STATES | 22 // SEMANTICS OF ERROR STATES |
56 // | 23 // |
57 // The object has two distinct error states: |shared_data_.state_| == kInError | 24 // The object has two distinct error states: |state_| == kInError |
58 // and |stop_stream_|. The |shared_data_.state_| state is only settable | 25 // and |stop_stream_|. The |stop_stream_| variable is used to indicate |
59 // by the client thread, and thus cannot be used to signal when the ALSA device | |
60 // fails due to a hardware (or other low-level) event. The |stop_stream_| | |
61 // variable is only accessed by the message loop thread; it is used to indicate | |
62 // that the playback_handle should no longer be used either because of a | 26 // that the playback_handle should no longer be used either because of a |
63 // hardware/low-level event, or because the CloseTask() has been run. | 27 // hardware/low-level event. |
64 // | 28 // |
65 // When |shared_data_.state_| == kInError, all public API functions will fail | 29 // When |state_| == kInError, all public API functions will fail |
66 // with an error (Start() will call the OnError() function on the callback | 30 // with an error (Start() will call the OnError() function on the callback |
67 // immediately), or no-op themselves with the exception of Close(). Even if an | 31 // immediately), or no-op themselves with the exception of Close(). Even if an |
68 // error state has been entered, if Open() has previously returned successfully, | 32 // error state has been entered, if Open() has previously returned successfully, |
69 // Close() must be called to cleanup the ALSA devices and release resources. | 33 // Close() must be called to cleanup the ALSA devices and release resources. |
70 // | 34 // |
71 // When |stop_stream_| is set, no more commands will be made against the | 35 // When |stop_stream_| is set, no more commands will be made against the |
72 // ALSA device, and playback will effectively stop. From the client's point of | 36 // ALSA device, and playback will effectively stop. From the client's point of |
73 // view, it will seem that the device has just clogged and stopped requesting | 37 // view, it will seem that the device has just clogged and stopped requesting |
74 // data. | 38 // data. |
75 | 39 |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 // Since we expect to only be able to wake up with a resolution of | 175 // Since we expect to only be able to wake up with a resolution of |
212 // kSleepErrorMilliseconds, double that for our minimum required latency. | 176 // kSleepErrorMilliseconds, double that for our minimum required latency. |
213 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = | 177 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = |
214 kSleepErrorMilliseconds * 2 * 1000; | 178 kSleepErrorMilliseconds * 2 * 1000; |
215 | 179 |
216 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, | 180 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, |
217 AudioParameters params, | 181 AudioParameters params, |
218 AlsaWrapper* wrapper, | 182 AlsaWrapper* wrapper, |
219 AudioManagerLinux* manager, | 183 AudioManagerLinux* manager, |
220 MessageLoop* message_loop) | 184 MessageLoop* message_loop) |
221 : shared_data_(MessageLoop::current()), | 185 : requested_device_name_(device_name), |
222 requested_device_name_(device_name), | |
223 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), | 186 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), |
224 channels_(params.channels), | 187 channels_(params.channels), |
225 sample_rate_(params.sample_rate), | 188 sample_rate_(params.sample_rate), |
226 bytes_per_sample_(params.bits_per_sample / 8), | 189 bytes_per_sample_(params.bits_per_sample / 8), |
227 bytes_per_frame_(channels_ * params.bits_per_sample / 8), | 190 bytes_per_frame_(channels_ * params.bits_per_sample / 8), |
228 should_downmix_(false), | 191 should_downmix_(false), |
229 packet_size_(params.GetPacketSize()), | 192 packet_size_(params.GetPacketSize()), |
230 micros_per_packet_(FramesToMicros( | 193 micros_per_packet_(FramesToMicros( |
231 params.samples_per_packet, sample_rate_)), | 194 params.samples_per_packet, sample_rate_)), |
232 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, | 195 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, |
233 micros_per_packet_ * 2)), | 196 micros_per_packet_ * 2)), |
234 bytes_per_output_frame_(bytes_per_frame_), | 197 bytes_per_output_frame_(bytes_per_frame_), |
235 alsa_buffer_frames_(0), | 198 alsa_buffer_frames_(0), |
236 stop_stream_(false), | 199 stop_stream_(false), |
237 wrapper_(wrapper), | 200 wrapper_(wrapper), |
238 manager_(manager), | 201 manager_(manager), |
239 playback_handle_(NULL), | 202 playback_handle_(NULL), |
240 frames_per_packet_(packet_size_ / bytes_per_frame_), | 203 frames_per_packet_(packet_size_ / bytes_per_frame_), |
241 client_thread_loop_(MessageLoop::current()), | 204 message_loop_(message_loop), |
242 message_loop_(message_loop) { | 205 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), |
| 206 state_(kCreated), |
| 207 volume_(1.0f), |
| 208 source_callback_(NULL) { |
| 209 DCHECK_EQ(MessageLoop::current(), message_loop_); |
243 | 210 |
244 // Sanity check input values. | 211 // Sanity check input values. |
245 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { | 212 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { |
246 LOG(WARNING) << "Unsupported audio frequency."; | 213 LOG(WARNING) << "Unsupported audio frequency."; |
247 shared_data_.TransitionTo(kInError); | 214 TransitionTo(kInError); |
248 } | 215 } |
249 | 216 |
250 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && | 217 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && |
251 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { | 218 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { |
252 LOG(WARNING) << "Unsupported audio format"; | 219 LOG(WARNING) << "Unsupported audio format"; |
253 shared_data_.TransitionTo(kInError); | 220 TransitionTo(kInError); |
254 } | 221 } |
255 | 222 |
256 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { | 223 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { |
257 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; | 224 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; |
258 shared_data_.TransitionTo(kInError); | 225 TransitionTo(kInError); |
259 } | 226 } |
260 } | 227 } |
261 | 228 |
262 AlsaPcmOutputStream::~AlsaPcmOutputStream() { | 229 AlsaPcmOutputStream::~AlsaPcmOutputStream() { |
263 InternalState state = shared_data_.state(); | 230 InternalState current_state = state(); |
264 DCHECK(state == kCreated || state == kIsClosed || state == kInError); | 231 DCHECK(current_state == kCreated || |
| 232 current_state == kIsClosed || |
| 233 current_state == kInError); |
265 | 234 |
266 // TODO(ajwong): Ensure that CloseTask has been called and the | 235 // TODO(ajwong): Ensure that CloseTask has been called and the |
267 // playback handle released by DCHECKing that playback_handle_ is NULL. | 236 // playback handle released by DCHECKing that playback_handle_ is NULL. |
268 // Currently, because of Bug 18217, there is a race condition on destruction | 237 // Currently, because of Bug 18217, there is a race condition on destruction |
269 // where the stream is not always stopped and closed, causing this to fail. | 238 // where the stream is not always stopped and closed, causing this to fail. |
270 } | 239 } |
271 | 240 |
272 bool AlsaPcmOutputStream::Open() { | 241 bool AlsaPcmOutputStream::Open() { |
273 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 242 DCHECK_EQ(MessageLoop::current(), message_loop_); |
274 | 243 |
275 if (shared_data_.state() == kInError) { | 244 if (state() == kInError) { |
276 return false; | 245 return false; |
277 } | 246 } |
278 | 247 |
279 if (!shared_data_.CanTransitionTo(kIsOpened)) { | 248 if (!CanTransitionTo(kIsOpened)) { |
280 NOTREACHED() << "Invalid state: " << shared_data_.state(); | 249 NOTREACHED() << "Invalid state: " << state(); |
281 return false; | 250 return false; |
282 } | 251 } |
283 | 252 |
284 // We do not need to check if the transition was successful because | 253 // We do not need to check if the transition was successful because |
285 // CanTransitionTo() was checked above, and it is assumed that this | 254 // CanTransitionTo() was checked above, and it is assumed that this |
286 // object's public API is only called on one thread so the state cannot | 255 // object's public API is only called on one thread so the state cannot |
287 // transition out from under us. | 256 // transition out from under us. |
288 shared_data_.TransitionTo(kIsOpened); | 257 TransitionTo(kIsOpened); |
289 message_loop_->PostTask( | 258 message_loop_->PostTask( |
290 FROM_HERE, | 259 FROM_HERE, |
291 NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask)); | 260 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::OpenTask)); |
292 | 261 |
293 return true; | 262 return true; |
294 } | 263 } |
295 | 264 |
296 void AlsaPcmOutputStream::Close() { | 265 void AlsaPcmOutputStream::Close() { |
297 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 266 DCHECK_EQ(MessageLoop::current(), message_loop_); |
298 | 267 |
299 // Sanity check that the transition occurs correctly. It is safe to | 268 // Sanity check that the transition occurs correctly. It is safe to |
300 // continue anyways because all operations for closing are idempotent. | 269 // continue anyways because all operations for closing are idempotent. |
301 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { | 270 if (TransitionTo(kIsClosed) != kIsClosed) { |
302 NOTREACHED() << "Unable to transition Closed."; | 271 NOTREACHED() << "Unable to transition Closed."; |
303 } | 272 } |
304 | 273 |
305 message_loop_->PostTask( | 274 message_loop_->PostTask( |
306 FROM_HERE, | 275 FROM_HERE, |
307 NewRunnableMethod(this, &AlsaPcmOutputStream::CloseTask)); | 276 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::CloseTask)); |
308 | |
309 // Signal to the manager that we're closed and can be removed. Since | |
310 // we just posted a CloseTask to the message loop, we won't be deleted | |
311 // immediately, but it will happen soon afterwards. | |
312 manager()->ReleaseOutputStream(this); | |
313 } | 277 } |
314 | 278 |
315 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { | 279 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { |
316 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 280 DCHECK_EQ(MessageLoop::current(), message_loop_); |
317 | 281 |
318 CHECK(callback); | 282 CHECK(callback); |
319 | 283 |
320 shared_data_.set_source_callback(callback); | 284 set_source_callback(callback); |
321 | 285 |
322 // Only post the task if we can enter the playing state. | 286 // Only post the task if we can enter the playing state. |
323 if (shared_data_.TransitionTo(kIsPlaying) == kIsPlaying) { | 287 if (TransitionTo(kIsPlaying) == kIsPlaying) { |
324 message_loop_->PostTask( | 288 message_loop_->PostTask( |
325 FROM_HERE, | 289 FROM_HERE, |
326 NewRunnableMethod(this, &AlsaPcmOutputStream::StartTask)); | 290 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::StartTask)); |
327 } | 291 } |
328 } | 292 } |
329 | 293 |
330 void AlsaPcmOutputStream::Stop() { | 294 void AlsaPcmOutputStream::Stop() { |
331 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 295 DCHECK_EQ(MessageLoop::current(), message_loop_); |
332 | 296 |
333 // Reset the callback, so that it is not called anymore. | 297 // Reset the callback, so that it is not called anymore. |
334 shared_data_.set_source_callback(NULL); | 298 set_source_callback(NULL); |
335 | 299 |
336 shared_data_.TransitionTo(kIsStopped); | 300 TransitionTo(kIsStopped); |
337 } | 301 } |
338 | 302 |
339 void AlsaPcmOutputStream::SetVolume(double volume) { | 303 void AlsaPcmOutputStream::SetVolume(double volume) { |
340 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 304 DCHECK_EQ(MessageLoop::current(), message_loop_); |
341 | 305 |
342 shared_data_.set_volume(static_cast<float>(volume)); | 306 volume_ = static_cast<float>(volume); |
343 } | 307 } |
344 | 308 |
345 void AlsaPcmOutputStream::GetVolume(double* volume) { | 309 void AlsaPcmOutputStream::GetVolume(double* volume) { |
346 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 310 DCHECK_EQ(MessageLoop::current(), message_loop_); |
347 | 311 |
348 *volume = shared_data_.volume(); | 312 *volume = volume_; |
349 } | 313 } |
350 | 314 |
351 void AlsaPcmOutputStream::OpenTask() { | 315 void AlsaPcmOutputStream::OpenTask() { |
352 DCHECK_EQ(message_loop_, MessageLoop::current()); | 316 DCHECK_EQ(message_loop_, MessageLoop::current()); |
353 | 317 |
354 // Try to open the device. | 318 // Try to open the device. |
355 if (requested_device_name_ == kAutoSelectDevice) { | 319 if (requested_device_name_ == kAutoSelectDevice) { |
356 playback_handle_ = AutoSelectDevice(latency_micros_); | 320 playback_handle_ = AutoSelectDevice(latency_micros_); |
357 if (playback_handle_) | 321 if (playback_handle_) |
358 VLOG(1) << "Auto-selected device: " << device_name_; | 322 VLOG(1) << "Auto-selected device: " << device_name_; |
(...skipping 30 matching lines...) Expand all Loading... |
389 } | 353 } |
390 } | 354 } |
391 | 355 |
392 void AlsaPcmOutputStream::StartTask() { | 356 void AlsaPcmOutputStream::StartTask() { |
393 DCHECK_EQ(message_loop_, MessageLoop::current()); | 357 DCHECK_EQ(message_loop_, MessageLoop::current()); |
394 | 358 |
395 if (stop_stream_) { | 359 if (stop_stream_) { |
396 return; | 360 return; |
397 } | 361 } |
398 | 362 |
399 if (shared_data_.state() != kIsPlaying) { | 363 if (state() != kIsPlaying) { |
400 return; | 364 return; |
401 } | 365 } |
402 | 366 |
403 // Before starting, the buffer might have audio from previous user of this | 367 // Before starting, the buffer might have audio from previous user of this |
404 // device. | 368 // device. |
405 buffer_->Clear(); | 369 buffer_->Clear(); |
406 | 370 |
407 // When starting again, drop all packets in the device and prepare it again | 371 // When starting again, drop all packets in the device and prepare it again |
408 // incase we are restarting from a pause state and need to flush old data. | 372 // incase we are restarting from a pause state and need to flush old data. |
409 int error = wrapper_->PcmDrop(playback_handle_); | 373 int error = wrapper_->PcmDrop(playback_handle_); |
(...skipping 11 matching lines...) Expand all Loading... |
421 << wrapper_->PcmName(playback_handle_) << "): " | 385 << wrapper_->PcmName(playback_handle_) << "): " |
422 << wrapper_->StrError(error); | 386 << wrapper_->StrError(error); |
423 stop_stream_ = true; | 387 stop_stream_ = true; |
424 return; | 388 return; |
425 } | 389 } |
426 | 390 |
427 ScheduleNextWrite(false); | 391 ScheduleNextWrite(false); |
428 } | 392 } |
429 | 393 |
430 void AlsaPcmOutputStream::CloseTask() { | 394 void AlsaPcmOutputStream::CloseTask() { |
431 // NOTE: Keep this function idempotent to handle errors that might cause | |
432 // multiple CloseTasks to be posted. | |
433 DCHECK_EQ(message_loop_, MessageLoop::current()); | 395 DCHECK_EQ(message_loop_, MessageLoop::current()); |
434 | 396 |
435 // Shutdown the audio device. | 397 // Shutdown the audio device. |
436 if (playback_handle_ && | 398 if (playback_handle_ && |
437 alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { | 399 alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { |
438 LOG(WARNING) << "Unable to close audio device. Leaking handle."; | 400 LOG(WARNING) << "Unable to close audio device. Leaking handle."; |
439 } | 401 } |
440 playback_handle_ = NULL; | 402 playback_handle_ = NULL; |
441 | 403 |
442 // Release the buffer. | 404 // Release the buffer. |
443 buffer_.reset(); | 405 buffer_.reset(); |
444 | 406 |
445 // Signal anything that might already be scheduled to stop. | 407 // Signal anything that might already be scheduled to stop. |
446 stop_stream_ = true; | 408 stop_stream_ = true; // Not necessary in production, but unit tests |
| 409 // uses the flag to verify that stream was closed. |
| 410 method_factory_.RevokeAll(); |
| 411 |
| 412 // Signal to the manager that we're closed and can be removed. |
| 413 // Should be last call in the method as it deletes "this". |
| 414 manager_->ReleaseOutputStream(this); |
447 } | 415 } |
448 | 416 |
449 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { | 417 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { |
450 DCHECK_EQ(message_loop_, MessageLoop::current()); | 418 DCHECK_EQ(message_loop_, MessageLoop::current()); |
451 | 419 |
452 // If stopped, simulate a 0-lengthed packet. | 420 // If stopped, simulate a 0-lengthed packet. |
453 if (stop_stream_) { | 421 if (stop_stream_) { |
454 buffer_->Clear(); | 422 buffer_->Clear(); |
455 *source_exhausted = true; | 423 *source_exhausted = true; |
456 return; | 424 return; |
457 } | 425 } |
458 | 426 |
459 *source_exhausted = false; | 427 *source_exhausted = false; |
460 | 428 |
461 // Request more data if we have capacity. | 429 // Request more data if we have capacity. |
462 if (buffer_->forward_capacity() > buffer_->forward_bytes()) { | 430 if (buffer_->forward_capacity() > buffer_->forward_bytes()) { |
463 // Before making a request to source for data we need to determine the | 431 // Before making a request to source for data we need to determine the |
464 // delay (in bytes) for the requested data to be played. | 432 // delay (in bytes) for the requested data to be played. |
465 | 433 |
466 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / | 434 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / |
467 bytes_per_output_frame_; | 435 bytes_per_output_frame_; |
468 | 436 |
469 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; | 437 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; |
470 | 438 |
471 scoped_refptr<media::DataBuffer> packet = | 439 scoped_refptr<media::DataBuffer> packet = |
472 new media::DataBuffer(packet_size_); | 440 new media::DataBuffer(packet_size_); |
473 size_t packet_size = | 441 size_t packet_size = RunDataCallback(packet->GetWritableData(), |
474 shared_data_.OnMoreData( | 442 packet->GetBufferSize(), |
475 this, packet->GetWritableData(), packet->GetBufferSize(), | 443 AudioBuffersState(buffer_delay, |
476 AudioBuffersState(buffer_delay, hardware_delay)); | 444 hardware_delay)); |
477 CHECK(packet_size <= packet->GetBufferSize()) << | 445 CHECK(packet_size <= packet->GetBufferSize()) << |
478 "Data source overran buffer."; | 446 "Data source overran buffer."; |
479 | 447 |
480 // This should not happen, but in case it does, drop any trailing bytes | 448 // This should not happen, but in case it does, drop any trailing bytes |
481 // that aren't large enough to make a frame. Without this, packet writing | 449 // that aren't large enough to make a frame. Without this, packet writing |
482 // may stall because the last few bytes in the packet may never get used by | 450 // may stall because the last few bytes in the packet may never get used by |
483 // WritePacket. | 451 // WritePacket. |
484 DCHECK(packet_size % bytes_per_frame_ == 0); | 452 DCHECK(packet_size % bytes_per_frame_ == 0); |
485 packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; | 453 packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; |
486 | 454 |
487 if (should_downmix_) { | 455 if (should_downmix_) { |
488 if (media::FoldChannels(packet->GetWritableData(), | 456 if (media::FoldChannels(packet->GetWritableData(), |
489 packet_size, | 457 packet_size, |
490 channels_, | 458 channels_, |
491 bytes_per_sample_, | 459 bytes_per_sample_, |
492 shared_data_.volume())) { | 460 volume_)) { |
493 // Adjust packet size for downmix. | 461 // Adjust packet size for downmix. |
494 packet_size = | 462 packet_size = |
495 packet_size / bytes_per_frame_ * bytes_per_output_frame_; | 463 packet_size / bytes_per_frame_ * bytes_per_output_frame_; |
496 } else { | 464 } else { |
497 LOG(ERROR) << "Folding failed"; | 465 LOG(ERROR) << "Folding failed"; |
498 } | 466 } |
499 } else { | 467 } else { |
500 // TODO(ajwong): Handle other channel orderings. | 468 // TODO(ajwong): Handle other channel orderings. |
501 | 469 |
502 // Handle channel order for 5.0 audio. | 470 // Handle channel order for 5.0 audio. |
(...skipping 15 matching lines...) Expand all Loading... |
518 Swizzle51Layout(packet->GetWritableData(), packet_size); | 486 Swizzle51Layout(packet->GetWritableData(), packet_size); |
519 } else if (bytes_per_sample_ == 4) { | 487 } else if (bytes_per_sample_ == 4) { |
520 Swizzle51Layout(packet->GetWritableData(), packet_size); | 488 Swizzle51Layout(packet->GetWritableData(), packet_size); |
521 } | 489 } |
522 } | 490 } |
523 | 491 |
524 media::AdjustVolume(packet->GetWritableData(), | 492 media::AdjustVolume(packet->GetWritableData(), |
525 packet_size, | 493 packet_size, |
526 channels_, | 494 channels_, |
527 bytes_per_sample_, | 495 bytes_per_sample_, |
528 shared_data_.volume()); | 496 volume_); |
529 } | 497 } |
530 | 498 |
531 if (packet_size > 0) { | 499 if (packet_size > 0) { |
532 packet->SetDataSize(packet_size); | 500 packet->SetDataSize(packet_size); |
533 // Add the packet to the buffer. | 501 // Add the packet to the buffer. |
534 buffer_->Append(packet); | 502 buffer_->Append(packet); |
535 } else { | 503 } else { |
536 *source_exhausted = true; | 504 *source_exhausted = true; |
537 } | 505 } |
538 } | 506 } |
539 } | 507 } |
540 | 508 |
541 void AlsaPcmOutputStream::WritePacket() { | 509 void AlsaPcmOutputStream::WritePacket() { |
542 DCHECK_EQ(message_loop_, MessageLoop::current()); | 510 DCHECK_EQ(message_loop_, MessageLoop::current()); |
543 | 511 |
544 // If the device is in error, just eat the bytes. | 512 // If the device is in error, just eat the bytes. |
545 if (stop_stream_) { | 513 if (stop_stream_) { |
546 buffer_->Clear(); | 514 buffer_->Clear(); |
547 return; | 515 return; |
548 } | 516 } |
549 | 517 |
550 if (shared_data_.state() == kIsStopped) { | 518 if (state() == kIsStopped) { |
551 return; | 519 return; |
552 } | 520 } |
553 | 521 |
554 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); | 522 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); |
555 | 523 |
556 const uint8* buffer_data; | 524 const uint8* buffer_data; |
557 size_t buffer_size; | 525 size_t buffer_size; |
558 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { | 526 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { |
559 buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_); | 527 buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_); |
560 snd_pcm_sframes_t frames = buffer_size / bytes_per_output_frame_; | 528 snd_pcm_sframes_t frames = buffer_size / bytes_per_output_frame_; |
(...skipping 11 matching lines...) Expand all Loading... |
572 frames_written, | 540 frames_written, |
573 kPcmRecoverIsSilent); | 541 kPcmRecoverIsSilent); |
574 } | 542 } |
575 | 543 |
576 if (frames_written < 0) { | 544 if (frames_written < 0) { |
577 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping | 545 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping |
578 // the pcm playback? | 546 // the pcm playback? |
579 if (frames_written != -EAGAIN) { | 547 if (frames_written != -EAGAIN) { |
580 LOG(ERROR) << "Failed to write to pcm device: " | 548 LOG(ERROR) << "Failed to write to pcm device: " |
581 << wrapper_->StrError(frames_written); | 549 << wrapper_->StrError(frames_written); |
582 shared_data_.OnError(this, frames_written); | 550 RunErrorCallback(frames_written); |
583 stop_stream_ = true; | 551 stop_stream_ = true; |
584 } | 552 } |
585 } else { | 553 } else { |
586 if (frames_written > frames) { | 554 if (frames_written > frames) { |
587 LOG(WARNING) | 555 LOG(WARNING) |
588 << "snd_pcm_writei() has written more frame that we asked."; | 556 << "snd_pcm_writei() has written more frame that we asked."; |
589 frames_written = frames; | 557 frames_written = frames; |
590 } | 558 } |
591 | 559 |
592 // Seek forward in the buffer after we've written some data to ALSA. | 560 // Seek forward in the buffer after we've written some data to ALSA. |
(...skipping 10 matching lines...) Expand all Loading... |
603 } | 571 } |
604 } | 572 } |
605 | 573 |
606 void AlsaPcmOutputStream::WriteTask() { | 574 void AlsaPcmOutputStream::WriteTask() { |
607 DCHECK_EQ(message_loop_, MessageLoop::current()); | 575 DCHECK_EQ(message_loop_, MessageLoop::current()); |
608 | 576 |
609 if (stop_stream_) { | 577 if (stop_stream_) { |
610 return; | 578 return; |
611 } | 579 } |
612 | 580 |
613 if (shared_data_.state() == kIsStopped) { | 581 if (state() == kIsStopped) { |
614 return; | 582 return; |
615 } | 583 } |
616 | 584 |
617 bool source_exhausted; | 585 bool source_exhausted; |
618 BufferPacket(&source_exhausted); | 586 BufferPacket(&source_exhausted); |
619 WritePacket(); | 587 WritePacket(); |
620 | 588 |
621 ScheduleNextWrite(source_exhausted); | 589 ScheduleNextWrite(source_exhausted); |
622 } | 590 } |
623 | 591 |
(...skipping 24 matching lines...) Expand all Loading... |
648 } else { | 616 } else { |
649 next_fill_time_ms -= kSleepErrorMilliseconds; | 617 next_fill_time_ms -= kSleepErrorMilliseconds; |
650 } | 618 } |
651 | 619 |
652 // Avoid busy looping if the data source is exhausted. | 620 // Avoid busy looping if the data source is exhausted. |
653 if (source_exhausted) { | 621 if (source_exhausted) { |
654 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); | 622 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); |
655 } | 623 } |
656 | 624 |
657 // Only schedule more reads/writes if we are still in the playing state. | 625 // Only schedule more reads/writes if we are still in the playing state. |
658 if (shared_data_.state() == kIsPlaying) { | 626 if (state() == kIsPlaying) { |
659 if (next_fill_time_ms == 0) { | 627 if (next_fill_time_ms == 0) { |
660 message_loop_->PostTask( | 628 message_loop_->PostTask( |
661 FROM_HERE, | 629 FROM_HERE, |
662 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask)); | 630 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::WriteTask)); |
663 } else { | 631 } else { |
664 // TODO(ajwong): Measure the reliability of the delay interval. Use | 632 // TODO(ajwong): Measure the reliability of the delay interval. Use |
665 // base/metrics/histogram.h. | 633 // base/metrics/histogram.h. |
666 message_loop_->PostDelayedTask( | 634 message_loop_->PostDelayedTask( |
667 FROM_HERE, | 635 FROM_HERE, |
668 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask), | 636 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::WriteTask), |
669 next_fill_time_ms); | 637 next_fill_time_ms); |
670 } | 638 } |
671 } | 639 } |
672 } | 640 } |
673 | 641 |
674 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { | 642 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { |
675 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; | 643 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; |
676 } | 644 } |
677 | 645 |
678 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { | 646 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
835 default_channels, sample_rate_, | 803 default_channels, sample_rate_, |
836 pcm_format_, latency)) != NULL) { | 804 pcm_format_, latency)) != NULL) { |
837 return handle; | 805 return handle; |
838 } | 806 } |
839 | 807 |
840 // Unable to open any device. | 808 // Unable to open any device. |
841 device_name_.clear(); | 809 device_name_.clear(); |
842 return NULL; | 810 return NULL; |
843 } | 811 } |
844 | 812 |
845 AudioManagerLinux* AlsaPcmOutputStream::manager() { | 813 bool AlsaPcmOutputStream::CanTransitionTo(InternalState to) { |
846 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | |
847 return manager_; | |
848 } | |
849 | |
850 AlsaPcmOutputStream::SharedData::SharedData( | |
851 MessageLoop* state_transition_loop) | |
852 : state_(kCreated), | |
853 volume_(1.0f), | |
854 source_callback_(NULL), | |
855 state_transition_loop_(state_transition_loop) { | |
856 } | |
857 | |
858 bool AlsaPcmOutputStream::SharedData::CanTransitionTo(InternalState to) { | |
859 base::AutoLock l(lock_); | |
860 return CanTransitionTo_Locked(to); | |
861 } | |
862 | |
863 bool AlsaPcmOutputStream::SharedData::CanTransitionTo_Locked( | |
864 InternalState to) { | |
865 lock_.AssertAcquired(); | |
866 | |
867 switch (state_) { | 814 switch (state_) { |
868 case kCreated: | 815 case kCreated: |
869 return to == kIsOpened || to == kIsClosed || to == kInError; | 816 return to == kIsOpened || to == kIsClosed || to == kInError; |
870 | 817 |
871 case kIsOpened: | 818 case kIsOpened: |
872 return to == kIsPlaying || to == kIsStopped || | 819 return to == kIsPlaying || to == kIsStopped || |
873 to == kIsClosed || to == kInError; | 820 to == kIsClosed || to == kInError; |
874 | 821 |
875 case kIsPlaying: | 822 case kIsPlaying: |
876 return to == kIsPlaying || to == kIsStopped || | 823 return to == kIsPlaying || to == kIsStopped || |
877 to == kIsClosed || to == kInError; | 824 to == kIsClosed || to == kInError; |
878 | 825 |
879 case kIsStopped: | 826 case kIsStopped: |
880 return to == kIsPlaying || to == kIsStopped || | 827 return to == kIsPlaying || to == kIsStopped || |
881 to == kIsClosed || to == kInError; | 828 to == kIsClosed || to == kInError; |
882 | 829 |
883 case kInError: | 830 case kInError: |
884 return to == kIsClosed || to == kInError; | 831 return to == kIsClosed || to == kInError; |
885 | 832 |
886 case kIsClosed: | 833 case kIsClosed: |
887 default: | 834 default: |
888 return false; | 835 return false; |
889 } | 836 } |
890 } | 837 } |
891 | 838 |
892 AlsaPcmOutputStream::InternalState | 839 AlsaPcmOutputStream::InternalState |
893 AlsaPcmOutputStream::SharedData::TransitionTo(InternalState to) { | 840 AlsaPcmOutputStream::TransitionTo(InternalState to) { |
894 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | 841 DCHECK_EQ(MessageLoop::current(), message_loop_); |
895 | 842 |
896 base::AutoLock l(lock_); | 843 if (!CanTransitionTo(to)) { |
897 if (!CanTransitionTo_Locked(to)) { | |
898 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; | 844 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; |
899 state_ = kInError; | 845 state_ = kInError; |
900 } else { | 846 } else { |
901 state_ = to; | 847 state_ = to; |
902 } | 848 } |
903 return state_; | 849 return state_; |
904 } | 850 } |
905 | 851 |
906 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::SharedData::state() { | 852 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { |
907 base::AutoLock l(lock_); | |
908 return state_; | 853 return state_; |
909 } | 854 } |
910 | 855 |
911 float AlsaPcmOutputStream::SharedData::volume() { | 856 uint32 AlsaPcmOutputStream::RunDataCallback(uint8* dest, |
912 base::AutoLock l(lock_); | 857 uint32 max_size, |
913 return volume_; | 858 AudioBuffersState buffers_state) { |
914 } | |
915 | |
916 void AlsaPcmOutputStream::SharedData::set_volume(float v) { | |
917 base::AutoLock l(lock_); | |
918 volume_ = v; | |
919 } | |
920 | |
921 uint32 AlsaPcmOutputStream::SharedData::OnMoreData( | |
922 AudioOutputStream* stream, uint8* dest, uint32 max_size, | |
923 AudioBuffersState buffers_state) { | |
924 base::AutoLock l(lock_); | |
925 if (source_callback_) { | 859 if (source_callback_) { |
926 return source_callback_->OnMoreData(stream, dest, max_size, buffers_state); | 860 return source_callback_->OnMoreData(this, dest, max_size, buffers_state); |
927 } | 861 } |
928 | 862 |
929 return 0; | 863 return 0; |
930 } | 864 } |
931 | 865 |
932 void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream, | 866 void AlsaPcmOutputStream::RunErrorCallback(int code) { |
933 int code) { | |
934 base::AutoLock l(lock_); | |
935 if (source_callback_) { | 867 if (source_callback_) { |
936 source_callback_->OnError(stream, code); | 868 source_callback_->OnError(this, code); |
937 } | 869 } |
938 } | 870 } |
939 | 871 |
940 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 872 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
941 // release ownership of the currently registered callback. | 873 // release ownership of the currently registered callback. |
942 void AlsaPcmOutputStream::SharedData::set_source_callback( | 874 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
943 AudioSourceCallback* callback) { | 875 DCHECK_EQ(MessageLoop::current(), message_loop_); |
944 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | |
945 base::AutoLock l(lock_); | |
946 source_callback_ = callback; | 876 source_callback_ = callback; |
947 } | 877 } |
OLD | NEW |