Import Cobalt 16.154703
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
new file mode 100644
index 0000000..ec1f8f9
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
@@ -0,0 +1,327 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file contains the explicit specialization of the AudioDecoderImpl class
+// for the value 'FFMPEG'.
+
+#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h"
+
+#include "starboard/audio_sink.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/starboard/media/media_util.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+namespace {
+
+SbMediaAudioSampleType GetSupportedSampleType() {
+ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+ return kSbMediaAudioSampleTypeFloat32;
+ }
+ return kSbMediaAudioSampleTypeInt16Deprecated;
+}
+
+AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaAudioCodec audio_codec) {
+ switch (audio_codec) {
+ case kSbMediaAudioCodecAac:
+ return AV_CODEC_ID_AAC;
+ case kSbMediaAudioCodecOpus:
+ return AV_CODEC_ID_OPUS;
+ default:
+ return AV_CODEC_ID_NONE;
+ }
+}
+
+const bool g_registered =
+ FFMPEGDispatch::RegisterSpecialization(FFMPEG,
+ LIBAVCODEC_VERSION_MAJOR,
+ LIBAVFORMAT_VERSION_MAJOR,
+ LIBAVUTIL_VERSION_MAJOR);
+
+} // namespace
+
+AudioDecoderImpl<FFMPEG>::AudioDecoderImpl(
+ SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header)
+ : audio_codec_(audio_codec),
+ codec_context_(NULL),
+ av_frame_(NULL),
+ stream_ended_(false),
+ audio_header_(audio_header) {
+ SB_DCHECK(g_registered) << "Decoder Specialization registration failed.";
+ SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE)
+ << "Unsupported audio codec " << audio_codec;
+ ffmpeg_ = FFMPEGDispatch::GetInstance();
+ SB_DCHECK(ffmpeg_);
+ if ((ffmpeg_->specialization_version()) == FFMPEG) {
+ InitializeCodec();
+ }
+}
+
+AudioDecoderImpl<FFMPEG>::~AudioDecoderImpl() {
+ TeardownCodec();
+}
+
+// static
+AudioDecoder* AudioDecoderImpl<FFMPEG>::Create(
+ SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header) {
+ return new AudioDecoderImpl<FFMPEG>(audio_codec, audio_header);
+}
+
+void AudioDecoderImpl<FFMPEG>::Initialize(const OutputCB& output_cb,
+ const ErrorCB& error_cb) {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(output_cb);
+ SB_DCHECK(!output_cb_);
+ SB_DCHECK(error_cb);
+ SB_DCHECK(!error_cb_);
+
+ output_cb_ = output_cb;
+ error_cb_ = error_cb;
+}
+
+void AudioDecoderImpl<FFMPEG>::Decode(
+ const scoped_refptr<InputBuffer>& input_buffer,
+ const ConsumedCB& consumed_cb) {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(input_buffer);
+ SB_DCHECK(output_cb_);
+ SB_CHECK(codec_context_ != NULL);
+
+ Schedule(consumed_cb);
+
+ if (stream_ended_) {
+ SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
+ return;
+ }
+
+ AVPacket packet;
+ ffmpeg_->av_init_packet(&packet);
+ packet.data = const_cast<uint8_t*>(input_buffer->data());
+ packet.size = input_buffer->size();
+
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ ffmpeg_->av_frame_unref(av_frame_);
+#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ ffmpeg_->avcodec_get_frame_defaults(av_frame_);
+#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ int frame_decoded = 0;
+ int result = ffmpeg_->avcodec_decode_audio4(codec_context_, av_frame_,
+ &frame_decoded, &packet);
+ if (result != input_buffer->size()) {
+ // TODO: Consider fill it with silence.
+ SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: " << result
+ << " with input buffer size: " << input_buffer->size()
+ << " and frame decoded: " << frame_decoded;
+ error_cb_();
+ return;
+ }
+
+ if (frame_decoded != 1) {
+ // TODO: Adjust timestamp accordingly when decoding result is shifted.
+ SB_DCHECK(frame_decoded == 0);
+ SB_DLOG(WARNING) << "avcodec_decode_audio4() returns with 0 frames decoded";
+ return;
+ }
+
+ int decoded_audio_size = ffmpeg_->av_samples_get_buffer_size(
+ NULL, codec_context_->channels, av_frame_->nb_samples,
+ codec_context_->sample_fmt, 1);
+ audio_header_.samples_per_second = codec_context_->sample_rate;
+
+ if (decoded_audio_size > 0) {
+ scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+ codec_context_->channels, GetSampleType(), GetStorageType(),
+ input_buffer->timestamp(),
+ codec_context_->channels * av_frame_->nb_samples *
+ starboard::media::GetBytesPerSample(GetSampleType()));
+ if (GetStorageType() == kSbMediaAudioFrameStorageTypeInterleaved) {
+ SbMemoryCopy(decoded_audio->buffer(), *av_frame_->extended_data,
+ decoded_audio->size());
+ } else {
+ SB_DCHECK(GetStorageType() == kSbMediaAudioFrameStorageTypePlanar);
+ const int per_channel_size_in_bytes =
+ decoded_audio->size() / decoded_audio->channels();
+ for (int i = 0; i < decoded_audio->channels(); ++i) {
+ SbMemoryCopy(decoded_audio->buffer() + per_channel_size_in_bytes * i,
+ av_frame_->extended_data[i], per_channel_size_in_bytes);
+ }
+ }
+ decoded_audios_.push(decoded_audio);
+ Schedule(output_cb_);
+ } else {
+ // TODO: Consider fill it with silence.
+ SB_LOG(ERROR) << "Decoded audio frame is empty.";
+ }
+}
+
+void AudioDecoderImpl<FFMPEG>::WriteEndOfStream() {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(output_cb_);
+
+ // AAC has no dependent frames so we needn't flush the decoder. Set the flag
+ // to ensure that Decode() is not called when the stream is ended.
+ stream_ended_ = true;
+ // Put EOS into the queue.
+ decoded_audios_.push(new DecodedAudio);
+
+ Schedule(output_cb_);
+}
+
+scoped_refptr<AudioDecoderImpl<FFMPEG>::DecodedAudio>
+AudioDecoderImpl<FFMPEG>::Read() {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(output_cb_);
+ SB_DCHECK(!decoded_audios_.empty());
+
+ scoped_refptr<DecodedAudio> result;
+ if (!decoded_audios_.empty()) {
+ result = decoded_audios_.front();
+ decoded_audios_.pop();
+ }
+ return result;
+}
+
+void AudioDecoderImpl<FFMPEG>::Reset() {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ stream_ended_ = false;
+ while (!decoded_audios_.empty()) {
+ decoded_audios_.pop();
+ }
+
+ CancelPendingJobs();
+}
+
+bool AudioDecoderImpl<FFMPEG>::is_valid() const {
+ return (ffmpeg_ != NULL) && ffmpeg_->is_valid() && (codec_context_ != NULL);
+}
+
+SbMediaAudioSampleType AudioDecoderImpl<FFMPEG>::GetSampleType() const {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 ||
+ codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) {
+ return kSbMediaAudioSampleTypeInt16Deprecated;
+ } else if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT ||
+ codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) {
+ return kSbMediaAudioSampleTypeFloat32;
+ }
+
+ SB_NOTREACHED();
+
+ return kSbMediaAudioSampleTypeFloat32;
+}
+
+SbMediaAudioFrameStorageType AudioDecoderImpl<FFMPEG>::GetStorageType() const {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 ||
+ codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) {
+ return kSbMediaAudioFrameStorageTypeInterleaved;
+ }
+ if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P ||
+ codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) {
+ return kSbMediaAudioFrameStorageTypePlanar;
+ }
+
+ SB_NOTREACHED();
+ return kSbMediaAudioFrameStorageTypeInterleaved;
+}
+
+int AudioDecoderImpl<FFMPEG>::GetSamplesPerSecond() const {
+ return audio_header_.samples_per_second;
+}
+
+void AudioDecoderImpl<FFMPEG>::InitializeCodec() {
+ codec_context_ = ffmpeg_->avcodec_alloc_context3(NULL);
+
+ if (codec_context_ == NULL) {
+ SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
+ return;
+ }
+
+ codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
+ codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_);
+ // Request_sample_fmt is set by us, but sample_fmt is set by the decoder.
+ if (GetSupportedSampleType() == kSbMediaAudioSampleTypeInt16Deprecated) {
+ codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
+ } else {
+ codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT;
+ }
+
+ codec_context_->channels = audio_header_.number_of_channels;
+ codec_context_->sample_rate = audio_header_.samples_per_second;
+ codec_context_->extradata = NULL;
+ codec_context_->extradata_size = 0;
+
+ if (codec_context_->codec_id == AV_CODEC_ID_OPUS &&
+ audio_header_.audio_specific_config_size > 0) {
+ // AV_INPUT_BUFFER_PADDING_SIZE is not defined in ancient avcodec.h. Use a
+ // large enough padding here explicitly.
+ const int kAvInputBufferPaddingSize = 256;
+ codec_context_->extradata_size = audio_header_.audio_specific_config_size;
+ codec_context_->extradata = static_cast<uint8_t*>(ffmpeg_->av_malloc(
+ codec_context_->extradata_size + kAvInputBufferPaddingSize));
+ SB_DCHECK(codec_context_->extradata);
+ SbMemoryCopy(codec_context_->extradata, audio_header_.audio_specific_config,
+ codec_context_->extradata_size);
+ SbMemorySet(codec_context_->extradata + codec_context_->extradata_size, 0,
+ kAvInputBufferPaddingSize);
+ }
+
+ AVCodec* codec = ffmpeg_->avcodec_find_decoder(codec_context_->codec_id);
+
+ if (codec == NULL) {
+ SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
+ TeardownCodec();
+ return;
+ }
+
+ int rv = ffmpeg_->OpenCodec(codec_context_, codec);
+ if (rv < 0) {
+ SB_LOG(ERROR) << "Unable to open codec";
+ TeardownCodec();
+ return;
+ }
+
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ av_frame_ = ffmpeg_->av_frame_alloc();
+#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ av_frame_ = ffmpeg_->avcodec_alloc_frame();
+#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ if (av_frame_ == NULL) {
+ SB_LOG(ERROR) << "Unable to allocate audio frame";
+ TeardownCodec();
+ }
+}
+
+void AudioDecoderImpl<FFMPEG>::TeardownCodec() {
+ if (codec_context_) {
+ ffmpeg_->CloseCodec(codec_context_);
+ if (codec_context_->extradata_size) {
+ ffmpeg_->av_freep(&codec_context_->extradata);
+ }
+ ffmpeg_->av_freep(&codec_context_);
+ }
+ ffmpeg_->av_freep(&av_frame_);
+}
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard