gstavviddec: Only allow a single closed caption meta
[platform/upstream/gstreamer.git] / ext / libav / gstavviddec.c
index c6ed9a5..502390d 100644 (file)
 #include <string.h>
 
 #include <libavcodec/avcodec.h>
-
-#include <gst/gst.h>
-#include <gst/video/video.h>
-#include <gst/video/gstvideodecoder.h>
-#include <gst/video/gstvideometa.h>
-#include <gst/video/gstvideopool.h>
+#include <libavutil/stereo3d.h>
+#include <libavutil/mastering_display_metadata.h>
 
 #include "gstav.h"
 #include "gstavcodecmap.h"
 #include "gstavutils.h"
 #include "gstavviddec.h"
 
-GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
 
 #define MAX_TS_MASK 0xff
 
@@ -50,6 +46,7 @@ GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
 #define REQUIRED_POOL_MAX_BUFFERS       32
 #define DEFAULT_STRIDE_ALIGN            31
 #define DEFAULT_ALLOC_PARAM             { 0, DEFAULT_STRIDE_ALIGN, 0, 0, }
+#define DEFAULT_THREAD_TYPE             0
 
 enum
 {
@@ -60,6 +57,7 @@ enum
   PROP_DEBUG_MV,
   PROP_MAX_THREADS,
   PROP_OUTPUT_CORRUPT,
+  PROP_THREAD_TYPE,
   PROP_LAST
 };
 
@@ -148,6 +146,27 @@ gst_ffmpegviddec_skipframe_get_type (void)
   return ffmpegdec_skipframe_type;
 }
 
+static const GFlagsValue ffmpegdec_thread_types[] = {
+  {0x0, "Auto", "auto"},
+  {0x1, "Frame", "frame"},
+  {0x2, "Slice", "slice"},
+  {0, NULL, NULL},
+};
+
+#define GST_FFMPEGVIDDEC_TYPE_THREAD_TYPE (gst_ffmpegviddec_thread_type_get_type())
+static GType
+gst_ffmpegviddec_thread_type_get_type (void)
+{
+  static GType ffmpegdec_thread_type_type = 0;
+
+  if (!ffmpegdec_thread_type_type) {
+    ffmpegdec_thread_type_type =
+        g_flags_register_static ("GstLibAVVidDecThreadType",
+        ffmpegdec_thread_types);
+  }
+  return ffmpegdec_thread_type_type;
+}
+
 static void
 gst_ffmpegviddec_base_init (GstFFMpegVidDecClass * klass)
 {
@@ -194,6 +213,9 @@ gst_ffmpegviddec_base_init (GstFFMpegVidDecClass * klass)
   gst_element_class_add_pad_template (element_class, srctempl);
   gst_element_class_add_pad_template (element_class, sinktempl);
 
+  gst_caps_unref (sinkcaps);
+  gst_caps_unref (srccaps);
+
   klass->in_plugin = in_plugin;
 }
 
@@ -235,12 +257,17 @@ gst_ffmpegviddec_class_init (GstFFMpegVidDecClass * klass)
           DEFAULT_OUTPUT_CORRUPT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   caps = klass->in_plugin->capabilities;
-  if (caps & (CODEC_CAP_FRAME_THREADS | CODEC_CAP_SLICE_THREADS)) {
+  if (caps & (AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS)) {
     g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_THREADS,
         g_param_spec_int ("max-threads", "Maximum decode threads",
             "Maximum number of worker threads to spawn. (0 = auto)",
             0, G_MAXINT, DEFAULT_MAX_THREADS,
             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+    g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_THREAD_TYPE,
+        g_param_spec_flags ("thread-type", "Thread type",
+            "Multithreading methods to use",
+            GST_FFMPEGVIDDEC_TYPE_THREAD_TYPE,
+            DEFAULT_THREAD_TYPE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   }
 
   viddec_class->set_format = gst_ffmpegviddec_set_format;
@@ -252,6 +279,12 @@ gst_ffmpegviddec_class_init (GstFFMpegVidDecClass * klass)
   viddec_class->drain = gst_ffmpegviddec_drain;
   viddec_class->decide_allocation = gst_ffmpegviddec_decide_allocation;
   viddec_class->propose_allocation = gst_ffmpegviddec_propose_allocation;
+
+  GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
+
+  gst_type_mark_as_plugin_api (GST_FFMPEGVIDDEC_TYPE_LOWRES, 0);
+  gst_type_mark_as_plugin_api (GST_FFMPEGVIDDEC_TYPE_SKIPFRAME, 0);
+  gst_type_mark_as_plugin_api (GST_FFMPEGVIDDEC_TYPE_THREAD_TYPE, 0);
 }
 
 static void
@@ -270,6 +303,7 @@ gst_ffmpegviddec_init (GstFFMpegVidDec * ffmpegdec)
   ffmpegdec->debug_mv = DEFAULT_DEBUG_MV;
   ffmpegdec->max_threads = DEFAULT_MAX_THREADS;
   ffmpegdec->output_corrupt = DEFAULT_OUTPUT_CORRUPT;
+  ffmpegdec->thread_type = DEFAULT_THREAD_TYPE;
 
   GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (ffmpegdec));
   gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST
@@ -311,7 +345,7 @@ static gboolean
 gst_ffmpegviddec_close (GstFFMpegVidDec * ffmpegdec, gboolean reset)
 {
   GstFFMpegVidDecClass *oclass;
-  gint i;
+  guint i;
 
   oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
 
@@ -331,10 +365,6 @@ gst_ffmpegviddec_close (GstFFMpegVidDec * ffmpegdec, gboolean reset)
     av_free (ffmpegdec->context->extradata);
     ffmpegdec->context->extradata = NULL;
   }
-  if (ffmpegdec->context->slice_offset) {
-    g_free (ffmpegdec->context->slice_offset);
-    ffmpegdec->context->slice_offset = NULL;
-  }
   if (reset) {
     if (avcodec_get_context_defaults3 (ffmpegdec->context,
             oclass->in_plugin) < 0) {
@@ -351,7 +381,7 @@ static gboolean
 gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec)
 {
   GstFFMpegVidDecClass *oclass;
-  gint i;
+  guint i;
 
   oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
 
@@ -362,25 +392,12 @@ gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec)
     ffmpegdec->stride[i] = -1;
 
   ffmpegdec->opened = TRUE;
-  ffmpegdec->is_realvideo = FALSE;
 
   GST_LOG_OBJECT (ffmpegdec, "Opened libav codec %s, id %d",
       oclass->in_plugin->name, oclass->in_plugin->id);
 
-  switch (oclass->in_plugin->id) {
-    case AV_CODEC_ID_RV10:
-    case AV_CODEC_ID_RV30:
-    case AV_CODEC_ID_RV20:
-    case AV_CODEC_ID_RV40:
-      ffmpegdec->is_realvideo = TRUE;
-      break;
-    default:
-      GST_LOG_OBJECT (ffmpegdec, "Parser deactivated for format");
-      break;
-  }
-
   gst_ffmpegviddec_context_set_flags (ffmpegdec->context,
-      CODEC_FLAG_OUTPUT_CORRUPT, ffmpegdec->output_corrupt);
+      AV_CODEC_FLAG_OUTPUT_CORRUPT, ffmpegdec->output_corrupt);
 
   return TRUE;
 
@@ -452,18 +469,20 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder,
     ffmpegdec->pic_height = 0;
     ffmpegdec->pic_par_n = 0;
     ffmpegdec->pic_par_d = 0;
+    ffmpegdec->pic_interlaced = 0;
+    ffmpegdec->pic_field_order = 0;
+    ffmpegdec->pic_field_order_changed = FALSE;
     ffmpegdec->ctx_ticks = 0;
     ffmpegdec->ctx_time_n = 0;
     ffmpegdec->ctx_time_d = 0;
+    ffmpegdec->cur_multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
+    ffmpegdec->cur_multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
   }
 
   gst_caps_replace (&ffmpegdec->last_caps, state->caps);
 
   /* set buffer functions */
   ffmpegdec->context->get_buffer2 = gst_ffmpegviddec_get_buffer2;
