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

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
« no previous file with comments | « media/audio/linux/alsa_output.h ('k') | media/audio/linux/alsa_output_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « media/audio/linux/alsa_output.h ('k') | media/audio/linux/alsa_output_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698