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