summaryrefslogtreecommitdiffstats
path: root/media/audio/pulse
diff options
context:
space:
mode:
authorskobes@google.com <skobes@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-07 22:08:05 +0000
committerskobes@google.com <skobes@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-07 22:08:05 +0000
commitab2066f6062f3acec61bce9b2cb52910549d051d (patch)
treea7120570dff5c48844cb0b312cf362f5ab41666e /media/audio/pulse
parent0cfa96c53fc8f28c5c60c950fb278db89a05d9ad (diff)
downloadchromium_src-ab2066f6062f3acec61bce9b2cb52910549d051d.zip
chromium_src-ab2066f6062f3acec61bce9b2cb52910549d051d.tar.gz
chromium_src-ab2066f6062f3acec61bce9b2cb52910549d051d.tar.bz2
Revert 249790 "Remove the unified IO code on the browser."
http://build.chromium.org/p/chromium.chromiumos/builders/ChromiumOS%20%28amd64%29/builds/14117 chromeos-chrome-34.0.1829.0_alpha-r1: ../../../../../../../home/chrome-bot/chrome_root/src/media/audio/linux/audio_manager_linux.cc: In function 'media::AudioManager* media::CreateAudioManager(media::AudioLogFactory*)': chromeos-chrome-34.0.1829.0_alpha-r1: ../../../../../../../home/chrome-bot/chrome_root/src/media/audio/linux/audio_manager_linux.cc:33:50: error: cannot allocate an object of abstract type 'media::AudioManagerCras' chromeos-chrome-34.0.1829.0_alpha-r1: return new AudioManagerCras(audio_log_factory); chromeos-chrome-34.0.1829.0_alpha-r1: ^ > Remove the unified IO code on the browser. > > Unified IO is not used any more and it should be removed. > > > BUG=337096 > TEST=bots, and nothing breaks. > > Review URL: https://codereview.chromium.org/153623004 TBR=xians@chromium.org Review URL: https://codereview.chromium.org/136233005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@249811 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio/pulse')
-rw-r--r--media/audio/pulse/audio_manager_pulse.cc14
-rw-r--r--media/audio/pulse/audio_manager_pulse.h6
-rw-r--r--media/audio/pulse/pulse_unified.cc292
-rw-r--r--media/audio/pulse/pulse_unified.h90
4 files changed, 396 insertions, 6 deletions
diff --git a/media/audio/pulse/audio_manager_pulse.cc b/media/audio/pulse/audio_manager_pulse.cc
index ea328ad..da106c2 100644
--- a/media/audio/pulse/audio_manager_pulse.cc
+++ b/media/audio/pulse/audio_manager_pulse.cc
@@ -16,6 +16,7 @@
#include "media/audio/audio_parameters.h"
#include "media/audio/pulse/pulse_input.h"
#include "media/audio/pulse/pulse_output.h"
+#include "media/audio/pulse/pulse_unified.h"
#include "media/audio/pulse/pulse_util.h"
#include "media/base/channel_layout.h"
@@ -132,15 +133,16 @@ AudioParameters AudioManagerPulse::GetInputStreamParameters(
AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
const AudioParameters& params) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
- return MakeOutputStream(params);
+ return MakeOutputStream(params, std::string());
}
AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
const AudioParameters& params,
- const std::string& device_id) {
+ const std::string& device_id,
+ const std::string& input_device_id) {
DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
- return MakeOutputStream(params);
+ return MakeOutputStream(params, input_device_id);
}
AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
@@ -187,7 +189,11 @@ AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters(
}
AudioOutputStream* AudioManagerPulse::MakeOutputStream(
- const AudioParameters& params) {
+ const AudioParameters& params, const std::string& input_device_id) {
+ if (params.input_channels()) {
+ return new PulseAudioUnifiedStream(params, input_device_id, this);
+ }
+
return new PulseAudioOutputStream(params, this);
}
diff --git a/media/audio/pulse/audio_manager_pulse.h b/media/audio/pulse/audio_manager_pulse.h
index b3b0031..45fb8cb 100644
--- a/media/audio/pulse/audio_manager_pulse.h
+++ b/media/audio/pulse/audio_manager_pulse.h
@@ -37,7 +37,8 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase {
const AudioParameters& params) OVERRIDE;
virtual AudioOutputStream* MakeLowLatencyOutputStream(
const AudioParameters& params,
- const std::string& device_id) OVERRIDE;
+ const std::string& device_id,
+ const std::string& input_device_id) OVERRIDE;
virtual AudioInputStream* MakeLinearInputStream(
const AudioParameters& params, const std::string& device_id) OVERRIDE;
virtual AudioInputStream* MakeLowLatencyInputStream(
@@ -69,7 +70,8 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase {
void* user_data);
// Called by MakeLinearOutputStream and MakeLowLatencyOutputStream.
- AudioOutputStream* MakeOutputStream(const AudioParameters& params);
+ AudioOutputStream* MakeOutputStream(const AudioParameters& params,
+ const std::string& input_device_id);
// Called by MakeLinearInputStream and MakeLowLatencyInputStream.
AudioInputStream* MakeInputStream(const AudioParameters& params,
diff --git a/media/audio/pulse/pulse_unified.cc b/media/audio/pulse/pulse_unified.cc
new file mode 100644
index 0000000..cd17b01
--- /dev/null
+++ b/media/audio/pulse/pulse_unified.cc
@@ -0,0 +1,292 @@
+// Copyright 2013 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.
+
+#include "media/audio/pulse/pulse_unified.h"
+
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/audio_parameters.h"
+#include "media/audio/pulse/pulse_util.h"
+#include "media/base/seekable_buffer.h"
+
+namespace media {
+
+using pulse::AutoPulseLock;
+using pulse::WaitForOperationCompletion;
+
+static const int kFifoSizeInPackets = 10;
+
+// static, pa_stream_notify_cb
+void PulseAudioUnifiedStream::StreamNotifyCallback(pa_stream* s,
+ void* user_data) {
+ PulseAudioUnifiedStream* stream =
+ static_cast<PulseAudioUnifiedStream*>(user_data);
+
+ // Forward unexpected failures to the AudioSourceCallback if available. All
+ // these variables are only modified under pa_threaded_mainloop_lock() so this
+ // should be thread safe.
+ if (s && stream->source_callback_ &&
+ pa_stream_get_state(s) == PA_STREAM_FAILED) {
+ stream->source_callback_->OnError(stream);
+ }
+
+ pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
+}
+
+// static, used by pa_stream_set_read_callback.
+void PulseAudioUnifiedStream::ReadCallback(pa_stream* handle, size_t length,
+ void* user_data) {
+ static_cast<PulseAudioUnifiedStream*>(user_data)->ReadData();
+}
+
+PulseAudioUnifiedStream::PulseAudioUnifiedStream(
+ const AudioParameters& params,
+ const std::string& input_device_id,
+ AudioManagerBase* manager)
+ : params_(params),
+ input_device_id_(input_device_id),
+ manager_(manager),
+ pa_context_(NULL),
+ pa_mainloop_(NULL),
+ input_stream_(NULL),
+ output_stream_(NULL),
+ volume_(1.0f),
+ source_callback_(NULL) {
+ DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
+ CHECK(params_.IsValid());
+ input_bus_ = AudioBus::Create(params_);
+ output_bus_ = AudioBus::Create(params_);
+}
+
+PulseAudioUnifiedStream::~PulseAudioUnifiedStream() {
+ // All internal structures should already have been freed in Close(), which
+ // calls AudioManagerBase::ReleaseOutputStream() which deletes this object.
+ DCHECK(!input_stream_);
+ DCHECK(!output_stream_);
+ DCHECK(!pa_context_);
+ DCHECK(!pa_mainloop_);
+}
+
+bool PulseAudioUnifiedStream::Open() {
+ DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
+ // Prepare the recording buffers for the callbacks.
+ fifo_.reset(new media::SeekableBuffer(
+ 0, kFifoSizeInPackets * params_.GetBytesPerBuffer()));
+ input_data_buffer_.reset(new uint8[params_.GetBytesPerBuffer()]);
+
+ if (!pulse::CreateOutputStream(&pa_mainloop_, &pa_context_, &output_stream_,
+ params_, &StreamNotifyCallback, NULL, this))
+ return false;
+
+ if (!pulse::CreateInputStream(pa_mainloop_, pa_context_, &input_stream_,
+ params_, input_device_id_,
+ &StreamNotifyCallback, this))
+ return false;
+
+ DCHECK(pa_mainloop_);
+ DCHECK(pa_context_);
+ DCHECK(input_stream_);
+ DCHECK(output_stream_);
+ return true;
+}
+
+void PulseAudioUnifiedStream::Reset() {
+ if (!pa_mainloop_) {
+ DCHECK(!input_stream_);
+ DCHECK(!output_stream_);
+ DCHECK(!pa_context_);
+ return;
+ }
+
+ {
+ AutoPulseLock auto_lock(pa_mainloop_);
+
+ // Close the input stream.
+ if (input_stream_) {
+ // Disable all the callbacks before disconnecting.
+ pa_stream_set_state_callback(input_stream_, NULL, NULL);
+ pa_stream_flush(input_stream_, NULL, NULL);
+ pa_stream_disconnect(input_stream_);
+
+ // Release PulseAudio structures.
+ pa_stream_unref(input_stream_);
+ input_stream_ = NULL;
+ }
+
+ // Close the ouput stream.
+ if (output_stream_) {
+ // Release PulseAudio output stream structures.
+ pa_stream_set_state_callback(output_stream_, NULL, NULL);
+ pa_stream_disconnect(output_stream_);
+ pa_stream_unref(output_stream_);
+ output_stream_ = NULL;
+ }
+
+ if (pa_context_) {
+ pa_context_disconnect(pa_context_);
+ pa_context_set_state_callback(pa_context_, NULL, NULL);
+ pa_context_unref(pa_context_);
+ pa_context_ = NULL;
+ }
+ }
+
+ pa_threaded_mainloop_stop(pa_mainloop_);
+ pa_threaded_mainloop_free(pa_mainloop_);
+ pa_mainloop_ = NULL;
+}
+
+void PulseAudioUnifiedStream::Close() {
+ DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
+ Reset();
+
+ // Signal to the manager that we're closed and can be removed.
+ // This should be the last call in the function as it deletes "this".
+ manager_->ReleaseOutputStream(this);
+}
+
+void PulseAudioUnifiedStream::WriteData(size_t requested_bytes) {
+ CHECK_EQ(requested_bytes, static_cast<size_t>(params_.GetBytesPerBuffer()));
+
+ void* buffer = NULL;
+ int frames_filled = 0;
+ if (source_callback_) {
+ CHECK_GE(pa_stream_begin_write(
+ output_stream_, &buffer, &requested_bytes), 0);
+ uint32 hardware_delay = pulse::GetHardwareLatencyInBytes(
+ output_stream_, params_.sample_rate(),
+ params_.GetBytesPerFrame());
+ fifo_->Read(input_data_buffer_.get(), requested_bytes);
+ input_bus_->FromInterleaved(
+ input_data_buffer_.get(), params_.frames_per_buffer(), 2);
+
+ frames_filled = source_callback_->OnMoreIOData(
+ input_bus_.get(),
+ output_bus_.get(),
+ AudioBuffersState(0, hardware_delay));
+ }
+
+ // Zero the unfilled data so it plays back as silence.
+ if (frames_filled < output_bus_->frames()) {
+ output_bus_->ZeroFramesPartial(
+ frames_filled, output_bus_->frames() - frames_filled);
+ }
+
+ // Note: If this ever changes to output raw float the data must be clipped
+ // and sanitized since it may come from an untrusted source such as NaCl.
+ output_bus_->Scale(volume_);
+ output_bus_->ToInterleaved(
+ output_bus_->frames(), params_.bits_per_sample() / 8, buffer);
+
+ if (pa_stream_write(output_stream_, buffer, requested_bytes, NULL, 0LL,
+ PA_SEEK_RELATIVE) < 0) {
+ if (source_callback_) {
+ source_callback_->OnError(this);
+ }
+ }
+}
+
+void PulseAudioUnifiedStream::ReadData() {
+ do {
+ size_t length = 0;
+ const void* data = NULL;
+ pa_stream_peek(input_stream_, &data, &length);
+ if (!data || length == 0)
+ break;
+
+ fifo_->Append(reinterpret_cast<const uint8*>(data), length);
+
+ // Deliver the recording data to the renderer and drive the playout.
+ int packet_size = params_.GetBytesPerBuffer();
+ while (fifo_->forward_bytes() >= packet_size) {
+ WriteData(packet_size);
+ }
+
+ // Checks if we still have data.
+ pa_stream_drop(input_stream_);
+ } while (pa_stream_readable_size(input_stream_) > 0);
+
+ pa_threaded_mainloop_signal(pa_mainloop_, 0);
+}
+
+void PulseAudioUnifiedStream::Start(AudioSourceCallback* callback) {
+ DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
+ CHECK(callback);
+ CHECK(input_stream_);
+ CHECK(output_stream_);
+ AutoPulseLock auto_lock(pa_mainloop_);
+
+ // Ensure the context and stream are ready.
+ if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY &&
+ pa_stream_get_state(output_stream_) != PA_STREAM_READY &&
+ pa_stream_get_state(input_stream_) != PA_STREAM_READY) {
+ callback->OnError(this);
+ return;
+ }
+
+ source_callback_ = callback;
+
+ fifo_->Clear();
+
+ // Uncork (resume) the input stream.
+ pa_stream_set_read_callback(input_stream_, &ReadCallback, this);
+ pa_stream_readable_size(input_stream_);
+ pa_operation* operation = pa_stream_cork(input_stream_, 0, NULL, NULL);
+ WaitForOperationCompletion(pa_mainloop_, operation);
+
+ // Uncork (resume) the output stream.
+ // We use the recording stream to drive the playback, so we do not need to
+ // register the write callback using pa_stream_set_write_callback().
+ operation = pa_stream_cork(output_stream_, 0,
+ &pulse::StreamSuccessCallback, pa_mainloop_);
+ WaitForOperationCompletion(pa_mainloop_, operation);
+}
+
+void PulseAudioUnifiedStream::Stop() {
+ DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
+
+ // Cork (pause) the stream. Waiting for the main loop lock will ensure
+ // outstanding callbacks have completed.
+ AutoPulseLock auto_lock(pa_mainloop_);
+
+ // Set |source_callback_| to NULL so all FulfillWriteRequest() calls which may
+ // occur while waiting on the flush and cork exit immediately.
+ source_callback_ = NULL;
+
+ // Set the read callback to NULL before flushing the stream, otherwise it
+ // will cause deadlock on the operation.
+ pa_stream_set_read_callback(input_stream_, NULL, NULL);
+ pa_operation* operation = pa_stream_flush(
+ input_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
+ WaitForOperationCompletion(pa_mainloop_, operation);
+
+ operation = pa_stream_cork(input_stream_, 1, &pulse::StreamSuccessCallback,
+ pa_mainloop_);
+ WaitForOperationCompletion(pa_mainloop_, operation);
+
+ // Flush the stream prior to cork, doing so after will cause hangs. Write
+ // callbacks are suspended while inside pa_threaded_mainloop_lock() so this
+ // is all thread safe.
+ operation = pa_stream_flush(
+ output_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
+ WaitForOperationCompletion(pa_mainloop_, operation);
+
+ operation = pa_stream_cork(output_stream_, 1, &pulse::StreamSuccessCallback,
+ pa_mainloop_);
+ WaitForOperationCompletion(pa_mainloop_, operation);
+}
+
+void PulseAudioUnifiedStream::SetVolume(double volume) {
+ DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
+
+ volume_ = static_cast<float>(volume);
+}
+
+void PulseAudioUnifiedStream::GetVolume(double* volume) {
+ DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
+
+ *volume = volume_;
+}
+
+} // namespace media
diff --git a/media/audio/pulse/pulse_unified.h b/media/audio/pulse/pulse_unified.h
new file mode 100644
index 0000000..a800d09
--- /dev/null
+++ b/media/audio/pulse/pulse_unified.h
@@ -0,0 +1,90 @@
+// Copyright 2013 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.
+
+#ifndef MEDIA_AUDIO_PULSE_PULSE_UNIFIED_H_
+#define MEDIA_AUDIO_PULSE_PULSE_UNIFIED_H_
+
+#include <pulse/pulseaudio.h>
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_parameters.h"
+#include "media/base/audio_fifo.h"
+
+namespace media {
+
+class AudioManagerBase;
+class SeekableBuffer;
+
+class PulseAudioUnifiedStream : public AudioOutputStream {
+ public:
+ PulseAudioUnifiedStream(const AudioParameters& params,
+ const std::string& input_device_id,
+ AudioManagerBase* manager);
+
+ virtual ~PulseAudioUnifiedStream();
+
+ // Implementation of PulseAudioUnifiedStream.
+ virtual bool Open() OVERRIDE;
+ virtual void Close() OVERRIDE;
+ virtual void Start(AudioSourceCallback* callback) OVERRIDE;
+ virtual void Stop() OVERRIDE;
+ virtual void SetVolume(double volume) OVERRIDE;
+ virtual void GetVolume(double* volume) OVERRIDE;
+
+ private:
+ // Called by PulseAudio when |pa_stream_| change state. If an unexpected
+ // failure state change happens and |source_callback_| is set
+ // this method will forward the error via OnError().
+ static void StreamNotifyCallback(pa_stream* s, void* user_data);
+
+ // Called by PulseAudio recording stream when it has data.
+ static void ReadCallback(pa_stream* s, size_t length, void* user_data);
+
+ // Helpers for ReadCallback() to read and write data.
+ void WriteData(size_t requested_bytes);
+ void ReadData();
+
+ // Close() helper function to free internal structs.
+ void Reset();
+
+ // AudioParameters from the constructor.
+ const AudioParameters params_;
+
+ // Device unique ID of the input device.
+ const std::string input_device_id_;
+
+ // Audio manager that created us. Used to report that we've closed.
+ AudioManagerBase* manager_;
+
+ // PulseAudio API structs.
+ pa_context* pa_context_;
+ pa_threaded_mainloop* pa_mainloop_;
+ pa_stream* input_stream_;
+ pa_stream* output_stream_;
+
+ // Float representation of volume from 0.0 to 1.0.
+ float volume_;
+
+ // Callback to audio data source. Must only be modified while holding a lock
+ // on |pa_mainloop_| via pa_threaded_mainloop_lock().
+ AudioSourceCallback* source_callback_;
+
+ scoped_ptr<AudioBus> input_bus_;
+ scoped_ptr<AudioBus> output_bus_;
+
+ // Used for input to output buffering.
+ scoped_ptr<media::SeekableBuffer> fifo_;
+
+ // Temporary storage for recorded data. It gets a packet of data from
+ // |fifo_| and deliver the data to OnMoreIOData() callback.
+ scoped_ptr<uint8[]> input_data_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PulseAudioUnifiedStream);
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_PULSE_PULSE_UNIFIED_H_