blob: 06f728a209ce6a474bea25e296d895d9a57c194b [file] [log] [blame]
// 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/base/android/audio_decoder_job.h"
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/threading/thread.h"
#include "media/base/android/sdk_media_codec_bridge.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/timestamp_constants.h"
namespace {
// Use 16bit PCM for audio output. Keep this value in sync with the output
// format we passed to AudioTrack in MediaCodecBridge.
const int kBytesPerAudioOutputSample = 2;
}
namespace media {
class AudioDecoderThread : public base::Thread {
public:
AudioDecoderThread() : base::Thread("MediaSource_AudioDecoderThread") {
Start();
}
};
// TODO(qinmin): Check if it is tolerable to use worker pool to handle all the
// decoding tasks so that we don't need a global thread here.
// http://crbug.com/245750
base::LazyInstance<AudioDecoderThread>::Leaky
g_audio_decoder_thread = LAZY_INSTANCE_INITIALIZER;
AudioDecoderJob::AudioDecoderJob(
const base::Closure& request_data_cb,
const base::Closure& on_demuxer_config_changed_cb)
: MediaDecoderJob(g_audio_decoder_thread.Pointer()->task_runner(),
request_data_cb,
on_demuxer_config_changed_cb),
audio_codec_(kUnknownAudioCodec),
num_channels_(0),
config_sampling_rate_(0),
volume_(-1.0),
bytes_per_frame_(0),
output_sampling_rate_(0),
frame_count_(0) {
}
AudioDecoderJob::~AudioDecoderJob() {}
bool AudioDecoderJob::HasStream() const {
return audio_codec_ != kUnknownAudioCodec;
}
void AudioDecoderJob::Flush() {
MediaDecoderJob::Flush();
frame_count_ = 0;
}
void AudioDecoderJob::SetDemuxerConfigs(const DemuxerConfigs& configs) {
// TODO(qinmin): split DemuxerConfig for audio and video separately so we
// can simply store the stucture here.
audio_codec_ = configs.audio_codec;
num_channels_ = configs.audio_channels;
config_sampling_rate_ = configs.audio_sampling_rate;
set_is_content_encrypted(configs.is_audio_encrypted);
audio_extra_data_ = configs.audio_extra_data;
audio_codec_delay_ns_ = configs.audio_codec_delay_ns;
audio_seek_preroll_ns_ = configs.audio_seek_preroll_ns;
bytes_per_frame_ = kBytesPerAudioOutputSample * num_channels_;
if (!media_codec_bridge_)
output_sampling_rate_ = config_sampling_rate_;
}
void AudioDecoderJob::SetVolume(double volume) {
volume_ = volume;
SetVolumeInternal();
}
void AudioDecoderJob::SetBaseTimestamp(base::TimeDelta base_timestamp) {
DCHECK(!is_decoding());
base_timestamp_ = base_timestamp;
if (audio_timestamp_helper_)
audio_timestamp_helper_->SetBaseTimestamp(base_timestamp_);
}
void AudioDecoderJob::ResetTimestampHelper() {
if (audio_timestamp_helper_)
base_timestamp_ = audio_timestamp_helper_->GetTimestamp();
audio_timestamp_helper_.reset(
new AudioTimestampHelper(output_sampling_rate_));
audio_timestamp_helper_->SetBaseTimestamp(base_timestamp_);
}
void AudioDecoderJob::ReleaseOutputBuffer(
int output_buffer_index,
size_t offset,
size_t size,
bool render_output,
bool /* is_late_frame */,
base::TimeDelta current_presentation_timestamp,
const ReleaseOutputCompletionCallback& callback) {
render_output = render_output && (size != 0u);
bool is_audio_underrun = false;
if (render_output) {
int64 head_position = (static_cast<AudioCodecBridge*>(
media_codec_bridge_.get()))->PlayOutputBuffer(
output_buffer_index, size, offset);
base::TimeTicks current_time = base::TimeTicks::Now();
size_t new_frames_count = size / bytes_per_frame_;
frame_count_ += new_frames_count;
audio_timestamp_helper_->AddFrames(new_frames_count);
int64 frames_to_play = frame_count_ - head_position;
DCHECK_GE(frames_to_play, 0);
const base::TimeDelta last_buffered =
audio_timestamp_helper_->GetTimestamp();
current_presentation_timestamp =
last_buffered -
audio_timestamp_helper_->GetFrameDuration(frames_to_play);
// Potential audio underrun is considered a late frame for UMA.
is_audio_underrun = !next_frame_time_limit_.is_null() &&
next_frame_time_limit_ < current_time;
next_frame_time_limit_ =
current_time + (last_buffered - current_presentation_timestamp);
} else {
current_presentation_timestamp = kNoTimestamp();
}
media_codec_bridge_->ReleaseOutputBuffer(output_buffer_index, false);
callback.Run(is_audio_underrun, current_presentation_timestamp,
audio_timestamp_helper_->GetTimestamp());
}
bool AudioDecoderJob::ComputeTimeToRender() const {
return false;
}
bool AudioDecoderJob::AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const {
return audio_codec_ != configs.audio_codec ||
num_channels_ != configs.audio_channels ||
config_sampling_rate_ != configs.audio_sampling_rate ||
is_content_encrypted() != configs.is_audio_encrypted ||
audio_extra_data_.size() != configs.audio_extra_data.size() ||
!std::equal(audio_extra_data_.begin(),
audio_extra_data_.end(),
configs.audio_extra_data.begin());
}
MediaDecoderJob::MediaDecoderJobStatus
AudioDecoderJob::CreateMediaCodecBridgeInternal() {
media_codec_bridge_.reset(AudioCodecBridge::Create(audio_codec_));
if (!media_codec_bridge_)
return STATUS_FAILURE;
if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get()))
->ConfigureAndStart(audio_codec_, config_sampling_rate_,
num_channels_, &audio_extra_data_[0],
audio_extra_data_.size(), audio_codec_delay_ns_,
audio_seek_preroll_ns_, true,
GetMediaCrypto().obj())) {
media_codec_bridge_.reset();
return STATUS_FAILURE;
}
SetVolumeInternal();
// Reset values used to track codec bridge output
frame_count_ = 0;
ResetTimestampHelper();
return STATUS_SUCCESS;
}
void AudioDecoderJob::SetVolumeInternal() {
if (media_codec_bridge_) {
static_cast<AudioCodecBridge*>(media_codec_bridge_.get())->SetVolume(
volume_);
}
}
void AudioDecoderJob::OnOutputFormatChanged() {
DCHECK(media_codec_bridge_);
int old_sampling_rate = output_sampling_rate_;
output_sampling_rate_ = media_codec_bridge_->GetOutputSamplingRate();
if (output_sampling_rate_ != old_sampling_rate)
ResetTimestampHelper();
}
} // namespace media