-  ffmpegdec->context->get_buffer = NULL;
-  ffmpegdec->context->reget_buffer = NULL;
-  ffmpegdec->context->release_buffer = NULL;
   ffmpegdec->context->draw_horiz_band = NULL;
 
   /* reset coded_width/_height to prevent it being reused from last time when
@@ -503,18 +522,14 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder,
    * supports it) */
   ffmpegdec->context->debug_mv = ffmpegdec->debug_mv;
 
-  {
+  if (ffmpegdec->thread_type) {
+    GST_DEBUG_OBJECT (ffmpegdec, "Use requested thread type 0x%x",
+        ffmpegdec->thread_type);
+    ffmpegdec->context->thread_type = ffmpegdec->thread_type;
+  } else {
     GstQuery *query;
     gboolean is_live;
 
-    if (ffmpegdec->max_threads == 0) {
-      if (!(oclass->in_plugin->capabilities & CODEC_CAP_AUTO_THREADS))
-        ffmpegdec->context->thread_count = gst_ffmpeg_auto_max_threads ();
-      else
-        ffmpegdec->context->thread_count = 0;
-    } else
-      ffmpegdec->context->thread_count = ffmpegdec->max_threads;
-
     query = gst_query_new_latency ();
     is_live = FALSE;
     /* Check if upstream is live. If it isn't we can enable frame based
@@ -530,6 +545,18 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder,
       ffmpegdec->context->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
   }
 
+  if (ffmpegdec->max_threads == 0) {
+    /* When thread type is FF_THREAD_FRAME, extra latency is introduced equal
+     * to one frame per thread. We thus need to calculate the thread count ourselves */
+    if ((!(oclass->in_plugin->capabilities & AV_CODEC_CAP_AUTO_THREADS)) ||
+        (ffmpegdec->context->thread_type & FF_THREAD_FRAME))
+      ffmpegdec->context->thread_count =
+          MIN (gst_ffmpeg_auto_max_threads (), 16);
+    else
+      ffmpegdec->context->thread_count = 0;
+  } else
+    ffmpegdec->context->thread_count = ffmpegdec->max_threads;
+
   /* open codec - we don't select an output pix_fmt yet,
    * simply because we don't know! We only get it
    * during playback... */
@@ -545,6 +572,12 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder,
     latency = gst_util_uint64_scale_ceil (
         (ffmpegdec->context->has_b_frames) * GST_SECOND, info->fps_d,
         info->fps_n);
+
+    if (ffmpegdec->context->thread_type & FF_THREAD_FRAME) {
+      latency +=
+          gst_util_uint64_scale_ceil (ffmpegdec->context->thread_count *
+          GST_SECOND, info->fps_d, info->fps_n);
+    }
   }
 
   ret = TRUE;
