| Index: media/filters/android/media_codec_audio_decoder.cc
|
| diff --git a/media/filters/android/media_codec_audio_decoder.cc b/media/filters/android/media_codec_audio_decoder.cc
|
| index 559ddfd3e4aceed2c04557be158d5b2a74e6ada6..c06cd44557e7fb634de7a903b5c0f0e9c1f0ae25 100644
|
| --- a/media/filters/android/media_codec_audio_decoder.cc
|
| +++ b/media/filters/android/media_codec_audio_decoder.cc
|
| @@ -64,12 +64,36 @@ std::unique_ptr<MediaCodecBridge> CreateMediaCodec(
|
| 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),
|
| @@ -596,15 +620,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 =
|
| @@ -617,20 +680,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);
|
| }
|
| @@ -638,18 +687,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;
|
| }
|
| }
|
|
|
|
|