• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // THREAD SAFETY
6 //
7 // AlsaPcmOutputStream object is *not* thread-safe and should only be used
8 // from the audio thread.  We DCHECK on this assumption whenever we can.
9 //
10 // SEMANTICS OF Close()
11 //
12 // Close() is responsible for cleaning up any resources that were acquired after
13 // a successful Open().  Close() will nullify any scheduled outstanding runnable
14 // methods.
15 //
16 //
17 // SEMANTICS OF ERROR STATES
18 //
19 // The object has two distinct error states: |state_| == kInError
20 // and |stop_stream_|.  The |stop_stream_| variable is used to indicate
21 // that the playback_handle should no longer be used either because of a
22 // hardware/low-level event.
23 //
24 // When |state_| == kInError, all public API functions will fail with an error
25 // (Start() will call the OnError() function on the callback immediately), or
26 // no-op themselves with the exception of Close().  Even if an error state has
27 // been entered, if Open() has previously returned successfully, Close() must be
28 // called to cleanup the ALSA devices and release resources.
29 //
30 // When |stop_stream_| is set, no more commands will be made against the
31 // ALSA device, and playback will effectively stop.  From the client's point of
32 // view, it will seem that the device has just clogged and stopped requesting
33 // data.
34 
35 #include "media/audio/alsa/alsa_output.h"
36 
37 #include <algorithm>
38 
39 #include "base/bind.h"
40 #include "base/debug/trace_event.h"
41 #include "base/logging.h"
42 #include "base/message_loop/message_loop.h"
43 #include "base/stl_util.h"
44 #include "base/time/time.h"
45 #include "media/audio/alsa/alsa_util.h"
46 #include "media/audio/alsa/alsa_wrapper.h"
47 #include "media/audio/alsa/audio_manager_alsa.h"
48 #include "media/base/channel_mixer.h"
49 #include "media/base/data_buffer.h"
50 #include "media/base/seekable_buffer.h"
51 
52 namespace media {
53 
54 // Set to 0 during debugging if you want error messages due to underrun
55 // events or other recoverable errors.
56 #if defined(NDEBUG)
57 static const int kPcmRecoverIsSilent = 1;
58 #else
59 static const int kPcmRecoverIsSilent = 0;
60 #endif
61 
62 // While the "default" device may support multi-channel audio, in Alsa, only
63 // the device names surround40, surround41, surround50, etc, have a defined
64 // channel mapping according to Lennart:
65 //
66 // http://0pointer.de/blog/projects/guide-to-sound-apis.html
67 //
68 // This function makes a best guess at the specific > 2 channel device name
69 // based on the number of channels requested.  NULL is returned if no device
70 // can be found to match the channel numbers.  In this case, using
71 // kDefaultDevice is probably the best bet.
72 //
73 // A five channel source is assumed to be surround50 instead of surround41
74 // (which is also 5 channels).
75 //
76 // TODO(ajwong): The source data should have enough info to tell us if we want
77 // surround41 versus surround51, etc., instead of needing us to guess based on
78 // channel number.  Fix API to pass that data down.
GuessSpecificDeviceName(uint32 channels)79 static const char* GuessSpecificDeviceName(uint32 channels) {
80   switch (channels) {
81     case 8:
82       return "surround71";
83 
84     case 7:
85       return "surround70";
86 
87     case 6:
88       return "surround51";
89 
90     case 5:
91       return "surround50";
92 
93     case 4:
94       return "surround40";
95 
96     default:
97       return NULL;
98   }
99 }
100 
operator <<(std::ostream & os,AlsaPcmOutputStream::InternalState state)101 std::ostream& operator<<(std::ostream& os,
102                          AlsaPcmOutputStream::InternalState state) {
103   switch (state) {
104     case AlsaPcmOutputStream::kInError:
105       os << "kInError";
106       break;
107     case AlsaPcmOutputStream::kCreated:
108       os << "kCreated";
109       break;
110     case AlsaPcmOutputStream::kIsOpened:
111       os << "kIsOpened";
112       break;
113     case AlsaPcmOutputStream::kIsPlaying:
114       os << "kIsPlaying";
115       break;
116     case AlsaPcmOutputStream::kIsStopped:
117       os << "kIsStopped";
118       break;
119     case AlsaPcmOutputStream::kIsClosed:
120       os << "kIsClosed";
121       break;
122   };
123   return os;
124 }
125 
126 const char AlsaPcmOutputStream::kDefaultDevice[] = "default";
127 const char AlsaPcmOutputStream::kAutoSelectDevice[] = "";
128 const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:";
129 
130 // We use 40ms as our minimum required latency. If it is needed, we may be able
131 // to get it down to 20ms.
132 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000;
133 
AlsaPcmOutputStream(const std::string & device_name,const AudioParameters & params,AlsaWrapper * wrapper,AudioManagerBase * manager)134 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name,
135                                          const AudioParameters& params,
136                                          AlsaWrapper* wrapper,
137                                          AudioManagerBase* manager)
138     : requested_device_name_(device_name),
139       pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())),
140       channels_(params.channels()),
141       channel_layout_(params.channel_layout()),
142       sample_rate_(params.sample_rate()),
143       bytes_per_sample_(params.bits_per_sample() / 8),
144       bytes_per_frame_(params.GetBytesPerFrame()),
145       packet_size_(params.GetBytesPerBuffer()),
146       latency_(std::max(
147           base::TimeDelta::FromMicroseconds(kMinLatencyMicros),
148           FramesToTimeDelta(params.frames_per_buffer() * 2, sample_rate_))),
149       bytes_per_output_frame_(bytes_per_frame_),
150       alsa_buffer_frames_(0),
151       stop_stream_(false),
152       wrapper_(wrapper),
153       manager_(manager),
154       message_loop_(base::MessageLoop::current()),
155       playback_handle_(NULL),
156       frames_per_packet_(packet_size_ / bytes_per_frame_),
157       weak_factory_(this),
158       state_(kCreated),
159       volume_(1.0f),
160       source_callback_(NULL),
161       audio_bus_(AudioBus::Create(params)) {
162   DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
163   DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_);
164 
165   // Sanity check input values.
166   if (!params.IsValid()) {
167     LOG(WARNING) << "Unsupported audio parameters.";
168     TransitionTo(kInError);
169   }
170 
171   if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) {
172     LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample();
173     TransitionTo(kInError);
174   }
175 }
176 
~AlsaPcmOutputStream()177 AlsaPcmOutputStream::~AlsaPcmOutputStream() {
178   InternalState current_state = state();
179   DCHECK(current_state == kCreated ||
180          current_state == kIsClosed ||
181          current_state == kInError);
182   DCHECK(!playback_handle_);
183 }
184 
Open()185 bool AlsaPcmOutputStream::Open() {
186   DCHECK(IsOnAudioThread());
187 
188   if (state() == kInError)
189     return false;
190 
191   if (!CanTransitionTo(kIsOpened)) {
192     NOTREACHED() << "Invalid state: " << state();
193     return false;
194   }
195 
196   // We do not need to check if the transition was successful because
197   // CanTransitionTo() was checked above, and it is assumed that this
198   // object's public API is only called on one thread so the state cannot
199   // transition out from under us.
200   TransitionTo(kIsOpened);
201 
202   // Try to open the device.
203   if (requested_device_name_ == kAutoSelectDevice) {
204     playback_handle_ = AutoSelectDevice(latency_.InMicroseconds());
205     if (playback_handle_)
206       DVLOG(1) << "Auto-selected device: " << device_name_;
207   } else {
208     device_name_ = requested_device_name_;
209     playback_handle_ = alsa_util::OpenPlaybackDevice(
210         wrapper_, device_name_.c_str(), channels_, sample_rate_,
211         pcm_format_, latency_.InMicroseconds());
212   }
213 
214   // Finish initializing the stream if the device was opened successfully.
215   if (playback_handle_ == NULL) {
216     stop_stream_ = true;
217     TransitionTo(kInError);
218     return false;
219   } else {
220     bytes_per_output_frame_ = channel_mixer_ ?
221         mixed_audio_bus_->channels() * bytes_per_sample_ : bytes_per_frame_;
222     uint32 output_packet_size = frames_per_packet_ * bytes_per_output_frame_;
223     buffer_.reset(new media::SeekableBuffer(0, output_packet_size));
224 
225     // Get alsa buffer size.
226     snd_pcm_uframes_t buffer_size;
227     snd_pcm_uframes_t period_size;
228     int error = wrapper_->PcmGetParams(playback_handle_, &buffer_size,
229                                        &period_size);
230     if (error < 0) {
231       LOG(ERROR) << "Failed to get playback buffer size from ALSA: "
232                  << wrapper_->StrError(error);
233       // Buffer size is at least twice of packet size.
234       alsa_buffer_frames_ = frames_per_packet_ * 2;
235     } else {
236       alsa_buffer_frames_ = buffer_size;
237     }
238   }
239 
240   return true;
241 }
242 
Close()243 void AlsaPcmOutputStream::Close() {
244   DCHECK(IsOnAudioThread());
245 
246   if (state() != kIsClosed)
247     TransitionTo(kIsClosed);
248 
249   // Shutdown the audio device.
250   if (playback_handle_) {
251     if (alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) {
252       LOG(WARNING) << "Unable to close audio device. Leaking handle.";
253     }
254     playback_handle_ = NULL;
255 
256     // Release the buffer.
257     buffer_.reset();
258 
259     // Signal anything that might already be scheduled to stop.
260     stop_stream_ = true;  // Not necessary in production, but unit tests
261                           // uses the flag to verify that stream was closed.
262   }
263 
264   weak_factory_.InvalidateWeakPtrs();
265 
266   // Signal to the manager that we're closed and can be removed.
267   // Should be last call in the method as it deletes "this".
268   manager_->ReleaseOutputStream(this);
269 }
270 
Start(AudioSourceCallback * callback)271 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) {
272   DCHECK(IsOnAudioThread());
273 
274   CHECK(callback);
275 
276   if (stop_stream_)
277     return;
278 
279   // Only post the task if we can enter the playing state.
280   if (TransitionTo(kIsPlaying) != kIsPlaying)
281     return;
282 
283   // Before starting, the buffer might have audio from previous user of this
284   // device.
285   buffer_->Clear();
286 
287   // When starting again, drop all packets in the device and prepare it again
288   // in case we are restarting from a pause state and need to flush old data.
289   int error = wrapper_->PcmDrop(playback_handle_);
290   if (error < 0 && error != -EAGAIN) {
291     LOG(ERROR) << "Failure clearing playback device ("
292                << wrapper_->PcmName(playback_handle_) << "): "
293                << wrapper_->StrError(error);
294     stop_stream_ = true;
295     return;
296   }
297 
298   error = wrapper_->PcmPrepare(playback_handle_);
299   if (error < 0 && error != -EAGAIN) {
300     LOG(ERROR) << "Failure preparing stream ("
301                << wrapper_->PcmName(playback_handle_) << "): "
302                << wrapper_->StrError(error);
303     stop_stream_ = true;
304     return;
305   }
306 
307   // Ensure the first buffer is silence to avoid startup glitches.
308   int buffer_size = GetAvailableFrames() * bytes_per_output_frame_;
309   scoped_refptr<DataBuffer> silent_packet = new DataBuffer(buffer_size);
310   silent_packet->set_data_size(buffer_size);
311   memset(silent_packet->writable_data(), 0, silent_packet->data_size());
312   buffer_->Append(silent_packet);
313   WritePacket();
314 
315   // Start the callback chain.
316   set_source_callback(callback);
317   WriteTask();
318 }
319 
Stop()320 void AlsaPcmOutputStream::Stop() {
321   DCHECK(IsOnAudioThread());
322 
323   // Reset the callback, so that it is not called anymore.
324   set_source_callback(NULL);
325   weak_factory_.InvalidateWeakPtrs();
326 
327   TransitionTo(kIsStopped);
328 }
329 
SetVolume(double volume)330 void AlsaPcmOutputStream::SetVolume(double volume) {
331   DCHECK(IsOnAudioThread());
332 
333   volume_ = static_cast<float>(volume);
334 }
335 
GetVolume(double * volume)336 void AlsaPcmOutputStream::GetVolume(double* volume) {
337   DCHECK(IsOnAudioThread());
338 
339   *volume = volume_;
340 }
341 
BufferPacket(bool * source_exhausted)342 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) {
343   DCHECK(IsOnAudioThread());
344 
345   // If stopped, simulate a 0-length packet.
346   if (stop_stream_) {
347     buffer_->Clear();
348     *source_exhausted = true;
349     return;
350   }
351 
352   *source_exhausted = false;
353 
354   // Request more data only when we run out of data in the buffer, because
355   // WritePacket() comsumes only the current chunk of data.
356   if (!buffer_->forward_bytes()) {
357     // Before making a request to source for data we need to determine the
358     // delay (in bytes) for the requested data to be played.
359     const uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_;
360 
361     scoped_refptr<media::DataBuffer> packet =
362         new media::DataBuffer(packet_size_);
363     int frames_filled = RunDataCallback(
364         audio_bus_.get(), AudioBuffersState(0, hardware_delay));
365 
366     size_t packet_size = frames_filled * bytes_per_frame_;
367     DCHECK_LE(packet_size, packet_size_);
368 
369     // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer;
370     // volume adjust should use SSE optimized vector_fmul() prior to interleave.
371     AudioBus* output_bus = audio_bus_.get();
372     if (channel_mixer_) {
373       output_bus = mixed_audio_bus_.get();
374       channel_mixer_->Transform(audio_bus_.get(), output_bus);
375       // Adjust packet size for downmix.
376       packet_size = packet_size / bytes_per_frame_ * bytes_per_output_frame_;
377     }
378 
379     // Note: If this ever changes to output raw float the data must be clipped
380     // and sanitized since it may come from an untrusted source such as NaCl.
381     output_bus->Scale(volume_);
382     output_bus->ToInterleaved(
383         frames_filled, bytes_per_sample_, packet->writable_data());
384 
385     if (packet_size > 0) {
386       packet->set_data_size(packet_size);
387       // Add the packet to the buffer.
388       buffer_->Append(packet);
389     } else {
390       *source_exhausted = true;
391     }
392   }
393 }
394 
WritePacket()395 void AlsaPcmOutputStream::WritePacket() {
396   DCHECK(IsOnAudioThread());
397 
398   // If the device is in error, just eat the bytes.
399   if (stop_stream_) {
400     buffer_->Clear();
401     return;
402   }
403 
404   if (state() != kIsPlaying)
405     return;
406 
407   CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u);
408 
409   const uint8* buffer_data;
410   int buffer_size;
411   if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) {
412     buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_);
413     snd_pcm_sframes_t frames = std::min(
414         static_cast<snd_pcm_sframes_t>(buffer_size / bytes_per_output_frame_),
415         GetAvailableFrames());
416 
417     if (!frames)
418       return;
419 
420     snd_pcm_sframes_t frames_written =
421         wrapper_->PcmWritei(playback_handle_, buffer_data, frames);
422     if (frames_written < 0) {
423       // Attempt once to immediately recover from EINTR,
424       // EPIPE (overrun/underrun), ESTRPIPE (stream suspended).  WritePacket
425       // will eventually be called again, so eventual recovery will happen if
426       // muliple retries are required.
427       frames_written = wrapper_->PcmRecover(playback_handle_,
428                                             frames_written,
429                                             kPcmRecoverIsSilent);
430       if (frames_written < 0) {
431         if (frames_written != -EAGAIN) {
432           LOG(ERROR) << "Failed to write to pcm device: "
433                      << wrapper_->StrError(frames_written);
434           RunErrorCallback(frames_written);
435           stop_stream_ = true;
436         }
437       }
438     } else {
439       DCHECK_EQ(frames_written, frames);
440 
441       // Seek forward in the buffer after we've written some data to ALSA.
442       buffer_->Seek(frames_written * bytes_per_output_frame_);
443     }
444   } else {
445     // If nothing left to write and playback hasn't started yet, start it now.
446     // This ensures that shorter sounds will still play.
447     if (playback_handle_ &&
448         (wrapper_->PcmState(playback_handle_) == SND_PCM_STATE_PREPARED) &&
449         GetCurrentDelay() > 0) {
450       wrapper_->PcmStart(playback_handle_);
451     }
452   }
453 }
454 
WriteTask()455 void AlsaPcmOutputStream::WriteTask() {
456   DCHECK(IsOnAudioThread());
457 
458   if (stop_stream_)
459     return;
460 
461   if (state() == kIsStopped)
462     return;
463 
464   bool source_exhausted;
465   BufferPacket(&source_exhausted);
466   WritePacket();
467 
468   ScheduleNextWrite(source_exhausted);
469 }
470 
ScheduleNextWrite(bool source_exhausted)471 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) {
472   DCHECK(IsOnAudioThread());
473 
474   if (stop_stream_ || state() != kIsPlaying)
475     return;
476 
477   const uint32 kTargetFramesAvailable = alsa_buffer_frames_ / 2;
478   uint32 available_frames = GetAvailableFrames();
479 
480   base::TimeDelta next_fill_time;
481   if (buffer_->forward_bytes() && available_frames) {
482     // If we've got data available and ALSA has room, deliver it immediately.
483     next_fill_time = base::TimeDelta();
484   } else if (buffer_->forward_bytes()) {
485     // If we've got data available and no room, poll until room is available.
486     // Polling in this manner allows us to ensure a more consistent callback
487     // schedule.  In testing this yields a variance of +/- 5ms versus the non-
488     // polling strategy which is around +/- 30ms and bimodal.
489     next_fill_time = base::TimeDelta::FromMilliseconds(5);
490   } else if (available_frames < kTargetFramesAvailable) {
491     // Schedule the next write for the moment when the available buffer of the
492     // sound card hits |kTargetFramesAvailable|.
493     next_fill_time = FramesToTimeDelta(
494         kTargetFramesAvailable - available_frames, sample_rate_);
495   } else if (!source_exhausted) {
496     // The sound card has |kTargetFramesAvailable| or more frames available.
497     // Invoke the next write immediately to avoid underrun.
498     next_fill_time = base::TimeDelta();
499   } else {
500     // The sound card has frames available, but our source is exhausted, so
501     // avoid busy looping by delaying a bit.
502     next_fill_time = base::TimeDelta::FromMilliseconds(10);
503   }
504 
505   message_loop_->PostDelayedTask(FROM_HERE, base::Bind(
506       &AlsaPcmOutputStream::WriteTask, weak_factory_.GetWeakPtr()),
507       next_fill_time);
508 }
509 
510 // static
FramesToTimeDelta(int frames,double sample_rate)511 base::TimeDelta AlsaPcmOutputStream::FramesToTimeDelta(int frames,
512                                                        double sample_rate) {
513   return base::TimeDelta::FromMicroseconds(
514       frames * base::Time::kMicrosecondsPerSecond / sample_rate);
515 }
516 
FindDeviceForChannels(uint32 channels)517 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) {
518   // Constants specified by the ALSA API for device hints.
519   static const int kGetAllDevices = -1;
520   static const char kPcmInterfaceName[] = "pcm";
521   static const char kIoHintName[] = "IOID";
522   static const char kNameHintName[] = "NAME";
523 
524   const char* wanted_device = GuessSpecificDeviceName(channels);
525   if (!wanted_device)
526     return std::string();
527 
528   std::string guessed_device;
529   void** hints = NULL;
530   int error = wrapper_->DeviceNameHint(kGetAllDevices,
531                                        kPcmInterfaceName,
532                                        &hints);
533   if (error == 0) {
534     // NOTE: Do not early return from inside this if statement.  The
535     // hints above need to be freed.
536     for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
537       // Only examine devices that are output capable..  Valid values are
538       // "Input", "Output", and NULL which means both input and output.
539       scoped_ptr_malloc<char> io(
540           wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName));
541       if (io != NULL && strcmp(io.get(), "Input") == 0)
542         continue;
543 
544       // Attempt to select the closest device for number of channels.
545       scoped_ptr_malloc<char> name(
546           wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
547       if (strncmp(wanted_device, name.get(), strlen(wanted_device)) == 0) {
548         guessed_device = name.get();
549         break;
550       }
551     }
552 
553     // Destroy the hint now that we're done with it.
554     wrapper_->DeviceNameFreeHint(hints);
555     hints = NULL;
556   } else {
557     LOG(ERROR) << "Unable to get hints for devices: "
558                << wrapper_->StrError(error);
559   }
560 
561   return guessed_device;
562 }
563 
GetCurrentDelay()564 snd_pcm_sframes_t AlsaPcmOutputStream::GetCurrentDelay() {
565   snd_pcm_sframes_t delay = -1;
566   // Don't query ALSA's delay if we have underrun since it'll be jammed at some
567   // non-zero value and potentially even negative!
568   //
569   // Also, if we're in the prepared state, don't query because that seems to
570   // cause an I/O error when we do query the delay.
571   snd_pcm_state_t pcm_state = wrapper_->PcmState(playback_handle_);
572   if (pcm_state != SND_PCM_STATE_XRUN &&
573       pcm_state != SND_PCM_STATE_PREPARED) {
574     int error = wrapper_->PcmDelay(playback_handle_, &delay);
575     if (error < 0) {
576       // Assume a delay of zero and attempt to recover the device.
577       delay = -1;
578       error = wrapper_->PcmRecover(playback_handle_,
579                                    error,
580                                    kPcmRecoverIsSilent);
581       if (error < 0) {
582         LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error);
583       }
584     }
585   }
586 
587   // snd_pcm_delay() sometimes returns crazy values.  In this case return delay
588   // of data we know currently is in ALSA's buffer.  Note: When the underlying
589   // driver is PulseAudio based, certain configuration settings (e.g., tsched=1)
590   // will generate much larger delay values than |alsa_buffer_frames_|, so only
591   // clip if delay is truly crazy (> 10x expected).
592   if (static_cast<snd_pcm_uframes_t>(delay) > alsa_buffer_frames_ * 10) {
593     delay = alsa_buffer_frames_ - GetAvailableFrames();
594   }
595 
596   if (delay < 0) {
597     delay = 0;
598   }
599 
600   return delay;
601 }
602 
GetAvailableFrames()603 snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() {
604   DCHECK(IsOnAudioThread());
605 
606   if (stop_stream_)
607     return 0;
608 
609   // Find the number of frames queued in the sound device.
610   snd_pcm_sframes_t available_frames =
611       wrapper_->PcmAvailUpdate(playback_handle_);
612   if (available_frames < 0) {
613     available_frames = wrapper_->PcmRecover(playback_handle_,
614                                             available_frames,
615                                             kPcmRecoverIsSilent);
616   }
617   if (available_frames < 0) {
618     LOG(ERROR) << "Failed querying available frames. Assuming 0: "
619                << wrapper_->StrError(available_frames);
620     return 0;
621   }
622   if (static_cast<uint32>(available_frames) > alsa_buffer_frames_ * 2) {
623     LOG(ERROR) << "ALSA returned " << available_frames << " of "
624                << alsa_buffer_frames_ << " frames available.";
625     return alsa_buffer_frames_;
626   }
627 
628   return available_frames;
629 }
630 
AutoSelectDevice(unsigned int latency)631 snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) {
632   // For auto-selection:
633   //   1) Attempt to open a device that best matches the number of channels
634   //      requested.
635   //   2) If that fails, attempt the "plug:" version of it in case ALSA can
636   //      remap do some software conversion to make it work.
637   //   3) Fallback to kDefaultDevice.
638   //   4) If that fails too, try the "plug:" version of kDefaultDevice.
639   //   5) Give up.
640   snd_pcm_t* handle = NULL;
641   device_name_ = FindDeviceForChannels(channels_);
642 
643   // Step 1.
644   if (!device_name_.empty()) {
645     if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(),
646                                                 channels_, sample_rate_,
647                                                 pcm_format_,
648                                                 latency)) != NULL) {
649       return handle;
650     }
651 
652     // Step 2.
653     device_name_ = kPlugPrefix + device_name_;
654     if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(),
655                                                 channels_, sample_rate_,
656                                                 pcm_format_,
657                                                 latency)) != NULL) {
658       return handle;
659     }
660   }
661 
662   // For the kDefaultDevice device, we can only reliably depend on 2-channel
663   // output to have the correct ordering according to Lennart.  For the channel
664   // formats that we know how to downmix from (3 channel to 8 channel), setup
665   // downmixing.
666   uint32 default_channels = channels_;
667   if (default_channels > 2) {
668     channel_mixer_.reset(new ChannelMixer(
669         channel_layout_, CHANNEL_LAYOUT_STEREO));
670     default_channels = 2;
671     mixed_audio_bus_ = AudioBus::Create(
672         default_channels, audio_bus_->frames());
673   }
674 
675   // Step 3.
676   device_name_ = kDefaultDevice;
677   if ((handle = alsa_util::OpenPlaybackDevice(
678       wrapper_, device_name_.c_str(), default_channels, sample_rate_,
679       pcm_format_, latency)) != NULL) {
680     return handle;
681   }
682 
683   // Step 4.
684   device_name_ = kPlugPrefix + device_name_;
685   if ((handle = alsa_util::OpenPlaybackDevice(
686       wrapper_, device_name_.c_str(), default_channels, sample_rate_,
687       pcm_format_, latency)) != NULL) {
688     return handle;
689   }
690 
691   // Unable to open any device.
692   device_name_.clear();
693   return NULL;
694 }
695 
CanTransitionTo(InternalState to)696 bool AlsaPcmOutputStream::CanTransitionTo(InternalState to) {
697   switch (state_) {
698     case kCreated:
699       return to == kIsOpened || to == kIsClosed || to == kInError;
700 
701     case kIsOpened:
702       return to == kIsPlaying || to == kIsStopped ||
703           to == kIsClosed || to == kInError;
704 
705     case kIsPlaying:
706       return to == kIsPlaying || to == kIsStopped ||
707           to == kIsClosed || to == kInError;
708 
709     case kIsStopped:
710       return to == kIsPlaying || to == kIsStopped ||
711           to == kIsClosed || to == kInError;
712 
713     case kInError:
714       return to == kIsClosed || to == kInError;
715 
716     case kIsClosed:
717     default:
718       return false;
719   }
720 }
721 
722 AlsaPcmOutputStream::InternalState
TransitionTo(InternalState to)723 AlsaPcmOutputStream::TransitionTo(InternalState to) {
724   DCHECK(IsOnAudioThread());
725 
726   if (!CanTransitionTo(to)) {
727     NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to;
728     state_ = kInError;
729   } else {
730     state_ = to;
731   }
732   return state_;
733 }
734 
state()735 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() {
736   return state_;
737 }
738 
IsOnAudioThread() const739 bool AlsaPcmOutputStream::IsOnAudioThread() const {
740   return message_loop_ && message_loop_ == base::MessageLoop::current();
741 }
742 
RunDataCallback(AudioBus * audio_bus,AudioBuffersState buffers_state)743 int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus,
744                                          AudioBuffersState buffers_state) {
745   TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback");
746 
747   if (source_callback_)
748     return source_callback_->OnMoreData(audio_bus, buffers_state);
749 
750   return 0;
751 }
752 
RunErrorCallback(int code)753 void AlsaPcmOutputStream::RunErrorCallback(int code) {
754   if (source_callback_)
755     source_callback_->OnError(this);
756 }
757 
758 // Changes the AudioSourceCallback to proxy calls to.  Pass in NULL to
759 // release ownership of the currently registered callback.
set_source_callback(AudioSourceCallback * callback)760 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) {
761   DCHECK(IsOnAudioThread());
762   source_callback_ = callback;
763 }
764 
765 }  // namespace media
766