@@ -627,7 +660,6 @@ gst_ffmpegvideodec_prepare_dr_pool (GstFFMpegVidDec * ffmpegdec,
   gint width, height;
   gint linesize_align[4];
   gint i;
-  guint edge;
   gsize max_align;
 
   width = GST_VIDEO_INFO_WIDTH (info);
@@ -637,19 +669,10 @@ gst_ffmpegvideodec_prepare_dr_pool (GstFFMpegVidDec * ffmpegdec,
   avcodec_align_dimensions2 (ffmpegdec->context, &width, &height,
       linesize_align);
 
-  if (ffmpegdec->context->flags & CODEC_FLAG_EMU_EDGE)
-    edge = 0;
-  else
-    edge = avcodec_get_edge_width ();
-
-  /* increase the size for the padding */
-  width += edge << 1;
-  height += edge << 1;
-
-  align.padding_top = edge;
-  align.padding_left = edge;
-  align.padding_right = width - GST_VIDEO_INFO_WIDTH (info) - edge;
-  align.padding_bottom = height - GST_VIDEO_INFO_HEIGHT (info) - edge;
+  align.padding_top = 0;
+  align.padding_left = 0;
+  align.padding_right = width - GST_VIDEO_INFO_WIDTH (info);
+  align.padding_bottom = height - GST_VIDEO_INFO_HEIGHT (info);
 
   /* add extra padding to match libav buffer allocation sizes */
   align.padding_bottom++;
@@ -693,7 +716,7 @@ gst_ffmpegviddec_ensure_internal_pool (GstFFMpegVidDec * ffmpegdec,
   GstVideoFormat format;
   GstCaps *caps;
   GstStructure *config;
-  gint i;
+  guint i;
 
   if (ffmpegdec->internal_pool != NULL &&
       ffmpegdec->pool_width == picture->width &&
@@ -707,6 +730,12 @@ gst_ffmpegviddec_ensure_internal_pool (GstFFMpegVidDec * ffmpegdec,
   format = gst_ffmpeg_pixfmt_to_videoformat (picture->format);
   gst_video_info_set_format (&info, format, picture->width, picture->height);
 
+  /* If we have not yet been negotiated, a NONE format here would
+   * result in invalid initial dimension alignments, and potential
+   * out of bounds writes.
+   */
+  ffmpegdec->context->pix_fmt = picture->format;
+
   for (i = 0; i < G_N_ELEMENTS (ffmpegdec->stride); i++)
     ffmpegdec->stride[i] = -1;
 
@@ -745,7 +774,8 @@ gst_ffmpegviddec_can_direct_render (GstFFMpegVidDec * ffmpegdec)
     return FALSE;
 
   oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
-  return ((oclass->in_plugin->capabilities & CODEC_CAP_DR1) == CODEC_CAP_DR1);
+  return ((oclass->in_plugin->capabilities & AV_CODEC_CAP_DR1) ==
+      AV_CODEC_CAP_DR1);
 }
 
 /* called when ffmpeg wants us to allocate a buffer to write the decoded frame
@@ -757,8 +787,9 @@ gst_ffmpegviddec_get_buffer2 (AVCodecContext * context, AVFrame * picture,
   GstVideoCodecFrame *frame;
   GstFFMpegVidDecVideoFrame *dframe;
   GstFFMpegVidDec *ffmpegdec;
-  gint c;
+  guint c;
   GstFlowReturn ret;
+  int create_buffer_flags = 0;
 
   ffmpegdec = (GstFFMpegVidDec *) context->opaque;
 
@@ -840,11 +871,16 @@ gst_ffmpegviddec_get_buffer2 (AVCodecContext * context, AVFrame * picture,
         picture->data[c]);
   }
 
-  picture->buf[0] = av_buffer_create (NULL, 0, dummy_free_buffer, dframe, 0);
-
-  /* tell ffmpeg we own this buffer, transfer the ref we have on the buffer to
-   * the opaque data. */
-  picture->type = FF_BUFFER_TYPE_USER;
+  if ((flags & AV_GET_BUFFER_FLAG_REF) == AV_GET_BUFFER_FLAG_REF) {
+    /* decoder might reuse this AVFrame and it would result to no more
+     * get_buffer() call if the AVFrame's AVBuffer is writable
+     * (meaning that the refcount of AVBuffer == 1).
+     * To enforce get_buffer() for the every output frame, set read-only flag here
+     */
+    create_buffer_flags = AV_BUFFER_FLAG_READONLY;
+  }
+  picture->buf[0] = av_buffer_create (NULL,
+      0, dummy_free_buffer, dframe, create_buffer_flags);
 
   GST_LOG_OBJECT (ffmpegdec, "returned frame %p", dframe->buffer);
 
@@ -907,12 +943,24 @@ no_frame:
 static gboolean
 picture_changed (GstFFMpegVidDec * ffmpegdec, AVFrame * picture)
 {
+  gint pic_field_order = 0;
+
+  if (picture->interlaced_frame) {
+    if (picture->repeat_pict)
+      pic_field_order |= GST_VIDEO_BUFFER_FLAG_RFF;
+    if (picture->top_field_first)
+      pic_field_order |= GST_VIDEO_BUFFER_FLAG_TFF;
+  }
+
   return !(ffmpegdec->pic_width == picture->width
       && ffmpegdec->pic_height == picture->height
       && ffmpegdec->pic_pix_fmt == picture->format
       && ffmpegdec->pic_par_n == picture->sample_aspect_ratio.num
       && ffmpegdec->pic_par_d == picture->sample_aspect_ratio.den
-      && ffmpegdec->pic_interlaced == picture->interlaced_frame);
+      && ffmpegdec->pic_interlaced == picture->interlaced_frame
+      && ffmpegdec->pic_field_order == pic_field_order
+      && ffmpegdec->cur_multiview_mode == ffmpegdec->picture_multiview_mode
+      && ffmpegdec->cur_multiview_flags == ffmpegdec->picture_multiview_flags);
 }
 
 static gboolean
@@ -927,6 +975,15 @@ static gboolean
 update_video_context (GstFFMpegVidDec * ffmpegdec, AVCodecContext * context,
     AVFrame * picture)
 {
+  gint pic_field_order = 0;
+
+  if (picture->interlaced_frame) {
+    if (picture->repeat_pict)
+      pic_field_order |= GST_VIDEO_BUFFER_FLAG_RFF;
+    if (picture->top_field_first)
+      pic_field_order |= GST_VIDEO_BUFFER_FLAG_TFF;
+  }
+
   if (!picture_changed (ffmpegdec, picture)
       && !context_changed (ffmpegdec, context))
     return FALSE;
@@ -947,7 +1004,23 @@ update_video_context (GstFFMpegVidDec * ffmpegdec, AVCodecContext * context,
   ffmpegdec->pic_height = picture->height;
   ffmpegdec->pic_par_n = picture->sample_aspect_ratio.num;
   ffmpegdec->pic_par_d = picture->sample_aspect_ratio.den;
+  ffmpegdec->cur_multiview_mode = ffmpegdec->picture_multiview_mode;
+  ffmpegdec->cur_multiview_flags = ffmpegdec->picture_multiview_flags;
+
+  /* Remember if we have interlaced content and the field order changed
+   * at least once. If that happens, we must be interlace-mode=mixed
+   */
+  if (ffmpegdec->pic_field_order_changed ||
+      (ffmpegdec->pic_field_order != pic_field_order &&
+          ffmpegdec->pic_interlaced))
+    ffmpegdec->pic_field_order_changed = TRUE;
+
+  ffmpegdec->pic_field_order = pic_field_order;
   ffmpegdec->pic_interlaced = picture->interlaced_frame;
+
+  if (!ffmpegdec->pic_interlaced)
+    ffmpegdec->pic_field_order_changed = FALSE;
+
   ffmpegdec->ctx_ticks = context->ticks_per_frame;
   ffmpegdec->ctx_time_n = context->time_base.num;
   ffmpegdec->ctx_time_d = context->time_base.den;
@@ -1029,6 +1102,76 @@ no_par:
   }
 }
 
+static GstVideoMultiviewMode
+stereo_av_to_gst (enum AVStereo3DType type)
+{
+  switch (type) {
+    case AV_STEREO3D_SIDEBYSIDE:
+      return GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
+    case AV_STEREO3D_TOPBOTTOM:
+      return GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM;
+    case AV_STEREO3D_FRAMESEQUENCE:
+      return GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
+    case AV_STEREO3D_CHECKERBOARD:
+      return GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD;
+    case AV_STEREO3D_SIDEBYSIDE_QUINCUNX:
+      return GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX;
+    case AV_STEREO3D_LINES:
+      return GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
+    case AV_STEREO3D_COLUMNS:
+      return GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED;
+    default:
+      break;
+  }
+
+  return GST_VIDEO_MULTIVIEW_MODE_NONE;
+}
+
+static gboolean
+mastering_display_metadata_av_to_gst (AVMasteringDisplayMetadata * av,
+    GstVideoMasteringDisplayInfo * gst)
+{
+  const guint64 chroma_scale = 50000;
+  const guint64 luma_scale = 10000;
+  gint i;
+
+  /* Use only complete mastering meta */
+  if (!av->has_primaries || !av->has_luminance)
+    return FALSE;
+
+  for (i = 0; i < G_N_ELEMENTS (gst->display_primaries); i++) {
+    gst->display_primaries[i].x = (guint16) gst_util_uint64_scale (chroma_scale,
+        av->display_primaries[i][0].num, av->display_primaries[i][0].den);
+    gst->display_primaries[i].y = (guint16) gst_util_uint64_scale (chroma_scale,
+        av->display_primaries[i][1].num, av->display_primaries[i][1].den);
+  }
+
+  gst->white_point.x = (guint16) gst_util_uint64_scale (chroma_scale,
+      av->white_point[0].num, av->white_point[0].den);
+  gst->white_point.y = (guint16) gst_util_uint64_scale (chroma_scale,
+      av->white_point[1].num, av->white_point[1].den);
+
+
+  gst->max_display_mastering_luminance =
+      (guint32) gst_util_uint64_scale (luma_scale,
+      av->max_luminance.num, av->max_luminance.den);
+  gst->min_display_mastering_luminance =
+      (guint32) gst_util_uint64_scale (luma_scale,
+      av->min_luminance.num, av->min_luminance.den);
+
+  return TRUE;
+}
+
+static gboolean
+content_light_metadata_av_to_gst (AVContentLightMetadata * av,
+    GstVideoContentLightLevel * gst)
+{
+  gst->max_content_light_level = av->MaxCLL;
+  gst->max_frame_average_light_level = av->MaxFALL;
+
+  return TRUE;
+}
+
 static gboolean
 gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec,
     AVCodecContext * context, AVFrame * picture)
@@ -1037,6 +1180,8 @@ gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec,
   GstVideoInfo *in_info, *out_info;
   GstVideoCodecState *output_state;
   gint fps_n, fps_d;
+  GstClockTime latency;
+  GstStructure *in_s;
 
   if (!update_video_context (ffmpegdec, context, picture))
     return TRUE;
@@ -1056,26 +1201,73 @@ gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec,
   out_info = &ffmpegdec->output_state->info;
 
   /* set the interlaced flag */
-  if (ffmpegdec->pic_interlaced)
-    out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
-  else
-    out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
+  in_s = gst_caps_get_structure (ffmpegdec->input_state->caps, 0);
 
-  switch (context->chroma_sample_location) {
-    case 1:
-      out_info->chroma_site = GST_VIDEO_CHROMA_SITE_MPEG2;
-      break;
-    case 2:
-      out_info->chroma_site = GST_VIDEO_CHROMA_SITE_JPEG;
-      break;
-    case 3:
-      out_info->chroma_site = GST_VIDEO_CHROMA_SITE_DV;
-      break;
-    case 4:
-      out_info->chroma_site = GST_VIDEO_CHROMA_SITE_V_COSITED;
-      break;
-    default:
-      break;
+  if (!gst_structure_has_field (in_s, "interlace-mode")) {
+    if (ffmpegdec->pic_interlaced) {
+      if (ffmpegdec->pic_field_order_changed ||
+          (ffmpegdec->pic_field_order & GST_VIDEO_BUFFER_FLAG_RFF)) {
+        out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
+      } else {
+        out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_INTERLEAVED;
+        if ((ffmpegdec->pic_field_order & GST_VIDEO_BUFFER_FLAG_TFF))
+          GST_VIDEO_INFO_FIELD_ORDER (out_info) =
+              GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST;
+        else
+          GST_VIDEO_INFO_FIELD_ORDER (out_info) =
+              GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST;
+      }
+    } else {
+      out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
+    }
+  }
+
+  if (!gst_structure_has_field (in_s, "chroma-site")) {
+    switch (context->chroma_sample_location) {
+      case AVCHROMA_LOC_LEFT:
+        out_info->chroma_site = GST_VIDEO_CHROMA_SITE_MPEG2;
+        break;
+      case AVCHROMA_LOC_CENTER:
+        out_info->chroma_site = GST_VIDEO_CHROMA_SITE_JPEG;
+        break;
+      case AVCHROMA_LOC_TOPLEFT:
+        out_info->chroma_site = GST_VIDEO_CHROMA_SITE_DV;
+        break;
+      case AVCHROMA_LOC_TOP:
+        out_info->chroma_site = GST_VIDEO_CHROMA_SITE_V_COSITED;
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (!gst_structure_has_field (in_s, "colorimetry")
+      || in_info->colorimetry.primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN) {
+    out_info->colorimetry.primaries =
+        gst_video_color_primaries_from_iso (context->color_primaries);
+  }
+
+  if (!gst_structure_has_field (in_s, "colorimetry")
+      || in_info->colorimetry.transfer == GST_VIDEO_TRANSFER_UNKNOWN) {
+    out_info->colorimetry.transfer =
+        gst_video_color_transfer_from_iso (context->color_trc);
+  }
+
+  if (!gst_structure_has_field (in_s, "colorimetry")
+      || in_info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
+    out_info->colorimetry.matrix =
+        gst_video_color_matrix_from_iso (context->colorspace);
+  }
+
+  if (!gst_structure_has_field (in_s, "colorimetry")
+      || in_info->colorimetry.range == GST_VIDEO_COLOR_RANGE_UNKNOWN) {
+    if (context->color_range == AVCOL_RANGE_JPEG) {
+      out_info->colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
+    } else if (context->color_range == AVCOL_RANGE_MPEG) {
+      out_info->colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
+    } else {
+      out_info->colorimetry.range = GST_VIDEO_COLOR_RANGE_UNKNOWN;
+    }
   }
 
   /* try to find a good framerate */
@@ -1108,17 +1300,81 @@ gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec,
   /* calculate and update par now */
   gst_ffmpegviddec_update_par (ffmpegdec, in_info, out_info);
 
-  /* Copy stereo/multiview info from upstream if set */
-  if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) != GST_VIDEO_MULTIVIEW_MODE_NONE) {
-    GST_VIDEO_INFO_MULTIVIEW_MODE (out_info) =
-        GST_VIDEO_INFO_MULTIVIEW_MODE (in_info);
-    GST_VIDEO_INFO_MULTIVIEW_FLAGS (out_info) =
-        GST_VIDEO_INFO_MULTIVIEW_FLAGS (in_info);
+  GST_VIDEO_INFO_MULTIVIEW_MODE (out_info) = ffmpegdec->cur_multiview_mode;
+  GST_VIDEO_INFO_MULTIVIEW_FLAGS (out_info) = ffmpegdec->cur_multiview_flags;
+
+  /* To passing HDR information to caps directly */
+  if (output_state->caps == NULL) {
+    output_state->caps = gst_video_info_to_caps (out_info);
+  } else {
+    output_state->caps = gst_caps_make_writable (output_state->caps);
+  }
+
+  if (!gst_structure_has_field (in_s, "mastering-display-info")) {
+    AVFrameSideData *sd = av_frame_get_side_data (picture,
+        AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
+    GstVideoMasteringDisplayInfo minfo;
+
+    if (sd
+        && mastering_display_metadata_av_to_gst ((AVMasteringDisplayMetadata *)
+            sd->data, &minfo)) {
+      GST_LOG_OBJECT (ffmpegdec, "update mastering display info: "
+          "Red(%u, %u) "
+          "Green(%u, %u) "
+          "Blue(%u, %u) "
+          "White(%u, %u) "
+          "max_luminance(%u) "
+          "min_luminance(%u) ",
+          minfo.display_primaries[0].x, minfo.display_primaries[0].y,
+          minfo.display_primaries[1].x, minfo.display_primaries[1].y,
+          minfo.display_primaries[2].x, minfo.display_primaries[2].y,
+          minfo.white_point.x, minfo.white_point.y,
+          minfo.max_display_mastering_luminance,
+          minfo.min_display_mastering_luminance);
+
+      if (!gst_video_mastering_display_info_add_to_caps (&minfo,
+              output_state->caps)) {
+        GST_WARNING_OBJECT (ffmpegdec,
+            "Couldn't set mastering display info to caps");
+      }
+    }
+  }
+
+  if (!gst_structure_has_field (in_s, "content-light-level")) {
+    AVFrameSideData *sd = av_frame_get_side_data (picture,
+        AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
+    GstVideoContentLightLevel cll;
+
+    if (sd && content_light_metadata_av_to_gst ((AVContentLightMetadata *)
+            sd->data, &cll)) {
+      GST_LOG_OBJECT (ffmpegdec, "update content light level: "
+          "maxCLL:(%u), maxFALL:(%u)", cll.max_content_light_level,
+          cll.max_frame_average_light_level);
+
+      if (!gst_video_content_light_level_add_to_caps (&cll, output_state->caps)) {
+        GST_WARNING_OBJECT (ffmpegdec,
+            "Couldn't set content light level to caps");
+      }
+    }
   }
 
   if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (ffmpegdec)))
     goto negotiate_failed;
 
+  /* The decoder is configured, we now know the true latency */
+  if (fps_n) {
+    latency =
+        gst_util_uint64_scale_ceil (ffmpegdec->context->has_b_frames *
+        GST_SECOND, fps_d, fps_n);
+    if (ffmpegdec->context->thread_type & FF_THREAD_FRAME) {
+      latency +=
+          gst_util_uint64_scale_ceil (ffmpegdec->context->thread_count *
+          GST_SECOND, fps_d, fps_n);
+    }
+    gst_video_decoder_set_latency (GST_VIDEO_DECODER (ffmpegdec), latency,
+        latency);
+  }
+
   return TRUE;
 
   /* ERRORS */
@@ -1136,6 +1392,9 @@ negotiate_failed:
     ffmpegdec->pic_height = 0;
     ffmpegdec->pic_par_n = 0;
     ffmpegdec->pic_par_d = 0;
+    ffmpegdec->pic_interlaced = 0;
+    ffmpegdec->pic_field_order = 0;
+    ffmpegdec->pic_field_order_changed = FALSE;
     ffmpegdec->ctx_ticks = 0;
     ffmpegdec->ctx_time_n = 0;
     ffmpegdec->ctx_time_d = 0;
@@ -1172,29 +1431,36 @@ gst_ffmpegviddec_do_qos (GstFFMpegVidDec * ffmpegdec,
     *mode_switch = TRUE;
   }
 
+  if (*mode_switch == TRUE) {
+    /* We've already switched mode, we can return straight away
+     * without any further calculation */
+    return;
+  }
+
   diff =
       gst_video_decoder_get_max_decode_time (GST_VIDEO_DECODER (ffmpegdec),
       frame);
 
   /* if we don't have timing info, then we don't do QoS */
-  if (G_UNLIKELY (diff == G_MAXINT64))
+  if (G_UNLIKELY (diff == G_MAXINT64)) {
+    /* Ensure the skipping strategy is the default one */
+    ffmpegdec->context->skip_frame = ffmpegdec->skip_frame;
     return;
+  }
 
   GST_DEBUG_OBJECT (ffmpegdec, "decoding time %" G_GINT64_FORMAT, diff);
 
-  if (*mode_switch == FALSE) {
-    if (diff > 0 && ffmpegdec->context->skip_frame != AVDISCARD_DEFAULT) {
-      ffmpegdec->context->skip_frame = AVDISCARD_DEFAULT;
-      *mode_switch = TRUE;
-      GST_DEBUG_OBJECT (ffmpegdec, "QOS: normal mode");
-    }
+  if (diff > 0 && ffmpegdec->context->skip_frame != AVDISCARD_DEFAULT) {
+    ffmpegdec->context->skip_frame = AVDISCARD_DEFAULT;
+    *mode_switch = TRUE;
+    GST_DEBUG_OBJECT (ffmpegdec, "QOS: normal mode");
+  }
 
-    else if (diff <= 0 && ffmpegdec->context->skip_frame != AVDISCARD_NONREF) {
-      ffmpegdec->context->skip_frame = AVDISCARD_NONREF;
-      *mode_switch = TRUE;
-      GST_DEBUG_OBJECT (ffmpegdec,
-          "QOS: hurry up, diff %" G_GINT64_FORMAT " >= 0", diff);
-    }
+  else if (diff <= 0 && ffmpegdec->context->skip_frame != AVDISCARD_NONREF) {
+    ffmpegdec->context->skip_frame = AVDISCARD_NONREF;
+    *mode_switch = TRUE;
+    GST_DEBUG_OBJECT (ffmpegdec,
+        "QOS: hurry up, diff %" G_GINT64_FORMAT " >= 0", diff);
   }
 }
 
@@ -1203,10 +1469,10 @@ static GstFlowReturn
 get_output_buffer (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame)
 {
   GstFlowReturn ret = GST_FLOW_OK;
-  AVPicture pic, *outpic;
+  AVFrame pic, *outpic;
   GstVideoFrame vframe;
   GstVideoInfo *info;
-  gint c;
+  guint c;
 
   GST_LOG_OBJECT (ffmpegdec, "get output buffer");
 
@@ -1227,6 +1493,10 @@ get_output_buffer (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame)
           GST_MAP_READ | GST_MAP_WRITE))
     goto map_failed;
 
+  memset (&pic, 0, sizeof (pic));
+  pic.format = ffmpegdec->pic_pix_fmt;
+  pic.width = GST_VIDEO_FRAME_WIDTH (&vframe);
+  pic.height = GST_VIDEO_FRAME_HEIGHT (&vframe);
   for (c = 0; c < AV_NUM_DATA_POINTERS; c++) {
     if (c < GST_VIDEO_INFO_N_PLANES (info)) {
       pic.data[c] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, c);
@@ -1239,10 +1509,12 @@ get_output_buffer (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame)
     }
   }
 
-  outpic = (AVPicture *) ffmpegdec->picture;
+  outpic = ffmpegdec->picture;
 
-  av_picture_copy (&pic, outpic, ffmpegdec->context->pix_fmt,
-      GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
+  if (av_frame_copy (&pic, outpic) != 0) {
+    GST_ERROR_OBJECT (ffmpegdec, "Failed to copy output frame");
+    ret = GST_FLOW_ERROR;
+  }
 
   gst_video_frame_unmap (&vframe);
 
@@ -1281,28 +1553,18 @@ gst_avpacket_init (AVPacket * packet, guint8 * data, guint size)
   packet->size = size;
 }
 
-/* gst_ffmpegviddec_[video|audio]_frame:
- * ffmpegdec:
- * data: pointer to the data to decode
- * size: size of data in bytes
- * in_timestamp: incoming timestamp.
- * in_duration: incoming duration.
- * in_offset: incoming offset (frame number).
- * ret: Return flow.
- *
- * Returns: number of bytes used in decoding. The check for successful decode is
- *   outbuf being non-NULL.
+/*
+ * Returns: whether a frame was decoded
  */
-static gint
+static gboolean
 gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
-    guint8 * data, guint size, gint * have_data, GstVideoCodecFrame * frame,
-    GstFlowReturn * ret)
+    GstVideoCodecFrame * frame, GstFlowReturn * ret)
 {
-  gint len = -1;
+  gint res;
+  gboolean got_frame = FALSE;
   gboolean mode_switch;
   GstVideoCodecFrame *out_frame;
   GstFFMpegVidDecVideoFrame *out_dframe;
-  AVPacket packet;
   GstBufferPool *pool;
 
   *ret = GST_FLOW_OK;
@@ -1314,66 +1576,22 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
    * else we might skip a reference frame */
   gst_ffmpegviddec_do_qos (ffmpegdec, frame, &mode_switch);
 
-  if (ffmpegdec->is_realvideo && data != NULL) {
-    gint slice_count;
-    gint i;
+  res = avcodec_receive_frame (ffmpegdec->context, ffmpegdec->picture);
 
-    /* setup the slice table for realvideo */
-    if (ffmpegdec->context->slice_offset == NULL)
-      ffmpegdec->context->slice_offset = g_malloc (sizeof (guint32) * 1000);
-
-    slice_count = (*data++) + 1;
-    ffmpegdec->context->slice_count = slice_count;
-
-    for (i = 0; i < slice_count; i++) {
-      data += 4;
-      ffmpegdec->context->slice_offset[i] = GST_READ_UINT32_LE (data);
-      data += 4;
-    }
-  }
-
-  if (frame) {
-    /* save reference to the timing info */
-    ffmpegdec->context->reordered_opaque = (gint64) frame->system_frame_number;
-    ffmpegdec->picture->reordered_opaque = (gint64) frame->system_frame_number;
-
-    GST_DEBUG_OBJECT (ffmpegdec, "stored opaque values idx %d",
-        frame->system_frame_number);
-  }
-
-  /* now decode the frame */
-  gst_avpacket_init (&packet, data, size);
-
-  if (ffmpegdec->palette) {
-    guint8 *pal;
-
-    pal = av_packet_new_side_data (&packet, AV_PKT_DATA_PALETTE,
-        AVPALETTE_SIZE);
-    gst_buffer_extract (ffmpegdec->palette, 0, pal, AVPALETTE_SIZE);
-    GST_DEBUG_OBJECT (ffmpegdec, "copy pal %p %p", &packet, pal);
+  /* No frames available at this time */
+  if (res == AVERROR (EAGAIN))
+    goto beach;
+  else if (res == AVERROR_EOF) {
+    *ret = GST_FLOW_EOS;
+    GST_DEBUG_OBJECT (ffmpegdec, "Context was entirely flushed");
+    goto beach;
+  } else if (res < 0) {
+    *ret = GST_FLOW_OK;
+    GST_WARNING_OBJECT (ffmpegdec, "Legitimate decoding error");
+    goto beach;
   }
 
-  /* This might call into get_buffer() from another thread,
-   * which would cause a deadlock. Release the lock here
-   * and taking it again later seems safe
-   * See https://bugzilla.gnome.org/show_bug.cgi?id=726020
-   */
-  GST_VIDEO_DECODER_STREAM_UNLOCK (ffmpegdec);
-  len = avcodec_decode_video2 (ffmpegdec->context,
-      ffmpegdec->picture, have_data, &packet);
-  GST_VIDEO_DECODER_STREAM_LOCK (ffmpegdec);
-
-  GST_DEBUG_OBJECT (ffmpegdec, "after decode: len %d, have_data %d",
-      len, *have_data);
-
-  /* when we are in skip_frame mode, don't complain when ffmpeg returned
-   * no data because we told it to skip stuff. */
-  if (len < 0 && (mode_switch || ffmpegdec->context->skip_frame))
-    len = 0;
-
-  /* no data, we're done */
-  if (len < 0 || *have_data == 0)
-    goto beach;
+  got_frame = TRUE;
 
   /* get the output picture timing info again */
   out_dframe = ffmpegdec->picture->opaque;
@@ -1383,6 +1601,31 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
   gst_buffer_replace (&out_frame->output_buffer, out_dframe->buffer);
   gst_buffer_replace (&out_dframe->buffer, NULL);
 
+  /* Extract auxilliary info not stored in the main AVframe */
+  {
+    GstVideoInfo *in_info = &ffmpegdec->input_state->info;
+    /* Take multiview mode from upstream if present */
+    ffmpegdec->picture_multiview_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (in_info);
+    ffmpegdec->picture_multiview_flags =
+        GST_VIDEO_INFO_MULTIVIEW_FLAGS (in_info);
+
+    /* Otherwise, see if there's info in the frame */
+    if (ffmpegdec->picture_multiview_mode == GST_VIDEO_MULTIVIEW_MODE_NONE) {
+      AVFrameSideData *side_data =
+          av_frame_get_side_data (ffmpegdec->picture, AV_FRAME_DATA_STEREO3D);
+      if (side_data) {
+        AVStereo3D *stereo = (AVStereo3D *) side_data->data;
+        ffmpegdec->picture_multiview_mode = stereo_av_to_gst (stereo->type);
+        if (stereo->flags & AV_STEREO3D_FLAG_INVERT) {
+          ffmpegdec->picture_multiview_flags =
+              GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
+        } else {
+          ffmpegdec->picture_multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
+        }
+      }
+    }
+  }
+
   GST_DEBUG_OBJECT (ffmpegdec,
       "pts %" G_GUINT64_FORMAT " duration %" G_GUINT64_FORMAT,
       out_frame->pts, out_frame->duration);
@@ -1390,8 +1633,6 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
       (guint64) ffmpegdec->picture->pts);
   GST_DEBUG_OBJECT (ffmpegdec, "picture: num %d",
       ffmpegdec->picture->coded_picture_number);
-  GST_DEBUG_OBJECT (ffmpegdec, "picture: ref %d",
-      ffmpegdec->picture->reference);
   GST_DEBUG_OBJECT (ffmpegdec, "picture: display %d",
       ffmpegdec->picture->display_picture_number);
   GST_DEBUG_OBJECT (ffmpegdec, "picture: opaque %p",
@@ -1421,8 +1662,8 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
     GstVideoMeta *vmeta = gst_buffer_get_video_meta (out_frame->output_buffer);
     if (vmeta) {
       GstVideoInfo *info = &ffmpegdec->output_state->info;
-      g_assert (vmeta->width == GST_VIDEO_INFO_WIDTH (info));
-      g_assert (vmeta->height == GST_VIDEO_INFO_HEIGHT (info));
+      g_assert ((gint) vmeta->width == GST_VIDEO_INFO_WIDTH (info));
+      g_assert ((gint) vmeta->height == GST_VIDEO_INFO_HEIGHT (info));
     }
   }
 #endif
@@ -1446,6 +1687,30 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
           GST_VIDEO_BUFFER_FLAG_INTERLACED);
   }
 
