diff options
Diffstat (limited to 'chromium/media')
32 files changed, 428 insertions, 161 deletions
diff --git a/chromium/media/audio/BUILD.gn b/chromium/media/audio/BUILD.gn index e69dc42dd6c..aa8ddfc5c51 100644 --- a/chromium/media/audio/BUILD.gn +++ b/chromium/media/audio/BUILD.gn @@ -188,6 +188,8 @@ source_set("audio") { "android/opensles_input.h", "android/opensles_output.cc", "android/opensles_output.h", + "android/opensles_util.cc", + "android/opensles_util.h", "android/opensles_wrapper.cc", ] deps += [ "//media/base/android:media_jni_headers" ] diff --git a/chromium/media/audio/android/audio_manager_android.cc b/chromium/media/audio/android/audio_manager_android.cc index 96a7c78c29c..0df73188b62 100644 --- a/chromium/media/audio/android/audio_manager_android.cc +++ b/chromium/media/audio/android/audio_manager_android.cc @@ -302,7 +302,16 @@ AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters( // Use the client's input parameters if they are valid. sample_rate = input_params.sample_rate(); bits_per_sample = input_params.bits_per_sample(); - channel_layout = input_params.channel_layout(); + + // Pre-Lollipop devices don't support > stereo OpenSLES output and the + // AudioManager APIs for GetOptimalOutputFrameSize() don't support channel + // layouts greater than stereo unless low latency audio is supported. + if (input_params.channels() <= 2 || + (base::android::BuildInfo::GetInstance()->sdk_int() >= 21 && + IsAudioLowLatencySupported())) { + channel_layout = input_params.channel_layout(); + } + buffer_size = GetOptimalOutputFrameSize( sample_rate, ChannelLayoutToChannelCount(channel_layout)); } diff --git a/chromium/media/audio/android/opensles_input.cc b/chromium/media/audio/android/opensles_input.cc index afbe02e5566..748a9cca802 100644 --- a/chromium/media/audio/android/opensles_input.cc +++ b/chromium/media/audio/android/opensles_input.cc @@ -39,12 +39,7 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, format_.bitsPerSample = params.bits_per_sample(); format_.containerSize = params.bits_per_sample(); format_.endianness = SL_BYTEORDER_LITTLEENDIAN; - if (format_.numChannels == 1) - format_.channelMask = SL_SPEAKER_FRONT_CENTER; - else if (format_.numChannels == 2) - format_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - else - NOTREACHED() << "Unsupported number of channels: " << format_.numChannels; + format_.channelMask = ChannelCountToSLESChannelMask(params.channels()); buffer_size_bytes_ = params.GetBytesPerBuffer(); diff --git a/chromium/media/audio/android/opensles_output.cc b/chromium/media/audio/android/opensles_output.cc index 005b0e70718..bf1957f7829 100644 --- a/chromium/media/audio/android/opensles_output.cc +++ b/chromium/media/audio/android/opensles_output.cc @@ -42,12 +42,7 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, format_.bitsPerSample = params.bits_per_sample(); format_.containerSize = params.bits_per_sample(); format_.endianness = SL_BYTEORDER_LITTLEENDIAN; - if (format_.numChannels == 1) - format_.channelMask = SL_SPEAKER_FRONT_CENTER; - else if (format_.numChannels == 2) - format_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - else - NOTREACHED() << "Unsupported number of channels: " << format_.numChannels; + format_.channelMask = ChannelCountToSLESChannelMask(params.channels()); buffer_size_bytes_ = params.GetBytesPerBuffer(); audio_bus_ = AudioBus::Create(params); diff --git a/chromium/media/audio/android/opensles_util.cc b/chromium/media/audio/android/opensles_util.cc new file mode 100644 index 00000000000..cf5ea3b98d8 --- /dev/null +++ b/chromium/media/audio/android/opensles_util.cc @@ -0,0 +1,50 @@ +// Copyright 2016 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/android/opensles_util.h" + +namespace media { + +#define SL_ANDROID_SPEAKER_QUAD \ + (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_BACK_LEFT | \ + SL_SPEAKER_BACK_RIGHT) +#define SL_ANDROID_SPEAKER_5DOT1 \ + (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_FRONT_CENTER | \ + SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT) +#define SL_ANDROID_SPEAKER_7DOT1 \ + (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT) + +// Ported from: +// https://android.googlesource.com/platform/frameworks/wilhelm/+/refs/heads/master/src/android/channels.h +// https://android.googlesource.com/platform/frameworks/wilhelm/+/refs/heads/master/src/android/channels.c +SLuint32 ChannelCountToSLESChannelMask(int channel_count) { + if (channel_count > 2) { + LOG(WARNING) << "Guessing channel layout for " << channel_count + << " channels; speaker order may be incorrect."; + } + + switch (channel_count) { + case 1: + return SL_SPEAKER_FRONT_LEFT; + case 2: + return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + case 3: + return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | + SL_SPEAKER_FRONT_CENTER; + case 4: + return SL_ANDROID_SPEAKER_QUAD; + case 5: + return SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER; + case 6: + return SL_ANDROID_SPEAKER_5DOT1; + case 7: + return SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_BACK_CENTER; + case 8: + return SL_ANDROID_SPEAKER_7DOT1; + } + + return 0; +} + +} // namespace media diff --git a/chromium/media/audio/android/opensles_util.h b/chromium/media/audio/android/opensles_util.h index bc6d0410af9..2673d14f3b6 100644 --- a/chromium/media/audio/android/opensles_util.h +++ b/chromium/media/audio/android/opensles_util.h @@ -8,6 +8,7 @@ #include <SLES/OpenSLES.h> #include "base/logging.h" +#include "media/base/media_export.h" namespace media { @@ -40,6 +41,11 @@ class ScopedSLObject { typedef ScopedSLObject<SLObjectItf, const SLObjectItf_*> ScopedSLObjectItf; +// Guesses the channel mask for a given channel count. Android does not offer a +// way to configure the layout, so this will be incorrect for less common +// channel layouts. +MEDIA_EXPORT SLuint32 ChannelCountToSLESChannelMask(int channel_count); + } // namespace media #endif // MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_ diff --git a/chromium/media/base/android/audio_decoder_job.cc b/chromium/media/base/android/audio_decoder_job.cc index f0baca4e949..9b237b8ec67 100644 --- a/chromium/media/base/android/audio_decoder_job.cc +++ b/chromium/media/base/android/audio_decoder_job.cc @@ -40,13 +40,12 @@ AudioDecoderJob::AudioDecoderJob( request_data_cb, on_demuxer_config_changed_cb), audio_codec_(kUnknownAudioCodec), - num_channels_(0), + config_num_channels_(0), config_sampling_rate_(0), volume_(-1.0), - bytes_per_frame_(0), output_sampling_rate_(0), - frame_count_(0) { -} + output_num_channels_(0), + frame_count_(0) {} AudioDecoderJob::~AudioDecoderJob() {} @@ -63,15 +62,17 @@ 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_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_) + + if (!media_codec_bridge_) { output_sampling_rate_ = config_sampling_rate_; + output_num_channels_ = config_num_channels_; + } } void AudioDecoderJob::SetVolume(double volume) { @@ -118,7 +119,8 @@ void AudioDecoderJob::ReleaseOutputBuffer( base::TimeTicks current_time = base::TimeTicks::Now(); - size_t new_frames_count = size / bytes_per_frame_; + size_t bytes_per_frame = kBytesPerAudioOutputSample * output_num_channels_; + size_t new_frames_count = size / bytes_per_frame; frame_count_ += new_frames_count; audio_timestamp_helper_->AddFrames(new_frames_count); int64_t frames_to_play = frame_count_ - head_position; @@ -153,13 +155,12 @@ bool AudioDecoderJob::ComputeTimeToRender() const { 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()); + config_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 @@ -170,7 +171,7 @@ MediaDecoderJob::MediaDecoderJobStatus if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get())) ->ConfigureAndStart(audio_codec_, config_sampling_rate_, - num_channels_, &audio_extra_data_[0], + config_num_channels_, &audio_extra_data_[0], audio_extra_data_.size(), audio_codec_delay_ns_, audio_seek_preroll_ns_, true, GetMediaCrypto())) { @@ -194,18 +195,42 @@ void AudioDecoderJob::SetVolumeInternal() { } } -void AudioDecoderJob::OnOutputFormatChanged() { +bool AudioDecoderJob::OnOutputFormatChanged() { DCHECK(media_codec_bridge_); - int old_sampling_rate = output_sampling_rate_; + // Recreate AudioTrack if either sample rate or output channel count changed. + // If we cannot obtain these values we assume they did not change. + bool needs_recreate_audio_track = false; + + const int old_sampling_rate = output_sampling_rate_; MediaCodecStatus status = media_codec_bridge_->GetOutputSamplingRate(&output_sampling_rate_); - // TODO(timav,watk): This CHECK maintains the behavior of this call before - // we started catching CodecException and returning it as MEDIA_CODEC_ERROR. - // It needs to be handled some other way. http://crbug.com/585978 - CHECK_EQ(status, MEDIA_CODEC_OK); - if (output_sampling_rate_ != old_sampling_rate) + + if (status == MEDIA_CODEC_OK && old_sampling_rate != output_sampling_rate_) { + DCHECK_GT(output_sampling_rate_, 0); + DVLOG(2) << __FUNCTION__ << ": new sampling rate " << output_sampling_rate_; + needs_recreate_audio_track = true; + ResetTimestampHelper(); + } + + const int old_num_channels = output_num_channels_; + status = media_codec_bridge_->GetOutputChannelCount(&output_num_channels_); + + if (status == MEDIA_CODEC_OK && old_num_channels != output_num_channels_) { + DCHECK_GT(output_sampling_rate_, 0); + DVLOG(2) << __FUNCTION__ << ": new channel count " << output_num_channels_; + needs_recreate_audio_track = true; + } + + if (needs_recreate_audio_track && + !static_cast<AudioCodecBridge*>(media_codec_bridge_.get()) + ->CreateAudioTrack(output_sampling_rate_, output_num_channels_)) { + DLOG(ERROR) << __FUNCTION__ << ": cannot create AudioTrack"; + return false; + } + + return true; } } // namespace media diff --git a/chromium/media/base/android/audio_decoder_job.h b/chromium/media/base/android/audio_decoder_job.h index 8b1b4e9c51f..1583185e497 100644 --- a/chromium/media/base/android/audio_decoder_job.h +++ b/chromium/media/base/android/audio_decoder_job.h @@ -54,7 +54,7 @@ class AudioDecoderJob : public MediaDecoderJob { bool ComputeTimeToRender() const override; bool AreDemuxerConfigsChanged(const DemuxerConfigs& configs) const override; MediaDecoderJobStatus CreateMediaCodecBridgeInternal() override; - void OnOutputFormatChanged() override; + bool OnOutputFormatChanged() override; // Helper method to set the audio output volume. void SetVolumeInternal(); @@ -63,18 +63,20 @@ class AudioDecoderJob : public MediaDecoderJob { // Audio configs from the demuxer. AudioCodec audio_codec_; - int num_channels_; + int config_num_channels_; int config_sampling_rate_; std::vector<uint8_t> audio_extra_data_; int64_t audio_codec_delay_ns_; int64_t audio_seek_preroll_ns_; double volume_; - int bytes_per_frame_; - // Audio output sample rate + // Audio output sample rate. int output_sampling_rate_; - // Frame count to sync with audio codec output + // Number of output audio channels. + int output_num_channels_; + + // Frame count to sync with audio codec output. int64_t frame_count_; // Base timestamp for the |audio_timestamp_helper_|. diff --git a/chromium/media/base/android/audio_media_codec_decoder.cc b/chromium/media/base/android/audio_media_codec_decoder.cc index 5654e7ca4a0..bf9939de1e5 100644 --- a/chromium/media/base/android/audio_media_codec_decoder.cc +++ b/chromium/media/base/android/audio_media_codec_decoder.cc @@ -40,8 +40,8 @@ AudioMediaCodecDecoder::AudioMediaCodecDecoder( waiting_for_decryption_key_cb, error_cb), volume_(-1.0), - bytes_per_frame_(0), output_sampling_rate_(0), + output_num_channels_(0), frame_count_(0), update_current_time_cb_(update_current_time_cb) {} @@ -65,8 +65,10 @@ void AudioMediaCodecDecoder::SetDemuxerConfigs(const DemuxerConfigs& configs) { DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << configs; configs_ = configs; - if (!media_codec_bridge_) + if (!media_codec_bridge_) { output_sampling_rate_ = configs.audio_sampling_rate; + output_num_channels_ = configs.audio_channels; + } } bool AudioMediaCodecDecoder::IsContentEncrypted() const { @@ -159,7 +161,6 @@ MediaCodecDecoder::ConfigStatus AudioMediaCodecDecoder::ConfigureInternal( SetVolumeInternal(); - bytes_per_frame_ = kBytesPerAudioOutputSample * configs_.audio_channels; frame_count_ = 0; ResetTimestampHelper(); @@ -169,16 +170,44 @@ MediaCodecDecoder::ConfigStatus AudioMediaCodecDecoder::ConfigureInternal( return kConfigOk; } -void AudioMediaCodecDecoder::OnOutputFormatChanged() { +bool AudioMediaCodecDecoder::OnOutputFormatChanged() { DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); DCHECK(media_codec_bridge_); - int old_sampling_rate = output_sampling_rate_; + // Recreate AudioTrack if either sample rate or output channel count changed. + // If we cannot obtain these values we assume they did not change. + bool needs_recreate_audio_track = false; + + const int old_sampling_rate = output_sampling_rate_; MediaCodecStatus status = media_codec_bridge_->GetOutputSamplingRate(&output_sampling_rate_); - if (status != MEDIA_CODEC_OK || output_sampling_rate_ != old_sampling_rate) + + if (status == MEDIA_CODEC_OK && old_sampling_rate != output_sampling_rate_) { + DCHECK_GT(output_sampling_rate_, 0); + DVLOG(2) << __FUNCTION__ << ": new sampling rate " << output_sampling_rate_; + needs_recreate_audio_track = true; + ResetTimestampHelper(); + } + + const int old_num_channels = output_num_channels_; + status = media_codec_bridge_->GetOutputChannelCount(&output_num_channels_); + + if (status == MEDIA_CODEC_OK && old_num_channels != output_num_channels_) { + DCHECK_GT(output_sampling_rate_, 0); + DVLOG(2) << __FUNCTION__ << ": new channel count " << output_num_channels_; + needs_recreate_audio_track = true; + } + + if (needs_recreate_audio_track && + !static_cast<AudioCodecBridge*>(media_codec_bridge_.get()) + ->CreateAudioTrack(output_sampling_rate_, output_num_channels_)) { + DLOG(ERROR) << __FUNCTION__ << ": cannot create AudioTrack"; + return false; + } + + return true; } void AudioMediaCodecDecoder::Render(int buffer_index, @@ -220,7 +249,9 @@ void AudioMediaCodecDecoder::Render(int buffer_index, if (postpone && !frame_count_) SetBaseTimestamp(pts); - size_t new_frames_count = size / bytes_per_frame_; + const size_t bytes_per_frame = + kBytesPerAudioOutputSample * output_num_channels_; + const size_t new_frames_count = size / bytes_per_frame; frame_count_ += new_frames_count; audio_timestamp_helper_->AddFrames(new_frames_count); @@ -286,7 +317,7 @@ void AudioMediaCodecDecoder::ResetTimestampHelper() { base_timestamp_ = audio_timestamp_helper_->GetTimestamp(); audio_timestamp_helper_.reset( - new AudioTimestampHelper(configs_.audio_sampling_rate)); + new AudioTimestampHelper(output_sampling_rate_)); audio_timestamp_helper_->SetBaseTimestamp(base_timestamp_); } diff --git a/chromium/media/base/android/audio_media_codec_decoder.h b/chromium/media/base/android/audio_media_codec_decoder.h index ce264b60ee6..d82322c96f9 100644 --- a/chromium/media/base/android/audio_media_codec_decoder.h +++ b/chromium/media/base/android/audio_media_codec_decoder.h @@ -50,7 +50,7 @@ class AudioMediaCodecDecoder : public MediaCodecDecoder { protected: bool IsCodecReconfigureNeeded(const DemuxerConfigs& next) const override; ConfigStatus ConfigureInternal(jobject media_crypto) override; - void OnOutputFormatChanged() override; + bool OnOutputFormatChanged() override; void Render(int buffer_index, size_t offset, size_t size, @@ -73,13 +73,12 @@ class AudioMediaCodecDecoder : public MediaCodecDecoder { // Requested volume double volume_; - // Number of bytes per audio frame. Depends on the output format and the - // number of channels. - int bytes_per_frame_; - // The sampling rate received from decoder. int output_sampling_rate_; + // The number of audio channels received from decoder. + int output_num_channels_; + // Frame count to sync with audio codec output. int64_t frame_count_; diff --git a/chromium/media/base/android/media_codec_bridge.cc b/chromium/media/base/android/media_codec_bridge.cc index 833d51ef890..8beb1a2c803 100644 --- a/chromium/media/base/android/media_codec_bridge.cc +++ b/chromium/media/base/android/media_codec_bridge.cc @@ -42,6 +42,21 @@ MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( subsamples.size(), presentation_time); } +MediaCodecStatus MediaCodecBridge::CopyFromOutputBuffer(int index, + size_t offset, + void* dst, + size_t num) { + const uint8_t* src_data = nullptr; + size_t src_capacity = 0; + MediaCodecStatus status = + GetOutputBufferAddress(index, offset, &src_data, &src_capacity); + if (status == MEDIA_CODEC_OK) { + CHECK_GE(src_capacity, num); + memcpy(dst, src_data, num); + } + return status; +} + bool MediaCodecBridge::FillInputBuffer(int index, const uint8_t* data, size_t size) { diff --git a/chromium/media/base/android/media_codec_bridge.h b/chromium/media/base/android/media_codec_bridge.h index 25d61f97cc3..434f1806761 100644 --- a/chromium/media/base/android/media_codec_bridge.h +++ b/chromium/media/base/android/media_codec_bridge.h @@ -75,6 +75,11 @@ class MEDIA_EXPORT MediaCodecBridge { // Returns MEDIA_CODEC_ERROR if an error occurs, or MEDIA_CODEC_OK otherwise. virtual MediaCodecStatus GetOutputSamplingRate(int* sampling_rate) = 0; + // Fills |channel_count| with the number of audio channels. Useful after + // INFO_OUTPUT_FORMAT_CHANGED. + // Returns MEDIA_CODEC_ERROR if an error occurs, or MEDIA_CODEC_OK otherwise. + virtual MediaCodecStatus GetOutputChannelCount(int* channel_count) = 0; + // Submits a byte array to the given input buffer. Call this after getting an // available buffer from DequeueInputBuffer(). If |data| is NULL, assume the // input buffer has already been populated (but still obey |size|). @@ -151,14 +156,23 @@ class MEDIA_EXPORT MediaCodecBridge { uint8_t** data, size_t* capacity) = 0; - // Copy |num| bytes from output buffer |index|'s |offset| into the memory + // Gives the access to buffer's data which is referenced by |index| and + // |offset|. The size of available data for reading is written to |*capacity| + // and the address is written to |*addr|. + // Returns MEDIA_CODEC_ERROR if a error occurs, or MEDIA_CODEC_OK otherwise. + virtual MediaCodecStatus GetOutputBufferAddress(int index, + size_t offset, + const uint8_t** addr, + size_t* capacity) = 0; + + // Copies |num| bytes from output buffer |index|'s |offset| into the memory // region pointed to by |dst|. To avoid overflows, the size of both source // and destination must be at least |num| bytes, and should not overlap. // Returns MEDIA_CODEC_ERROR if an error occurs, or MEDIA_CODEC_OK otherwise. - virtual MediaCodecStatus CopyFromOutputBuffer(int index, - size_t offset, - void* dst, - size_t num) = 0; + MediaCodecStatus CopyFromOutputBuffer(int index, + size_t offset, + void* dst, + size_t num); protected: MediaCodecBridge(); diff --git a/chromium/media/base/android/media_codec_decoder.cc b/chromium/media/base/android/media_codec_decoder.cc index bf3c8a31591..942cd6351c3 100644 --- a/chromium/media/base/android/media_codec_decoder.cc +++ b/chromium/media/base/android/media_codec_decoder.cc @@ -891,7 +891,12 @@ bool MediaCodecDecoder::DepleteOutputBufferQueue() { case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: DVLOG(2) << class_name() << "::" << __FUNCTION__ << " MEDIA_CODEC_OUTPUT_FORMAT_CHANGED"; - OnOutputFormatChanged(); + if (!OnOutputFormatChanged()) { + DVLOG(0) << class_name() << "::" << __FUNCTION__ + << ": OnOutputFormatChanged failed"; + status = MEDIA_CODEC_ERROR; + media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); + } break; case MEDIA_CODEC_OK: diff --git a/chromium/media/base/android/media_codec_decoder.h b/chromium/media/base/android/media_codec_decoder.h index 4135d27fb01..e9d281ff58b 100644 --- a/chromium/media/base/android/media_codec_decoder.h +++ b/chromium/media/base/android/media_codec_decoder.h @@ -281,7 +281,8 @@ class MediaCodecDecoder { virtual void DissociatePTSFromTime() {} // Processes the change of the output format, varies by stream. - virtual void OnOutputFormatChanged() = 0; + // Returns true if this processing succeeded. + virtual bool OnOutputFormatChanged() = 0; // Renders the decoded frame and releases output buffer, or posts // a delayed task to do it at a later time, diff --git a/chromium/media/base/android/media_decoder_job.cc b/chromium/media/base/android/media_decoder_job.cc index 9e0a2168adb..0e036870bb9 100644 --- a/chromium/media/base/android/media_decoder_job.cc +++ b/chromium/media/base/android/media_decoder_job.cc @@ -457,8 +457,10 @@ void MediaDecoderJob::DecodeInternal( if (status == MEDIA_CODEC_OUTPUT_FORMAT_CHANGED) { // 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. - OnOutputFormatChanged(); - has_format_change = true; + if (OnOutputFormatChanged()) + has_format_change = true; + else + status = MEDIA_CODEC_ERROR; } } while (status != MEDIA_CODEC_OK && status != MEDIA_CODEC_ERROR && status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER); @@ -675,7 +677,9 @@ bool MediaDecoderJob::IsCodecReconfigureNeeded( return true; } -void MediaDecoderJob::OnOutputFormatChanged() {} +bool MediaDecoderJob::OnOutputFormatChanged() { + return true; +} bool MediaDecoderJob::UpdateOutputFormat() { return false; diff --git a/chromium/media/base/android/media_decoder_job.h b/chromium/media/base/android/media_decoder_job.h index b0136aa8191..ce3cd767120 100644 --- a/chromium/media/base/android/media_decoder_job.h +++ b/chromium/media/base/android/media_decoder_job.h @@ -253,8 +253,8 @@ class MediaDecoderJob { // 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(); + // incoming buffers. Returns true if this internal configuration succeeded. + virtual bool OnOutputFormatChanged(); // Update the output format from the decoder, returns true if the output // format changes, or false otherwise. diff --git a/chromium/media/base/android/ndk_media_codec_bridge.cc b/chromium/media/base/android/ndk_media_codec_bridge.cc index 3a66db675c9..c39e0f9ca3e 100644 --- a/chromium/media/base/android/ndk_media_codec_bridge.cc +++ b/chromium/media/base/android/ndk_media_codec_bridge.cc @@ -85,6 +85,7 @@ MediaCodecStatus NdkMediaCodecBridge::GetOutputSize(gfx::Size* size) { AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width); AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height); } + AMediaFormat_delete(format); size->SetSize(width, height); return MEDIA_CODEC_OK; } @@ -94,10 +95,21 @@ MediaCodecStatus NdkMediaCodecBridge::GetOutputSamplingRate( AMediaFormat* format = AMediaCodec_getOutputFormat(media_codec_.get()); *sampling_rate = 0; AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampling_rate); + AMediaFormat_delete(format); DCHECK_NE(*sampling_rate, 0); return MEDIA_CODEC_OK; } +MediaCodecStatus NdkMediaCodecBridge::GetOutputChannelCount( + int* channel_count) { + AMediaFormat* format = AMediaCodec_getOutputFormat(media_codec_.get()); + *channel_count = 0; + AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channel_count); + AMediaFormat_delete(format); + DCHECK_NE(*channel_count, 0); + return MEDIA_CODEC_OK; +} + MediaCodecStatus NdkMediaCodecBridge::QueueInputBuffer( int index, const uint8_t* data, @@ -225,15 +237,15 @@ MediaCodecStatus NdkMediaCodecBridge::GetInputBuffer(int input_buffer_index, return MEDIA_CODEC_OK; } -MediaCodecStatus NdkMediaCodecBridge::CopyFromOutputBuffer(int index, - size_t offset, - void* dst, - size_t num) { - size_t capacity; +MediaCodecStatus NdkMediaCodecBridge::GetOutputBufferAddress( + int index, + size_t offset, + const uint8_t** addr, + size_t* capacity) { const uint8_t* src_data = - AMediaCodec_getOutputBuffer(media_codec_.get(), index, &capacity); - CHECK_GE(capacity, offset + num); - memcpy(dst, src_data + offset, num); + AMediaCodec_getOutputBuffer(media_codec_.get(), index, capacity); + *addr = src_data + offset; + *capacity -= offset; return MEDIA_CODEC_OK; } diff --git a/chromium/media/base/android/ndk_media_codec_bridge.h b/chromium/media/base/android/ndk_media_codec_bridge.h index d7f2b108a1a..d247d9f1e12 100644 --- a/chromium/media/base/android/ndk_media_codec_bridge.h +++ b/chromium/media/base/android/ndk_media_codec_bridge.h @@ -28,6 +28,7 @@ class MEDIA_EXPORT NdkMediaCodecBridge : public MediaCodecBridge { void Stop() override; MediaCodecStatus GetOutputSize(gfx::Size* size) override; MediaCodecStatus GetOutputSamplingRate(int* sampling_rate) override; + MediaCodecStatus GetOutputChannelCount(int* channel_count) override; MediaCodecStatus QueueInputBuffer( int index, const uint8_t* data, @@ -57,10 +58,10 @@ class MEDIA_EXPORT NdkMediaCodecBridge : public MediaCodecBridge { MediaCodecStatus GetInputBuffer(int input_buffer_index, uint8_t** data, size_t* capacity) override; - MediaCodecStatus CopyFromOutputBuffer(int index, - size_t offset, - void* dst, - size_t num) override; + MediaCodecStatus GetOutputBufferAddress(int index, + size_t offset, + const uint8_t** addr, + size_t* capacity) override; protected: NdkMediaCodecBridge(const std::string& mime, diff --git a/chromium/media/base/android/ndk_media_codec_wrapper.cc b/chromium/media/base/android/ndk_media_codec_wrapper.cc index e4709a63a52..ba8a250c9ee 100644 --- a/chromium/media/base/android/ndk_media_codec_wrapper.cc +++ b/chromium/media/base/android/ndk_media_codec_wrapper.cc @@ -26,6 +26,7 @@ // The constants used in chromium. Those symbols are defined as extern symbols // in the NdkMediaFormat headers. They will be initialized to their correct // values when the library is loaded. +const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT; const char* AMEDIAFORMAT_KEY_HEIGHT; const char* AMEDIAFORMAT_KEY_SAMPLE_RATE; const char* AMEDIAFORMAT_KEY_WIDTH; @@ -50,9 +51,10 @@ base::NativeLibrary IntializeLibraryHandle() { // The list of defined symbols. const SymbolDefinition kSymbols[] = { + {"AMEDIAFORMAT_KEY_CHANNEL_COUNT", &AMEDIAFORMAT_KEY_CHANNEL_COUNT}, {"AMEDIAFORMAT_KEY_HEIGHT", &AMEDIAFORMAT_KEY_HEIGHT}, {"AMEDIAFORMAT_KEY_SAMPLE_RATE", &AMEDIAFORMAT_KEY_SAMPLE_RATE}, - {"AMEDIAFORMAT_KEY_SAMPLE_RATE", &AMEDIAFORMAT_KEY_SAMPLE_RATE}, + {"AMEDIAFORMAT_KEY_WIDTH", &AMEDIAFORMAT_KEY_WIDTH}, }; for (size_t i = 0; i < sizeof(kSymbols) / sizeof(kSymbols[0]); ++i) { @@ -158,6 +160,10 @@ LOOKUP_FUNC(AMediaCodecCryptoInfo_delete, media_status_t, (AMediaCodecCryptoInfo * info), (info)); +LOOKUP_FUNC(AMediaFormat_delete, + media_status_t, + (AMediaFormat * format), + (format)); LOOKUP_FUNC(AMediaFormat_getInt32, bool, (AMediaFormat * format, const char* name, int32_t* out), diff --git a/chromium/media/base/android/sdk_media_codec_bridge.cc b/chromium/media/base/android/sdk_media_codec_bridge.cc index 226a90a6cbf..e35fd4b7a8d 100644 --- a/chromium/media/base/android/sdk_media_codec_bridge.cc +++ b/chromium/media/base/android/sdk_media_codec_bridge.cc @@ -137,6 +137,18 @@ MediaCodecStatus SdkMediaCodecBridge::GetOutputSamplingRate( return status; } +MediaCodecStatus SdkMediaCodecBridge::GetOutputChannelCount( + int* channel_count) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> result = + Java_MediaCodecBridge_getOutputFormat(env, j_media_codec_.obj()); + MediaCodecStatus status = static_cast<MediaCodecStatus>( + Java_GetOutputFormatResult_status(env, result.obj())); + if (status == MEDIA_CODEC_OK) + *channel_count = Java_GetOutputFormatResult_channelCount(env, result.obj()); + return status; +} + MediaCodecStatus SdkMediaCodecBridge::QueueInputBuffer( int index, const uint8_t* data, @@ -301,25 +313,11 @@ MediaCodecStatus SdkMediaCodecBridge::GetInputBuffer(int input_buffer_index, return MEDIA_CODEC_OK; } -MediaCodecStatus SdkMediaCodecBridge::CopyFromOutputBuffer(int index, - size_t offset, - void* dst, - size_t num) { - void* src_data = nullptr; - size_t src_capacity = 0; - MediaCodecStatus status = - GetOutputBufferAddress(index, offset, &src_data, &src_capacity); - if (status == MEDIA_CODEC_OK) { - CHECK_GE(src_capacity, num); - memcpy(dst, src_data, num); - } - return status; -} - -MediaCodecStatus SdkMediaCodecBridge::GetOutputBufferAddress(int index, - size_t offset, - void** addr, - size_t* capacity) { +MediaCodecStatus SdkMediaCodecBridge::GetOutputBufferAddress( + int index, + size_t offset, + const uint8_t** addr, + size_t* capacity) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> j_buffer( Java_MediaCodecBridge_getOutputBuffer(env, j_media_codec_.obj(), index)); @@ -327,9 +325,9 @@ MediaCodecStatus SdkMediaCodecBridge::GetOutputBufferAddress(int index, return MEDIA_CODEC_ERROR; const size_t total_capacity = env->GetDirectBufferCapacity(j_buffer.obj()); CHECK_GE(total_capacity, offset); - *addr = - reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(j_buffer.obj())) + - offset; + *addr = reinterpret_cast<const uint8_t*>( + env->GetDirectBufferAddress(j_buffer.obj())) + + offset; *capacity = total_capacity - offset; return MEDIA_CODEC_OK; } @@ -511,6 +509,7 @@ bool AudioCodecBridge::ConfigureMediaFormat(jobject j_format, LOG(ERROR) << "Invalid AAC header"; return false; } + const size_t kCsdLength = 2; uint8_t csd[kCsdLength]; csd[0] = profile << 3 | frequency_index >> 1; @@ -558,6 +557,15 @@ bool AudioCodecBridge::ConfigureMediaFormat(jobject j_format, return true; } +bool AudioCodecBridge::CreateAudioTrack(int sampling_rate, int channel_count) { + DVLOG(2) << __FUNCTION__ << ": samping_rate:" << sampling_rate + << " channel_count:" << channel_count; + + JNIEnv* env = AttachCurrentThread(); + return Java_MediaCodecBridge_createAudioTrack(env, media_codec(), + sampling_rate, channel_count); +} + MediaCodecStatus AudioCodecBridge::PlayOutputBuffer(int index, size_t size, size_t offset, @@ -566,7 +574,7 @@ MediaCodecStatus AudioCodecBridge::PlayOutputBuffer(int index, DCHECK_LE(0, index); int numBytes = base::checked_cast<int>(size); - void* buffer = nullptr; + const uint8_t* buffer = nullptr; size_t capacity = 0; MediaCodecStatus status = GetOutputBufferAddress(index, offset, &buffer, &capacity); @@ -577,8 +585,8 @@ MediaCodecStatus AudioCodecBridge::PlayOutputBuffer(int index, CHECK_GE(numBytes, 0); JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jbyteArray> byte_array = base::android::ToJavaByteArray( - env, static_cast<uint8_t*>(buffer), numBytes); + ScopedJavaLocalRef<jbyteArray> byte_array = + base::android::ToJavaByteArray(env, buffer, numBytes); *playback_pos = Java_MediaCodecBridge_playOutputBuffer( env, media_codec(), byte_array.obj(), postpone); return status; diff --git a/chromium/media/base/android/sdk_media_codec_bridge.h b/chromium/media/base/android/sdk_media_codec_bridge.h index e23400df319..2c52ede9060 100644 --- a/chromium/media/base/android/sdk_media_codec_bridge.h +++ b/chromium/media/base/android/sdk_media_codec_bridge.h @@ -34,6 +34,7 @@ class MEDIA_EXPORT SdkMediaCodecBridge : public MediaCodecBridge { void Stop() override; MediaCodecStatus GetOutputSize(gfx::Size* size) override; MediaCodecStatus GetOutputSamplingRate(int* sampling_rate) override; + MediaCodecStatus GetOutputChannelCount(int* channel_count) override; MediaCodecStatus QueueInputBuffer( int index, const uint8_t* data, @@ -63,10 +64,10 @@ class MEDIA_EXPORT SdkMediaCodecBridge : public MediaCodecBridge { MediaCodecStatus GetInputBuffer(int input_buffer_index, uint8_t** data, size_t* capacity) override; - MediaCodecStatus CopyFromOutputBuffer(int index, - size_t offset, - void* dst, - size_t num) override; + MediaCodecStatus GetOutputBufferAddress(int index, + size_t offset, + const uint8_t** addr, + size_t* capacity) override; static bool RegisterSdkMediaCodecBridge(JNIEnv* env); @@ -75,14 +76,6 @@ class MEDIA_EXPORT SdkMediaCodecBridge : public MediaCodecBridge { bool is_secure, MediaCodecDirection direction); - // Called to get the buffer address given the output buffer index and offset. - // The size of available data to read is written to |*capacity| and the - // address to read from is written to |*addr|. - // Returns MEDIA_CODEC_ERROR if a error occurs, or MEDIA_CODEC_OK otherwise. - MediaCodecStatus GetOutputBufferAddress(int index, - size_t offset, - void** addr, - size_t* capacity); jobject media_codec() { return j_media_codec_.obj(); } MediaCodecDirection direction_; @@ -106,7 +99,7 @@ class MEDIA_EXPORT AudioCodecBridge : public SdkMediaCodecBridge { // See MediaCodecUtil::IsKnownUnaccelerated(). static bool IsKnownUnaccelerated(const AudioCodec& codec); - // Start the audio codec bridge. If |play_audio| is true this method creates + // Starts the audio codec bridge. If |play_audio| is true this method creates // Android AudioTrack object for the actual audio playback // (http://developer.android.com/reference/android/media/AudioTrack.html). bool ConfigureAndStart(const AudioDecoderConfig& config, @@ -126,6 +119,11 @@ class MEDIA_EXPORT AudioCodecBridge : public SdkMediaCodecBridge { bool play_audio, jobject media_crypto) WARN_UNUSED_RESULT; + // Creates AudioTrack object for |sampling_rate| and |channel_count| + // (http://developer.android.com/reference/android/media/AudioTrack.html). + // Returns true in the case of success, false otherwise. + bool CreateAudioTrack(int sampling_rate, int channel_count); + // Plays the output buffer right away or save for later playback if |postpone| // is set to true. This call must be called after DequeueOutputBuffer() and // before ReleaseOutputBuffer. The data is extracted from the output buffers diff --git a/chromium/media/base/android/video_media_codec_decoder.cc b/chromium/media/base/android/video_media_codec_decoder.cc index e9a305830a4..e43dfbe51f4 100644 --- a/chromium/media/base/android/video_media_codec_decoder.cc +++ b/chromium/media/base/android/video_media_codec_decoder.cc @@ -205,7 +205,7 @@ void VideoMediaCodecDecoder::DissociatePTSFromTime() { start_pts_ = last_seen_pts_ = kNoTimestamp(); } -void VideoMediaCodecDecoder::OnOutputFormatChanged() { +bool VideoMediaCodecDecoder::OnOutputFormatChanged() { DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); gfx::Size prev_size = video_size_; @@ -218,6 +218,7 @@ void VideoMediaCodecDecoder::OnOutputFormatChanged() { media_task_runner_->PostTask( FROM_HERE, base::Bind(video_size_changed_cb_, video_size_)); } + return true; } void VideoMediaCodecDecoder::Render(int buffer_index, diff --git a/chromium/media/base/android/video_media_codec_decoder.h b/chromium/media/base/android/video_media_codec_decoder.h index 03497269ba1..9aab5e30f80 100644 --- a/chromium/media/base/android/video_media_codec_decoder.h +++ b/chromium/media/base/android/video_media_codec_decoder.h @@ -67,7 +67,7 @@ class VideoMediaCodecDecoder : public MediaCodecDecoder { ConfigStatus ConfigureInternal(jobject media_crypto) override; void AssociateCurrentTimeWithPTS(base::TimeDelta pts) override; void DissociatePTSFromTime() override; - void OnOutputFormatChanged() override; + bool OnOutputFormatChanged() override; void Render(int buffer_index, size_t offset, size_t size, diff --git a/chromium/media/base/video_decoder.h b/chromium/media/base/video_decoder.h index d6abf03e175..5a474b56e21 100644 --- a/chromium/media/base/video_decoder.h +++ b/chromium/media/base/video_decoder.h @@ -65,6 +65,10 @@ class MEDIA_EXPORT VideoDecoder { // Upon reinitialization, all internal buffered frames will be dropped. // 2) This method should not be called during pending decode or reset. // 3) No VideoDecoder calls should be made before |init_cb| is executed. + // 4) VideoDecoders should take care to run |output_cb| as soon as the frame + // is ready (i.e. w/o thread trampolining) since it can strongly affect frame + // delivery times with high-frame-rate material. See Decode() for additional + // notes. virtual void Initialize(const VideoDecoderConfig& config, bool low_delay, CdmContext* cdm_context, @@ -84,9 +88,10 @@ class MEDIA_EXPORT VideoDecoder { // called again). // // After decoding is finished the decoder calls |output_cb| specified in - // Initialize() for each decoded frame. In general |output_cb| may be called - // before or after |decode_cb|, but software decoders normally call - // |output_cb| before calling |decode_cb|, i.e. while Decode() is pending. + // Initialize() for each decoded frame. |output_cb| is always called before + // |decode_cb|. However, |output_cb| may be called before Decode() returns, if + // other behavior is desired callers should ensure that |output_cb| will + // trampoline as necessary. // // If |buffer| is an EOS buffer then the decoder must be flushed, i.e. // |output_cb| must be called for each frame pending in the queue and diff --git a/chromium/media/blink/webmediaplayer_impl.cc b/chromium/media/blink/webmediaplayer_impl.cc index 326522931d4..0b02e112ab6 100644 --- a/chromium/media/blink/webmediaplayer_impl.cc +++ b/chromium/media/blink/webmediaplayer_impl.cc @@ -281,14 +281,6 @@ void WebMediaPlayerImpl::load(LoadType load_type, const blink::WebURL& url, DoLoad(load_type, url, cors_mode); } -bool WebMediaPlayerImpl::supportsOverlayFullscreenVideo() { -#if defined(OS_ANDROID) - return true; -#else - return false; -#endif -} - void WebMediaPlayerImpl::enteredFullscreen() { fullscreen_ = true; if (decoder_requires_restart_for_fullscreen_) diff --git a/chromium/media/blink/webmediaplayer_impl.h b/chromium/media/blink/webmediaplayer_impl.h index 8ae217b806b..1af8a5209f3 100644 --- a/chromium/media/blink/webmediaplayer_impl.h +++ b/chromium/media/blink/webmediaplayer_impl.h @@ -169,7 +169,6 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl blink::WebContentDecryptionModule* cdm, blink::WebContentDecryptionModuleResult result) override; - bool supportsOverlayFullscreenVideo() override; void enteredFullscreen() override; void exitedFullscreen() override; diff --git a/chromium/media/filters/android/media_codec_audio_decoder.cc b/chromium/media/filters/android/media_codec_audio_decoder.cc index dbcd801a0fc..cb98232982a 100644 --- a/chromium/media/filters/android/media_codec_audio_decoder.cc +++ b/chromium/media/filters/android/media_codec_audio_decoder.cc @@ -63,12 +63,36 @@ scoped_ptr<MediaCodecBridge> CreateMediaCodec(const AudioDecoderConfig& config, return std::move(audio_codec_bridge); } +// Converts interleaved data into planar data and writes it to |planes|. +// The planes are populated in the order of channels in the interleaved frame. +// If |channel_count| is less than the number of available planes the extra +// destination planes will not be touched. +void SeparatePlanes(const uint8_t* interleaved_data, + size_t frame_count, + size_t bytes_per_frame, + size_t channel_count, + const std::vector<uint8_t*>& planes) { + DCHECK(interleaved_data); + DCHECK_LE(channel_count, planes.size()); + + const uint8_t* src_frame = interleaved_data; + for (size_t i = 0; i < frame_count; ++i, src_frame += bytes_per_frame) { + for (size_t ch = 0; ch < channel_count; ++ch) { + const int16_t* src_sample = + reinterpret_cast<const int16_t*>(src_frame) + ch; + int16_t* dst_sample = reinterpret_cast<int16_t*>(planes[ch]) + i; + *dst_sample = *src_sample; + } + } +} + } // namespace (anonymous) MediaCodecAudioDecoder::MediaCodecAudioDecoder( scoped_refptr<base::SingleThreadTaskRunner> task_runner) : task_runner_(task_runner), state_(STATE_UNINITIALIZED), + channel_count_(0), pending_input_buf_index_(kInvalidBufferIndex), media_drm_bridge_cdm_context_(nullptr), cdm_registration_id_(0), @@ -595,15 +619,54 @@ void MediaCodecAudioDecoder::OnDecodedFrame(const OutputBufferInfo& out) { DCHECK_NE(out.buf_index, kInvalidBufferIndex); DCHECK(media_codec_); - // Create AudioOutput buffer based on configuration. - const int channel_count = GetChannelCount(config_); - const int bytes_per_frame = kBytesPerOutputSample * channel_count; + // For proper |frame_count| calculation we need to use the actual number + // of channels which can be different from |config_| value. + const int bytes_per_frame = kBytesPerOutputSample * channel_count_; const size_t frame_count = out.size / bytes_per_frame; + // Create AudioOutput buffer based on configuration. + const int config_channel_count = GetChannelCount(config_); + const SampleFormat sample_format = config_channel_count == channel_count_ + ? kSampleFormatS16 // can copy + : kSampleFormatPlanarS16; // upsample + scoped_refptr<AudioBuffer> audio_buffer = AudioBuffer::CreateBuffer( - kSampleFormatS16, config_.channel_layout(), channel_count, + sample_format, config_.channel_layout(), config_channel_count, config_.samples_per_second(), frame_count); + if (config_channel_count == channel_count_) { + // Copy data into AudioBuffer. + CHECK_LE(out.size, audio_buffer->data_size()); + + MediaCodecStatus status = media_codec_->CopyFromOutputBuffer( + out.buf_index, out.offset, audio_buffer->channel_data()[0], out.size); + + // TODO(timav,watk): This CHECK maintains the behavior of this call before + // we started catching CodecException and returning it as MEDIA_CODEC_ERROR. + // It needs to be handled some other way. http://crbug.com/585978 + CHECK_EQ(status, MEDIA_CODEC_OK); + } else { + // Separate the planes while copying MediaCodec buffer into AudioBuffer. + DCHECK_LT(channel_count_, config_channel_count); + + const uint8_t* interleaved_data = nullptr; + size_t interleaved_capacity = 0; + MediaCodecStatus status = media_codec_->GetOutputBufferAddress( + out.buf_index, out.offset, &interleaved_data, &interleaved_capacity); + + // TODO(timav): Handle wrong status properly, http://crbug.com/585978. + CHECK_EQ(status, MEDIA_CODEC_OK); + + DCHECK_LE(out.size, interleaved_capacity); + + memset(audio_buffer->channel_data()[0], 0, audio_buffer->data_size()); + SeparatePlanes(interleaved_data, frame_count, bytes_per_frame, + channel_count_, audio_buffer->channel_data()); + } + + // Release MediaCodec output buffer. + media_codec_->ReleaseOutputBuffer(out.buf_index, false); + // Calculate and set buffer timestamp. const bool first_buffer = @@ -616,20 +679,6 @@ void MediaCodecAudioDecoder::OnDecodedFrame(const OutputBufferInfo& out) { audio_buffer->set_timestamp(timestamp_helper_->GetTimestamp()); timestamp_helper_->AddFrames(frame_count); - // Copy data into AudioBuffer. - CHECK_LE(out.size, audio_buffer->data_size()); - - MediaCodecStatus status = media_codec_->CopyFromOutputBuffer( - out.buf_index, out.offset, audio_buffer->channel_data()[0], - audio_buffer->data_size()); - // TODO(timav,watk): This CHECK maintains the behavior of this call before - // we started catching CodecException and returning it as MEDIA_CODEC_ERROR. - // It needs to be handled some other way. http://crbug.com/585978 - CHECK_EQ(status, MEDIA_CODEC_OK); - - // Release MediaCodec output buffer. - media_codec_->ReleaseOutputBuffer(out.buf_index, false); - // Call the |output_cb_|. output_cb_.Run(audio_buffer); } @@ -637,18 +686,40 @@ void MediaCodecAudioDecoder::OnDecodedFrame(const OutputBufferInfo& out) { void MediaCodecAudioDecoder::OnOutputFormatChanged() { DVLOG(2) << __FUNCTION__; - int new_sampling_rate; + int new_sampling_rate = 0; MediaCodecStatus status = media_codec_->GetOutputSamplingRate(&new_sampling_rate); if (status != MEDIA_CODEC_OK) { - DVLOG(0) << "GetOutputSamplingRate failed."; + DLOG(ERROR) << "GetOutputSamplingRate failed."; SetState(STATE_ERROR); - } else if (new_sampling_rate != config_.samples_per_second()) { + return; + } + if (new_sampling_rate != config_.samples_per_second()) { // We do not support the change of sampling rate on the fly - DVLOG(0) << "Sampling rate change is not supported by" << GetDisplayName() - << " (detected change " << config_.samples_per_second() << "->" - << new_sampling_rate << ")"; + DLOG(ERROR) << "Sampling rate change is not supported by " + << GetDisplayName() << " (detected change " + << config_.samples_per_second() << "->" << new_sampling_rate + << ")"; + SetState(STATE_ERROR); + return; + } + + status = media_codec_->GetOutputChannelCount(&channel_count_); + if (status != MEDIA_CODEC_OK) { + DLOG(ERROR) << "GetOutputChannelCount failed."; SetState(STATE_ERROR); + return; + } + + const int config_channel_count = GetChannelCount(config_); + DVLOG(1) << __FUNCTION__ << ": new channel count:" << channel_count_ + << " (configured for " << config_channel_count << ")"; + + if (channel_count_ > config_channel_count) { + DLOG(ERROR) << "Actual channel count " << channel_count_ + << " is greater than configured " << config_channel_count; + SetState(STATE_ERROR); + return; } } diff --git a/chromium/media/filters/android/media_codec_audio_decoder.h b/chromium/media/filters/android/media_codec_audio_decoder.h index 3e68ba4e2d0..bccbd3fc3dc 100644 --- a/chromium/media/filters/android/media_codec_audio_decoder.h +++ b/chromium/media/filters/android/media_codec_audio_decoder.h @@ -7,6 +7,7 @@ #include <deque> #include <utility> +#include <vector> #include "base/macros.h" #include "base/memory/scoped_ptr.h" @@ -237,6 +238,9 @@ class MEDIA_EXPORT MediaCodecAudioDecoder : public AudioDecoder { // Cached decoder config. AudioDecoderConfig config_; + // Actual channel count that comes from decoder may be different than config. + int channel_count_; + // Callback that delivers output frames. OutputCB output_cb_; diff --git a/chromium/media/filters/ffmpeg_video_decoder.cc b/chromium/media/filters/ffmpeg_video_decoder.cc index 92028dce636..2ca570890f6 100644 --- a/chromium/media/filters/ffmpeg_video_decoder.cc +++ b/chromium/media/filters/ffmpeg_video_decoder.cc @@ -194,7 +194,7 @@ void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config, return; } - output_cb_ = BindToCurrentLoop(output_cb); + output_cb_ = output_cb; // Success! state_ = kNormal; diff --git a/chromium/media/filters/gpu_video_decoder.cc b/chromium/media/filters/gpu_video_decoder.cc index 2e11d878686..84f70b4ca5d 100644 --- a/chromium/media/filters/gpu_video_decoder.cc +++ b/chromium/media/filters/gpu_video_decoder.cc @@ -130,6 +130,20 @@ static void ReportGpuVideoDecoderInitializeStatusToUMAAndRunCB( cb.Run(success); } +// static +void ReleaseMailboxTrampoline( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, + const VideoFrame::ReleaseMailboxCB& release_mailbox_cb, + const gpu::SyncToken& release_sync_token) { + if (task_runner->BelongsToCurrentThread()) { + release_mailbox_cb.Run(release_sync_token); + return; + } + + task_runner->PostTask(FROM_HERE, + base::Bind(release_mailbox_cb, release_sync_token)); +} + std::string GpuVideoDecoder::GetDisplayName() const { return kDecoderName; } @@ -187,7 +201,7 @@ void GpuVideoDecoder::Initialize(const VideoDecoderConfig& config, supports_deferred_initialization_ = !!( capabilities.flags & VideoDecodeAccelerator::Capabilities::SUPPORTS_DEFERRED_INITIALIZATION); - output_cb_ = BindToCurrentLoop(output_cb); + output_cb_ = output_cb; if (config.is_encrypted() && !supports_deferred_initialization_) { DVLOG(1) << __FUNCTION__ @@ -547,9 +561,10 @@ void GpuVideoDecoder::PictureReady(const media::Picture& picture) { opaque ? PIXEL_FORMAT_XRGB : PIXEL_FORMAT_ARGB, gpu::MailboxHolder(pb.texture_mailbox(0), gpu::SyncToken(), decoder_texture_target_), - BindToCurrentLoop(base::Bind( - &GpuVideoDecoder::ReleaseMailbox, weak_factory_.GetWeakPtr(), - factories_, picture.picture_buffer_id(), pb.texture_ids())), + base::Bind(&ReleaseMailboxTrampoline, factories_->GetTaskRunner(), + base::Bind(&GpuVideoDecoder::ReleaseMailbox, + weak_factory_.GetWeakPtr(), factories_, + picture.picture_buffer_id(), pb.texture_ids())), pb.size(), visible_rect, natural_size, timestamp)); if (!frame) { DLOG(ERROR) << "Create frame failed for: " << picture.picture_buffer_id(); diff --git a/chromium/media/filters/vpx_video_decoder.cc b/chromium/media/filters/vpx_video_decoder.cc index 2d60d917a43..e19e3a6be15 100644 --- a/chromium/media/filters/vpx_video_decoder.cc +++ b/chromium/media/filters/vpx_video_decoder.cc @@ -391,7 +391,7 @@ void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config, // Success! config_ = config; state_ = kNormal; - output_cb_ = BindToCurrentLoop(output_cb); + output_cb_ = offload_task_runner_ ? BindToCurrentLoop(output_cb) : output_cb; bound_init_cb.Run(true); } diff --git a/chromium/media/media.gyp b/chromium/media/media.gyp index ee32a6fe21b..9752856db3f 100644 --- a/chromium/media/media.gyp +++ b/chromium/media/media.gyp @@ -121,6 +121,8 @@ 'audio/android/opensles_input.h', 'audio/android/opensles_output.cc', 'audio/android/opensles_output.h', + 'audio/android/opensles_util.cc', + 'audio/android/opensles_util.h', 'audio/android/opensles_wrapper.cc', 'audio/audio_device_name.cc', 'audio/audio_device_name.h', |