1 // Copyright (c) 2012 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.
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.
10 // SEMANTICS OF Close()
12 // Close() is responsible for cleaning up any resources that were acquired after
13 // a successful Open(). Close() will nullify any scheduled outstanding runnable
17 // SEMANTICS OF ERROR STATES
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.
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.
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
35 #include "media/audio/linux/alsa_output.h"
39 #include "base/bind.h"
40 #include "base/debug/trace_event.h"
41 #include "base/logging.h"
42 #include "base/message_loop.h"
43 #include "base/stl_util.h"
44 #include "base/time.h"
45 #include "media/audio/audio_util.h"
46 #include "media/audio/linux/alsa_util.h"
47 #include "media/audio/linux/alsa_wrapper.h"
48 #include "media/audio/linux/audio_manager_linux.h"
49 #include "media/base/data_buffer.h"
50 #include "media/base/seekable_buffer.h"
54 // Amount of time to wait if we've exhausted the data source. This is to avoid
56 static const uint32 kNoDataSleepMilliseconds
= 10;
58 // Mininum interval between OnMoreData() calls.
59 const uint32 kMinIntervalBetweenOnMoreDataCallsInMs
= 5;
61 // According to the linux nanosleep manpage, nanosleep on linux can miss the
62 // deadline by up to 10ms because the kernel timeslice is 10ms. Give a 2x
63 // buffer to compensate for the timeslice, and any additional slowdowns.
64 static const uint32 kSleepErrorMilliseconds
= 20;
66 // Set to 0 during debugging if you want error messages due to underrun
67 // events or other recoverable errors.
69 static const int kPcmRecoverIsSilent
= 1;
71 static const int kPcmRecoverIsSilent
= 0;
74 // ALSA is currently limited to 48kHz.
75 // TODO(fbarchard): Resample audio from higher frequency to 48000.
76 static const int kAlsaMaxSampleRate
= 48000;
78 // While the "default" device may support multi-channel audio, in Alsa, only
79 // the device names surround40, surround41, surround50, etc, have a defined
80 // channel mapping according to Lennart:
82 // http://0pointer.de/blog/projects/guide-to-sound-apis.html
84 // This function makes a best guess at the specific > 2 channel device name
85 // based on the number of channels requested. NULL is returned if no device
86 // can be found to match the channel numbers. In this case, using
87 // kDefaultDevice is probably the best bet.
89 // A five channel source is assumed to be surround50 instead of surround41
90 // (which is also 5 channels).
92 // TODO(ajwong): The source data should have enough info to tell us if we want
93 // surround41 versus surround51, etc., instead of needing us to guess base don
94 // channel number. Fix API to pass that data down.
95 static const char* GuessSpecificDeviceName(uint32 channels
) {
117 std::ostream
& operator<<(std::ostream
& os
,
118 AlsaPcmOutputStream::InternalState state
) {
120 case AlsaPcmOutputStream::kInError
:
123 case AlsaPcmOutputStream::kCreated
:
126 case AlsaPcmOutputStream::kIsOpened
:
129 case AlsaPcmOutputStream::kIsPlaying
:
132 case AlsaPcmOutputStream::kIsStopped
:
135 case AlsaPcmOutputStream::kIsClosed
:
142 const char AlsaPcmOutputStream::kDefaultDevice
[] = "default";
143 const char AlsaPcmOutputStream::kAutoSelectDevice
[] = "";
144 const char AlsaPcmOutputStream::kPlugPrefix
[] = "plug:";
146 // Since we expect to only be able to wake up with a resolution of
147 // kSleepErrorMilliseconds, double that for our minimum required latency.
148 const uint32
AlsaPcmOutputStream::kMinLatencyMicros
=
149 kSleepErrorMilliseconds
* 2 * 1000;
151 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string
& device_name
,
152 const AudioParameters
& params
,
153 AlsaWrapper
* wrapper
,
154 AudioManagerLinux
* manager
)
155 : requested_device_name_(device_name
),
156 pcm_format_(alsa_util::BitsToFormat(params
.bits_per_sample())),
157 channels_(params
.channels()),
158 sample_rate_(params
.sample_rate()),
159 bytes_per_sample_(params
.bits_per_sample() / 8),
160 bytes_per_frame_(channels_
* params
.bits_per_sample() / 8),
161 should_downmix_(false),
162 packet_size_(params
.GetBytesPerBuffer()),
163 micros_per_packet_(FramesToMicros(
164 params
.frames_per_buffer(), sample_rate_
)),
165 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros
,
166 micros_per_packet_
* 2)),
167 bytes_per_output_frame_(bytes_per_frame_
),
168 alsa_buffer_frames_(0),
172 playback_handle_(NULL
),
173 frames_per_packet_(packet_size_
/ bytes_per_frame_
),
174 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
177 source_callback_(NULL
) {
178 DCHECK(IsOnAudioThread());
180 // Sanity check input values.
181 if (params
.sample_rate() > kAlsaMaxSampleRate
||
182 params
.sample_rate() <= 0) {
183 LOG(WARNING
) << "Unsupported audio frequency.";
184 TransitionTo(kInError
);
187 if (AudioParameters::AUDIO_PCM_LINEAR
!= params
.format() &&
188 AudioParameters::AUDIO_PCM_LOW_LATENCY
!= params
.format()) {
189 LOG(WARNING
) << "Unsupported audio format";
190 TransitionTo(kInError
);
193 if (pcm_format_
== SND_PCM_FORMAT_UNKNOWN
) {
194 LOG(WARNING
) << "Unsupported bits per sample: " << params
.bits_per_sample();
195 TransitionTo(kInError
);
199 AlsaPcmOutputStream::~AlsaPcmOutputStream() {
200 InternalState current_state
= state();
201 DCHECK(current_state
== kCreated
||
202 current_state
== kIsClosed
||
203 current_state
== kInError
);
204 DCHECK(!playback_handle_
);
207 bool AlsaPcmOutputStream::Open() {
208 DCHECK(IsOnAudioThread());
210 if (state() == kInError
)
213 if (!CanTransitionTo(kIsOpened
)) {
214 NOTREACHED() << "Invalid state: " << state();
218 // We do not need to check if the transition was successful because
219 // CanTransitionTo() was checked above, and it is assumed that this
220 // object's public API is only called on one thread so the state cannot
221 // transition out from under us.
222 TransitionTo(kIsOpened
);
224 // Try to open the device.
225 if (requested_device_name_
== kAutoSelectDevice
) {
226 playback_handle_
= AutoSelectDevice(latency_micros_
);
227 if (playback_handle_
)
228 DVLOG(1) << "Auto-selected device: " << device_name_
;
230 device_name_
= requested_device_name_
;
231 playback_handle_
= alsa_util::OpenPlaybackDevice(
232 wrapper_
, device_name_
.c_str(), channels_
, sample_rate_
,
233 pcm_format_
, latency_micros_
);
236 // Finish initializing the stream if the device was opened successfully.
237 if (playback_handle_
== NULL
) {
239 TransitionTo(kInError
);
242 bytes_per_output_frame_
= should_downmix_
? 2 * bytes_per_sample_
:
244 uint32 output_packet_size
= frames_per_packet_
* bytes_per_output_frame_
;
245 buffer_
.reset(new media::SeekableBuffer(0, output_packet_size
));
247 // Get alsa buffer size.
248 snd_pcm_uframes_t buffer_size
;
249 snd_pcm_uframes_t period_size
;
250 int error
= wrapper_
->PcmGetParams(playback_handle_
, &buffer_size
,
253 LOG(ERROR
) << "Failed to get playback buffer size from ALSA: "
254 << wrapper_
->StrError(error
);
255 // Buffer size is at least twice of packet size.
256 alsa_buffer_frames_
= frames_per_packet_
* 2;
258 alsa_buffer_frames_
= buffer_size
;
265 void AlsaPcmOutputStream::Close() {
266 DCHECK(IsOnAudioThread());
268 if (state() != kIsClosed
)
269 TransitionTo(kIsClosed
);
271 // Shutdown the audio device.
272 if (playback_handle_
) {
273 if (alsa_util::CloseDevice(wrapper_
, playback_handle_
) < 0) {
274 LOG(WARNING
) << "Unable to close audio device. Leaking handle.";
276 playback_handle_
= NULL
;
278 // Release the buffer.
281 // Signal anything that might already be scheduled to stop.
282 stop_stream_
= true; // Not necessary in production, but unit tests
283 // uses the flag to verify that stream was closed.
286 weak_factory_
.InvalidateWeakPtrs();
288 // Signal to the manager that we're closed and can be removed.
289 // Should be last call in the method as it deletes "this".
290 manager_
->ReleaseOutputStream(this);
293 void AlsaPcmOutputStream::Start(AudioSourceCallback
* callback
) {
294 DCHECK(IsOnAudioThread());
301 set_source_callback(callback
);
303 // Only post the task if we can enter the playing state.
304 if (TransitionTo(kIsPlaying
) == kIsPlaying
) {
305 // Before starting, the buffer might have audio from previous user of this
309 // When starting again, drop all packets in the device and prepare it again
310 // in case we are restarting from a pause state and need to flush old data.
311 int error
= wrapper_
->PcmDrop(playback_handle_
);
312 if (error
< 0 && error
!= -EAGAIN
) {
313 LOG(ERROR
) << "Failure clearing playback device ("
314 << wrapper_
->PcmName(playback_handle_
) << "): "
315 << wrapper_
->StrError(error
);
318 error
= wrapper_
->PcmPrepare(playback_handle_
);
319 if (error
< 0 && error
!= -EAGAIN
) {
320 LOG(ERROR
) << "Failure preparing stream ("
321 << wrapper_
->PcmName(playback_handle_
) << "): "
322 << wrapper_
->StrError(error
);
328 ScheduleNextWrite(false);
332 void AlsaPcmOutputStream::Stop() {
333 DCHECK(IsOnAudioThread());
335 // Reset the callback, so that it is not called anymore.
336 set_source_callback(NULL
);
338 TransitionTo(kIsStopped
);
341 void AlsaPcmOutputStream::SetVolume(double volume
) {
342 DCHECK(IsOnAudioThread());
344 volume_
= static_cast<float>(volume
);
347 void AlsaPcmOutputStream::GetVolume(double* volume
) {
348 DCHECK(IsOnAudioThread());
353 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted
) {
354 DCHECK(IsOnAudioThread());
356 // If stopped, simulate a 0-length packet.
359 *source_exhausted
= true;
363 *source_exhausted
= false;
365 // Request more data only when we run out of data in the buffer, because
366 // WritePacket() comsumes only the current chunk of data.
367 if (!buffer_
->forward_bytes()) {
368 // Before making a request to source for data we need to determine the
369 // delay (in bytes) for the requested data to be played.
371 uint32 buffer_delay
= buffer_
->forward_bytes() * bytes_per_frame_
/
372 bytes_per_output_frame_
;
374 uint32 hardware_delay
= GetCurrentDelay() * bytes_per_frame_
;
376 scoped_refptr
<media::DataBuffer
> packet
=
377 new media::DataBuffer(packet_size_
);
378 int packet_size
= RunDataCallback(packet
->GetWritableData(),
379 packet
->GetBufferSize(),
380 AudioBuffersState(buffer_delay
,
382 CHECK_LE(packet_size
, packet
->GetBufferSize());
384 // This should not happen, but in case it does, drop any trailing bytes
385 // that aren't large enough to make a frame. Without this, packet writing
386 // may stall because the last few bytes in the packet may never get used by
388 DCHECK(packet_size
% bytes_per_frame_
== 0);
389 packet_size
= (packet_size
/ bytes_per_frame_
) * bytes_per_frame_
;
391 if (should_downmix_
) {
392 if (media::FoldChannels(packet
->GetWritableData(),
397 // Adjust packet size for downmix.
398 packet_size
= packet_size
/ bytes_per_frame_
* bytes_per_output_frame_
;
400 LOG(ERROR
) << "Folding failed";
403 media::AdjustVolume(packet
->GetWritableData(),
410 if (packet_size
> 0) {
411 packet
->SetDataSize(packet_size
);
412 // Add the packet to the buffer.
413 buffer_
->Append(packet
);
415 *source_exhausted
= true;
420 void AlsaPcmOutputStream::WritePacket() {
421 DCHECK(IsOnAudioThread());
423 // If the device is in error, just eat the bytes.
429 if (state() == kIsStopped
)
432 CHECK_EQ(buffer_
->forward_bytes() % bytes_per_output_frame_
, 0u);
434 const uint8
* buffer_data
;
436 if (buffer_
->GetCurrentChunk(&buffer_data
, &buffer_size
)) {
437 buffer_size
= buffer_size
- (buffer_size
% bytes_per_output_frame_
);
438 snd_pcm_sframes_t frames
= buffer_size
/ bytes_per_output_frame_
;
440 DCHECK_GT(frames
, 0);
442 snd_pcm_sframes_t frames_written
=
443 wrapper_
->PcmWritei(playback_handle_
, buffer_data
, frames
);
444 if (frames_written
< 0) {
445 // Attempt once to immediately recover from EINTR,
446 // EPIPE (overrun/underrun), ESTRPIPE (stream suspended). WritePacket
447 // will eventually be called again, so eventual recovery will happen if
448 // muliple retries are required.
449 frames_written
= wrapper_
->PcmRecover(playback_handle_
,
451 kPcmRecoverIsSilent
);
454 if (frames_written
< 0) {
455 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping
457 if (frames_written
!= -EAGAIN
) {
458 LOG(ERROR
) << "Failed to write to pcm device: "
459 << wrapper_
->StrError(frames_written
);
460 RunErrorCallback(frames_written
);
464 if (frames_written
> frames
) {
466 << "snd_pcm_writei() has written more frame that we asked.";
467 frames_written
= frames
;
470 // Seek forward in the buffer after we've written some data to ALSA.
471 buffer_
->Seek(frames_written
* bytes_per_output_frame_
);
474 // If nothing left to write and playback hasn't started yet, start it now.
475 // This ensures that shorter sounds will still play.
476 if (playback_handle_
&&
477 (wrapper_
->PcmState(playback_handle_
) == SND_PCM_STATE_PREPARED
) &&
478 GetCurrentDelay() > 0) {
479 wrapper_
->PcmStart(playback_handle_
);
484 void AlsaPcmOutputStream::WriteTask() {
485 DCHECK(IsOnAudioThread());
490 if (state() == kIsStopped
)
493 bool source_exhausted
;
494 BufferPacket(&source_exhausted
);
497 ScheduleNextWrite(source_exhausted
);
500 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted
) {
501 DCHECK(IsOnAudioThread());
506 uint32 frames_avail_wanted
= alsa_buffer_frames_
/ 2;
507 uint32 available_frames
= GetAvailableFrames();
508 uint32 frames_in_buffer
= buffer_
->forward_bytes() / bytes_per_output_frame_
;
510 // Next write is initially scheduled for the moment when half of a packet
511 // has been played out.
512 uint32 next_fill_time_ms
=
513 FramesToMillis(frames_per_packet_
/ 2, sample_rate_
);
515 if (frames_in_buffer
&& (frames_in_buffer
<= available_frames
)) {
516 // There is data in the current buffer, consume them immediately if we have
517 // enough space in the soundcard.
518 next_fill_time_ms
= 0;
520 // Otherwise schedule the next write for the moment when half of the alsa
521 // buffer becomes available.
522 if (available_frames
< frames_avail_wanted
) {
523 uint32 frames_until_empty_enough
= frames_avail_wanted
- available_frames
;
525 FramesToMillis(frames_until_empty_enough
, sample_rate_
);
527 // Adjust for time resolution.
528 if (next_fill_time_ms
> kNoDataSleepMilliseconds
)
529 next_fill_time_ms
-= kNoDataSleepMilliseconds
;
531 // Avoid back-to-back writing.
532 if (next_fill_time_ms
< kMinIntervalBetweenOnMoreDataCallsInMs
)
533 next_fill_time_ms
= kMinIntervalBetweenOnMoreDataCallsInMs
;
534 } else if (available_frames
== alsa_buffer_frames_
) {
535 // Buffer is empty, invoke next write immediately.
536 next_fill_time_ms
= 0;
540 // Avoid busy looping if the data source is exhausted.
541 if (source_exhausted
)
542 next_fill_time_ms
= std::max(next_fill_time_ms
, kNoDataSleepMilliseconds
);
544 // Only schedule more reads/writes if we are still in the playing state.
545 if (state() == kIsPlaying
) {
546 if (next_fill_time_ms
== 0) {
547 manager_
->GetMessageLoop()->PostTask(FROM_HERE
, base::Bind(
548 &AlsaPcmOutputStream::WriteTask
, weak_factory_
.GetWeakPtr()));
550 // TODO(ajwong): Measure the reliability of the delay interval. Use
551 // base/metrics/histogram.h.
552 manager_
->GetMessageLoop()->PostDelayedTask(
554 base::Bind(&AlsaPcmOutputStream::WriteTask
,
555 weak_factory_
.GetWeakPtr()),
556 base::TimeDelta::FromMilliseconds(next_fill_time_ms
));
561 uint32
AlsaPcmOutputStream::FramesToMicros(uint32 frames
,
562 uint32 sample_rate
) {
563 return frames
* base::Time::kMicrosecondsPerSecond
/ sample_rate
;
566 uint32
AlsaPcmOutputStream::FramesToMillis(uint32 frames
,
567 uint32 sample_rate
) {
568 return frames
* base::Time::kMillisecondsPerSecond
/ sample_rate
;
571 std::string
AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels
) {
572 // Constants specified by the ALSA API for device hints.
573 static const int kGetAllDevices
= -1;
574 static const char kPcmInterfaceName
[] = "pcm";
575 static const char kIoHintName
[] = "IOID";
576 static const char kNameHintName
[] = "NAME";
578 const char* wanted_device
= GuessSpecificDeviceName(channels
);
582 std::string guessed_device
;
584 int error
= wrapper_
->DeviceNameHint(kGetAllDevices
,
588 // NOTE: Do not early return from inside this if statement. The
589 // hints above need to be freed.
590 for (void** hint_iter
= hints
; *hint_iter
!= NULL
; hint_iter
++) {
591 // Only examine devices that are output capable.. Valid values are
592 // "Input", "Output", and NULL which means both input and output.
593 scoped_ptr_malloc
<char> io(
594 wrapper_
->DeviceNameGetHint(*hint_iter
, kIoHintName
));
595 if (io
!= NULL
&& strcmp(io
.get(), "Input") == 0)
598 // Attempt to select the closest device for number of channels.
599 scoped_ptr_malloc
<char> name(
600 wrapper_
->DeviceNameGetHint(*hint_iter
, kNameHintName
));
601 if (strncmp(wanted_device
, name
.get(), strlen(wanted_device
)) == 0) {
602 guessed_device
= name
.get();
607 // Destroy the hint now that we're done with it.
608 wrapper_
->DeviceNameFreeHint(hints
);
611 LOG(ERROR
) << "Unable to get hints for devices: "
612 << wrapper_
->StrError(error
);
615 return guessed_device
;
618 snd_pcm_sframes_t
AlsaPcmOutputStream::GetCurrentDelay() {
619 snd_pcm_sframes_t delay
= -1;
621 // Don't query ALSA's delay if we have underrun since it'll be jammed at
622 // some non-zero value and potentially even negative!
623 if (wrapper_
->PcmState(playback_handle_
) != SND_PCM_STATE_XRUN
) {
624 int error
= wrapper_
->PcmDelay(playback_handle_
, &delay
);
626 // Assume a delay of zero and attempt to recover the device.
628 error
= wrapper_
->PcmRecover(playback_handle_
,
630 kPcmRecoverIsSilent
);
632 LOG(ERROR
) << "Failed querying delay: " << wrapper_
->StrError(error
);
637 // snd_pcm_delay() may not work in the beginning of the stream. In this case
638 // return delay of data we know currently is in the ALSA's buffer.
640 delay
= alsa_buffer_frames_
- GetAvailableFrames();
645 snd_pcm_sframes_t
AlsaPcmOutputStream::GetAvailableFrames() {
646 DCHECK(IsOnAudioThread());
651 // Find the number of frames queued in the sound device.
652 snd_pcm_sframes_t available_frames
=
653 wrapper_
->PcmAvailUpdate(playback_handle_
);
654 if (available_frames
< 0) {
655 available_frames
= wrapper_
->PcmRecover(playback_handle_
,
657 kPcmRecoverIsSilent
);
659 if (available_frames
< 0) {
660 LOG(ERROR
) << "Failed querying available frames. Assuming 0: "
661 << wrapper_
->StrError(available_frames
);
665 return available_frames
;
668 snd_pcm_t
* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency
) {
669 // For auto-selection:
670 // 1) Attempt to open a device that best matches the number of channels
672 // 2) If that fails, attempt the "plug:" version of it in case ALSA can
673 // remap do some software conversion to make it work.
674 // 3) Fallback to kDefaultDevice.
675 // 4) If that fails too, try the "plug:" version of kDefaultDevice.
677 snd_pcm_t
* handle
= NULL
;
678 device_name_
= FindDeviceForChannels(channels_
);
681 if (!device_name_
.empty()) {
682 if ((handle
= alsa_util::OpenPlaybackDevice(wrapper_
, device_name_
.c_str(),
683 channels_
, sample_rate_
,
690 device_name_
= kPlugPrefix
+ device_name_
;
691 if ((handle
= alsa_util::OpenPlaybackDevice(wrapper_
, device_name_
.c_str(),
692 channels_
, sample_rate_
,
699 // For the kDefaultDevice device, we can only reliably depend on 2-channel
700 // output to have the correct ordering according to Lennart. For the channel
701 // formats that we know how to downmix from (3 channel to 8 channel), setup
704 // TODO(ajwong): We need a SupportsFolding() function.
705 uint32 default_channels
= channels_
;
706 if (default_channels
> 2 && default_channels
<= 8) {
707 should_downmix_
= true;
708 default_channels
= 2;
712 device_name_
= kDefaultDevice
;
713 if ((handle
= alsa_util::OpenPlaybackDevice(
714 wrapper_
, device_name_
.c_str(), default_channels
, sample_rate_
,
715 pcm_format_
, latency
)) != NULL
) {
720 device_name_
= kPlugPrefix
+ device_name_
;
721 if ((handle
= alsa_util::OpenPlaybackDevice(
722 wrapper_
, device_name_
.c_str(), default_channels
, sample_rate_
,
723 pcm_format_
, latency
)) != NULL
) {
727 // Unable to open any device.
728 device_name_
.clear();
732 bool AlsaPcmOutputStream::CanTransitionTo(InternalState to
) {
735 return to
== kIsOpened
|| to
== kIsClosed
|| to
== kInError
;
738 return to
== kIsPlaying
|| to
== kIsStopped
||
739 to
== kIsClosed
|| to
== kInError
;
742 return to
== kIsPlaying
|| to
== kIsStopped
||
743 to
== kIsClosed
|| to
== kInError
;
746 return to
== kIsPlaying
|| to
== kIsStopped
||
747 to
== kIsClosed
|| to
== kInError
;
750 return to
== kIsClosed
|| to
== kInError
;
758 AlsaPcmOutputStream::InternalState
759 AlsaPcmOutputStream::TransitionTo(InternalState to
) {
760 DCHECK(IsOnAudioThread());
762 if (!CanTransitionTo(to
)) {
763 NOTREACHED() << "Cannot transition from: " << state_
<< " to: " << to
;
771 AlsaPcmOutputStream::InternalState
AlsaPcmOutputStream::state() {
775 bool AlsaPcmOutputStream::IsOnAudioThread() const {
776 return !manager_
->GetMessageLoop() ||
777 manager_
->GetMessageLoop()->BelongsToCurrentThread();
780 uint32
AlsaPcmOutputStream::RunDataCallback(uint8
* dest
,
782 AudioBuffersState buffers_state
) {
783 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback");
785 if (source_callback_
)
786 return source_callback_
->OnMoreData(this, dest
, max_size
, buffers_state
);
791 void AlsaPcmOutputStream::RunErrorCallback(int code
) {
792 if (source_callback_
)
793 source_callback_
->OnError(this, code
);
796 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to
797 // release ownership of the currently registered callback.
798 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback
* callback
) {
799 DCHECK(IsOnAudioThread());
800 source_callback_
= callback
;