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