+  {
+    AVFrameSideData *side_data =
+        av_frame_get_side_data (ffmpegdec->picture, AV_FRAME_DATA_A53_CC);
+    if (side_data) {
+      GST_LOG_OBJECT (ffmpegdec,
+          "Found CC side data of type AV_FRAME_DATA_A53_CC, size %d",
+          side_data->size);
+      GST_MEMDUMP ("A53 CC", side_data->data, side_data->size);
+
+      /* do not add closed caption meta if it already exists */
+      if (!gst_buffer_get_meta (out_frame->output_buffer,
+              GST_VIDEO_CAPTION_META_API_TYPE)) {
+        out_frame->output_buffer =
+            gst_buffer_make_writable (out_frame->output_buffer);
+        gst_buffer_add_video_caption_meta (out_frame->output_buffer,
+            GST_VIDEO_CAPTION_TYPE_CEA708_RAW, side_data->data,
+            side_data->size);
+      } else {
+        GST_LOG_OBJECT (ffmpegdec,
+            "Closed caption meta already exists: will not add new caption meta");
+      }
+    }
+  }
+
   /* cleaning time */
   /* so we decoded this frame, frames preceding it in decoding order
    * that still do not have a buffer allocated seem rather useless,
@@ -1490,16 +1755,15 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
       gst_video_decoder_finish_frame (GST_VIDEO_DECODER (ffmpegdec), out_frame);
 
 beach:
-  GST_DEBUG_OBJECT (ffmpegdec, "return flow %s, len %d",
-      gst_flow_get_name (*ret), len);
-  return len;
+  GST_DEBUG_OBJECT (ffmpegdec, "return flow %s, got frame: %d",
+      gst_flow_get_name (*ret), got_frame);
+  return got_frame;
 
   /* special cases */
 no_output:
   {
     GST_DEBUG_OBJECT (ffmpegdec, "no output buffer");
     gst_video_decoder_drop_frame (GST_VIDEO_DECODER (ffmpegdec), out_frame);
-    len = -1;
     goto beach;
   }
 
