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

Side by Side Diff: media/audio/linux/alsa_output.cc

Issue 7117001: Simplify AlsaPcmOutputStream and AudioManagerLinux. Code was thread-safe, but now (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 9 years, 6 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
OLDNEW
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698