Chromium Code Reviews| Index: media/base/android/media_codec_audio_decoder.cc |
| diff --git a/media/base/android/media_codec_audio_decoder.cc b/media/base/android/media_codec_audio_decoder.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1453f42e5e697366e2c5f1b0b3e8798ea9fcba81 |
| --- /dev/null |
| +++ b/media/base/android/media_codec_audio_decoder.cc |
| @@ -0,0 +1,201 @@ |
| +// Copyright 2015 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/media_codec_audio_decoder.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/logging.h" |
| + |
| +#include "media/base/android/media_codec_bridge.h" |
| +#include "media/base/audio_timestamp_helper.h" |
| +#include "media/base/demuxer_stream.h" |
| + |
| +//#include "base/tvlog.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 { |
| + |
| +MediaCodecAudioDecoder::MediaCodecAudioDecoder( |
| + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| + const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, |
| + const base::Closure& request_data_cb, |
| + const base::Closure& starvation_cb, |
| + const base::Closure& stop_done_cb, |
| + const base::Closure& error_cb, |
| + const SetTimeCallback& update_current_time_cb) |
| + : MediaCodecDecoder(media_task_runner, |
| + ui_task_runner, |
| + request_data_cb, |
| + starvation_cb, |
| + stop_done_cb, |
| + error_cb, |
| + "AudioDecoder"), |
| + volume_(-1.0), |
| + bytes_per_frame_(0), |
| + frame_count_(0), |
| + update_current_time_cb_(update_current_time_cb) |
| +{} |
|
qinmin
2015/05/13 23:17:31
{ placed incorrectly
Tima Vaisburd
2015/05/14 00:14:59
Fixed
|
| + |
| +MediaCodecAudioDecoder::~MediaCodecAudioDecoder() |
| +{ |
|
qinmin
2015/05/13 23:17:31
ditto
Tima Vaisburd
2015/05/14 00:14:59
Fixed
|
| + DVLOG(1) << "AudioDecoder::~AudioDecoder()"; |
| +} |
| + |
| +void MediaCodecAudioDecoder::SetVolume(double volume) { |
| + // Media thread |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| + DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << volume; |
| + |
| + volume_ = volume; |
| + SetVolumeInternal(); |
| +} |
| + |
| +bool MediaCodecAudioDecoder::HasStream() const { |
| + // Media thread |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| + return configs_.audio_codec != kUnknownAudioCodec; |
| +} |
| + |
| +void MediaCodecAudioDecoder::Flush() { |
| + // Media thread |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| + MediaCodecDecoder::Flush(); |
| + frame_count_ = 0; |
| +} |
| + |
| +bool MediaCodecAudioDecoder::IsCodecReconfigureNeeded( |
| + const DemuxerConfigs& curr, |
| + const DemuxerConfigs& next) const { |
| + return curr.audio_codec != next.audio_codec || |
| + curr.audio_channels != next.audio_channels || |
| + curr.audio_sampling_rate != next.audio_sampling_rate || |
| + next.is_audio_encrypted != next.is_audio_encrypted || |
| + curr.audio_extra_data.size() != next.audio_extra_data.size() || |
| + !std::equal(curr.audio_extra_data.begin(), |
| + curr.audio_extra_data.end(), |
| + next.audio_extra_data.begin()); |
| +} |
| + |
| +MediaCodecDecoder::ConfigStatus |
| +MediaCodecAudioDecoder::ConfigureInternal() { |
| + // Media thread |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| + DVLOG(1) << class_name() << "::" << __FUNCTION__; |
| + |
| + media_codec_bridge_.reset(AudioCodecBridge::Create(configs_.audio_codec)); |
| + if (!media_codec_bridge_) |
| + return CONFIG_FAILURE; |
| + |
| + if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get()))->Start( |
| + configs_.audio_codec, |
| + configs_.audio_sampling_rate, |
| + configs_.audio_channels, |
| + &configs_.audio_extra_data[0], |
| + configs_.audio_extra_data.size(), |
| + configs_.audio_codec_delay_ns, |
| + configs_.audio_seek_preroll_ns, |
| + true, |
| + GetMediaCrypto().obj())) { |
| + |
| + DVLOG(1) << class_name() << "::" << __FUNCTION__ << " failed"; |
| + |
| + media_codec_bridge_.reset(); |
| + return CONFIG_FAILURE; |
| + } |
| + |
| + DVLOG(1) << class_name() << "::" << __FUNCTION__ << " succeeded"; |
| + |
| + SetVolumeInternal(); |
| + |
| + bytes_per_frame_ = kBytesPerAudioOutputSample * configs_.audio_channels; |
| + frame_count_ = 0; |
| + ResetTimestampHelper(); |
| + |
| + return CONFIG_OK; |
| +} |
| + |
| +void MediaCodecAudioDecoder::SetBaseTimestamp(base::TimeDelta base_timestamp) { |
| + // Media thread |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| + DVLOG(1) << __FUNCTION__ << " " << base_timestamp; |
| + |
| + base_timestamp_ = base_timestamp; |
| + if (audio_timestamp_helper_) |
| + audio_timestamp_helper_->SetBaseTimestamp(base_timestamp_); |
| +} |
| + |
| +void MediaCodecAudioDecoder::ResetTimestampHelper() { |
| + // Media thread |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| + if (audio_timestamp_helper_) |
| + base_timestamp_ = audio_timestamp_helper_->GetTimestamp(); |
| + |
| + audio_timestamp_helper_.reset( |
| + new AudioTimestampHelper(configs_.audio_sampling_rate)); |
| + |
| + audio_timestamp_helper_->SetBaseTimestamp(base_timestamp_); |
| +} |
| + |
| +void MediaCodecAudioDecoder::SetVolumeInternal() { |
| + // Media thread |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| + if (media_codec_bridge_) { |
| + static_cast<AudioCodecBridge*>(media_codec_bridge_.get())->SetVolume( |
| + volume_); |
| + } |
| +} |
| + |
| +void MediaCodecAudioDecoder::Render(int buffer_index, |
| + size_t size, |
| + bool render_output, |
| + base::TimeDelta pts, |
| + bool eos_encountered) { |
| + // Decoder thread |
| + DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| + |
| + DVLOG(2) << class_name() << "::" << __FUNCTION__ << " pts:" << pts; |
| + |
| + render_output = render_output && (size != 0u); |
| + |
| + if (render_output) { |
| + int64 head_position = (static_cast<AudioCodecBridge*>( |
| + media_codec_bridge_.get()))->PlayOutputBuffer( |
| + buffer_index, size); |
| + |
| + 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); |
| + |
| + base::TimeDelta last_buffered = audio_timestamp_helper_->GetTimestamp(); |
| + base::TimeDelta now_playing = |
| + last_buffered - |
| + audio_timestamp_helper_->GetFrameDuration(frames_to_play); |
| + |
| + DVLOG(2) << class_name() << "::" << __FUNCTION__ << " pts:" << pts |
| + << " will play: [" << now_playing << "," << last_buffered << "]"; |
| + |
| + update_current_time_cb_.Run(now_playing, last_buffered); |
| + } |
| + |
| + media_codec_bridge_->ReleaseOutputBuffer(buffer_index, false); |
| + |
| + ProcessLastFrame(eos_encountered, false); // no delayed tasks |
| +} |
| + |
| +} // namespace media |