root/media/audio/sounds/audio_stream_handler.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. delayed_stop_posted_
  2. AudioStreamContainer
  3. Play
  4. Stop
  5. OnMoreData
  6. OnMoreIOData
  7. OnError
  8. StopStream
  9. initialized_
  10. IsInitialized
  11. Play
  12. Stop
  13. SetObserverForTesting
  14. SetAudioSourceForTesting

// 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/sounds/audio_stream_handler.h"

#include <string>

#include "base/cancelable_callback.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h"
#include "media/base/channel_layout.h"

namespace media {

namespace {

// Volume percent.
const double kOutputVolumePercent = 0.8;

// The number of frames each OnMoreData() call will request.
const int kDefaultFrameCount = 1024;

// Keep alive timeout for audio stream.
const int kKeepAliveMs = 1500;

AudioStreamHandler::TestObserver* g_observer_for_testing = NULL;
AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL;

}  // namespace

class AudioStreamHandler::AudioStreamContainer
    : public AudioOutputStream::AudioSourceCallback {
 public:
  AudioStreamContainer(const WavAudioHandler& wav_audio)
      : stream_(NULL),
        wav_audio_(wav_audio),
        cursor_(0),
        started_(false),
        delayed_stop_posted_(false) {
  }

  virtual ~AudioStreamContainer() {
    DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
  }

  void Play() {
    DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());

    if (!stream_) {
      const AudioParameters& p = wav_audio_.params();
      const AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                   p.channel_layout(),
                                   p.sample_rate(),
                                   p.bits_per_sample(),
                                   kDefaultFrameCount);
      stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(
          params, std::string());
      if (!stream_ || !stream_->Open()) {
        LOG(ERROR) << "Failed to open an output stream.";
        return;
      }
      stream_->SetVolume(kOutputVolumePercent);
    }

    {
      base::AutoLock al(state_lock_);

      delayed_stop_posted_ = false;
      stop_closure_.Reset(base::Bind(
          &AudioStreamContainer::StopStream, base::Unretained(this)));

      if (started_) {
        if (wav_audio_.AtEnd(cursor_))
          cursor_ = 0;
        return;
      }

      cursor_ = 0;
      started_ = true;
    }

    if (g_audio_source_for_testing)
      stream_->Start(g_audio_source_for_testing);
    else
      stream_->Start(this);

    if (g_observer_for_testing)
      g_observer_for_testing->OnPlay();
  }

  void Stop() {
    DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
    StopStream();
    if (stream_)
      stream_->Close();
    stream_ = NULL;
  }

 private:
  // AudioOutputStream::AudioSourceCallback overrides:
  // Following methods could be called from *ANY* thread.
  virtual int OnMoreData(AudioBus* dest,
                         AudioBuffersState /* state */) OVERRIDE {
    base::AutoLock al(state_lock_);
    size_t bytes_written = 0;

    if (wav_audio_.AtEnd(cursor_) ||
        !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) {
      if (delayed_stop_posted_)
        return 0;
      delayed_stop_posted_ = true;
      AudioManager::Get()->GetTaskRunner()->PostDelayedTask(
          FROM_HERE,
          stop_closure_.callback(),
          base::TimeDelta::FromMilliseconds(kKeepAliveMs));
      return 0;
    }
    cursor_ += bytes_written;
    return dest->frames();
  }

  virtual int OnMoreIOData(AudioBus* /* source */,
                           AudioBus* dest,
                           AudioBuffersState state) OVERRIDE {
    return OnMoreData(dest, state);
  }

  virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE {
    LOG(ERROR) << "Error during system sound reproduction.";
  }

  void StopStream() {
    DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());

    base::AutoLock al(state_lock_);

    if (stream_ && started_) {
      stream_->Stop();
      if (g_observer_for_testing)
        g_observer_for_testing->OnStop(cursor_);
    }
    started_ = false;
  }

  AudioOutputStream* stream_;

  const WavAudioHandler wav_audio_;

  base::Lock state_lock_;
  size_t cursor_;
  bool started_;
  bool delayed_stop_posted_;
  base::CancelableClosure stop_closure_;

  DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer);
};

AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data)
    : wav_audio_(wav_data),
      initialized_(false) {
  AudioManager* manager = AudioManager::Get();
  if (!manager) {
    LOG(ERROR) << "Can't get access to audio manager.";
    return;
  }
  if (!wav_audio_.params().IsValid()) {
    LOG(ERROR) << "Audio params are invalid.";
    return;
  }
  stream_.reset(new AudioStreamContainer(wav_audio_));
  initialized_ = true;
}

AudioStreamHandler::~AudioStreamHandler() {
  DCHECK(CalledOnValidThread());
  AudioManager::Get()->GetTaskRunner()->PostTask(
      FROM_HERE,
      base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
  AudioManager::Get()->GetTaskRunner()->DeleteSoon(FROM_HERE,
                                                   stream_.release());
}

bool AudioStreamHandler::IsInitialized() const {
  DCHECK(CalledOnValidThread());
  return initialized_;
}

bool AudioStreamHandler::Play() {
  DCHECK(CalledOnValidThread());

  if (!IsInitialized())
    return false;

  AudioManager::Get()->GetTaskRunner()->PostTask(
      FROM_HERE,
      base::Bind(base::IgnoreResult(&AudioStreamContainer::Play),
                 base::Unretained(stream_.get())));
  return true;
}

void AudioStreamHandler::Stop() {
  DCHECK(CalledOnValidThread());
  AudioManager::Get()->GetTaskRunner()->PostTask(
      FROM_HERE,
      base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
}

// static
void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) {
  g_observer_for_testing = observer;
}

// static
void AudioStreamHandler::SetAudioSourceForTesting(
    AudioOutputStream::AudioSourceCallback* source) {
  g_audio_source_for_testing = source;
}

}  // namespace media

/* [<][>][^][v][top][bottom][index][help] */