| Index: content/renderer/media/audio_device.cc
|
| ===================================================================
|
| --- content/renderer/media/audio_device.cc (revision 148738)
|
| +++ content/renderer/media/audio_device.cc (working copy)
|
| @@ -1,277 +0,0 @@
|
| -// Copyright (c) 2012 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 "content/renderer/media/audio_device.h"
|
| -
|
| -#include "base/debug/trace_event.h"
|
| -#include "base/message_loop.h"
|
| -#include "base/threading/thread_restrictions.h"
|
| -#include "base/time.h"
|
| -#include "media/audio/audio_output_controller.h"
|
| -#include "media/audio/audio_util.h"
|
| -
|
| -using media::AudioRendererSink;
|
| -
|
| -// Takes care of invoking the render callback on the audio thread.
|
| -// An instance of this class is created for each capture stream in
|
| -// OnStreamCreated().
|
| -class AudioDevice::AudioThreadCallback
|
| - : public AudioDeviceThread::Callback {
|
| - public:
|
| - AudioThreadCallback(const media::AudioParameters& audio_parameters,
|
| - base::SharedMemoryHandle memory,
|
| - int memory_length,
|
| - AudioRendererSink::RenderCallback* render_callback);
|
| - virtual ~AudioThreadCallback();
|
| -
|
| - virtual void MapSharedMemory() OVERRIDE;
|
| -
|
| - // Called whenever we receive notifications about pending data.
|
| - virtual void Process(int pending_data) OVERRIDE;
|
| -
|
| - private:
|
| - AudioRendererSink::RenderCallback* render_callback_;
|
| - DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
|
| -};
|
| -
|
| -AudioDevice::AudioDevice(
|
| - media::AudioOutputIPC* ipc,
|
| - const scoped_refptr<base::MessageLoopProxy>& io_loop)
|
| - : ScopedLoopObserver(io_loop),
|
| - callback_(NULL),
|
| - ipc_(ipc),
|
| - stream_id_(0),
|
| - play_on_start_(true),
|
| - is_started_(false) {
|
| - CHECK(ipc_);
|
| -}
|
| -
|
| -void AudioDevice::Initialize(const media::AudioParameters& params,
|
| - RenderCallback* callback) {
|
| - CHECK_EQ(0, stream_id_) <<
|
| - "AudioDevice::Initialize() must be called before Start()";
|
| -
|
| - CHECK(!callback_); // Calling Initialize() twice?
|
| -
|
| - audio_parameters_ = params;
|
| - callback_ = callback;
|
| -}
|
| -
|
| -AudioDevice::~AudioDevice() {
|
| - // The current design requires that the user calls Stop() before deleting
|
| - // this class.
|
| - CHECK_EQ(0, stream_id_);
|
| -}
|
| -
|
| -void AudioDevice::Start() {
|
| - DCHECK(callback_) << "Initialize hasn't been called";
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioDevice::CreateStreamOnIOThread, this,
|
| - audio_parameters_));
|
| -}
|
| -
|
| -void AudioDevice::Stop() {
|
| - {
|
| - base::AutoLock auto_lock(audio_thread_lock_);
|
| - audio_thread_.Stop(MessageLoop::current());
|
| - }
|
| -
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioDevice::ShutDownOnIOThread, this));
|
| -}
|
| -
|
| -void AudioDevice::Play() {
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioDevice::PlayOnIOThread, this));
|
| -}
|
| -
|
| -void AudioDevice::Pause(bool flush) {
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioDevice::PauseOnIOThread, this, flush));
|
| -}
|
| -
|
| -bool AudioDevice::SetVolume(double volume) {
|
| - if (volume < 0 || volume > 1.0)
|
| - return false;
|
| -
|
| - if (!message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioDevice::SetVolumeOnIOThread, this, volume))) {
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void AudioDevice::CreateStreamOnIOThread(const media::AudioParameters& params) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - // Make sure we don't create the stream more than once.
|
| - DCHECK_EQ(0, stream_id_);
|
| - if (stream_id_)
|
| - return;
|
| -
|
| - stream_id_ = ipc_->AddDelegate(this);
|
| - ipc_->CreateStream(stream_id_, params);
|
| -}
|
| -
|
| -void AudioDevice::PlayOnIOThread() {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - if (stream_id_ && is_started_)
|
| - ipc_->PlayStream(stream_id_);
|
| - else
|
| - play_on_start_ = true;
|
| -}
|
| -
|
| -void AudioDevice::PauseOnIOThread(bool flush) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - if (stream_id_ && is_started_) {
|
| - ipc_->PauseStream(stream_id_);
|
| - if (flush)
|
| - ipc_->FlushStream(stream_id_);
|
| - } else {
|
| - // Note that |flush| isn't relevant here since this is the case where
|
| - // the stream is first starting.
|
| - play_on_start_ = false;
|
| - }
|
| -}
|
| -
|
| -void AudioDevice::ShutDownOnIOThread() {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| -
|
| - // Make sure we don't call shutdown more than once.
|
| - if (stream_id_) {
|
| - is_started_ = false;
|
| -
|
| - ipc_->CloseStream(stream_id_);
|
| - ipc_->RemoveDelegate(stream_id_);
|
| - stream_id_ = 0;
|
| - }
|
| -
|
| - // We can run into an issue where ShutDownOnIOThread is called right after
|
| - // OnStreamCreated is called in cases where Start/Stop are called before we
|
| - // get the OnStreamCreated callback. To handle that corner case, we call
|
| - // Stop(). In most cases, the thread will already be stopped.
|
| - // Another situation is when the IO thread goes away before Stop() is called
|
| - // in which case, we cannot use the message loop to close the thread handle
|
| - // and can't not rely on the main thread existing either.
|
| - base::ThreadRestrictions::ScopedAllowIO allow_io;
|
| - audio_thread_.Stop(NULL);
|
| - audio_callback_.reset();
|
| -}
|
| -
|
| -void AudioDevice::SetVolumeOnIOThread(double volume) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - if (stream_id_)
|
| - ipc_->SetVolume(stream_id_, volume);
|
| -}
|
| -
|
| -void AudioDevice::OnStateChanged(media::AudioOutputIPCDelegate::State state) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| -
|
| - // Do nothing if the stream has been closed.
|
| - if (!stream_id_)
|
| - return;
|
| -
|
| - if (state == media::AudioOutputIPCDelegate::kError) {
|
| - DLOG(WARNING) << "AudioDevice::OnStateChanged(kError)";
|
| - // Don't dereference the callback object if the audio thread
|
| - // is stopped or stopping. That could mean that the callback
|
| - // object has been deleted.
|
| - // TODO(tommi): Add an explicit contract for clearing the callback
|
| - // object. Possibly require calling Initialize again or provide
|
| - // a callback object via Start() and clear it in Stop().
|
| - if (!audio_thread_.IsStopped())
|
| - callback_->OnRenderError();
|
| - }
|
| -}
|
| -
|
| -void AudioDevice::OnStreamCreated(
|
| - base::SharedMemoryHandle handle,
|
| - base::SyncSocket::Handle socket_handle,
|
| - int length) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - DCHECK_GE(length, audio_parameters_.GetBytesPerBuffer());
|
| -#if defined(OS_WIN)
|
| - DCHECK(handle);
|
| - DCHECK(socket_handle);
|
| -#else
|
| - DCHECK_GE(handle.fd, 0);
|
| - DCHECK_GE(socket_handle, 0);
|
| -#endif
|
| -
|
| - // We should only get this callback if stream_id_ is valid. If it is not,
|
| - // the IPC layer should have closed the shared memory and socket handles
|
| - // for us and not invoked the callback. The basic assertion is that when
|
| - // stream_id_ is 0 the AudioDevice instance is not registered as a delegate
|
| - // and hence it should not receive callbacks.
|
| - DCHECK(stream_id_);
|
| -
|
| - base::AutoLock auto_lock(audio_thread_lock_);
|
| -
|
| - DCHECK(audio_thread_.IsStopped());
|
| - audio_callback_.reset(new AudioDevice::AudioThreadCallback(audio_parameters_,
|
| - handle, length, callback_));
|
| - audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioDevice");
|
| -
|
| - // We handle the case where Play() and/or Pause() may have been called
|
| - // multiple times before OnStreamCreated() gets called.
|
| - is_started_ = true;
|
| - if (play_on_start_)
|
| - PlayOnIOThread();
|
| -}
|
| -
|
| -void AudioDevice::OnIPCClosed() {
|
| - ipc_ = NULL;
|
| -}
|
| -
|
| -void AudioDevice::WillDestroyCurrentMessageLoop() {
|
| - LOG(ERROR) << "IO loop going away before the audio device has been stopped";
|
| - ShutDownOnIOThread();
|
| -}
|
| -
|
| -// AudioDevice::AudioThreadCallback
|
| -
|
| -AudioDevice::AudioThreadCallback::AudioThreadCallback(
|
| - const media::AudioParameters& audio_parameters,
|
| - base::SharedMemoryHandle memory,
|
| - int memory_length,
|
| - media::AudioRendererSink::RenderCallback* render_callback)
|
| - : AudioDeviceThread::Callback(audio_parameters, memory, memory_length),
|
| - render_callback_(render_callback) {
|
| -}
|
| -
|
| -AudioDevice::AudioThreadCallback::~AudioThreadCallback() {
|
| -}
|
| -
|
| -void AudioDevice::AudioThreadCallback::MapSharedMemory() {
|
| - shared_memory_.Map(media::TotalSharedMemorySizeInBytes(memory_length_));
|
| -}
|
| -
|
| -// Called whenever we receive notifications about pending data.
|
| -void AudioDevice::AudioThreadCallback::Process(int pending_data) {
|
| - if (pending_data == media::AudioOutputController::kPauseMark) {
|
| - memset(shared_memory_.memory(), 0, memory_length_);
|
| - media::SetActualDataSizeInBytes(&shared_memory_, memory_length_, 0);
|
| - return;
|
| - }
|
| -
|
| - // Convert the number of pending bytes in the render buffer
|
| - // into milliseconds.
|
| - int audio_delay_milliseconds = pending_data / bytes_per_ms_;
|
| -
|
| - TRACE_EVENT0("audio", "AudioDevice::FireRenderCallback");
|
| -
|
| - // Update the audio-delay measurement then ask client to render audio.
|
| - size_t num_frames = render_callback_->Render(audio_data_,
|
| - audio_parameters_.frames_per_buffer(), audio_delay_milliseconds);
|
| -
|
| - // Interleave, scale, and clip to int.
|
| - // TODO(crogers/vrk): Figure out a way to avoid the float -> int -> float
|
| - // conversions that happen in the <audio> and WebRTC scenarios.
|
| - media::InterleaveFloatToInt(audio_data_, shared_memory_.memory(),
|
| - num_frames, audio_parameters_.bits_per_sample() / 8);
|
| -
|
| - // Let the host know we are done.
|
| - media::SetActualDataSizeInBytes(&shared_memory_, memory_length_,
|
| - num_frames * audio_parameters_.GetBytesPerFrame());
|
| -}
|
|
|