This source file includes following definitions.
- started
- RecordStats
- RecordFallbackStats
- SetupFallbackParams
- streams_opened_
- Initialize
- OpenStream
- StartStream
- StreamVolumeSet
- StopStream
- CloseStream
- Shutdown
- CloseStreamsForWedgeFix
- RestartStreamsForWedgeFix
- audio_converter_
- Start
- Stop
- OnMoreIOData
- ProvideInput
- OnError
#include "media/audio/audio_output_resampler.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/metrics/histogram.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_output_dispatcher_impl.h"
#include "media/audio/audio_output_proxy.h"
#include "media/audio/sample_rates.h"
#include "media/base/audio_converter.h"
#include "media/base/limits.h"
namespace media {
class OnMoreDataConverter
: public AudioOutputStream::AudioSourceCallback,
public AudioConverter::InputCallback {
public:
OnMoreDataConverter(const AudioParameters& input_params,
const AudioParameters& output_params);
virtual ~OnMoreDataConverter();
virtual int OnMoreData(AudioBus* dest,
AudioBuffersState buffers_state) OVERRIDE;
virtual int OnMoreIOData(AudioBus* source,
AudioBus* dest,
AudioBuffersState buffers_state) OVERRIDE;
virtual void OnError(AudioOutputStream* stream) OVERRIDE;
void Start(AudioOutputStream::AudioSourceCallback* callback);
void Stop();
bool started() { return source_callback_ != NULL; }
private:
virtual double ProvideInput(AudioBus* audio_bus,
base::TimeDelta buffer_delay) OVERRIDE;
const double io_ratio_;
AudioOutputStream::AudioSourceCallback* source_callback_;
AudioBuffersState current_buffers_state_;
const int input_bytes_per_second_;
AudioConverter audio_converter_;
DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
};
static void RecordStats(const AudioParameters& output_params) {
UMA_HISTOGRAM_ENUMERATION(
"Media.HardwareAudioBitsPerChannel",
output_params.bits_per_sample(),
limits::kMaxBitsPerSample);
UMA_HISTOGRAM_ENUMERATION(
"Media.HardwareAudioChannelLayout", output_params.channel_layout(),
CHANNEL_LAYOUT_MAX + 1);
UMA_HISTOGRAM_ENUMERATION(
"Media.HardwareAudioChannelCount", output_params.channels(),
limits::kMaxChannels);
AudioSampleRate asr;
if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
UMA_HISTOGRAM_ENUMERATION(
"Media.HardwareAudioSamplesPerSecond", asr, kAudioSampleRateMax + 1);
} else {
UMA_HISTOGRAM_COUNTS(
"Media.HardwareAudioSamplesPerSecondUnexpected",
output_params.sample_rate());
}
}
static void RecordFallbackStats(const AudioParameters& output_params) {
UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
UMA_HISTOGRAM_ENUMERATION(
"Media.FallbackHardwareAudioBitsPerChannel",
output_params.bits_per_sample(),
limits::kMaxBitsPerSample);
UMA_HISTOGRAM_ENUMERATION(
"Media.FallbackHardwareAudioChannelLayout",
output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1);
UMA_HISTOGRAM_ENUMERATION(
"Media.FallbackHardwareAudioChannelCount", output_params.channels(),
limits::kMaxChannels);
AudioSampleRate asr;
if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
UMA_HISTOGRAM_ENUMERATION(
"Media.FallbackHardwareAudioSamplesPerSecond",
asr, kAudioSampleRateMax + 1);
} else {
UMA_HISTOGRAM_COUNTS(
"Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
output_params.sample_rate());
}
}
void AudioOutputResampler::SetupFallbackParams() {
#if defined(OS_WIN)
static const int kMinLowLatencyFrameSize = 2048;
const int frames_per_buffer =
std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize);
output_params_ = AudioParameters(
AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
params_.sample_rate(), params_.bits_per_sample(),
frames_per_buffer);
device_id_ = "";
Initialize();
#endif
}
AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
const AudioParameters& input_params,
const AudioParameters& output_params,
const std::string& output_device_id,
const base::TimeDelta& close_delay)
: AudioOutputDispatcher(audio_manager, input_params, output_device_id),
close_delay_(close_delay),
output_params_(output_params),
streams_opened_(false) {
DCHECK(input_params.IsValid());
DCHECK(output_params.IsValid());
DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
RecordStats(output_params);
Initialize();
}
AudioOutputResampler::~AudioOutputResampler() {
DCHECK(callbacks_.empty());
}
void AudioOutputResampler::Initialize() {
DCHECK(!streams_opened_);
DCHECK(callbacks_.empty());
dispatcher_ = new AudioOutputDispatcherImpl(
audio_manager_, output_params_, device_id_, close_delay_);
}
bool AudioOutputResampler::OpenStream() {
DCHECK(task_runner_->BelongsToCurrentThread());
if (dispatcher_->OpenStream()) {
if (!streams_opened_ &&
output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
}
streams_opened_ = true;
return true;
}
if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
streams_opened_ || !callbacks_.empty()) {
return false;
}
DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
RecordFallbackStats(output_params_);
#if defined(OS_WIN)
DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling "
<< "back to high latency audio output.";
SetupFallbackParams();
if (dispatcher_->OpenStream()) {
streams_opened_ = true;
return true;
}
#endif
DLOG(ERROR) << "Unable to open audio device in high latency mode. Falling "
<< "back to fake audio output.";
output_params_.Reset(
AudioParameters::AUDIO_FAKE, params_.channel_layout(),
params_.channels(), params_.input_channels(), params_.sample_rate(),
params_.bits_per_sample(), params_.frames_per_buffer());
Initialize();
if (dispatcher_->OpenStream()) {
streams_opened_ = true;
return true;
}
return false;
}
bool AudioOutputResampler::StartStream(
AudioOutputStream::AudioSourceCallback* callback,
AudioOutputProxy* stream_proxy) {
DCHECK(task_runner_->BelongsToCurrentThread());
OnMoreDataConverter* resampler_callback = NULL;
CallbackMap::iterator it = callbacks_.find(stream_proxy);
if (it == callbacks_.end()) {
resampler_callback = new OnMoreDataConverter(params_, output_params_);
callbacks_[stream_proxy] = resampler_callback;
} else {
resampler_callback = it->second;
}
resampler_callback->Start(callback);
bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
if (!result)
resampler_callback->Stop();
return result;
}
void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
double volume) {
DCHECK(task_runner_->BelongsToCurrentThread());
dispatcher_->StreamVolumeSet(stream_proxy, volume);
}
void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
DCHECK(task_runner_->BelongsToCurrentThread());
dispatcher_->StopStream(stream_proxy);
CallbackMap::iterator it = callbacks_.find(stream_proxy);
if (it != callbacks_.end())
it->second->Stop();
}
void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
DCHECK(task_runner_->BelongsToCurrentThread());
dispatcher_->CloseStream(stream_proxy);
CallbackMap::iterator it = callbacks_.find(stream_proxy);
if (it != callbacks_.end()) {
delete it->second;
callbacks_.erase(it);
}
}
void AudioOutputResampler::Shutdown() {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
dispatcher_->Shutdown();
DCHECK(callbacks_.empty());
}
void AudioOutputResampler::CloseStreamsForWedgeFix() {
DCHECK(task_runner_->BelongsToCurrentThread());
for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
++it) {
if (it->second->started())
dispatcher_->StopStream(it->first);
dispatcher_->CloseStream(it->first);
}
dispatcher_->CloseStreamsForWedgeFix();
}
void AudioOutputResampler::RestartStreamsForWedgeFix() {
DCHECK(task_runner_->BelongsToCurrentThread());
for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
++it) {
dispatcher_->OpenStream();
}
for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
++it) {
if (it->second->started())
dispatcher_->StartStream(it->second, it->first);
}
}
OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
const AudioParameters& output_params)
: io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
output_params.GetBytesPerSecond()),
source_callback_(NULL),
input_bytes_per_second_(input_params.GetBytesPerSecond()),
audio_converter_(input_params, output_params, false) {}
OnMoreDataConverter::~OnMoreDataConverter() {
CHECK(!source_callback_);
}
void OnMoreDataConverter::Start(
AudioOutputStream::AudioSourceCallback* callback) {
CHECK(!source_callback_);
source_callback_ = callback;
audio_converter_.AddInput(this);
}
void OnMoreDataConverter::Stop() {
CHECK(source_callback_);
source_callback_ = NULL;
audio_converter_.RemoveInput(this);
}
int OnMoreDataConverter::OnMoreData(AudioBus* dest,
AudioBuffersState buffers_state) {
return OnMoreIOData(NULL, dest, buffers_state);
}
int OnMoreDataConverter::OnMoreIOData(AudioBus* source,
AudioBus* dest,
AudioBuffersState buffers_state) {
current_buffers_state_ = buffers_state;
audio_converter_.Convert(dest);
return dest->frames();
}
double OnMoreDataConverter::ProvideInput(AudioBus* dest,
base::TimeDelta buffer_delay) {
AudioBuffersState new_buffers_state;
new_buffers_state.pending_bytes =
io_ratio_ * (current_buffers_state_.total_bytes() +
buffer_delay.InSecondsF() * input_bytes_per_second_);
const int frames = source_callback_->OnMoreIOData(
NULL, dest, new_buffers_state);
if (frames > 0 && frames < dest->frames())
dest->ZeroFramesPartial(frames, dest->frames() - frames);
return frames > 0 ? 1 : 0;
}
void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
source_callback_->OnError(stream);
}
}