summaryrefslogtreecommitdiffstats
path: root/media/audio/linux/alsa_output.h
diff options
context:
space:
mode:
Diffstat (limited to 'media/audio/linux/alsa_output.h')
-rw-r--r--media/audio/linux/alsa_output.h165
1 files changed, 165 insertions, 0 deletions
diff --git a/media/audio/linux/alsa_output.h b/media/audio/linux/alsa_output.h
new file mode 100644
index 0000000..b6d405b
--- /dev/null
+++ b/media/audio/linux/alsa_output.h
@@ -0,0 +1,165 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Creates an output stream based on the ALSA PCM interface. The current
+// implementation creates one thread per ALSA playback handle that is
+// responsible for synchronously pulling data from the audio data source.
+//
+// This output stream buffers in two places:
+// (1) In the ALSA device
+// (2) In an in-memory buffer.
+//
+// The ALSA device buffer is kept as full as possible. The in-memory buffer
+// attempts to keep enough extra data so that |min_buffer_ms| worth of data
+// is available between the in-memory buffer and the device buffer. Requests
+// to the audio data source are made if the total amount buffered falls below
+// |min_buffer_ms|.
+//
+// On device write failure, the stream will move into an invalid state. No
+// more data will be pulled from the data source, and the playback thread will
+// be stopped.
+//
+// If the stream is successfully opened, Close() must be called before the
+// stream is deleted.
+
+#ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_
+#define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_
+
+#include <alsa/asoundlib.h>
+
+#include <deque>
+#include <string>
+
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/thread.h"
+#include "media/audio/audio_output.h"
+
+class Thread;
+
+class AlsaPCMOutputStream :
+ public AudioOutputStream,
+ public base::RefCountedThreadSafe<AlsaPCMOutputStream> {
+ public:
+ // Set to "plug:default" which should allow ALSA to do whatever is
+ // necessary to simulate an audio device capable of handling our PCMs.
+ static const char* kDefaultDevice;
+
+ // Create a PCM Output stream for the ALSA device identified by
+ // |device_name|. If unsure of hte device_name, use kDefaultDevice.
+ AlsaPCMOutputStream(const std::string& device_name,
+ int min_buffer_ms,
+ AudioManager::Format format,
+ int channels,
+ int sample_rate,
+ char bits_per_sample);
+ virtual ~AlsaPCMOutputStream();
+
+ // Implementation of AudioOutputStream.
+ virtual bool Open(size_t packet_size);
+ virtual void Close();
+ virtual void Start(AudioSourceCallback* callback);
+ virtual void Stop();
+ virtual void SetVolume(double left_level, double right_level);
+ virtual void GetVolume(double* left_level, double* right_level);
+ virtual size_t GetNumBuffers();
+
+ private:
+ // Closes the playback handle, reporting errors if any occur. Returns true
+ // if the device was successfully closed.
+ bool CloseDevice_Locked();
+
+ // Stops playback, ignoring state checks.
+ void StopInternal_Locked();
+
+ // Moves the stream into the error state, setting the correct internal flags.
+ // Ensure that all resources are cleaned up before executing this function.
+ void EnterStateError_Locked();
+
+ // Releases all the resources in the audio stream. This method will also
+ // terminate the playback thread itself.
+ //
+ // This method must be run in the |playback_thead_|.
+ void ReleaseResources();
+
+ // Retrieve the total number of frames buffered in both memory and in the
+ // audio device. Use this to determine if more data should be requested from
+ // the audio source.
+ snd_pcm_sframes_t GetFramesOfDelay_Locked();
+
+ // Buffer more packets from data source if necessary.
+ //
+ // This function must be run in the |playback_thread_|.
+ void BufferPackets();
+
+ // Returns true if our buffer is underfull.
+ bool ShouldBufferMore_NoLock();
+
+ // Write as many buffered packets into the device as there is room in the
+ // device buffer.
+ //
+ // This function must be run in the |playback_thread_|.
+ void FillAlsaDeviceBuffer();
+
+ // State of the stream.
+ State state_;
+
+ // The ALSA device name to use.
+ std::string device_name_;
+
+ // Handle to the actual PCM playback device.
+ snd_pcm_t* playback_handle_;
+
+ // Period size for ALSA ring-buffer. Basically, how long to wait between
+ // writes.
+ snd_pcm_sframes_t period_size_;
+
+ // Callback used to request more data from the data source.
+ AudioSourceCallback* source_callback_;
+
+ // Playback thread.
+ base::Thread playback_thread_;
+
+ // Lock for field access to this object.
+ Lock lock_;
+
+ // Sample format configuration.
+ snd_pcm_format_t pcm_format_;
+ const int channels_;
+ const int sample_rate_;
+ const char bits_per_sample_;
+ char bytes_per_frame_;
+
+ // In-memory buffer to hold sound samples before pushing to the sound device.
+ // TODO(ajwong): There are now about 3 buffer queue implementations. Factor
+ // them out.
+ struct Packet {
+ explicit Packet(int new_capacity)
+ : capacity(new_capacity),
+ size(0),
+ used(0),
+ buffer(new char[capacity]) {
+ }
+ size_t capacity;
+ size_t size;
+ size_t used;
+ scoped_array<char> buffer;
+ };
+ int min_buffer_frames_;
+ std::deque<Packet*> buffered_packets_;
+ size_t packet_size_;
+
+ // Flag indiciating the device write tasks have stopped scheduling
+ // themselves. This should only be modified by the BufferPackets() and
+ // FillAlsaDeviceBuffer() methods.
+ bool device_write_suspended_;
+
+ // Flag indicating that the resources are already cleaned.
+ bool resources_released_;
+
+ DISALLOW_COPY_AND_ASSIGN(AlsaPCMOutputStream);
+};
+
+#endif // MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_