Commit aa9cc0db authored by kjoswiak's avatar kjoswiak Committed by Commit bot

Android: Propagate sample rate change to audio decoder job

AudioDecoderJob maintains an object audio_timestamp_helper_ for use with AV sync. Previously, this object was configured with demuxer value for sample rate, whereas it should instead be synced with the decoder's reported sample rate.

This CL enables AudioDecoderJob to track decoder output rate, and ensures change in output format is propagated to this object.

BUG=internal b/18873488

Review URL: https://codereview.chromium.org/805273007

Cr-Commit-Position: refs/heads/master@{#313416}
parent bd0f0208
...@@ -40,9 +40,11 @@ AudioDecoderJob::AudioDecoderJob( ...@@ -40,9 +40,11 @@ AudioDecoderJob::AudioDecoderJob(
on_demuxer_config_changed_cb), on_demuxer_config_changed_cb),
audio_codec_(kUnknownAudioCodec), audio_codec_(kUnknownAudioCodec),
num_channels_(0), num_channels_(0),
sampling_rate_(0), config_sampling_rate_(0),
volume_(-1.0), volume_(-1.0),
bytes_per_frame_(0) { bytes_per_frame_(0),
output_sampling_rate_(0),
frame_count_(0) {
} }
AudioDecoderJob::~AudioDecoderJob() {} AudioDecoderJob::~AudioDecoderJob() {}
...@@ -51,15 +53,22 @@ bool AudioDecoderJob::HasStream() const { ...@@ -51,15 +53,22 @@ bool AudioDecoderJob::HasStream() const {
return audio_codec_ != kUnknownAudioCodec; return audio_codec_ != kUnknownAudioCodec;
} }
void AudioDecoderJob::Flush() {
MediaDecoderJob::Flush();
frame_count_ = 0;
}
void AudioDecoderJob::SetDemuxerConfigs(const DemuxerConfigs& configs) { void AudioDecoderJob::SetDemuxerConfigs(const DemuxerConfigs& configs) {
// TODO(qinmin): split DemuxerConfig for audio and video separately so we // TODO(qinmin): split DemuxerConfig for audio and video separately so we
// can simply store the stucture here. // can simply store the stucture here.
audio_codec_ = configs.audio_codec; audio_codec_ = configs.audio_codec;
num_channels_ = configs.audio_channels; num_channels_ = configs.audio_channels;
sampling_rate_ = configs.audio_sampling_rate; config_sampling_rate_ = configs.audio_sampling_rate;
set_is_content_encrypted(configs.is_audio_encrypted); set_is_content_encrypted(configs.is_audio_encrypted);
audio_extra_data_ = configs.audio_extra_data; audio_extra_data_ = configs.audio_extra_data;
bytes_per_frame_ = kBytesPerAudioOutputSample * num_channels_; bytes_per_frame_ = kBytesPerAudioOutputSample * num_channels_;
if (!media_codec_bridge_)
output_sampling_rate_ = config_sampling_rate_;
} }
void AudioDecoderJob::SetVolume(double volume) { void AudioDecoderJob::SetVolume(double volume) {
...@@ -74,6 +83,14 @@ void AudioDecoderJob::SetBaseTimestamp(base::TimeDelta base_timestamp) { ...@@ -74,6 +83,14 @@ void AudioDecoderJob::SetBaseTimestamp(base::TimeDelta base_timestamp) {
audio_timestamp_helper_->SetBaseTimestamp(base_timestamp_); 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( void AudioDecoderJob::ReleaseOutputBuffer(
int output_buffer_index, int output_buffer_index,
size_t size, size_t size,
...@@ -85,9 +102,10 @@ void AudioDecoderJob::ReleaseOutputBuffer( ...@@ -85,9 +102,10 @@ void AudioDecoderJob::ReleaseOutputBuffer(
int64 head_position = (static_cast<AudioCodecBridge*>( int64 head_position = (static_cast<AudioCodecBridge*>(
media_codec_bridge_.get()))->PlayOutputBuffer( media_codec_bridge_.get()))->PlayOutputBuffer(
output_buffer_index, size); output_buffer_index, size);
audio_timestamp_helper_->AddFrames(size / bytes_per_frame_); size_t new_frames_count = size / bytes_per_frame_;
int64 frames_to_play = frame_count_ += new_frames_count;
audio_timestamp_helper_->frame_count() - head_position; audio_timestamp_helper_->AddFrames(new_frames_count);
int64 frames_to_play = frame_count_ - head_position;
DCHECK_GE(frames_to_play, 0); DCHECK_GE(frames_to_play, 0);
current_presentation_timestamp = current_presentation_timestamp =
audio_timestamp_helper_->GetTimestamp() - audio_timestamp_helper_->GetTimestamp() -
...@@ -109,7 +127,7 @@ bool AudioDecoderJob::AreDemuxerConfigsChanged( ...@@ -109,7 +127,7 @@ bool AudioDecoderJob::AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const { const DemuxerConfigs& configs) const {
return audio_codec_ != configs.audio_codec || return audio_codec_ != configs.audio_codec ||
num_channels_ != configs.audio_channels || num_channels_ != configs.audio_channels ||
sampling_rate_ != configs.audio_sampling_rate || config_sampling_rate_ != configs.audio_sampling_rate ||
is_content_encrypted() != configs.is_audio_encrypted || is_content_encrypted() != configs.is_audio_encrypted ||
audio_extra_data_.size() != configs.audio_extra_data.size() || audio_extra_data_.size() != configs.audio_extra_data.size() ||
!std::equal(audio_extra_data_.begin(), !std::equal(audio_extra_data_.begin(),
...@@ -123,7 +141,7 @@ bool AudioDecoderJob::CreateMediaCodecBridgeInternal() { ...@@ -123,7 +141,7 @@ bool AudioDecoderJob::CreateMediaCodecBridgeInternal() {
return false; return false;
if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get()))->Start( if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get()))->Start(
audio_codec_, sampling_rate_, num_channels_, &audio_extra_data_[0], audio_codec_, config_sampling_rate_, num_channels_, &audio_extra_data_[0],
audio_extra_data_.size(), true, GetMediaCrypto().obj())) { audio_extra_data_.size(), true, GetMediaCrypto().obj())) {
media_codec_bridge_.reset(); media_codec_bridge_.reset();
return false; return false;
...@@ -131,11 +149,10 @@ bool AudioDecoderJob::CreateMediaCodecBridgeInternal() { ...@@ -131,11 +149,10 @@ bool AudioDecoderJob::CreateMediaCodecBridgeInternal() {
SetVolumeInternal(); SetVolumeInternal();
// Need to pass the base timestamp to the new decoder. // Reset values used to track codec bridge output
if (audio_timestamp_helper_) frame_count_ = 0;
base_timestamp_ = audio_timestamp_helper_->GetTimestamp(); ResetTimestampHelper();
audio_timestamp_helper_.reset(new AudioTimestampHelper(sampling_rate_));
audio_timestamp_helper_->SetBaseTimestamp(base_timestamp_);
return true; return true;
} }
...@@ -146,4 +163,13 @@ void AudioDecoderJob::SetVolumeInternal() { ...@@ -146,4 +163,13 @@ void AudioDecoderJob::SetVolumeInternal() {
} }
} }
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 } // namespace media
...@@ -29,6 +29,7 @@ class AudioDecoderJob : public MediaDecoderJob { ...@@ -29,6 +29,7 @@ class AudioDecoderJob : public MediaDecoderJob {
// MediaDecoderJob implementation. // MediaDecoderJob implementation.
virtual bool HasStream() const override; virtual bool HasStream() const override;
virtual void Flush() override;
virtual void SetDemuxerConfigs(const DemuxerConfigs& configs) override; virtual void SetDemuxerConfigs(const DemuxerConfigs& configs) override;
// Sets the volume of the audio output. // Sets the volume of the audio output.
...@@ -49,18 +50,27 @@ class AudioDecoderJob : public MediaDecoderJob { ...@@ -49,18 +50,27 @@ class AudioDecoderJob : public MediaDecoderJob {
virtual bool AreDemuxerConfigsChanged( virtual bool AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const override; const DemuxerConfigs& configs) const override;
virtual bool CreateMediaCodecBridgeInternal() override; virtual bool CreateMediaCodecBridgeInternal() override;
virtual void OnOutputFormatChanged() override;
// Helper method to set the audio output volume. // Helper method to set the audio output volume.
void SetVolumeInternal(); void SetVolumeInternal();
void ResetTimestampHelper();
// Audio configs from the demuxer. // Audio configs from the demuxer.
AudioCodec audio_codec_; AudioCodec audio_codec_;
int num_channels_; int num_channels_;
int sampling_rate_; int config_sampling_rate_;
std::vector<uint8> audio_extra_data_; std::vector<uint8> audio_extra_data_;
double volume_; double volume_;
int bytes_per_frame_; int bytes_per_frame_;
// Audio output sample rate
int output_sampling_rate_;
// Frame count to sync with audio codec output
int64 frame_count_;
// Base timestamp for the |audio_timestamp_helper_|. // Base timestamp for the |audio_timestamp_helper_|.
base::TimeDelta base_timestamp_; base::TimeDelta base_timestamp_;
......
...@@ -77,7 +77,6 @@ class MediaCodecBridge { ...@@ -77,7 +77,6 @@ class MediaCodecBridge {
private long mLastPresentationTimeUs; private long mLastPresentationTimeUs;
private String mMime; private String mMime;
private boolean mAdaptivePlaybackSupported; private boolean mAdaptivePlaybackSupported;
private int mSampleRate;
private static class DequeueInputResult { private static class DequeueInputResult {
private final int mStatus; private final int mStatus;
...@@ -448,6 +447,12 @@ class MediaCodecBridge { ...@@ -448,6 +447,12 @@ class MediaCodecBridge {
: format.getInteger(MediaFormat.KEY_WIDTH); : format.getInteger(MediaFormat.KEY_WIDTH);
} }
@CalledByNative
private int getOutputSamplingRate() {
MediaFormat format = mMediaCodec.getOutputFormat();
return format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
}
@CalledByNative @CalledByNative
private ByteBuffer getInputBuffer(int index) { private ByteBuffer getInputBuffer(int index) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
...@@ -569,7 +574,7 @@ class MediaCodecBridge { ...@@ -569,7 +574,7 @@ class MediaCodecBridge {
MediaFormat newFormat = mMediaCodec.getOutputFormat(); MediaFormat newFormat = mMediaCodec.getOutputFormat();
if (mAudioTrack != null && newFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE)) { if (mAudioTrack != null && newFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE)) {
int newSampleRate = newFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); int newSampleRate = newFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
if (newSampleRate != mSampleRate && !reconfigureAudioTrack(newFormat)) { if (mAudioTrack.setPlaybackRate(newSampleRate) != AudioTrack.SUCCESS) {
status = MEDIA_CODEC_ERROR; status = MEDIA_CODEC_ERROR;
} }
} }
...@@ -672,8 +677,20 @@ class MediaCodecBridge { ...@@ -672,8 +677,20 @@ class MediaCodecBridge {
boolean playAudio) { boolean playAudio) {
try { try {
mMediaCodec.configure(format, null, crypto, flags); mMediaCodec.configure(format, null, crypto, flags);
if (playAudio && !reconfigureAudioTrack(format)) { if (playAudio) {
return false; int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
int channelConfig = getAudioFormat(channelCount);
// Using 16bit PCM for output. Keep this value in sync with
// kBytesPerAudioOutputSample in media_codec_bridge.cc.
int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig,
AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {
mAudioTrack = null;
return false;
}
} }
return true; return true;
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
...@@ -682,35 +699,6 @@ class MediaCodecBridge { ...@@ -682,35 +699,6 @@ class MediaCodecBridge {
return false; return false;
} }
/**
* Resets the AudioTrack instance, configured according to the given format.
* If a previous AudioTrack instance already exists, release it.
*
* @param format The format from which to get sample rate and channel count.
* @return Whether or not creating the AudioTrack succeeded.
*/
private boolean reconfigureAudioTrack(MediaFormat format) {
if (mAudioTrack != null) {
mAudioTrack.release();
}
mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
int channelConfig = getAudioFormat(channelCount);
// Using 16bit PCM for output. Keep this value in sync with
// kBytesPerAudioOutputSample in media_codec_bridge.cc.
int minBufferSize = AudioTrack.getMinBufferSize(mSampleRate, channelConfig,
AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mSampleRate, channelConfig,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {
mAudioTrack = null;
Log.e(TAG, "Failed to initialize AudioTrack");
return false;
}
return true;
}
/** /**
* Play the audio buffer that is passed in. * Play the audio buffer that is passed in.
* *
......
...@@ -291,6 +291,12 @@ void MediaCodecBridge::GetOutputFormat(int* width, int* height) { ...@@ -291,6 +291,12 @@ void MediaCodecBridge::GetOutputFormat(int* width, int* height) {
*height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj()); *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj());
} }
int MediaCodecBridge::GetOutputSamplingRate() {
JNIEnv* env = AttachCurrentThread();
return Java_MediaCodecBridge_getOutputSamplingRate(env, j_media_codec_.obj());
}
MediaCodecStatus MediaCodecBridge::QueueInputBuffer( MediaCodecStatus MediaCodecBridge::QueueInputBuffer(
int index, int index,
const uint8* data, const uint8* data,
......
...@@ -107,6 +107,10 @@ class MEDIA_EXPORT MediaCodecBridge { ...@@ -107,6 +107,10 @@ class MEDIA_EXPORT MediaCodecBridge {
// returns a format change by returning INFO_OUTPUT_FORMAT_CHANGED // returns a format change by returning INFO_OUTPUT_FORMAT_CHANGED
void GetOutputFormat(int* width, int* height); void GetOutputFormat(int* width, int* height);
// Used for checking for new sampling rate after DequeueInputBuffer() returns
// INFO_OUTPUT_FORMAT_CHANGED
int GetOutputSamplingRate();
// Submits a byte array to the given input buffer. Call this after getting an // Submits a byte array to the given input buffer. Call this after getting an
// available buffer from DequeueInputBuffer(). If |data| is NULL, assume the // available buffer from DequeueInputBuffer(). If |data| is NULL, assume the
// input buffer has already been populated (but still obey |size|). // input buffer has already been populated (but still obey |size|).
......
...@@ -432,6 +432,7 @@ void MediaDecoderJob::DecodeInternal( ...@@ -432,6 +432,7 @@ void MediaDecoderJob::DecodeInternal(
if (status == MEDIA_CODEC_OUTPUT_FORMAT_CHANGED) { if (status == MEDIA_CODEC_OUTPUT_FORMAT_CHANGED) {
// TODO(qinmin): instead of waiting for the next output buffer to be // TODO(qinmin): instead of waiting for the next output buffer to be
// dequeued, post a task on the UI thread to signal the format change. // dequeued, post a task on the UI thread to signal the format change.
OnOutputFormatChanged();
has_format_change = true; has_format_change = true;
} }
} while (status != MEDIA_CODEC_OK && status != MEDIA_CODEC_ERROR && } while (status != MEDIA_CODEC_OK && status != MEDIA_CODEC_ERROR &&
...@@ -649,6 +650,8 @@ bool MediaDecoderJob::IsCodecReconfigureNeeded( ...@@ -649,6 +650,8 @@ bool MediaDecoderJob::IsCodecReconfigureNeeded(
return true; return true;
} }
void MediaDecoderJob::OnOutputFormatChanged() {}
bool MediaDecoderJob::UpdateOutputFormat() { bool MediaDecoderJob::UpdateOutputFormat() {
return false; return false;
} }
......
...@@ -231,6 +231,11 @@ class MediaDecoderJob { ...@@ -231,6 +231,11 @@ class MediaDecoderJob {
// new DemuxerConfigs, or false otherwise. // new DemuxerConfigs, or false otherwise.
virtual bool IsCodecReconfigureNeeded(const DemuxerConfigs& configs) const; virtual bool IsCodecReconfigureNeeded(const DemuxerConfigs& configs) const;
// Signals to decoder job that decoder has updated output format. Decoder job
// may need to do internal reconfiguration in order to correctly interpret
// incoming buffers
virtual void OnOutputFormatChanged();
// Update the output format from the decoder, returns true if the output // Update the output format from the decoder, returns true if the output
// format changes, or false otherwise. // format changes, or false otherwise.
virtual bool UpdateOutputFormat(); virtual bool UpdateOutputFormat();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment