Import Cobalt 16.154703
diff --git a/src/starboard/shared/ffmpeg/ffmpeg.gyp b/src/starboard/shared/ffmpeg/ffmpeg.gyp
new file mode 100644
index 0000000..bb08955
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg.gyp
@@ -0,0 +1,89 @@
+# 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.
+
+{
+ 'target_defaults': {
+ 'defines': [
+ 'STARBOARD_IMPLEMENTATION',
+ ],
+ },
+ 'variables': {
+ 'ffmpeg_specialization_sources': [
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h',
+ ],
+ 'ffmpeg_dispatch_sources': [
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dispatch.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dispatch.h',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder_internal.cc',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'libav.54.35.1',
+ 'type': '<(library)',
+ 'sources': [ '<@(ffmpeg_specialization_sources)' ],
+ 'dependencies': [
+ '<(DEPTH)/third_party/ffmpeg_includes/ffmpeg_includes.gyp:libav.54.35.1',
+ ],
+ },
+ {
+ 'target_name': 'libav.56.1.0',
+ 'type': '<(library)',
+ 'sources': [ '<@(ffmpeg_specialization_sources)' ],
+ 'dependencies': [
+ '<(DEPTH)/third_party/ffmpeg_includes/ffmpeg_includes.gyp:libav.56.1.0',
+ ],
+ },
+ {
+ 'target_name': 'ffmpeg.57.107.100',
+ 'type': '<(library)',
+ 'sources': [ '<@(ffmpeg_specialization_sources)' ],
+ 'dependencies': [
+ '<(DEPTH)/third_party/ffmpeg_includes/ffmpeg_includes.gyp:ffmpeg.57.107.100',
+ ],
+ },
+ {
+ 'target_name': 'ffmpeg_dynamic_load',
+ 'type': '<(library)',
+ 'sources': [
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc',
+ '<@(ffmpeg_dispatch_sources)',
+ ],
+ 'dependencies': [
+ 'ffmpeg.57.107.100',
+ 'libav.54.35.1',
+ 'libav.56.1.0',
+ ],
+ },
+ {
+ 'target_name': 'ffmpeg_linked',
+ 'type': '<(library)',
+ 'sources': [
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_linked_video_decoder_impl.cc',
+ '<@(ffmpeg_dispatch_sources)',
+ '<@(ffmpeg_specialization_sources)',
+ ],
+ },
+ ],
+}
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
deleted file mode 100644
index 3d94a3e..0000000
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright 2016 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.
-
-#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.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 kSbMediaAudioSampleTypeInt16;
-}
-
-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;
- }
-}
-
-} // namespace
-
-AudioDecoder::AudioDecoder(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(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE)
- << "Unsupported audio codec " << audio_codec;
-
- InitializeCodec();
-}
-
-AudioDecoder::~AudioDecoder() {
- TeardownCodec();
-}
-
-void AudioDecoder::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 AudioDecoder::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;
- av_init_packet(&packet);
- packet.data = const_cast<uint8_t*>(input_buffer->data());
- packet.size = input_buffer->size();
-
-#if LIBAVUTIL_VERSION_MAJOR > 52
- av_frame_unref(av_frame_);
-#else // LIBAVUTIL_VERSION_MAJOR > 52
- avcodec_get_frame_defaults(av_frame_);
-#endif // LIBAVUTIL_VERSION_MAJOR > 52
- int frame_decoded = 0;
- int result =
- avcodec_decode_audio4(codec_context_, av_frame_, &frame_decoded, &packet);
- if (result != input_buffer->size() || frame_decoded != 1) {
- // 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;
- }
-
- int decoded_audio_size = 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->pts(),
- 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 AudioDecoder::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<AudioDecoder::DecodedAudio> AudioDecoder::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 AudioDecoder::Reset() {
- SB_DCHECK(BelongsToCurrentThread());
-
- stream_ended_ = false;
- while (!decoded_audios_.empty()) {
- decoded_audios_.pop();
- }
-
- CancelPendingJobs();
-}
-
-SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
- SB_DCHECK(BelongsToCurrentThread());
-
- if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 ||
- codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) {
- return kSbMediaAudioSampleTypeInt16;
- } 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 AudioDecoder::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 AudioDecoder::GetSamplesPerSecond() const {
- return audio_header_.samples_per_second;
-}
-
-void AudioDecoder::InitializeCodec() {
- InitializeFfmpeg();
-
- codec_context_ = 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() == kSbMediaAudioSampleTypeInt16) {
- 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;
-
- AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
-
- if (codec == NULL) {
- SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
- TeardownCodec();
- return;
- }
-
- int rv = OpenCodec(codec_context_, codec);
- if (rv < 0) {
- SB_LOG(ERROR) << "Unable to open codec";
- TeardownCodec();
- return;
- }
-
-#if LIBAVUTIL_VERSION_MAJOR > 52
- av_frame_ = av_frame_alloc();
-#else // LIBAVUTIL_VERSION_MAJOR > 52
- av_frame_ = avcodec_alloc_frame();
-#endif // LIBAVUTIL_VERSION_MAJOR > 52
- if (av_frame_ == NULL) {
- SB_LOG(ERROR) << "Unable to allocate audio frame";
- TeardownCodec();
- }
-}
-
-void AudioDecoder::TeardownCodec() {
- if (codec_context_) {
- CloseCodec(codec_context_);
- av_free(codec_context_);
- codec_context_ = NULL;
- }
- if (av_frame_) {
- av_free(av_frame_);
- av_frame_ = NULL;
- }
-}
-
-} // namespace ffmpeg
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
index 45e466e..f6d715a 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -15,54 +15,21 @@
#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
#define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
-#include <queue>
-
-#include "starboard/common/ref_counted.h"
#include "starboard/media.h"
-#include "starboard/shared/ffmpeg/ffmpeg_common.h"
#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/player/decoded_audio_internal.h"
#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
-#include "starboard/shared/starboard/player/job_queue.h"
namespace starboard {
namespace shared {
namespace ffmpeg {
-class AudioDecoder : public starboard::player::filter::AudioDecoder,
- private starboard::player::JobQueue::JobOwner {
+class AudioDecoder : public starboard::player::filter::AudioDecoder {
public:
- AudioDecoder(SbMediaAudioCodec audio_codec,
- const SbMediaAudioHeader& audio_header);
- ~AudioDecoder() override;
-
- void Initialize(const OutputCB& output_cb, const ErrorCB& error_cb) override;
- void Decode(const scoped_refptr<InputBuffer>& input_buffer,
- const ConsumedCB& consumed_cb) override;
- void WriteEndOfStream() override;
- scoped_refptr<DecodedAudio> Read() override;
- void Reset() override;
- SbMediaAudioSampleType GetSampleType() const override;
- SbMediaAudioFrameStorageType GetStorageType() const override;
- int GetSamplesPerSecond() const override;
-
- bool is_valid() const { return codec_context_ != NULL; }
-
- private:
- void InitializeCodec();
- void TeardownCodec();
-
- static const int kMaxDecodedAudiosSize = 64;
-
- OutputCB output_cb_;
- ErrorCB error_cb_;
- SbMediaAudioCodec audio_codec_;
- AVCodecContext* codec_context_;
- AVFrame* av_frame_;
-
- bool stream_ended_;
- std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
- SbMediaAudioHeader audio_header_;
+ // Create an audio decoder for the currently loaded ffmpeg library.
+ static AudioDecoder* Create(SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header);
+ // Returns true if the audio decoder is initialized successfully.
+ virtual bool is_valid() const = 0;
};
} // namespace ffmpeg
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
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h
new file mode 100644
index 0000000..17ab6ae
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h
@@ -0,0 +1,94 @@
+// 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.
+
+#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_IMPL_H_
+#define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_IMPL_H_
+
+#include <queue>
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/media.h"
+#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h"
+#include "starboard/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+// For each version V that is supported, there will be an implementation of an
+// explicit specialization of the AudioDecoder class.
+template <int V>
+class AudioDecoderImpl : public AudioDecoder {
+ public:
+ static AudioDecoder* Create(SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header);
+};
+
+// Forward class declaration of the explicit specialization with value FFMPEG.
+template <>
+class AudioDecoderImpl<FFMPEG>;
+
+// Declare the explicit specialization of the class with value FFMPEG.
+template <>
+class AudioDecoderImpl<FFMPEG> : public AudioDecoder,
+ private starboard::player::JobQueue::JobOwner {
+ public:
+ AudioDecoderImpl(SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header);
+ ~AudioDecoderImpl() override;
+
+ // From: AudioDecoder
+ static AudioDecoder* Create(SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header);
+ bool is_valid() const override;
+
+ // From: starboard::player::filter::AudioDecoder
+ void Initialize(const OutputCB& output_cb, const ErrorCB& error_cb) override;
+ void Decode(const scoped_refptr<InputBuffer>& input_buffer,
+ const ConsumedCB& consumed_cb) override;
+ void WriteEndOfStream() override;
+ scoped_refptr<DecodedAudio> Read() override;
+ void Reset() override;
+ SbMediaAudioSampleType GetSampleType() const override;
+ SbMediaAudioFrameStorageType GetStorageType() const override;
+ int GetSamplesPerSecond() const override;
+
+ private:
+ void InitializeCodec();
+ void TeardownCodec();
+
+ static const int kMaxDecodedAudiosSize = 64;
+
+ FFMPEGDispatch* ffmpeg_;
+ OutputCB output_cb_;
+ ErrorCB error_cb_;
+ SbMediaAudioCodec audio_codec_;
+ AVCodecContext* codec_context_;
+ AVFrame* av_frame_;
+
+ bool stream_ended_;
+ std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
+ SbMediaAudioHeader audio_header_;
+};
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_IMPL_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc
deleted file mode 100644
index 3892b04..0000000
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2017 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.
-
-#include "starboard/shared/ffmpeg/ffmpeg_audio_resampler.h"
-
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/starboard/media/media_util.h"
-
-namespace starboard {
-namespace shared {
-namespace ffmpeg {
-
-namespace {
-
-const int kMaxChannels = 8;
-const int kMaxCachedSamples = 65536;
-
-int GetChannelLayoutFromChannels(int channels) {
- if (channels == 1) {
- return AV_CH_LAYOUT_MONO;
- }
- if (channels == 2) {
- return AV_CH_LAYOUT_STEREO;
- }
- if (channels == 6) {
- return AV_CH_LAYOUT_5POINT1;
- }
- if (channels == 8) {
- return AV_CH_LAYOUT_7POINT1;
- }
- SB_NOTREACHED() << "Unsupported channel count: " << channels;
- return AV_CH_LAYOUT_STEREO;
-}
-
-int GetSampleFormatBySampleTypeAndStorageType(
- SbMediaAudioSampleType sample_type,
- SbMediaAudioFrameStorageType storage_type) {
- if (sample_type == kSbMediaAudioSampleTypeInt16 &&
- storage_type == kSbMediaAudioFrameStorageTypeInterleaved) {
- return AV_SAMPLE_FMT_S16;
- }
- if (sample_type == kSbMediaAudioSampleTypeFloat32 &&
- storage_type == kSbMediaAudioFrameStorageTypeInterleaved) {
- return AV_SAMPLE_FMT_FLT;
- }
- if (sample_type == kSbMediaAudioSampleTypeInt16 &&
- storage_type == kSbMediaAudioFrameStorageTypePlanar) {
- return AV_SAMPLE_FMT_S16P;
- }
- if (sample_type == kSbMediaAudioSampleTypeFloat32 &&
- storage_type == kSbMediaAudioFrameStorageTypePlanar) {
- return AV_SAMPLE_FMT_FLTP;
- }
- SB_NOTREACHED() << "Unsupported sample type (" << sample_type << ") and "
- << " storage type (" << storage_type << ") combination";
- return AV_SAMPLE_FMT_FLT;
-}
-
-} // namespace
-
-AudioResampler::AudioResampler(
- SbMediaAudioSampleType source_sample_type,
- SbMediaAudioFrameStorageType source_storage_type,
- int source_sample_rate,
- SbMediaAudioSampleType destination_sample_type,
- SbMediaAudioFrameStorageType destination_storage_type,
- int destination_sample_rate,
- int channels)
- : source_sample_type_(source_sample_type),
- source_storage_type_(source_storage_type),
- destination_sample_type_(destination_sample_type),
- destination_storage_type_(destination_storage_type),
- destination_sample_rate_(destination_sample_rate),
- channels_(channels),
- start_pts_(-1),
- samples_returned_(0),
- eos_reached_(false) {
- SB_DCHECK(channels_ <= kMaxChannels);
-
- context_ = avresample_alloc_context();
- SB_DCHECK(context_ != NULL);
-
- av_opt_set_int(context_, "in_channel_layout",
- GetChannelLayoutFromChannels(channels_), 0);
- av_opt_set_int(context_, "out_channel_layout",
- GetChannelLayoutFromChannels(channels_), 0);
- av_opt_set_int(context_, "in_sample_rate", source_sample_rate, 0);
- av_opt_set_int(context_, "out_sample_rate", destination_sample_rate, 0);
- av_opt_set_int(context_, "in_sample_fmt",
- GetSampleFormatBySampleTypeAndStorageType(source_sample_type,
- source_storage_type),
- 0);
- av_opt_set_int(context_, "out_sample_fmt",
- GetSampleFormatBySampleTypeAndStorageType(
- destination_sample_type, destination_storage_type),
- 0);
- av_opt_set_int(context_, "internal_sample_fmt", AV_SAMPLE_FMT_S16P, 0);
-
- int result = avresample_open(context_);
- SB_DCHECK(!result);
-}
-
-AudioResampler::~AudioResampler() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- avresample_close(context_);
- av_free(context_);
-}
-
-scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::Resample(
- const scoped_refptr<DecodedAudio>& audio_data) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(audio_data);
- SB_DCHECK(audio_data->sample_type() == source_sample_type_);
- SB_DCHECK(audio_data->storage_type() == source_storage_type_);
- SB_DCHECK(audio_data->channels() == channels_);
- SB_DCHECK(!eos_reached_);
-
- if (channels_ > kMaxChannels) {
- return new DecodedAudio;
- }
-
- if (eos_reached_) {
- return new DecodedAudio;
- }
-
- if (start_pts_ < 0) {
- start_pts_ = audio_data->pts();
- }
-
- uint8_t* input_buffers[kMaxChannels] = {
- const_cast<uint8_t*>(audio_data->buffer())};
- if (source_storage_type_ == kSbMediaAudioFrameStorageTypePlanar) {
- for (int i = 1; i < channels_; ++i) {
- input_buffers[i] = const_cast<uint8_t*>(
- audio_data->buffer() + audio_data->size() / channels_ * i);
- }
- }
-
- int result = avresample_convert(context_, NULL, 0, 0, input_buffers, 0,
- audio_data->frames());
- SB_DCHECK(result == 0);
-
- return RetrieveOutput();
-}
-
-scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::WriteEndOfStream() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(!eos_reached_);
-
- eos_reached_ = true;
- int result = avresample_convert(context_, NULL, 0, 0, NULL, 0, 0);
- SB_DCHECK(result == 0);
-
- return RetrieveOutput();
-}
-
-scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::RetrieveOutput() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- int frames_in_buffer = avresample_available(context_);
- SbMediaTime pts = start_pts_ + samples_returned_ * kSbMediaTimeSecond /
- destination_sample_rate_;
- int bytes_per_sample =
- starboard::media::GetBytesPerSample(destination_sample_type_);
- scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
- channels_, destination_sample_type_, destination_storage_type_, pts,
- frames_in_buffer * bytes_per_sample * channels_);
- samples_returned_ += frames_in_buffer;
-
- if (frames_in_buffer > 0) {
- uint8_t* output_buffers[kMaxChannels] = {
- const_cast<uint8_t*>(decoded_audio->buffer())};
- if (destination_storage_type_ == kSbMediaAudioFrameStorageTypePlanar) {
- for (int i = 1; i < channels_; ++i) {
- output_buffers[i] = const_cast<uint8_t*>(
- decoded_audio->buffer() + decoded_audio->size() / channels_ * i);
- }
- }
-
- int frames_read =
- avresample_read(context_, output_buffers, frames_in_buffer);
- SB_DCHECK(frames_read == frames_in_buffer);
- }
-
- return decoded_audio;
-}
-
-} // namespace ffmpeg
-
-namespace starboard {
-namespace player {
-namespace filter {
-
-// static
-scoped_ptr<AudioResampler> AudioResampler::Create(
- SbMediaAudioSampleType source_sample_type,
- SbMediaAudioFrameStorageType source_storage_type,
- int source_sample_rate,
- SbMediaAudioSampleType destination_sample_type,
- SbMediaAudioFrameStorageType destination_storage_type,
- int destination_sample_rate,
- int channels) {
- return scoped_ptr<AudioResampler>(new ffmpeg::AudioResampler(
- source_sample_type, source_storage_type, source_sample_rate,
- destination_sample_type, destination_storage_type,
- destination_sample_rate, channels));
-}
-
-} // namespace filter
-} // namespace player
-} // namespace starboard
-
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h
deleted file mode 100644
index dac58a4..0000000
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 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.
-
-#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_
-#define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_
-
-#include <queue>
-
-#include "starboard/media.h"
-#include "starboard/shared/ffmpeg/ffmpeg_common.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/player/decoded_audio_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_resampler.h"
-#include "starboard/shared/starboard/thread_checker.h"
-
-namespace starboard {
-namespace shared {
-namespace ffmpeg {
-
-class AudioResampler : public starboard::player::filter::AudioResampler {
- public:
- AudioResampler(SbMediaAudioSampleType source_sample_type,
- SbMediaAudioFrameStorageType source_storage_type,
- int source_sample_rate,
- SbMediaAudioSampleType destination_sample_type,
- SbMediaAudioFrameStorageType destination_storage_type,
- int destination_sample_rate,
- int channels);
- ~AudioResampler() override;
-
- scoped_refptr<DecodedAudio> Resample(
- const scoped_refptr<DecodedAudio>& audio_data) override;
- scoped_refptr<DecodedAudio> WriteEndOfStream() override;
-
- private:
- scoped_refptr<DecodedAudio> RetrieveOutput();
-
- shared::starboard::ThreadChecker thread_checker_;
-
- SbMediaAudioSampleType source_sample_type_;
- SbMediaAudioFrameStorageType source_storage_type_;
- SbMediaAudioSampleType destination_sample_type_;
- SbMediaAudioFrameStorageType destination_storage_type_;
- int destination_sample_rate_; // Used for timestamp adjustment.
- int channels_;
- SbMediaTime start_pts_;
- int samples_returned_;
- bool eos_reached_;
- AVAudioResampleContext* context_;
-};
-
-} // namespace ffmpeg
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.cc b/src/starboard/shared/ffmpeg/ffmpeg_common.cc
deleted file mode 100644
index b3fd3b6..0000000
--- a/src/starboard/shared/ffmpeg/ffmpeg_common.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 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.
-
-#include "starboard/shared/ffmpeg/ffmpeg_common.h"
-
-#include "starboard/log.h"
-#include "starboard/mutex.h"
-#include "starboard/once.h"
-
-namespace starboard {
-namespace shared {
-namespace ffmpeg {
-
-namespace {
-
-SbOnceControl ffmpeg_initialization_once = SB_ONCE_INITIALIZER;
-SbMutex codec_mutex = SB_MUTEX_INITIALIZER;
-
-} // namespace
-
-void InitializeFfmpeg() {
- bool initialized = SbOnce(&ffmpeg_initialization_once, av_register_all);
- SB_DCHECK(initialized);
-}
-
-int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec) {
- SbMutexAcquire(&codec_mutex);
- int result = avcodec_open2(codec_context, codec, NULL);
- SbMutexRelease(&codec_mutex);
- return result;
-}
-
-void CloseCodec(AVCodecContext* codec_context) {
- SbMutexAcquire(&codec_mutex);
- avcodec_close(codec_context);
- SbMutexRelease(&codec_mutex);
-}
-
-} // namespace ffmpeg
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.h b/src/starboard/shared/ffmpeg/ffmpeg_common.h
index 9c63c1d..86f0169 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_common.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_common.h
@@ -37,22 +37,23 @@
#define PIX_FMT_YUVJ420P AV_PIX_FMT_YUVJ420P
#endif // LIBAVUTIL_VERSION_MAJOR > 52
-namespace starboard {
-namespace shared {
-namespace ffmpeg {
+#if !defined(LIBAVCODEC_VERSION_MAJOR)
+#error "LIBAVCODEC_VERSION_MAJOR not defined"
+#endif // !defined(LIBAVCODEC_VERSION_MAJOR)
-void InitializeFfmpeg();
+#if !defined(LIBAVCODEC_VERSION_MICRO)
+#error "LIBAVCODEC_VERSION_MICRO not defined"
+#endif // !defined(LIBAVCODEC_VERSION_MICRO)
-// In Ffmpeg, the calls to avcodec_open2() and avcodec_close() are not
-// synchronized internally so it is the responsibility of its user to ensure
-// that these calls don't overlap. The following functions acquires a lock
-// internally before calling avcodec_open2() and avcodec_close() to enforce
-// this.
-int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec);
-void CloseCodec(AVCodecContext* codec_context);
+#if LIBAVCODEC_VERSION_MICRO >= 100
+#define LIBAVCODEC_LIBRARY_IS_FFMPEG 1
+#else
+#define LIBAVCODEC_LIBRARY_IS_FFMPEG 0
+#endif // LIBAVCODEC_VERSION_MICRO >= 100
-} // namespace ffmpeg
-} // namespace shared
-} // namespace starboard
+// Use the major version number of libavcodec plus a single digit distinguishing
+// between ffmpeg and libav as the template parameter for the
+// explicit specialization of the audio and video decoder classes.
+#define FFMPEG ((LIBAVCODEC_VERSION_MAJOR * 10) + LIBAVCODEC_LIBRARY_IS_FFMPEG)
#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_COMMON_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dispatch.cc b/src/starboard/shared/ffmpeg/ffmpeg_dispatch.cc
new file mode 100644
index 0000000..384baf2
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_dispatch.cc
@@ -0,0 +1,47 @@
+// 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 implements the FFMPEGDispatch interface with dynamic loading of
+// the libraries.
+
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+
+#include "starboard/mutex.h"
+#include "starboard/once.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+namespace {
+SbMutex g_codec_mutex = SB_MUTEX_INITIALIZER;
+} // namespace
+
+int FFMPEGDispatch::OpenCodec(AVCodecContext* codec_context,
+ const AVCodec* codec) {
+ SbMutexAcquire(&g_codec_mutex);
+ int result = avcodec_open2(codec_context, codec, NULL);
+ SbMutexRelease(&g_codec_mutex);
+ return result;
+}
+
+void FFMPEGDispatch::CloseCodec(AVCodecContext* codec_context) {
+ SbMutexAcquire(&g_codec_mutex);
+ avcodec_close(codec_context);
+ SbMutexRelease(&g_codec_mutex);
+}
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dispatch.h b/src/starboard/shared/ffmpeg/ffmpeg_dispatch.h
new file mode 100644
index 0000000..4d1713f
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_dispatch.h
@@ -0,0 +1,114 @@
+// 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 class defines an interface for dispatching calls to the ffmpeg
+// libraries.
+
+#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_DISPATCH_H_
+#define STARBOARD_SHARED_FFMPEG_FFMPEG_DISPATCH_H_
+
+#include "starboard/mutex.h"
+#include "starboard/types.h"
+
+struct AVCodec;
+struct AVCodecContext;
+struct AVDictionary;
+struct AVFrame;
+struct AVPacket;
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+class FFMPEGDispatch {
+ public:
+ FFMPEGDispatch();
+ ~FFMPEGDispatch();
+
+ static FFMPEGDispatch* GetInstance();
+
+ // Register an ffmpeg decoder specialization for the given combination of
+ // major versions of the libavcodec, libavformat, and libavutil libraries.
+ // Returns true if the specialization did not exist yet, or if it matched
+ // an already existing registration.
+ static bool RegisterSpecialization(int specialization,
+ int avcodec,
+ int avformat,
+ int avutil);
+
+ bool is_valid() const;
+
+ unsigned (*avutil_version)(void);
+ void* (*av_malloc)(size_t size);
+ void (*av_freep)(void* ptr);
+ AVFrame* (*av_frame_alloc)(void);
+ void (*av_frame_unref)(AVFrame* frame);
+ int (*av_samples_get_buffer_size)(int* linesize,
+ int nb_channels,
+ int nb_samples,
+ int sample_fmt,
+ int align);
+ int (*av_opt_set_int)(void* obj,
+ const char* name,
+ int64_t val,
+ int search_flags);
+ int (*av_image_check_size)(unsigned int w,
+ unsigned int h,
+ int log_offset,
+ void* log_ctx);
+ void* (*av_buffer_create)(uint8_t* data,
+ int size,
+ void (*free)(void* opaque, uint8_t* data),
+ void* opaque,
+ int flags);
+
+ unsigned (*avcodec_version)(void);
+ AVCodecContext* (*avcodec_alloc_context3)(const AVCodec* codec);
+ AVCodec* (*avcodec_find_decoder)(int id);
+ int (*avcodec_close)(AVCodecContext* avctx);
+ int (*avcodec_open2)(AVCodecContext* avctx,
+ const AVCodec* codec,
+ AVDictionary** options);
+ void (*av_init_packet)(AVPacket* pkt);
+ int (*avcodec_decode_audio4)(AVCodecContext* avctx,
+ AVFrame* frame,
+ int* got_frame_ptr,
+ const AVPacket* avpkt);
+ int (*avcodec_decode_video2)(AVCodecContext* avctx,
+ AVFrame* picture,
+ int* got_picture_ptr,
+ const AVPacket* avpkt);
+ void (*avcodec_flush_buffers)(AVCodecContext* avctx);
+ AVFrame* (*avcodec_alloc_frame)(void);
+ void (*avcodec_get_frame_defaults)(AVFrame* frame);
+
+ unsigned (*avformat_version)(void);
+ void (*av_register_all)(void);
+
+ int specialization_version() const;
+
+ // In Ffmpeg, the calls to avcodec_open2() and avcodec_close() are not
+ // synchronized internally so it is the responsibility of its user to ensure
+ // that these calls don't overlap. The following functions acquires a lock
+ // internally before calling avcodec_open2() and avcodec_close() to enforce
+ // this.
+ int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec);
+ void CloseCodec(AVCodecContext* codec_context);
+};
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_DISPATCH_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
new file mode 100644
index 0000000..2a8dcaf
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
@@ -0,0 +1,60 @@
+// 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 creation of the specialized AudioDecoderImpl object
+// corresponding to the version of the dynamically loaded ffmpeg library.
+
+#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h"
+
+#include "starboard/memory.h"
+#include "starboard/player.h"
+#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h"
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+// static
+AudioDecoder* AudioDecoder::Create(SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header) {
+ FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance();
+ if (!ffmpeg || !ffmpeg->is_valid()) {
+ SB_NOTREACHED();
+ return NULL;
+ }
+
+ AudioDecoder* audio_decoder = NULL;
+ switch (ffmpeg->specialization_version()) {
+ case 540:
+ audio_decoder = AudioDecoderImpl<540>::Create(audio_codec, audio_header);
+ break;
+ case 550:
+ case 560:
+ audio_decoder = AudioDecoderImpl<560>::Create(audio_codec, audio_header);
+ break;
+ case 571:
+ audio_decoder = AudioDecoderImpl<571>::Create(audio_codec, audio_header);
+ break;
+ default:
+ SB_NOTREACHED() << "Unsupported FFMPEG specialization " << std::hex
+ << ffmpeg->specialization_version();
+ break;
+ }
+ return audio_decoder;
+}
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
new file mode 100644
index 0000000..8afa042
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
@@ -0,0 +1,279 @@
+// 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 implements the FFMPEGDispatch interface with dynamic loading of
+// the libraries.
+
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+
+#include <dlfcn.h>
+#include <map>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/log.h"
+#include "starboard/once.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+namespace {
+
+const char kAVCodecLibraryName[] = "libavcodec.so";
+const char kAVFormatLibraryName[] = "libavformat.so";
+const char kAVUtilLibraryName[] = "libavutil.so";
+
+SbOnceControl g_dynamic_load_once = SB_ONCE_INITIALIZER;
+
+struct LibraryMajorVersions {
+ int avcodec;
+ int avformat;
+ int avutil;
+ LibraryMajorVersions(int avcodec, int avformat, int avutil)
+ : avcodec(avcodec), avformat(avformat), avutil(avutil) {}
+};
+
+// Map containing the major version for all combinations of libraries in
+// supported ffmpeg installations. The index is the specialization version
+// matching the template parameter for the available specialization.
+typedef std::map<int, LibraryMajorVersions> LibraryMajorVersionsMap;
+
+class FFMPEGDispatchImpl {
+ public:
+ FFMPEGDispatchImpl();
+ ~FFMPEGDispatchImpl();
+
+ static FFMPEGDispatchImpl* GetInstance();
+
+ bool RegisterSpecialization(int specialization,
+ int avcodec,
+ int avformat,
+ int avutil);
+
+ bool is_valid() const { return avcodec_ && avformat_ && avutil_; }
+
+ FFMPEGDispatch* get_ffmpeg_dispatch();
+
+ private:
+ SbMutex mutex_;
+ FFMPEGDispatch* ffmpeg_;
+ // Load the ffmpeg shared libraries, return true if successful.
+ bool OpenLibraries();
+ // Load the symbols from the shared libraries.
+ void LoadSymbols();
+
+ void* avcodec_;
+ void* avformat_;
+ void* avutil_;
+ LibraryMajorVersionsMap versions_;
+};
+
+FFMPEGDispatchImpl* g_ffmpeg_dispatch_impl;
+
+void construct_ffmpeg_dispatch_impl() {
+ g_ffmpeg_dispatch_impl = new FFMPEGDispatchImpl();
+}
+
+FFMPEGDispatchImpl::FFMPEGDispatchImpl()
+ : mutex_(SB_MUTEX_INITIALIZER),
+ ffmpeg_(NULL),
+ avcodec_(NULL),
+ avformat_(NULL),
+ avutil_(NULL) {}
+
+FFMPEGDispatchImpl::~FFMPEGDispatchImpl() {
+ delete ffmpeg_;
+ if (avformat_) {
+ dlclose(avformat_);
+ }
+ if (avcodec_) {
+ dlclose(avcodec_);
+ }
+ if (avutil_) {
+ dlclose(avutil_);
+ }
+}
+
+// static
+FFMPEGDispatchImpl* FFMPEGDispatchImpl::GetInstance() {
+ bool initialized =
+ SbOnce(&g_dynamic_load_once, construct_ffmpeg_dispatch_impl);
+ SB_DCHECK(initialized);
+ return g_ffmpeg_dispatch_impl;
+}
+
+bool FFMPEGDispatchImpl::RegisterSpecialization(int specialization,
+ int avcodec,
+ int avformat,
+ int avutil) {
+ SbMutexAcquire(&mutex_);
+ auto result = versions_.insert(std::make_pair(
+ specialization, LibraryMajorVersions(avcodec, avformat, avutil)));
+ bool success = result.second;
+ if (!success) {
+ // Element was not inserted because an entry with the same key already
+ // exists. Registration is still successful if the parameters are the same.
+ const LibraryMajorVersions& existing_versions = result.first->second;
+ success = existing_versions.avcodec == avcodec &&
+ existing_versions.avformat == avformat &&
+ existing_versions.avutil == avutil;
+ }
+ SbMutexRelease(&mutex_);
+ return success;
+}
+
+FFMPEGDispatch* FFMPEGDispatchImpl::get_ffmpeg_dispatch() {
+ SbMutexAcquire(&mutex_);
+ if (!ffmpeg_) {
+ ffmpeg_ = new FFMPEGDispatch();
+ // Dynamically load the libraries and retrieve the function pointers.
+ if (OpenLibraries()) {
+ LoadSymbols();
+ ffmpeg_->av_register_all();
+ }
+ }
+ SbMutexRelease(&mutex_);
+ return ffmpeg_;
+}
+
+const int kMaxVersionedLibraryNameLength = 32;
+
+std::string GetVersionedLibraryName(const char* name, const int version) {
+ char s[kMaxVersionedLibraryNameLength];
+ SbStringFormatF(s, kMaxVersionedLibraryNameLength, "%s.%d", name, version);
+ return std::string(s);
+}
+
+bool FFMPEGDispatchImpl::OpenLibraries() {
+ for (auto version_iterator = versions_.rbegin();
+ version_iterator != versions_.rend(); ++version_iterator) {
+ LibraryMajorVersions& versions = version_iterator->second;
+ avutil_ = dlopen(
+ GetVersionedLibraryName(kAVUtilLibraryName, versions.avutil).c_str(),
+ RTLD_NOW | RTLD_GLOBAL);
+ if (!avutil_) {
+ SB_DLOG(WARNING) << "Unable to open shared library "
+ << kAVUtilLibraryName;
+ continue;
+ }
+
+ avcodec_ = dlopen(
+ GetVersionedLibraryName(kAVCodecLibraryName, versions.avcodec).c_str(),
+ RTLD_NOW | RTLD_GLOBAL);
+ if (!avcodec_) {
+ SB_DLOG(WARNING) << "Unable to open shared library "
+ << kAVCodecLibraryName;
+ dlclose(avutil_);
+ avutil_ = NULL;
+ continue;
+ }
+
+ avformat_ =
+ dlopen(GetVersionedLibraryName(kAVFormatLibraryName, versions.avformat)
+ .c_str(),
+ RTLD_NOW | RTLD_GLOBAL);
+ if (!avformat_) {
+ SB_DLOG(WARNING) << "Unable to open shared library "
+ << kAVFormatLibraryName;
+ dlclose(avcodec_);
+ avcodec_ = NULL;
+ dlclose(avutil_);
+ avutil_ = NULL;
+ continue;
+ }
+ SB_DCHECK(is_valid());
+ break;
+ }
+ return is_valid();
+}
+
+void FFMPEGDispatchImpl::LoadSymbols() {
+ SB_DCHECK(is_valid());
+// Load the desired symbols from the shared libraries. Note: If a symbol is
+// listed as a '.text' entry in the output of 'objdump -T' on the shared
+// library file, then it is directly available from it.
+
+#define INITSYMBOL(library, symbol) \
+ ffmpeg_->symbol = reinterpret_cast<decltype(FFMPEGDispatch::symbol)>( \
+ dlsym(library, #symbol));
+
+ // Load symbols from the avutil shared library.
+ INITSYMBOL(avutil_, avutil_version);
+ SB_DCHECK(ffmpeg_->avutil_version);
+ INITSYMBOL(avutil_, av_malloc);
+ INITSYMBOL(avutil_, av_freep);
+ INITSYMBOL(avutil_, av_frame_alloc);
+ INITSYMBOL(avutil_, av_frame_unref);
+ INITSYMBOL(avutil_, av_samples_get_buffer_size);
+ INITSYMBOL(avutil_, av_opt_set_int);
+ INITSYMBOL(avutil_, av_image_check_size);
+ INITSYMBOL(avutil_, av_buffer_create);
+
+ // Load symbols from the avcodec shared library.
+ INITSYMBOL(avcodec_, avcodec_version);
+ SB_DCHECK(ffmpeg_->avcodec_version);
+ INITSYMBOL(avcodec_, avcodec_alloc_context3);
+ INITSYMBOL(avcodec_, avcodec_find_decoder);
+ INITSYMBOL(avcodec_, avcodec_close);
+ INITSYMBOL(avcodec_, avcodec_open2);
+ INITSYMBOL(avcodec_, av_init_packet);
+ INITSYMBOL(avcodec_, avcodec_decode_audio4);
+ INITSYMBOL(avcodec_, avcodec_decode_video2);
+ INITSYMBOL(avcodec_, avcodec_flush_buffers);
+ INITSYMBOL(avcodec_, avcodec_alloc_frame);
+ INITSYMBOL(avcodec_, avcodec_get_frame_defaults);
+
+ // Load symbols from the avformat shared library.
+ INITSYMBOL(avformat_, avformat_version);
+ SB_DCHECK(ffmpeg_->avformat_version);
+ INITSYMBOL(avformat_, av_register_all);
+ SB_DCHECK(ffmpeg_->av_register_all);
+
+#undef INITSYMBOL
+}
+} // namespace
+
+FFMPEGDispatch::FFMPEGDispatch() {}
+FFMPEGDispatch::~FFMPEGDispatch() {}
+
+// static
+FFMPEGDispatch* FFMPEGDispatch::GetInstance() {
+ return FFMPEGDispatchImpl::GetInstance()->get_ffmpeg_dispatch();
+}
+
+// static
+bool FFMPEGDispatch::RegisterSpecialization(int specialization,
+ int avcodec,
+ int avformat,
+ int avutil) {
+ return FFMPEGDispatchImpl::GetInstance()->RegisterSpecialization(
+ specialization, avcodec, avformat, avutil);
+}
+
+bool FFMPEGDispatch::is_valid() const {
+ return FFMPEGDispatchImpl::GetInstance()->is_valid();
+}
+
+int FFMPEGDispatch::specialization_version() const {
+ // Return the specialization version, which is the major version of the
+ // avcodec library plus a single digit indicating if it's ffmpeg or libav,
+ // derived from the libavcodec micro version number.
+ return (avcodec_version() >> 16) * 10 + ((avcodec_version() & 0xFF) >= 100);
+}
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc
new file mode 100644
index 0000000..7825b72
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc
@@ -0,0 +1,65 @@
+// 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 creation of the specialized VideoDecoderImpl object
+// corresponding to the version of the dynamically loaded ffmpeg library.
+
+#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h"
+
+#include "starboard/memory.h"
+#include "starboard/player.h"
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+#include "starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+// static
+VideoDecoder* VideoDecoder::Create(
+ SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider) {
+ FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance();
+ if (!ffmpeg || !ffmpeg->is_valid()) {
+ SB_NOTREACHED();
+ return NULL;
+ }
+
+ VideoDecoder* video_decoder = NULL;
+ switch (ffmpeg->specialization_version()) {
+ case 540:
+ video_decoder = VideoDecoderImpl<540>::Create(
+ video_codec, output_mode, decode_target_graphics_context_provider);
+ break;
+ case 550:
+ case 560:
+ video_decoder = VideoDecoderImpl<560>::Create(
+ video_codec, output_mode, decode_target_graphics_context_provider);
+ break;
+ case 571:
+ video_decoder = VideoDecoderImpl<571>::Create(
+ video_codec, output_mode, decode_target_graphics_context_provider);
+ break;
+ default:
+ SB_NOTREACHED() << "Unsupported FFMPEG version " << std::hex
+ << ffmpeg->avutil_version();
+ break;
+ }
+ return video_decoder;
+}
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc
new file mode 100644
index 0000000..7c667b3
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc
@@ -0,0 +1,42 @@
+// 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 creation of the specialized AudioDecoderImpl object
+// corresponding to the version of the linked ffmpeg library.
+
+#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h"
+
+#include "starboard/memory.h"
+#include "starboard/player.h"
+#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h"
+#include "starboard/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+// static
+AudioDecoder* AudioDecoder::Create(SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header) {
+ FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance();
+ SB_DCHECK(ffmpeg && ffmpeg->is_valid());
+ SB_DCHECK(FFMPEG == ffmpeg->specialization_version());
+
+ return AudioDecoderImpl<FFMPEG>::Create(audio_codec, audio_header);
+}
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc
new file mode 100644
index 0000000..181e224
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc
@@ -0,0 +1,128 @@
+// 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 implements the FFMPEGDispatch interface for a library linked
+// directly, or which the symbols are already available in the global namespace.
+
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+
+#include <dlfcn.h>
+#include <map>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/log.h"
+#include "starboard/once.h"
+#include "starboard/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+namespace {
+
+FFMPEGDispatch* g_ffmpeg_dispatch_impl;
+SbOnceControl g_construct_ffmpeg_dispatch_once = SB_ONCE_INITIALIZER;
+
+void construct_ffmpeg_dispatch() {
+ g_ffmpeg_dispatch_impl = new FFMPEGDispatch();
+}
+
+void LoadSymbols(FFMPEGDispatch* ffmpeg) {
+ SB_DCHECK(ffmpeg->is_valid());
+// Load the desired symbols from the shared libraries. Note: If a symbol is
+// listed as a '.text' entry in the output of 'objdump -T' on the shared
+// library file, then it is directly available from it.
+
+#define INITSYMBOL(symbol) \
+ ffmpeg->symbol = reinterpret_cast<decltype(FFMPEGDispatch::symbol)>(symbol);
+
+ // Load symbols from the avutil shared library.
+ INITSYMBOL(avutil_version);
+ SB_DCHECK(ffmpeg->avutil_version);
+ INITSYMBOL(av_malloc);
+ INITSYMBOL(av_freep);
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ INITSYMBOL(av_frame_alloc);
+ INITSYMBOL(av_frame_unref);
+#endif
+ INITSYMBOL(av_samples_get_buffer_size);
+ INITSYMBOL(av_opt_set_int);
+ INITSYMBOL(av_image_check_size);
+ INITSYMBOL(av_buffer_create);
+
+ // Load symbols from the avcodec shared library.
+ INITSYMBOL(avcodec_version);
+ SB_DCHECK(ffmpeg->avcodec_version);
+ INITSYMBOL(avcodec_alloc_context3);
+ INITSYMBOL(avcodec_find_decoder);
+ INITSYMBOL(avcodec_close);
+ INITSYMBOL(avcodec_open2);
+ INITSYMBOL(av_init_packet);
+ INITSYMBOL(avcodec_decode_audio4);
+ INITSYMBOL(avcodec_decode_video2);
+ INITSYMBOL(avcodec_flush_buffers);
+#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 8, 0)
+ INITSYMBOL(avcodec_alloc_frame);
+ INITSYMBOL(avcodec_get_frame_defaults);
+#endif
+
+ // Load symbols from the avformat shared library.
+ INITSYMBOL(avformat_version);
+ SB_DCHECK(ffmpeg->avformat_version);
+ INITSYMBOL(av_register_all);
+ SB_DCHECK(ffmpeg->av_register_all);
+
+#undef INITSYMBOL
+}
+} // namespace
+
+FFMPEGDispatch::FFMPEGDispatch() {
+ LoadSymbols(this);
+ av_register_all();
+}
+
+FFMPEGDispatch::~FFMPEGDispatch() {}
+
+// static
+FFMPEGDispatch* FFMPEGDispatch::GetInstance() {
+ bool initialized =
+ SbOnce(&g_construct_ffmpeg_dispatch_once, construct_ffmpeg_dispatch);
+ SB_DCHECK(initialized);
+ return g_ffmpeg_dispatch_impl;
+}
+
+// static
+bool FFMPEGDispatch::RegisterSpecialization(int specialization,
+ int avcodec,
+ int avformat,
+ int avutil) {
+ // There is always only a single version of the library linked to the
+ // application, so there is no need to explicitly track multiple versions.
+ return true;
+}
+
+bool FFMPEGDispatch::is_valid() const {
+ // Loading of a linked library is always successful.
+ return true;
+}
+
+int FFMPEGDispatch::specialization_version() const {
+ // There is only one version of ffmpeg linked to the binary.
+ return FFMPEG;
+}
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_linked_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_linked_video_decoder_impl.cc
new file mode 100644
index 0000000..eb13a07
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_linked_video_decoder_impl.cc
@@ -0,0 +1,45 @@
+// 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 creation of the specialized VideoDecoderImpl object
+// corresponding to the version of the linked ffmpeg library.
+
+#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h"
+
+#include "starboard/memory.h"
+#include "starboard/player.h"
+#include "starboard/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+#include "starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+// static
+VideoDecoder* VideoDecoder::Create(
+ SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider) {
+ FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance();
+ SB_DCHECK(ffmpeg && ffmpeg->is_valid());
+ SB_DCHECK(FFMPEG == ffmpeg->specialization_version());
+
+ return VideoDecoderImpl<FFMPEG>::Create(
+ video_codec, output_mode, decode_target_graphics_context_provider);
+}
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
index 83a527b..64227c5 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -15,16 +15,10 @@
#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
#define STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
-#include "starboard/common/ref_counted.h"
-#include "starboard/log.h"
#include "starboard/media.h"
-#include "starboard/queue.h"
-#include "starboard/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/player.h"
#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/player/filter/cpu_video_frame.h"
#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
-#include "starboard/shared/starboard/player/input_buffer_internal.h"
-#include "starboard/thread.h"
namespace starboard {
namespace shared {
@@ -32,98 +26,14 @@
class VideoDecoder : public starboard::player::filter::VideoDecoder {
public:
- VideoDecoder(SbMediaVideoCodec video_codec,
- SbPlayerOutputMode output_mode,
- SbDecodeTargetGraphicsContextProvider*
- decode_target_graphics_context_provider);
- ~VideoDecoder() override;
+ // Create a video decoder for the currently loaded ffmpeg library.
+ static VideoDecoder* Create(SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider);
- void Initialize(const DecoderStatusCB& decoder_status_cb,
- const ErrorCB& error_cb) override;
- size_t GetPrerollFrameCount() const override { return 8; }
- SbTime GetPrerollTimeout() const override { return kSbTimeMax; }
-
- void WriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer)
- override;
- void WriteEndOfStream() override;
- void Reset() override;
-
- bool is_valid() const { return codec_context_ != NULL; }
-
- private:
- typedef ::starboard::shared::starboard::player::filter::CpuVideoFrame
- CpuVideoFrame;
-
- enum EventType {
- kInvalid,
- kReset,
- kWriteInputBuffer,
- kWriteEndOfStream,
- };
-
- struct Event {
- EventType type;
- // |input_buffer| is only used when |type| is kWriteInputBuffer.
- scoped_refptr<InputBuffer> input_buffer;
-
- explicit Event(EventType type = kInvalid) : type(type) {
- SB_DCHECK(type != kWriteInputBuffer);
- }
-
- explicit Event(const scoped_refptr<InputBuffer>& input_buffer)
- : type(kWriteInputBuffer), input_buffer(input_buffer) {}
- };
-
- static void* ThreadEntryPoint(void* context);
- void DecoderThreadFunc();
-
- bool DecodePacket(AVPacket* packet);
- void InitializeCodec();
- void TeardownCodec();
- SbDecodeTarget GetCurrentDecodeTarget() override;
-
- bool UpdateDecodeTarget(const scoped_refptr<CpuVideoFrame>& frame);
-
- // |video_codec_| will be initialized inside ctor and won't be changed during
- // the life time of this class.
- const SbMediaVideoCodec video_codec_;
- // The following callbacks will be initialized in Initialize() and won't be
- // changed during the life time of this class.
- DecoderStatusCB decoder_status_cb_;
- ErrorCB error_cb_;
-
- Queue<Event> queue_;
-
- // The AV related classes will only be created and accessed on the decoder
- // thread.
- AVCodecContext* codec_context_;
- AVFrame* av_frame_;
-
- bool stream_ended_;
- bool error_occured_;
-
- // Working thread to avoid lengthy decoding work block the player thread.
- SbThread decoder_thread_;
-
- // Decode-to-texture related state.
- SbPlayerOutputMode output_mode_;
-
- SbDecodeTargetGraphicsContextProvider*
- decode_target_graphics_context_provider_;
-
- // If decode-to-texture is enabled, then we store the decode target texture
- // inside of this |decode_target_| member.
- SbDecodeTarget decode_target_;
-
- // GetCurrentDecodeTarget() needs to be called from an arbitrary thread
- // to obtain the current decode target (which ultimately ends up being a
- // copy of |decode_target_|), we need to safe-guard access to |decode_target_|
- // and we do so through this mutex.
- Mutex decode_target_mutex_;
- // Mutex frame_mutex_;
-
- // int frame_last_rendered_pts_;
- // scoped_refptr<VideoFrame> frame_;
+ // Returns true if the video decoder is initialized successfully.
+ virtual bool is_valid() const = 0;
};
} // namespace ffmpeg
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
similarity index 65%
rename from src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
rename to src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
index 3def412..233b93e 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// 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.
@@ -12,7 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h"
+// This file contains the explicit specialization of the VideoDecoderImpl class
+// for the value 'FFMPEG'.
+
+#include "starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h"
#include "starboard/linux/shared/decode_target_internal.h"
#include "starboard/memory.h"
@@ -39,21 +42,365 @@
return width * height * 3 / 2;
}
-#if LIBAVUTIL_VERSION_MAJOR > 52
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
void ReleaseBuffer(void* opaque, uint8_t* data) {
SbMemoryDeallocate(data);
}
-int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame, int flags) {
+int AllocateBufferCallback(AVCodecContext* codec_context,
+ AVFrame* frame,
+ int flags) {
+ VideoDecoderImpl<FFMPEG>* video_decoder =
+ static_cast<VideoDecoderImpl<FFMPEG>*>(codec_context->opaque);
+ return video_decoder->AllocateBuffer(codec_context, frame, flags);
+}
+
+#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+
+int AllocateBufferCallback(AVCodecContext* codec_context, AVFrame* frame) {
+ VideoDecoderImpl<FFMPEG>* video_decoder =
+ static_cast<VideoDecoderImpl<FFMPEG>*>(codec_context->opaque);
+ return video_decoder->AllocateBuffer(codec_context, frame);
+}
+
+void ReleaseBuffer(AVCodecContext*, AVFrame* frame) {
+ SbMemoryDeallocate(frame->opaque);
+ frame->opaque = NULL;
+
+ // The FFmpeg API expects us to zero the data pointers in this callback.
+ SbMemorySet(frame->data, 0, sizeof(frame->data));
+}
+#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+
+const bool g_registered =
+ FFMPEGDispatch::RegisterSpecialization(FFMPEG,
+ LIBAVCODEC_VERSION_MAJOR,
+ LIBAVFORMAT_VERSION_MAJOR,
+ LIBAVUTIL_VERSION_MAJOR);
+
+} // namespace
+
+VideoDecoderImpl<FFMPEG>::VideoDecoderImpl(
+ SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider)
+ : video_codec_(video_codec),
+ codec_context_(NULL),
+ av_frame_(NULL),
+ stream_ended_(false),
+ error_occured_(false),
+ decoder_thread_(kSbThreadInvalid),
+ output_mode_(output_mode),
+ decode_target_graphics_context_provider_(
+ decode_target_graphics_context_provider),
+ decode_target_(kSbDecodeTargetInvalid) {
+ SB_DCHECK(g_registered) << "Decoder Specialization registration failed.";
+ ffmpeg_ = FFMPEGDispatch::GetInstance();
+ SB_DCHECK(ffmpeg_);
+ if ((ffmpeg_->specialization_version()) == FFMPEG) {
+ InitializeCodec();
+ }
+}
+
+VideoDecoderImpl<FFMPEG>::~VideoDecoderImpl() {
+ Reset();
+ TeardownCodec();
+}
+
+// static
+VideoDecoder* VideoDecoderImpl<FFMPEG>::Create(
+ SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider) {
+ return new VideoDecoderImpl<FFMPEG>(video_codec, output_mode,
+ decode_target_graphics_context_provider);
+}
+
+void VideoDecoderImpl<FFMPEG>::Initialize(
+ const DecoderStatusCB& decoder_status_cb,
+ const ErrorCB& error_cb) {
+ SB_DCHECK(decoder_status_cb);
+ SB_DCHECK(!decoder_status_cb_);
+ SB_DCHECK(error_cb);
+ SB_DCHECK(!error_cb_);
+
+ decoder_status_cb_ = decoder_status_cb;
+ error_cb_ = error_cb;
+}
+
+void VideoDecoderImpl<FFMPEG>::WriteInputBuffer(
+ const scoped_refptr<InputBuffer>& input_buffer) {
+ SB_DCHECK(input_buffer);
+ SB_DCHECK(queue_.Poll().type == kInvalid);
+ SB_DCHECK(decoder_status_cb_);
+
+ if (stream_ended_) {
+ SB_LOG(ERROR) << "WriteInputFrame() was called after WriteEndOfStream().";
+ return;
+ }
+
+ if (!SbThreadIsValid(decoder_thread_)) {
+ decoder_thread_ = SbThreadCreate(
+ 0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true, "ff_video_dec",
+ &VideoDecoderImpl<FFMPEG>::ThreadEntryPoint, this);
+ SB_DCHECK(SbThreadIsValid(decoder_thread_));
+ }
+
+ queue_.Put(Event(input_buffer));
+}
+
+void VideoDecoderImpl<FFMPEG>::WriteEndOfStream() {
+ SB_DCHECK(decoder_status_cb_);
+
+ // We have to flush the decoder to decode the rest frames and to ensure that
+ // Decode() is not called when the stream is ended.
+ stream_ended_ = true;
+
+ if (!SbThreadIsValid(decoder_thread_)) {
+ // In case there is no WriteInputBuffer() call before WriteEndOfStream(),
+ // don't create the decoder thread and send the EOS frame directly.
+ decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
+ return;
+ }
+
+ queue_.Put(Event(kWriteEndOfStream));
+}
+
+void VideoDecoderImpl<FFMPEG>::Reset() {
+ // Join the thread to ensure that all callbacks in process are finished.
+ if (SbThreadIsValid(decoder_thread_)) {
+ queue_.Put(Event(kReset));
+ SbThreadJoin(decoder_thread_, NULL);
+ }
+
+ if (codec_context_ != NULL) {
+ ffmpeg_->avcodec_flush_buffers(codec_context_);
+ }
+
+ decoder_thread_ = kSbThreadInvalid;
+ stream_ended_ = false;
+
+ if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
+ TeardownCodec();
+ InitializeCodec();
+ }
+}
+
+bool VideoDecoderImpl<FFMPEG>::is_valid() const {
+ return (ffmpeg_ != NULL) && ffmpeg_->is_valid() && (codec_context_ != NULL);
+}
+
+// static
+void* VideoDecoderImpl<FFMPEG>::ThreadEntryPoint(void* context) {
+ SB_DCHECK(context);
+ VideoDecoderImpl<FFMPEG>* decoder =
+ reinterpret_cast<VideoDecoderImpl<FFMPEG>*>(context);
+ decoder->DecoderThreadFunc();
+ return NULL;
+}
+
+void VideoDecoderImpl<FFMPEG>::DecoderThreadFunc() {
+ for (;;) {
+ Event event = queue_.Get();
+ if (event.type == kReset) {
+ return;
+ }
+ if (error_occured_) {
+ continue;
+ }
+ if (event.type == kWriteInputBuffer) {
+ // Send |input_buffer| to ffmpeg and try to decode one frame.
+ AVPacket packet;
+ ffmpeg_->av_init_packet(&packet);
+ packet.data = const_cast<uint8_t*>(event.input_buffer->data());
+ packet.size = event.input_buffer->size();
+ packet.pts = event.input_buffer->timestamp();
+ codec_context_->reordered_opaque = packet.pts;
+
+ DecodePacket(&packet);
+ decoder_status_cb_(kNeedMoreInput, NULL);
+ } else {
+ SB_DCHECK(event.type == kWriteEndOfStream);
+ // Stream has ended, try to decode any frames left in ffmpeg.
+ AVPacket packet;
+ do {
+ ffmpeg_->av_init_packet(&packet);
+ packet.data = NULL;
+ packet.size = 0;
+ packet.pts = 0;
+ } while (DecodePacket(&packet));
+
+ decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
+ }
+ }
+}
+
+bool VideoDecoderImpl<FFMPEG>::DecodePacket(AVPacket* packet) {
+ SB_DCHECK(packet != NULL);
+
+#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 decode_result = ffmpeg_->avcodec_decode_video2(codec_context_, av_frame_,
+ &frame_decoded, packet);
+ if (decode_result < 0) {
+ SB_DLOG(ERROR) << "avcodec_decode_video2() failed with result "
+ << decode_result;
+ error_cb_();
+ error_occured_ = true;
+ return false;
+ }
+ if (frame_decoded == 0) {
+ return false;
+ }
+
+ if (av_frame_->opaque == NULL) {
+ SB_DLOG(ERROR) << "Video frame was produced yet has invalid frame data.";
+ error_cb_();
+ error_occured_ = true;
+ return false;
+ }
+
+ int pitch = AlignUp(av_frame_->width, kAlignment * 2);
+
+ scoped_refptr<CpuVideoFrame> frame = CpuVideoFrame::CreateYV12Frame(
+ av_frame_->width, av_frame_->height, pitch, av_frame_->reordered_opaque,
+ av_frame_->data[0], av_frame_->data[1], av_frame_->data[2]);
+
+ bool result = true;
+ if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
+ result = UpdateDecodeTarget(frame);
+ }
+
+ decoder_status_cb_(kBufferFull, frame);
+
+ return result;
+}
+
+bool VideoDecoderImpl<FFMPEG>::UpdateDecodeTarget(
+ const scoped_refptr<CpuVideoFrame>& frame) {
+ SbDecodeTarget decode_target = DecodeTargetCreate(
+ decode_target_graphics_context_provider_, frame, decode_target_);
+
+ // Lock only after the post to the renderer thread, to prevent deadlock.
+ ScopedLock lock(decode_target_mutex_);
+ decode_target_ = decode_target;
+
+ if (!SbDecodeTargetIsValid(decode_target)) {
+ SB_LOG(ERROR) << "Could not acquire a decode target from provider.";
+ return false;
+ }
+
+ return true;
+}
+
+void VideoDecoderImpl<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_VIDEO;
+ codec_context_->codec_id = AV_CODEC_ID_H264;
+ codec_context_->profile = FF_PROFILE_UNKNOWN;
+ codec_context_->coded_width = 0;
+ codec_context_->coded_height = 0;
+ codec_context_->pix_fmt = PIX_FMT_NONE;
+
+ codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
+ codec_context_->thread_count = 2;
+ codec_context_->opaque = this;
+ codec_context_->flags |= CODEC_FLAG_EMU_EDGE;
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ codec_context_->get_buffer2 = AllocateBufferCallback;
+#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ codec_context_->get_buffer = AllocateBufferCallback;
+ codec_context_->release_buffer = ReleaseBuffer;
+#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+
+ codec_context_->extradata = NULL;
+ codec_context_->extradata_size = 0;
+
+ 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 VideoDecoderImpl<FFMPEG>::TeardownCodec() {
+ if (codec_context_) {
+ ffmpeg_->CloseCodec(codec_context_);
+ ffmpeg_->av_freep(&codec_context_);
+ }
+ ffmpeg_->av_freep(&av_frame_);
+
+ if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
+ ScopedLock lock(decode_target_mutex_);
+ if (SbDecodeTargetIsValid(decode_target_)) {
+ DecodeTargetRelease(decode_target_graphics_context_provider_,
+ decode_target_);
+ decode_target_ = kSbDecodeTargetInvalid;
+ }
+ }
+}
+
+// When in decode-to-texture mode, this returns the current decoded video frame.
+SbDecodeTarget VideoDecoderImpl<FFMPEG>::GetCurrentDecodeTarget() {
+ SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture);
+
+ // We must take a lock here since this function can be called from a
+ // separate thread.
+ ScopedLock lock(decode_target_mutex_);
+ if (SbDecodeTargetIsValid(decode_target_)) {
+ // Make a disposable copy, since the state is internally reused by this
+ // class (to avoid recreating GL objects).
+ return DecodeTargetCopy(decode_target_);
+ } else {
+ return kSbDecodeTargetInvalid;
+ }
+}
+
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+
+int VideoDecoderImpl<FFMPEG>::AllocateBuffer(AVCodecContext* codec_context,
+ AVFrame* frame,
+ int flags) {
if (codec_context->pix_fmt != PIX_FMT_YUV420P &&
codec_context->pix_fmt != PIX_FMT_YUVJ420P) {
SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt;
return AVERROR(EINVAL);
}
- int ret =
- av_image_check_size(codec_context->width, codec_context->height, 0, NULL);
+ int ret = ffmpeg_->av_image_check_size(codec_context->width,
+ codec_context->height, 0, NULL);
if (ret < 0) {
return ret;
}
@@ -82,23 +429,24 @@
frame->reordered_opaque = codec_context->reordered_opaque;
- frame->buf[0] = av_buffer_create(frame_buffer,
- GetYV12SizeInBytes(y_stride, aligned_height),
- &ReleaseBuffer, frame->opaque, 0);
+ frame->buf[0] = static_cast<AVBufferRef*>(ffmpeg_->av_buffer_create(
+ frame_buffer, GetYV12SizeInBytes(y_stride, aligned_height),
+ &ReleaseBuffer, frame->opaque, 0));
return 0;
}
-#else // LIBAVUTIL_VERSION_MAJOR > 52
+#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
-int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame) {
+int VideoDecoderImpl<FFMPEG>::AllocateBuffer(AVCodecContext* codec_context,
+ AVFrame* frame) {
if (codec_context->pix_fmt != PIX_FMT_YUV420P &&
codec_context->pix_fmt != PIX_FMT_YUVJ420P) {
SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt;
return AVERROR(EINVAL);
}
- int ret =
- av_image_check_size(codec_context->width, codec_context->height, 0, NULL);
+ int ret = ffmpeg_->av_image_check_size(codec_context->width,
+ codec_context->height, 0, NULL);
if (ret < 0) {
return ret;
}
@@ -135,339 +483,8 @@
return 0;
}
-
-void ReleaseBuffer(AVCodecContext*, AVFrame* frame) {
- SbMemoryDeallocate(frame->opaque);
- frame->opaque = NULL;
-
- // The FFmpeg API expects us to zero the data pointers in this callback.
- SbMemorySet(frame->data, 0, sizeof(frame->data));
-}
-
-#endif // LIBAVUTIL_VERSION_MAJOR > 52
-} // namespace
-
-VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec,
- SbPlayerOutputMode output_mode,
- SbDecodeTargetGraphicsContextProvider*
- decode_target_graphics_context_provider)
- : video_codec_(video_codec),
- codec_context_(NULL),
- av_frame_(NULL),
- stream_ended_(false),
- error_occured_(false),
- decoder_thread_(kSbThreadInvalid),
- output_mode_(output_mode),
- decode_target_graphics_context_provider_(
- decode_target_graphics_context_provider),
- decode_target_(kSbDecodeTargetInvalid) {
- InitializeCodec();
-}
-
-VideoDecoder::~VideoDecoder() {
- Reset();
- TeardownCodec();
-}
-
-void VideoDecoder::Initialize(const DecoderStatusCB& decoder_status_cb,
- const ErrorCB& error_cb) {
- SB_DCHECK(decoder_status_cb);
- SB_DCHECK(!decoder_status_cb_);
- SB_DCHECK(error_cb);
- SB_DCHECK(!error_cb_);
-
- decoder_status_cb_ = decoder_status_cb;
- error_cb_ = error_cb;
-}
-
-void VideoDecoder::WriteInputBuffer(
- const scoped_refptr<InputBuffer>& input_buffer) {
- SB_DCHECK(input_buffer);
- SB_DCHECK(queue_.Poll().type == kInvalid);
- SB_DCHECK(decoder_status_cb_);
-
- if (stream_ended_) {
- SB_LOG(ERROR) << "WriteInputFrame() was called after WriteEndOfStream().";
- return;
- }
-
- if (!SbThreadIsValid(decoder_thread_)) {
- decoder_thread_ =
- SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true,
- "ff_video_dec", &VideoDecoder::ThreadEntryPoint, this);
- SB_DCHECK(SbThreadIsValid(decoder_thread_));
- }
-
- queue_.Put(Event(input_buffer));
-}
-
-void VideoDecoder::WriteEndOfStream() {
- SB_DCHECK(decoder_status_cb_);
-
- // We have to flush the decoder to decode the rest frames and to ensure that
- // Decode() is not called when the stream is ended.
- stream_ended_ = true;
-
- if (!SbThreadIsValid(decoder_thread_)) {
- // In case there is no WriteInputBuffer() call before WriteEndOfStream(),
- // don't create the decoder thread and send the EOS frame directly.
- decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
- return;
- }
-
- queue_.Put(Event(kWriteEndOfStream));
-}
-
-void VideoDecoder::Reset() {
- // Join the thread to ensure that all callbacks in process are finished.
- if (SbThreadIsValid(decoder_thread_)) {
- queue_.Put(Event(kReset));
- SbThreadJoin(decoder_thread_, NULL);
- }
-
- if (codec_context_ != NULL) {
- avcodec_flush_buffers(codec_context_);
- }
-
- decoder_thread_ = kSbThreadInvalid;
- stream_ended_ = false;
-
- if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
- TeardownCodec();
- InitializeCodec();
- }
-}
-
-// static
-void* VideoDecoder::ThreadEntryPoint(void* context) {
- SB_DCHECK(context);
- VideoDecoder* decoder = reinterpret_cast<VideoDecoder*>(context);
- decoder->DecoderThreadFunc();
- return NULL;
-}
-
-void VideoDecoder::DecoderThreadFunc() {
- for (;;) {
- Event event = queue_.Get();
- if (event.type == kReset) {
- return;
- }
- if (error_occured_) {
- continue;
- }
- if (event.type == kWriteInputBuffer) {
- // Send |input_buffer| to ffmpeg and try to decode one frame.
- AVPacket packet;
- av_init_packet(&packet);
- packet.data = const_cast<uint8_t*>(event.input_buffer->data());
- packet.size = event.input_buffer->size();
- packet.pts = event.input_buffer->pts();
- codec_context_->reordered_opaque = packet.pts;
-
- DecodePacket(&packet);
- decoder_status_cb_(kNeedMoreInput, NULL);
- } else {
- SB_DCHECK(event.type == kWriteEndOfStream);
- // Stream has ended, try to decode any frames left in ffmpeg.
- AVPacket packet;
- do {
- av_init_packet(&packet);
- packet.data = NULL;
- packet.size = 0;
- packet.pts = 0;
- } while (DecodePacket(&packet));
-
- decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
- }
- }
-}
-
-bool VideoDecoder::DecodePacket(AVPacket* packet) {
- SB_DCHECK(packet != NULL);
-
-#if LIBAVUTIL_VERSION_MAJOR > 52
- av_frame_unref(av_frame_);
-#else // LIBAVUTIL_VERSION_MAJOR > 52
- avcodec_get_frame_defaults(av_frame_);
-#endif // LIBAVUTIL_VERSION_MAJOR > 52
- int frame_decoded = 0;
- int decode_result =
- avcodec_decode_video2(codec_context_, av_frame_, &frame_decoded, packet);
- if (decode_result < 0) {
- SB_DLOG(ERROR) << "avcodec_decode_video2() failed with result "
- << decode_result;
- error_cb_();
- error_occured_ = true;
- return false;
- }
- if (frame_decoded == 0) {
- return false;
- }
-
- if (av_frame_->opaque == NULL) {
- SB_DLOG(ERROR) << "Video frame was produced yet has invalid frame data.";
- error_cb_();
- error_occured_ = true;
- return false;
- }
-
- int pitch = AlignUp(av_frame_->width, kAlignment * 2);
-
- scoped_refptr<CpuVideoFrame> frame = CpuVideoFrame::CreateYV12Frame(
- av_frame_->width, av_frame_->height, pitch, av_frame_->reordered_opaque,
- av_frame_->data[0], av_frame_->data[1], av_frame_->data[2]);
-
- bool result = true;
- if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
- result = UpdateDecodeTarget(frame);
- }
-
- decoder_status_cb_(kBufferFull, frame);
-
- return result;
-}
-
-bool VideoDecoder::UpdateDecodeTarget(
- const scoped_refptr<CpuVideoFrame>& frame) {
- SbDecodeTarget decode_target = DecodeTargetCreate(
- decode_target_graphics_context_provider_, frame, decode_target_);
-
- // Lock only after the post to the renderer thread, to prevent deadlock.
- ScopedLock lock(decode_target_mutex_);
- decode_target_ = decode_target;
-
- if (!SbDecodeTargetIsValid(decode_target)) {
- SB_LOG(ERROR) << "Could not acquire a decode target from provider.";
- return false;
- }
-
- return true;
-}
-
-void VideoDecoder::InitializeCodec() {
- InitializeFfmpeg();
-
- codec_context_ = avcodec_alloc_context3(NULL);
-
- if (codec_context_ == NULL) {
- SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
- return;
- }
-
- codec_context_->codec_type = AVMEDIA_TYPE_VIDEO;
- codec_context_->codec_id = AV_CODEC_ID_H264;
- codec_context_->profile = FF_PROFILE_UNKNOWN;
- codec_context_->coded_width = 0;
- codec_context_->coded_height = 0;
- codec_context_->pix_fmt = PIX_FMT_NONE;
-
- codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
- codec_context_->thread_count = 2;
- codec_context_->opaque = this;
- codec_context_->flags |= CODEC_FLAG_EMU_EDGE;
-#if LIBAVUTIL_VERSION_MAJOR > 52
- codec_context_->get_buffer2 = AllocateBuffer;
-#else // LIBAVUTIL_VERSION_MAJOR > 52
- codec_context_->get_buffer = AllocateBuffer;
- codec_context_->release_buffer = ReleaseBuffer;
-#endif // LIBAVUTIL_VERSION_MAJOR > 52
-
- codec_context_->extradata = NULL;
- codec_context_->extradata_size = 0;
-
- AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
-
- if (codec == NULL) {
- SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
- TeardownCodec();
- return;
- }
-
- int rv = OpenCodec(codec_context_, codec);
- if (rv < 0) {
- SB_LOG(ERROR) << "Unable to open codec";
- TeardownCodec();
- return;
- }
-
-#if LIBAVUTIL_VERSION_MAJOR > 52
- av_frame_ = av_frame_alloc();
-#else // LIBAVUTIL_VERSION_MAJOR > 52
- av_frame_ = avcodec_alloc_frame();
-#endif // LIBAVUTIL_VERSION_MAJOR > 52
- if (av_frame_ == NULL) {
- SB_LOG(ERROR) << "Unable to allocate audio frame";
- TeardownCodec();
- }
-}
-
-void VideoDecoder::TeardownCodec() {
- if (codec_context_) {
- CloseCodec(codec_context_);
- av_free(codec_context_);
- codec_context_ = NULL;
- }
- if (av_frame_) {
- av_free(av_frame_);
- av_frame_ = NULL;
- }
-
- if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
- ScopedLock lock(decode_target_mutex_);
- if (SbDecodeTargetIsValid(decode_target_)) {
- DecodeTargetRelease(decode_target_graphics_context_provider_,
- decode_target_);
- decode_target_ = kSbDecodeTargetInvalid;
- }
- }
-}
-
-// When in decode-to-texture mode, this returns the current decoded video frame.
-SbDecodeTarget VideoDecoder::GetCurrentDecodeTarget() {
- SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture);
-
- // We must take a lock here since this function can be called from a
- // separate thread.
- ScopedLock lock(decode_target_mutex_);
- if (SbDecodeTargetIsValid(decode_target_)) {
- // Make a disposable copy, since the state is internally reused by this
- // class (to avoid recreating GL objects).
- return DecodeTargetCopy(decode_target_);
- } else {
- return kSbDecodeTargetInvalid;
- }
-}
+#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
} // namespace ffmpeg
-
-namespace starboard {
-namespace player {
-namespace filter {
-
-// static
-bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
- SbMediaVideoCodec codec,
- SbDrmSystem drm_system) {
- SB_UNREFERENCED_PARAMETER(codec);
- SB_UNREFERENCED_PARAMETER(drm_system);
-
-#if defined(SB_FORCE_DECODE_TO_TEXTURE_ONLY)
- // Starboard lib targets may not draw directly to the window, so punch through
- // video is not made available.
- return output_mode == kSbPlayerOutputModeDecodeToTexture;
-#endif // defined(SB_FORCE_DECODE_TO_TEXTURE_ONLY)
-
- if (output_mode == kSbPlayerOutputModePunchOut ||
- output_mode == kSbPlayerOutputModeDecodeToTexture) {
- return true;
- }
-
- return false;
-}
-
-} // namespace filter
-} // namespace player
-} // namespace starboard
-
} // namespace shared
} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h
new file mode 100644
index 0000000..86f2cd6
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h
@@ -0,0 +1,166 @@
+// 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.
+
+#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_IMPL_H_
+#define STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_IMPL_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/log.h"
+#include "starboard/media.h"
+#include "starboard/queue.h"
+#include "starboard/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/filter/cpu_video_frame.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/thread.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+// For each version V that is supported, there will be an explicit
+// specialization of the VideoDecoder class.
+template <int V>
+class VideoDecoderImpl : public VideoDecoder {
+ public:
+ static VideoDecoder* Create(SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider);
+};
+
+// Forward class declaration of the explicit specialization with value FFMPEG.
+template <>
+class VideoDecoderImpl<FFMPEG>;
+
+// Declare the explicit specialization of the class with value FFMPEG.
+template <>
+class VideoDecoderImpl<FFMPEG> : public VideoDecoder {
+ public:
+ VideoDecoderImpl(SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider);
+ ~VideoDecoderImpl() override;
+
+ // From: VideoDecoder
+ static VideoDecoder* Create(SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider);
+ bool is_valid() const override;
+
+ // From: starboard::player::filter::VideoDecoder
+ void Initialize(const DecoderStatusCB& decoder_status_cb,
+ const ErrorCB& error_cb) override;
+ size_t GetPrerollFrameCount() const override { return 8; }
+ SbTime GetPrerollTimeout() const override { return kSbTimeMax; }
+
+ void WriteInputBuffer(
+ const scoped_refptr<InputBuffer>& input_buffer) override;
+ void WriteEndOfStream() override;
+ void Reset() override;
+
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame, int flags);
+#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+ int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame);
+#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
+
+ private:
+ typedef ::starboard::shared::starboard::player::filter::CpuVideoFrame
+ CpuVideoFrame;
+
+ enum EventType {
+ kInvalid,
+ kReset,
+ kWriteInputBuffer,
+ kWriteEndOfStream,
+ };
+
+ struct Event {
+ EventType type;
+ // |input_buffer| is only used when |type| is kWriteInputBuffer.
+ scoped_refptr<InputBuffer> input_buffer;
+
+ explicit Event(EventType type = kInvalid) : type(type) {
+ SB_DCHECK(type != kWriteInputBuffer);
+ }
+
+ explicit Event(const scoped_refptr<InputBuffer>& input_buffer)
+ : type(kWriteInputBuffer), input_buffer(input_buffer) {}
+ };
+
+ static void* ThreadEntryPoint(void* context);
+ void DecoderThreadFunc();
+
+ bool DecodePacket(AVPacket* packet);
+ void InitializeCodec();
+ void TeardownCodec();
+ SbDecodeTarget GetCurrentDecodeTarget() override;
+
+ bool UpdateDecodeTarget(const scoped_refptr<CpuVideoFrame>& frame);
+
+ FFMPEGDispatch* ffmpeg_;
+
+ // |video_codec_| will be initialized inside ctor and won't be changed during
+ // the life time of this class.
+ const SbMediaVideoCodec video_codec_;
+ // The following callbacks will be initialized in Initialize() and won't be
+ // changed during the life time of this class.
+ DecoderStatusCB decoder_status_cb_;
+ ErrorCB error_cb_;
+
+ Queue<Event> queue_;
+
+ // The AV related classes will only be created and accessed on the decoder
+ // thread.
+ AVCodecContext* codec_context_;
+ AVFrame* av_frame_;
+
+ bool stream_ended_;
+ bool error_occured_;
+
+ // Working thread to avoid lengthy decoding work block the player thread.
+ SbThread decoder_thread_;
+
+ // Decode-to-texture related state.
+ SbPlayerOutputMode output_mode_;
+
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider_;
+
+ // If decode-to-texture is enabled, then we store the decode target texture
+ // inside of this |decode_target_| member.
+ SbDecodeTarget decode_target_;
+
+ // GetCurrentDecodeTarget() needs to be called from an arbitrary thread
+ // to obtain the current decode target (which ultimately ends up being a
+ // copy of |decode_target_|), we need to safe-guard access to |decode_target_|
+ // and we do so through this mutex.
+ Mutex decode_target_mutex_;
+ // Mutex frame_mutex_;
+
+ // int frame_last_rendered_pts_;
+ // scoped_refptr<VideoFrame> frame_;
+};
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_IMPL_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_internal.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_internal.cc
new file mode 100644
index 0000000..7ad130e
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_internal.cc
@@ -0,0 +1,53 @@
+// 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 implementation of methods from
+// starboard::player::filter::VideoDecoder for the VideoDecoder class that are
+// the same for dynamically loaded and linked implementation for the ffmpeg
+// library.
+
+#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+// static
+bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
+ SbMediaVideoCodec codec,
+ SbDrmSystem drm_system) {
+ SB_UNREFERENCED_PARAMETER(codec);
+ SB_UNREFERENCED_PARAMETER(drm_system);
+
+#if defined(SB_FORCE_DECODE_TO_TEXTURE_ONLY)
+ // Starboard lib targets may not draw directly to the window, so punch through
+ // video is not made available.
+ return output_mode == kSbPlayerOutputModeDecodeToTexture;
+#endif // defined(SB_FORCE_DECODE_TO_TEXTURE_ONLY)
+
+ if (output_mode == kSbPlayerOutputModePunchOut ||
+ output_mode == kSbPlayerOutputModeDecodeToTexture) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard