diff options
author | skobes@google.com <skobes@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-07 22:08:05 +0000 |
---|---|---|
committer | skobes@google.com <skobes@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-07 22:08:05 +0000 |
commit | ab2066f6062f3acec61bce9b2cb52910549d051d (patch) | |
tree | a7120570dff5c48844cb0b312cf362f5ab41666e /media/audio/pulse | |
parent | 0cfa96c53fc8f28c5c60c950fb278db89a05d9ad (diff) | |
download | chromium_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.cc | 14 | ||||
-rw-r--r-- | media/audio/pulse/audio_manager_pulse.h | 6 | ||||
-rw-r--r-- | media/audio/pulse/pulse_unified.cc | 292 | ||||
-rw-r--r-- | media/audio/pulse/pulse_unified.h | 90 |
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_ |