@@ -1516,49 +1780,22 @@ negotiation_error:
 }
 
 
-/* gst_ffmpegviddec_frame:
- * ffmpegdec:
- * data: pointer to the data to decode
- * size: size of data in bytes
- * got_data: 0 if no data was decoded, != 0 otherwise.
- * in_time: timestamp of data
- * in_duration: duration of data
- * ret: GstFlowReturn to return in the chain function
- *
- * Decode the given frame and pushes it downstream.
- *
- * Returns: Number of bytes used in decoding, -1 on error/failure.
- */
-
-static gint
-gst_ffmpegviddec_frame (GstFFMpegVidDec * ffmpegdec,
-    guint8 * data, guint size, gint * have_data, GstVideoCodecFrame * frame,
+ /* Returns: Whether a frame was decoded */
+static gboolean
+gst_ffmpegviddec_frame (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame,
     GstFlowReturn * ret)
 {
-  GstFFMpegVidDecClass *oclass;
-  gint len = 0;
+  gboolean got_frame = FALSE;
 
   if (G_UNLIKELY (ffmpegdec->context->codec == NULL))
     goto no_codec;
 
-  GST_LOG_OBJECT (ffmpegdec, "data:%p, size:%d", data, size);
-
   *ret = GST_FLOW_OK;
   ffmpegdec->context->frame_number++;
 
-  oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
-
-  len =
-      gst_ffmpegviddec_video_frame (ffmpegdec, data, size, have_data, frame,
-      ret);
+  got_frame = gst_ffmpegviddec_video_frame (ffmpegdec, frame, ret);
 
-  if (len < 0) {
-    GST_WARNING_OBJECT (ffmpegdec,
-        "avdec_%s: decoding error (len: %d, have_data: %d)",
-        oclass->in_plugin->name, len, *have_data);
-  }
-
-  return len;
+  return got_frame;
 
   /* ERRORS */
 no_codec:
@@ -1573,26 +1810,26 @@ static GstFlowReturn
 gst_ffmpegviddec_drain (GstVideoDecoder * decoder)
 {
   GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder;
-  GstFFMpegVidDecClass *oclass;
+  GstFlowReturn ret;
+  gboolean got_frame = FALSE;
 
   if (!ffmpegdec->opened)
     return GST_FLOW_OK;
 
-  oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
-
-  if (oclass->in_plugin->capabilities & CODEC_CAP_DELAY) {
-    gint have_data, len;
-    GstFlowReturn ret;
+  if (avcodec_send_packet (ffmpegdec->context, NULL))
+    goto send_packet_failed;
 
-    GST_LOG_OBJECT (ffmpegdec,
-        "codec has delay capabilities, calling until ffmpeg has drained everything");
-
-    do {
-      len = gst_ffmpegviddec_frame (ffmpegdec, NULL, 0, &have_data, NULL, &ret);
-    } while (len >= 0 && have_data == 1 && ret == GST_FLOW_OK);
-  }
+  do {
+    got_frame = gst_ffmpegviddec_frame (ffmpegdec, NULL, &ret);
+  } while (got_frame && ret == GST_FLOW_OK);
+  avcodec_flush_buffers (ffmpegdec->context);
 
+done:
   return GST_FLOW_OK;
+
+send_packet_failed:
+  GST_WARNING_OBJECT (ffmpegdec, "send packet failed, could not drain decoder");
+  goto done;
 }
 
 static GstFlowReturn
@@ -1600,11 +1837,12 @@ gst_ffmpegviddec_handle_frame (GstVideoDecoder * decoder,
     GstVideoCodecFrame * frame)
 {
   GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder;
-  guint8 *data, *bdata;
-  gint size, len, have_data, bsize;
+  guint8 *data;
+  gint size;
+  gboolean got_frame;
   GstMapInfo minfo;
   GstFlowReturn ret = GST_FLOW_OK;
-  gboolean do_padding;
+  AVPacket packet;
 
   GST_LOG_OBJECT (ffmpegdec,
       "Received new data of size %" G_GSIZE_FORMAT ", dts %" GST_TIME_FORMAT
@@ -1622,94 +1860,82 @@ gst_ffmpegviddec_handle_frame (GstVideoDecoder * decoder,
   GST_VIDEO_CODEC_FRAME_FLAG_SET (frame,
       GST_VIDEO_CODEC_FRAME_FLAG_DECODE_ONLY);
 
-  bdata = minfo.data;
-  bsize = minfo.size;
+  data = minfo.data;
+  size = minfo.size;
 
-  if (bsize > 0 && (!GST_MEMORY_IS_ZERO_PADDED (minfo.memory)
-          || (minfo.maxsize - minfo.size) < FF_INPUT_BUFFER_PADDING_SIZE)) {
+  if (size > 0 && (!GST_MEMORY_IS_ZERO_PADDED (minfo.memory)
+          || (minfo.maxsize - minfo.size) < AV_INPUT_BUFFER_PADDING_SIZE)) {
     /* add padding */
-    if (ffmpegdec->padded_size < bsize + FF_INPUT_BUFFER_PADDING_SIZE) {
-      ffmpegdec->padded_size = bsize + FF_INPUT_BUFFER_PADDING_SIZE;
+    if (ffmpegdec->padded_size < size + AV_INPUT_BUFFER_PADDING_SIZE) {
+      ffmpegdec->padded_size = size + AV_INPUT_BUFFER_PADDING_SIZE;
       ffmpegdec->padded = g_realloc (ffmpegdec->padded, ffmpegdec->padded_size);
       GST_LOG_OBJECT (ffmpegdec, "resized padding buffer to %d",
           ffmpegdec->padded_size);
     }
     GST_CAT_TRACE_OBJECT (GST_CAT_PERFORMANCE, ffmpegdec,
         "Copy input to add padding");
-    memcpy (ffmpegdec->padded, bdata, bsize);
-    memset (ffmpegdec->padded + bsize, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+    memcpy (ffmpegdec->padded, data, size);
+    memset (ffmpegdec->padded + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
 
-    bdata = ffmpegdec->padded;
-    do_padding = TRUE;
-  } else {
-    do_padding = FALSE;
+    data = ffmpegdec->padded;
   }
 
-  do {
-    guint8 tmp_padding[FF_INPUT_BUFFER_PADDING_SIZE];
-
-    /* parse, if at all possible */
-    data = bdata;
-    size = bsize;
-
-    if (do_padding) {
-      /* add temporary padding */
-      GST_CAT_TRACE_OBJECT (GST_CAT_PERFORMANCE, ffmpegdec,
-          "Add temporary input padding");
-      memcpy (tmp_padding, data + size, FF_INPUT_BUFFER_PADDING_SIZE);
-      memset (data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
-    }
-
-    /* decode a frame of audio/video now */
-    len =
-        gst_ffmpegviddec_frame (ffmpegdec, data, size, &have_data, frame, &ret);
+  /* now decode the frame */
+  gst_avpacket_init (&packet, data, size);
 
-    if (ret != GST_FLOW_OK) {
-      GST_LOG_OBJECT (ffmpegdec, "breaking because of flow ret %s",
-          gst_flow_get_name (ret));
-      /* bad flow return, make sure we discard all data and exit */
-      bsize = 0;
-      break;
-    }
+  if (ffmpegdec->palette) {
+    guint8 *pal;
 
-    if (do_padding) {
-      memcpy (data + size, tmp_padding, FF_INPUT_BUFFER_PADDING_SIZE);
-    }
+    pal = av_packet_new_side_data (&packet, AV_PKT_DATA_PALETTE,
+        AVPALETTE_SIZE);
+    gst_buffer_extract (ffmpegdec->palette, 0, pal, AVPALETTE_SIZE);
+    GST_DEBUG_OBJECT (ffmpegdec, "copy pal %p %p", &packet, pal);
+  }
 
-    if (len == 0 && have_data == 0) {
-      /* nothing was decoded, this could be because no data was available or
-       * because we were skipping frames.
-       * If we have no context we must exit and wait for more data, we keep the
-       * data we tried. */
-      GST_LOG_OBJECT (ffmpegdec, "Decoding didn't return any data, breaking");
-      break;
-    }
+  if (!packet.size)
+    goto done;
 
-    if (len < 0) {
-      /* a decoding error happened, we must break and try again with next data. */
-      GST_LOG_OBJECT (ffmpegdec, "Decoding error, breaking");
-      bsize = 0;
-      break;
-    }
+  /* save reference to the timing info */
+  ffmpegdec->context->reordered_opaque = (gint64) frame->system_frame_number;
+  ffmpegdec->picture->reordered_opaque = (gint64) frame->system_frame_number;
 
-    /* prepare for the next round, for codecs with a context we did this
-     * already when using the parser. */
-    bsize -= len;
-    bdata += len;
+  GST_DEBUG_OBJECT (ffmpegdec, "stored opaque values idx %d",
+      frame->system_frame_number);
 
-    do_padding = TRUE;
+  /* This might call into get_buffer() from another thread,
+   * which would cause a deadlock. Release the lock here
+   * and taking it again later seems safe
+   * See https://bugzilla.gnome.org/show_bug.cgi?id=726020
+   */
+  GST_VIDEO_DECODER_STREAM_UNLOCK (ffmpegdec);
+  if (avcodec_send_packet (ffmpegdec->context, &packet) < 0) {
+    GST_VIDEO_DECODER_STREAM_LOCK (ffmpegdec);
+    goto send_packet_failed;
+  }
+  GST_VIDEO_DECODER_STREAM_LOCK (ffmpegdec);
 
-    GST_LOG_OBJECT (ffmpegdec, "Before (while bsize>0).  bsize:%d , bdata:%p",
-        bsize, bdata);
-  } while (bsize > 0);
+  do {
+    /* decode a frame of audio/video now */
+    got_frame = gst_ffmpegviddec_frame (ffmpegdec, frame, &ret);
 
-  if (bsize > 0)
-    GST_DEBUG_OBJECT (ffmpegdec, "Dropping %d bytes of data", bsize);
+    if (ret != GST_FLOW_OK) {
+      GST_LOG_OBJECT (ffmpegdec, "breaking because of flow ret %s",
+          gst_flow_get_name (ret));
+      break;
+    }
+  } while (got_frame);
 
+done:
   gst_buffer_unmap (frame->input_buffer, &minfo);
   gst_video_codec_frame_unref (frame);
 
   return ret;
+
+send_packet_failed:
+  {
+    GST_WARNING_OBJECT (ffmpegdec, "Failed to send data for decoding");
+    goto done;
+  }
 }
 
 static gboolean
@@ -1760,6 +1986,9 @@ gst_ffmpegviddec_stop (GstVideoDecoder * decoder)
   ffmpegdec->pic_height = 0;
   ffmpegdec->pic_par_n = 0;
   ffmpegdec->pic_par_d = 0;
+  ffmpegdec->pic_interlaced = 0;
+  ffmpegdec->pic_field_order = 0;
+  ffmpegdec->pic_field_order_changed = FALSE;
   ffmpegdec->ctx_ticks = 0;
   ffmpegdec->ctx_time_n = 0;
   ffmpegdec->ctx_time_d = 0;
@@ -1873,7 +2102,7 @@ gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
       if (ret == GST_FLOW_OK) {
         GstVideoMeta *vmeta = gst_buffer_get_video_meta (tmp);
         gboolean same_stride = TRUE;
-        gint i;
+        guint i;
 
         for (i = 0; i < vmeta->n_planes; i++) {
           if (vmeta->stride[i] != ffmpegdec->stride[i]) {
@@ -1951,7 +2180,7 @@ gst_ffmpegviddec_propose_allocation (GstVideoDecoder * decoder,
   gst_allocation_params_init (&params);
   params.flags = GST_MEMORY_FLAG_ZERO_PADDED;
   params.align = DEFAULT_STRIDE_ALIGN;
-  params.padding = FF_INPUT_BUFFER_PADDING_SIZE;
+  params.padding = AV_INPUT_BUFFER_PADDING_SIZE;
   /* we would like to have some padding so that we don't have to
    * memcpy. We don't suggest an allocator. */
   gst_query_add_allocation_param (query, NULL, &params);
@@ -1987,6 +2216,9 @@ gst_ffmpegviddec_set_property (GObject * object,
     case PROP_OUTPUT_CORRUPT:
       ffmpegdec->output_corrupt = g_value_get_boolean (value);
       break;
+    case PROP_THREAD_TYPE:
+      ffmpegdec->thread_type = g_value_get_flags (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2018,6 +2250,9 @@ gst_ffmpegviddec_get_property (GObject * object,
     case PROP_OUTPUT_CORRUPT:
       g_value_set_boolean (value, ffmpegdec->output_corrupt);
       break;
+    case PROP_THREAD_TYPE:
+      g_value_set_flags (value, ffmpegdec->thread_type);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2041,28 +2276,37 @@ gst_ffmpegviddec_register (GstPlugin * plugin)
   GType type;
   AVCodec *in_plugin;
   gint rank;
-
-  in_plugin = av_codec_next (NULL);
+  void *i = 0;
 
   GST_LOG ("Registering decoders");
 
-  while (in_plugin) {
+  while ((in_plugin = (AVCodec *) av_codec_iterate (&i))) {
     gchar *type_name;
     gchar *plugin_name;
 
     /* only video decoders */
     if (!av_codec_is_decoder (in_plugin)
         || in_plugin->type != AVMEDIA_TYPE_VIDEO)
-      goto next;
+      continue;
 
-    /* no quasi-codecs, please */
+    /* no quasi codecs, please */
     if (in_plugin->id == AV_CODEC_ID_RAWVIDEO ||
         in_plugin->id == AV_CODEC_ID_V210 ||
         in_plugin->id == AV_CODEC_ID_V210X ||
-        in_plugin->id == AV_CODEC_ID_R210 ||
-        (in_plugin->id >= AV_CODEC_ID_PCM_S16LE &&
-            in_plugin->id <= AV_CODEC_ID_PCM_BLURAY)) {
-      goto next;
+        in_plugin->id == AV_CODEC_ID_V308 ||
+        in_plugin->id == AV_CODEC_ID_V408 ||
+        in_plugin->id == AV_CODEC_ID_V410 ||
+        in_plugin->id == AV_CODEC_ID_R210
+        || in_plugin->id == AV_CODEC_ID_AYUV
+        || in_plugin->id == AV_CODEC_ID_Y41P
+        || in_plugin->id == AV_CODEC_ID_012V
+        || in_plugin->id == AV_CODEC_ID_YUV4
+#if AV_VERSION_INT (LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR, LIBAVCODEC_VERSION_MICRO) >= \
+        AV_VERSION_INT (57,4,0)
+        || in_plugin->id == AV_CODEC_ID_WRAPPED_AVFRAME
+#endif
+        || in_plugin->id == AV_CODEC_ID_ZLIB) {
+      continue;
     }
 
     /* No decoders depending on external libraries (we don't build them, but
@@ -2072,7 +2316,23 @@ gst_ffmpegviddec_register (GstPlugin * plugin)
       GST_DEBUG
           ("Not using external library decoder %s. Use the gstreamer-native ones instead.",
           in_plugin->name);
-      goto next;
+      continue;
+    }
+
+    /* Skip hardware or hybrid (hardware with software fallback) */
+    if ((in_plugin->capabilities & AV_CODEC_CAP_HARDWARE) ==
+        AV_CODEC_CAP_HARDWARE) {
+      GST_DEBUG
+          ("Ignoring hardware decoder %s. We can't handle this outside of ffmpeg",
+          in_plugin->name);
+      continue;
+    }
+
+    if ((in_plugin->capabilities & AV_CODEC_CAP_HYBRID) == AV_CODEC_CAP_HYBRID) {
+      GST_DEBUG
+          ("Ignoring hybrid decoder %s. We can't handle this outside of ffmpeg",
+          in_plugin->name);
+      continue;
     }
 
     /* No vdpau plugins until we can figure out how to properly use them
@@ -2081,14 +2341,28 @@ gst_ffmpegviddec_register (GstPlugin * plugin)
       GST_DEBUG
           ("Ignoring VDPAU decoder %s. We can't handle this outside of ffmpeg",
           in_plugin->name);
-      goto next;
+      continue;
     }
 
     if (g_str_has_suffix (in_plugin->name, "_xvmc")) {
       GST_DEBUG
           ("Ignoring XVMC decoder %s. We can't handle this outside of ffmpeg",
           in_plugin->name);
-      goto next;
+      continue;
+    }
+
+    if (strstr (in_plugin->name, "vaapi")) {
+      GST_DEBUG
+          ("Ignoring VAAPI decoder %s. We can't handle this outside of ffmpeg",
+          in_plugin->name);
+      continue;
+    }
+
+    if (g_str_has_suffix (in_plugin->name, "_qsv")) {
+      GST_DEBUG
+          ("Ignoring qsv decoder %s. We can't handle this outside of ffmpeg",
+          in_plugin->name);
+      continue;
     }
 
     GST_DEBUG ("Trying plugin %s [%s]", in_plugin->name, in_plugin->long_name);
@@ -2098,17 +2372,18 @@ gst_ffmpegviddec_register (GstPlugin * plugin)
     /* MP1 : Use MP3 for decoding */
     /* MP2 : Use MP3 for decoding */
     /* Theora: Use libtheora based theoradec */
-    if (!strcmp (in_plugin->name, "gif") ||
-        !strcmp (in_plugin->name, "theora") ||
+    /* CDG: use cdgdec */
+    if (!strcmp (in_plugin->name, "theora") ||
         !strcmp (in_plugin->name, "mpeg1video") ||
         strstr (in_plugin->name, "crystalhd") != NULL ||
         !strcmp (in_plugin->name, "ass") ||
         !strcmp (in_plugin->name, "srt") ||
         !strcmp (in_plugin->name, "pgssub") ||
         !strcmp (in_plugin->name, "dvdsub") ||
-        !strcmp (in_plugin->name, "dvbsub")) {
+        !strcmp (in_plugin->name, "dvbsub") ||
+        !strcmp (in_plugin->name, "cdgraphics")) {
       GST_LOG ("Ignoring decoder %s", in_plugin->name);
-      goto next;
+      continue;
     }
 
     /* construct the type */
@@ -2136,6 +2411,8 @@ gst_ffmpegviddec_register (GstPlugin * plugin)
      * msmpeg4v3 same, as it outperforms divxdec for divx3 playback.
      * VC1/WMV3 are not working and thus unpreferred for now. */
     switch (in_plugin->id) {
+      case AV_CODEC_ID_MPEG1VIDEO:
+      case AV_CODEC_ID_MPEG2VIDEO:
       case AV_CODEC_ID_MPEG4:
       case AV_CODEC_ID_MSMPEG4V3:
       case AV_CODEC_ID_H264:
@@ -2164,9 +2441,6 @@ gst_ffmpegviddec_register (GstPlugin * plugin)
     }
 
     g_free (type_name);
-
-  next:
-    in_plugin = av_codec_next (in_plugin);
   }
 
   GST_LOG ("Finished Registering decoders");