gstavviddec: Only allow a single closed caption meta
[platform/upstream/gstreamer.git] / ext / libav / gstavviddec.c
index 55ec594..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
 
@@ -48,6 +44,9 @@ GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
 #define DEFAULT_MAX_THREADS            0
 #define DEFAULT_OUTPUT_CORRUPT         TRUE
 #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
 {
@@ -58,6 +57,7 @@ enum
   PROP_DEBUG_MV,
   PROP_MAX_THREADS,
   PROP_OUTPUT_CORRUPT,
+  PROP_THREAD_TYPE,
   PROP_LAST
 };
 
@@ -85,14 +85,14 @@ static void gst_ffmpegviddec_get_property (GObject * object,
     guint prop_id, GValue * value, GParamSpec * pspec);
 
 static gboolean gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec,
-    AVCodecContext * context, AVFrame * picture, gboolean force);
+    AVCodecContext * context, AVFrame * picture);
 
 /* some sort of bufferpool handling, but different */
 static int gst_ffmpegviddec_get_buffer2 (AVCodecContext * context,
     AVFrame * picture, int flags);
 
 static GstFlowReturn gst_ffmpegviddec_finish (GstVideoDecoder * decoder);
-static void gst_ffmpegviddec_drain (GstFFMpegVidDec * ffmpegdec);
+static GstFlowReturn gst_ffmpegviddec_drain (GstVideoDecoder * decoder);
 
 static gboolean picture_changed (GstFFMpegVidDec * ffmpegdec,
     AVFrame * picture);
@@ -146,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)
 {
@@ -192,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;
 }
 
@@ -233,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;
@@ -247,9 +276,15 @@ gst_ffmpegviddec_class_init (GstFFMpegVidDecClass * klass)
   viddec_class->stop = gst_ffmpegviddec_stop;
   viddec_class->flush = gst_ffmpegviddec_flush;
   viddec_class->finish = gst_ffmpegviddec_finish;
-  viddec_class->drain = gst_ffmpegviddec_finish;        /* drain and finish are the same to us */
+  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
@@ -268,6 +303,11 @@ 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
+      (ffmpegdec), TRUE);
 
   gst_video_decoder_set_needs_format (GST_VIDEO_DECODER (ffmpegdec), TRUE);
 }
@@ -305,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));
 
@@ -325,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) {
@@ -345,7 +381,7 @@ static gboolean
 gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec)
 {
   GstFFMpegVidDecClass *oclass;
-  gint i;
+  guint i;
 
   oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
 
@@ -356,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;
 
@@ -435,7 +458,7 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder,
   /* close old session */
   if (ffmpegdec->opened) {
     GST_OBJECT_UNLOCK (ffmpegdec);
-    gst_ffmpegviddec_drain (ffmpegdec);
+    gst_ffmpegviddec_finish (decoder);
     GST_OBJECT_LOCK (ffmpegdec);
     if (!gst_ffmpegviddec_close (ffmpegdec, TRUE)) {
       GST_OBJECT_UNLOCK (ffmpegdec);
@@ -446,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
@@ -497,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
@@ -524,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... */
@@ -539,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;
@@ -608,6 +647,137 @@ dummy_free_buffer (void *opaque, uint8_t * data)
   gst_ffmpegviddec_video_frame_free (frame->ffmpegdec, frame);
 }
 
+/* This function prepares the pool configuration for direct rendering. To use
+ * this method, the codec should support direct rendering and the pool should
+ * support video meta and video alignment */
+static void
+gst_ffmpegvideodec_prepare_dr_pool (GstFFMpegVidDec * ffmpegdec,
+    GstBufferPool * pool, GstVideoInfo * info, GstStructure * config)
+{
+  GstAllocationParams params;
+  GstVideoAlignment align;
+  GstAllocator *allocator = NULL;
+  gint width, height;
+  gint linesize_align[4];
+  gint i;
+  gsize max_align;
+
+  width = GST_VIDEO_INFO_WIDTH (info);
+  height = GST_VIDEO_INFO_HEIGHT (info);
+
+  /* let ffmpeg find the alignment and padding */
+  avcodec_align_dimensions2 (ffmpegdec->context, &width, &height,
+      linesize_align);
+
+  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++;
+
+  gst_buffer_pool_config_get_allocator (config, &allocator, &params);
+
+  max_align = DEFAULT_STRIDE_ALIGN;
+  max_align |= params.align;
+
+  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
+    if (linesize_align[i] > 0)
+      max_align |= linesize_align[i] - 1;
+  }
+
+  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
+    align.stride_align[i] = max_align;
+
+  params.align = max_align;
+
+  gst_buffer_pool_config_set_allocator (config, allocator, &params);
+
+  GST_DEBUG_OBJECT (ffmpegdec, "aligned dimension %dx%d -> %dx%d "
+      "padding t:%u l:%u r:%u b:%u, stride_align %d:%d:%d:%d",
+      GST_VIDEO_INFO_WIDTH (info),
+      GST_VIDEO_INFO_HEIGHT (info), width, height, align.padding_top,
+      align.padding_left, align.padding_right, align.padding_bottom,
+      align.stride_align[0], align.stride_align[1], align.stride_align[2],
+      align.stride_align[3]);
+
+  gst_buffer_pool_config_add_option (config,
+      GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
+  gst_buffer_pool_config_set_video_alignment (config, &align);
+}
+
+static void
+gst_ffmpegviddec_ensure_internal_pool (GstFFMpegVidDec * ffmpegdec,
+    AVFrame * picture)
+{
+  GstAllocationParams params = DEFAULT_ALLOC_PARAM;
+  GstVideoInfo info;
+  GstVideoFormat format;
+  GstCaps *caps;
+  GstStructure *config;
+  guint i;
+
+  if (ffmpegdec->internal_pool != NULL &&
+      ffmpegdec->pool_width == picture->width &&
+      ffmpegdec->pool_height == picture->height &&
+      ffmpegdec->pool_format == picture->format)
+    return;
+
+  GST_DEBUG_OBJECT (ffmpegdec, "Updating internal pool (%i, %i)",
+      picture->width, picture->height);
+
+  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;
+
+  if (ffmpegdec->internal_pool)
+    gst_object_unref (ffmpegdec->internal_pool);
+
+  ffmpegdec->internal_pool = gst_video_buffer_pool_new ();
+  config = gst_buffer_pool_get_config (ffmpegdec->internal_pool);
+
+  caps = gst_video_info_to_caps (&info);
+  gst_buffer_pool_config_set_params (config, caps, info.size, 2, 0);
+  gst_buffer_pool_config_set_allocator (config, NULL, &params);
+  gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
+
+  gst_ffmpegvideodec_prepare_dr_pool (ffmpegdec,
+      ffmpegdec->internal_pool, &info, config);
+  /* generic video pool never fails */
+  gst_buffer_pool_set_config (ffmpegdec->internal_pool, config);
+  gst_caps_unref (caps);
+
+  gst_buffer_pool_set_active (ffmpegdec->internal_pool, TRUE);
+
+  /* Remember pool size so we can detect changes */
+  ffmpegdec->pool_width = picture->width;
+  ffmpegdec->pool_height = picture->height;
+  ffmpegdec->pool_format = picture->format;
+  ffmpegdec->pool_info = info;
+}
+
+static gboolean
+gst_ffmpegviddec_can_direct_render (GstFFMpegVidDec * ffmpegdec)
+{
+  GstFFMpegVidDecClass *oclass;
+
+  if (!ffmpegdec->direct_rendering)
+    return FALSE;
+
+  oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
+  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
  * into. We try to give it memory from our pool */
 static int
@@ -617,9 +787,9 @@ gst_ffmpegviddec_get_buffer2 (AVCodecContext * context, AVFrame * picture,
   GstVideoCodecFrame *frame;
   GstFFMpegVidDecVideoFrame *dframe;
   GstFFMpegVidDec *ffmpegdec;
-  gint c;
-  GstVideoInfo *info;
+  guint c;
   GstFlowReturn ret;
+  int create_buffer_flags = 0;
 
   ffmpegdec = (GstFFMpegVidDec *) context->opaque;
 
@@ -656,29 +826,13 @@ gst_ffmpegviddec_get_buffer2 (AVCodecContext * context, AVFrame * picture,
 
   GST_DEBUG_OBJECT (ffmpegdec, "storing opaque %p", dframe);
 
-  /* If the picture format changed but we already negotiated before,
-   * we will have to do fallback allocation until output and input
-   * formats are in sync again. We will renegotiate on the output
-   */
-  if (ffmpegdec->pic_width != 0 && picture_changed (ffmpegdec, picture))
-    goto fallback;
-
-  /* see if we need renegotiation */
-  /* Disabled for now as ffmpeg doesn't give us the output dimensions
-   * in the getbuffer2 callback. The real dimensions are only available
-   * when a buffer is produced.
-   */
-  if (FALSE
-      && G_UNLIKELY (!gst_ffmpegviddec_negotiate (ffmpegdec, context, picture,
-              FALSE)))
-    goto negotiate_failed;
-
-  if (!ffmpegdec->current_dr)
+  if (!gst_ffmpegviddec_can_direct_render (ffmpegdec))
     goto no_dr;
 
-  ret =
-      gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (ffmpegdec),
-      frame);
+  gst_ffmpegviddec_ensure_internal_pool (ffmpegdec, picture);
+
+  ret = gst_buffer_pool_acquire_buffer (ffmpegdec->internal_pool,
+      &frame->output_buffer, NULL);
   if (ret != GST_FLOW_OK)
     goto alloc_failed;
 
@@ -690,41 +844,25 @@ gst_ffmpegviddec_get_buffer2 (AVCodecContext * context, AVFrame * picture,
   gst_buffer_replace (&frame->output_buffer, NULL);
 
   /* Fill avpicture */
-  info = &ffmpegdec->output_state->info;
-  if (!gst_video_frame_map (&dframe->vframe, info, dframe->buffer,
-          GST_MAP_READWRITE))
-    goto invalid_frame;
+  if (!gst_video_frame_map (&dframe->vframe, &ffmpegdec->pool_info,
+          dframe->buffer, GST_MAP_READWRITE))
+    goto map_failed;
   dframe->mapped = TRUE;
 
   for (c = 0; c < AV_NUM_DATA_POINTERS; c++) {
-    if (c < GST_VIDEO_INFO_N_PLANES (info)) {
+    if (c < GST_VIDEO_INFO_N_PLANES (&ffmpegdec->pool_info)) {
       picture->data[c] = GST_VIDEO_FRAME_PLANE_DATA (&dframe->vframe, c);
       picture->linesize[c] = GST_VIDEO_FRAME_PLANE_STRIDE (&dframe->vframe, c);
 
-      /* libav does not allow stride changes currently, fall back to
-       * non-direct rendering here:
+      if (ffmpegdec->stride[c] == -1)
+        ffmpegdec->stride[c] = picture->linesize[c];
+
+      /* libav does not allow stride changes, decide allocation should check
+       * before replacing the internal pool with a downstream pool.
        * https://bugzilla.gnome.org/show_bug.cgi?id=704769
        * https://bugzilla.libav.org/show_bug.cgi?id=556
        */
-      if (ffmpegdec->stride[c] == -1) {
-        ffmpegdec->stride[c] = picture->linesize[c];
-      } else if (picture->linesize[c] != ffmpegdec->stride[c]) {
-        GST_LOG_OBJECT (ffmpegdec,
-            "No direct rendering, stride changed c=%d %d->%d", c,
-            ffmpegdec->stride[c], picture->linesize[c]);
-
-        for (c = 0; c < AV_NUM_DATA_POINTERS; c++) {
-          picture->data[c] = NULL;
-          picture->linesize[c] = 0;
-          av_buffer_unref (&picture->buf[c]);
-        }
-        gst_video_frame_unmap (&dframe->vframe);
-        dframe->mapped = FALSE;
-        gst_buffer_replace (&dframe->buffer, NULL);
-        ffmpegdec->current_dr = FALSE;
-
-        goto no_dr;
-      }
+      g_assert (picture->linesize[c] == ffmpegdec->stride[c]);
     } else {
       picture->data[c] = NULL;
       picture->linesize[c] = 0;
@@ -733,45 +871,28 @@ 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, tranfer 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);
 
   return 0;
 
-  /* fallbacks */
-negotiate_failed:
-  {
-    GST_DEBUG_OBJECT (ffmpegdec, "negotiate failed");
-    goto fallback;
-  }
 no_dr:
   {
-    GST_LOG_OBJECT (ffmpegdec, "direct rendering disabled, fallback alloc");
-    goto fallback;
-  }
-alloc_failed:
-  {
-    /* alloc default buffer when we can't get one from downstream */
-    GST_LOG_OBJECT (ffmpegdec, "alloc failed, fallback alloc");
-    goto fallback;
-  }
-invalid_frame:
-  {
-    /* alloc default buffer when we can't get one from downstream */
-    GST_LOG_OBJECT (ffmpegdec, "failed to map frame, fallback alloc");
-    gst_buffer_replace (&dframe->buffer, NULL);
-    goto fallback;
-  }
-fallback:
-  {
     int c;
     int ret = avcodec_default_get_buffer2 (context, picture, flags);
 
+    GST_LOG_OBJECT (ffmpegdec, "direct rendering disabled, fallback alloc");
+
     for (c = 0; c < AV_NUM_DATA_POINTERS; c++) {
       ffmpegdec->stride[c] = picture->linesize[c];
     }
@@ -791,6 +912,21 @@ fallback:
 
     return ret;
   }
+alloc_failed:
+  {
+    GST_ELEMENT_ERROR (ffmpegdec, RESOURCE, FAILED,
+        ("Unable to allocate memory"),
+        ("The downstream pool failed to allocated buffer."));
+    return -1;
+  }
+map_failed:
+  {
+    GST_ELEMENT_ERROR (ffmpegdec, RESOURCE, OPEN_READ_WRITE,
+        ("Cannot access memory for read and write operation."),
+        ("The video memory allocated from downstream pool could not mapped for"
+            "read and write."));
+    return -1;
+  }
 duplicate_frame:
   {
     GST_WARNING_OBJECT (ffmpegdec, "already alloc'ed output buffer for frame");
@@ -807,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
@@ -825,9 +973,18 @@ context_changed (GstFFMpegVidDec * ffmpegdec, AVCodecContext * context)
 
 static gboolean
 update_video_context (GstFFMpegVidDec * ffmpegdec, AVCodecContext * context,
-    AVFrame * picture, gboolean force)
+    AVFrame * picture)
 {
-  if (!force && !picture_changed (ffmpegdec, 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;
 
@@ -847,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;
@@ -929,16 +1102,88 @@ 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, gboolean force)
+    AVCodecContext * context, AVFrame * picture)
 {
   GstVideoFormat fmt;
   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, force))
+  if (!update_video_context (ffmpegdec, context, picture))
     return TRUE;
 
   fmt = gst_ffmpeg_pixfmt_to_videoformat (ffmpegdec->pic_pix_fmt);
@@ -956,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 */
@@ -1008,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 */
@@ -1036,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;
@@ -1072,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);
   }
 }
 
@@ -1103,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");
 
@@ -1125,24 +1491,30 @@ get_output_buffer (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame)
   info = &ffmpegdec->output_state->info;
   if (!gst_video_frame_map (&vframe, info, frame->output_buffer,
           GST_MAP_READ | GST_MAP_WRITE))
-    goto alloc_failed;
+    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);
       pic.linesize[c] = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, c);
+      GST_LOG_OBJECT (ffmpegdec, "[%i] linesize %d, data %p", c,
+          pic.linesize[c], pic.data[c]);
     } else {
       pic.data[c] = NULL;
       pic.linesize[c] = 0;
     }
-    GST_LOG_OBJECT (ffmpegdec, "linesize %d, data %p", pic.linesize[c],
-        pic.data[c]);
   }
 
-  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);
 
@@ -1153,7 +1525,17 @@ get_output_buffer (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame)
   /* special cases */
 alloc_failed:
   {
-    GST_DEBUG_OBJECT (ffmpegdec, "allocation failed");
+    GST_ELEMENT_ERROR (ffmpegdec, RESOURCE, FAILED,
+        ("Unable to allocate memory"),
+        ("The downstream pool failed to allocated buffer."));
+    return ret;
+  }
+map_failed:
+  {
+    GST_ELEMENT_ERROR (ffmpegdec, RESOURCE, OPEN_READ_WRITE,
+        ("Cannot access memory for read and write operation."),
+        ("The video memory allocated from downstream pool could not mapped for"
+            "read and write."));
     return ret;
   }
 not_negotiated:
@@ -1171,28 +1553,19 @@ 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;
 
@@ -1203,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;
-
-    /* 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;
-    }
-  }
+  res = avcodec_receive_frame (ffmpegdec->context, ffmpegdec->picture);
 
-  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);
+  /* 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;
   }
 
-  /* 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);
-  }
-
-  /* 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;
@@ -1272,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);
@@ -1279,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",
@@ -1293,11 +1645,29 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
       ! !(ffmpegdec->picture->flags & AV_FRAME_FLAG_CORRUPT));
 
   if (!gst_ffmpegviddec_negotiate (ffmpegdec, ffmpegdec->context,
-          ffmpegdec->picture, FALSE))
+          ffmpegdec->picture))
     goto negotiation_error;
 
-  if (G_UNLIKELY (out_frame->output_buffer == NULL))
+  pool = gst_video_decoder_get_buffer_pool (GST_VIDEO_DECODER (ffmpegdec));
+  if (G_UNLIKELY (out_frame->output_buffer == NULL)) {
+    *ret = get_output_buffer (ffmpegdec, out_frame);
+  } else if (G_UNLIKELY (out_frame->output_buffer->pool != pool)) {
+    GstBuffer *tmp = out_frame->output_buffer;
+    out_frame->output_buffer = NULL;
     *ret = get_output_buffer (ffmpegdec, out_frame);
+    gst_buffer_unref (tmp);
+  }
+#ifndef G_DISABLE_ASSERT
+  else {
+    GstVideoMeta *vmeta = gst_buffer_get_video_meta (out_frame->output_buffer);
+    if (vmeta) {
+      GstVideoInfo *info = &ffmpegdec->output_state->info;
+      g_assert ((gint) vmeta->width == GST_VIDEO_INFO_WIDTH (info));
+      g_assert ((gint) vmeta->height == GST_VIDEO_INFO_HEIGHT (info));
+    }
+  }
+#endif
+  gst_object_unref (pool);
 
   if (G_UNLIKELY (*ret != GST_FLOW_OK))
     goto no_output;
@@ -1317,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,
@@ -1357,21 +1751,19 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
   /* FIXME: Ideally we would remap the buffer read-only now before pushing but
    * libav might still have a reference to it!
    */
-
   *ret =
       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;
   }
 
@@ -1388,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:
@@ -1441,28 +1806,30 @@ no_codec:
   }
 }
 
-static void
-gst_ffmpegviddec_drain (GstFFMpegVidDec * ffmpegdec)
+static GstFlowReturn
+gst_ffmpegviddec_drain (GstVideoDecoder * decoder)
 {
-  GstFFMpegVidDecClass *oclass;
+  GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder;
+  GstFlowReturn ret;
+  gboolean got_frame = FALSE;
 
   if (!ffmpegdec->opened)
-    return;
+    return GST_FLOW_OK;
 
-  oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
+  if (avcodec_send_packet (ffmpegdec->context, NULL))
+    goto send_packet_failed;
 
-  if (oclass->in_plugin->capabilities & CODEC_CAP_DELAY) {
-    gint have_data, len;
-    GstFlowReturn ret;
+  do {
+    got_frame = gst_ffmpegviddec_frame (ffmpegdec, NULL, &ret);
+  } while (got_frame && ret == GST_FLOW_OK);
+  avcodec_flush_buffers (ffmpegdec->context);
 
-    GST_LOG_OBJECT (ffmpegdec,
-        "codec has delay capabilities, calling until ffmpeg has drained everything");
+done:
+  return GST_FLOW_OK;
 
-    do {
-      len = gst_ffmpegviddec_frame (ffmpegdec, NULL, 0, &have_data, NULL, &ret);
-    } while (len >= 0 && have_data == 1 && ret == GST_FLOW_OK);
-    avcodec_flush_buffers (ffmpegdec->context);
-  }
+send_packet_failed:
+  GST_WARNING_OBJECT (ffmpegdec, "send packet failed, could not drain decoder");
+  goto done;
 }
 
 static GstFlowReturn
@@ -1470,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
@@ -1492,96 +1860,83 @@ 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
 gst_ffmpegviddec_start (GstVideoDecoder * decoder)
@@ -1622,24 +1977,36 @@ gst_ffmpegviddec_stop (GstVideoDecoder * decoder)
     gst_video_codec_state_unref (ffmpegdec->output_state);
   ffmpegdec->output_state = NULL;
 
+  if (ffmpegdec->internal_pool)
+    gst_object_unref (ffmpegdec->internal_pool);
+  ffmpegdec->internal_pool = NULL;
+
   ffmpegdec->pic_pix_fmt = 0;
   ffmpegdec->pic_width = 0;
   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->pool_width = 0;
+  ffmpegdec->pool_height = 0;
+  ffmpegdec->pool_format = 0;
+
   return TRUE;
 }
 
 static GstFlowReturn
 gst_ffmpegviddec_finish (GstVideoDecoder * decoder)
 {
-  GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder;
-
-  gst_ffmpegviddec_drain (ffmpegdec);
+  gst_ffmpegviddec_drain (decoder);
+  /* note that finish can and should clean up more drastically,
+   * but drain is also invoked on e.g. packet loss in GAP handling */
+  gst_ffmpegviddec_flush (decoder);
 
   return GST_FLOW_OK;
 }
@@ -1649,8 +2016,10 @@ gst_ffmpegviddec_flush (GstVideoDecoder * decoder)
 {
   GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder;
 
-  if (ffmpegdec->opened)
+  if (ffmpegdec->opened) {
+    GST_LOG_OBJECT (decoder, "flushing buffers");
     avcodec_flush_buffers (ffmpegdec->context);
+  }
 
   return TRUE;
 }
@@ -1663,9 +2032,11 @@ gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
   GstBufferPool *pool;
   guint size, min, max;
   GstStructure *config;
-  gboolean have_videometa, have_alignment, update_pool = FALSE;
+  gboolean have_pool, have_videometa, have_alignment, update_pool = FALSE;
   GstAllocator *allocator = NULL;
-  GstAllocationParams params = { 0, 15, 0, 0, };
+  GstAllocationParams params = DEFAULT_ALLOC_PARAM;
+
+  have_pool = (gst_query_get_n_allocation_pools (query) != 0);
 
   if (!GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (decoder,
           query))
@@ -1675,7 +2046,7 @@ gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
 
   if (gst_query_get_n_allocation_params (query) > 0) {
     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
-    params.align = MAX (params.align, 15);
+    params.align = MAX (params.align, DEFAULT_STRIDE_ALIGN);
   } else {
     gst_query_add_allocation_param (query, allocator, &params);
   }
@@ -1689,6 +2060,7 @@ gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
     pool = gst_video_buffer_pool_new ();
     max = 0;
     update_pool = TRUE;
+    have_pool = FALSE;
 
     /* if there is an allocator, also drop it, as it might be the reason we
      * have this limit. Default will be used */
@@ -1700,12 +2072,11 @@ gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
 
   config = gst_buffer_pool_get_config (pool);
   gst_buffer_pool_config_set_params (config, state->caps, size, min, max);
-  /* we are happy with the default allocator but we would like to have 16 bytes
-   * aligned and padded memory */
   gst_buffer_pool_config_set_allocator (config, allocator, &params);
 
   have_videometa =
       gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
+
   if (have_videometa)
     gst_buffer_pool_config_add_option (config,
         GST_BUFFER_POOL_OPTION_VIDEO_META);
@@ -1713,78 +2084,82 @@ gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
   have_alignment =
       gst_buffer_pool_has_option (pool, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
 
-  /* we can only enable the alignment if downstream supports the
-   * videometa api */
-  if (have_alignment && have_videometa) {
-    GstVideoAlignment align;
-    gint width, height;
-    gint linesize_align[4];
-    gint i;
-    guint edge;
-
-    width = GST_VIDEO_INFO_WIDTH (&state->info);
-    height = GST_VIDEO_INFO_HEIGHT (&state->info);
-    /* let ffmpeg find the alignment and padding */
-    avcodec_align_dimensions2 (ffmpegdec->context, &width, &height,
-        linesize_align);
-    edge =
-        ffmpegdec->
-        context->flags & CODEC_FLAG_EMU_EDGE ? 0 : 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 (&state->info) - edge;
-    align.padding_bottom = height - GST_VIDEO_INFO_HEIGHT (&state->info) - edge;
-
-    /* add extra padding to match libav buffer allocation sizes */
-    align.padding_bottom++;
-
-    for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
-      align.stride_align[i] =
-          (linesize_align[i] > 0 ? linesize_align[i] - 1 : 0);
-
-    GST_DEBUG_OBJECT (ffmpegdec, "aligned dimension %dx%d -> %dx%d "
-        "padding t:%u l:%u r:%u b:%u, stride_align %d:%d:%d:%d",
-        GST_VIDEO_INFO_WIDTH (&state->info),
-        GST_VIDEO_INFO_HEIGHT (&state->info), width, height, align.padding_top,
-        align.padding_left, align.padding_right, align.padding_bottom,
-        align.stride_align[0], align.stride_align[1], align.stride_align[2],
-        align.stride_align[3]);
+  /* If we have videometa, we never have to copy */
+  if (have_videometa && have_pool && have_alignment &&
+      gst_ffmpegviddec_can_direct_render (ffmpegdec)) {
+    GstStructure *config_copy = gst_structure_copy (config);
+
+    gst_ffmpegvideodec_prepare_dr_pool (ffmpegdec, pool, &state->info,
+        config_copy);
+
+    /* FIXME validate and retry */
+    if (gst_buffer_pool_set_config (pool, config_copy)) {
+      GstFlowReturn ret;
+      GstBuffer *tmp;
+
+      gst_buffer_pool_set_active (pool, TRUE);
+      ret = gst_buffer_pool_acquire_buffer (pool, &tmp, NULL);
+      if (ret == GST_FLOW_OK) {
+        GstVideoMeta *vmeta = gst_buffer_get_video_meta (tmp);
+        gboolean same_stride = TRUE;
+        guint i;
+
+        for (i = 0; i < vmeta->n_planes; i++) {
+          if (vmeta->stride[i] != ffmpegdec->stride[i]) {
+            same_stride = FALSE;
+            break;
+          }
+        }
 
-    gst_buffer_pool_config_add_option (config,
-        GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
-    gst_buffer_pool_config_set_video_alignment (config, &align);
+        gst_buffer_unref (tmp);
 
-    if (ffmpegdec->direct_rendering) {
-      GstFFMpegVidDecClass *oclass;
+        if (same_stride) {
+          if (ffmpegdec->internal_pool)
+            gst_object_unref (ffmpegdec->internal_pool);
+          ffmpegdec->internal_pool = gst_object_ref (pool);
+          ffmpegdec->pool_info = state->info;
+          gst_structure_free (config);
+          goto done;
+        }
+      }
+    }
+  }
 
-      GST_DEBUG_OBJECT (ffmpegdec, "trying to enable direct rendering");
+  if (have_videometa && ffmpegdec->internal_pool
+      && ffmpegdec->pool_width == state->info.width
+      && ffmpegdec->pool_height == state->info.height) {
+    update_pool = TRUE;
+    gst_object_unref (pool);
+    pool = gst_object_ref (ffmpegdec->internal_pool);
+    gst_structure_free (config);
+    goto done;
+  }
 
-      oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
+  /* configure */
+  if (!gst_buffer_pool_set_config (pool, config)) {
+    gboolean working_pool = FALSE;
+    config = gst_buffer_pool_get_config (pool);
 
-      if (oclass->in_plugin->capabilities & CODEC_CAP_DR1) {
-        GST_DEBUG_OBJECT (ffmpegdec, "enabled direct rendering");
-        ffmpegdec->current_dr = TRUE;
-      } else {
-        GST_DEBUG_OBJECT (ffmpegdec, "direct rendering not supported");
-      }
+    if (gst_buffer_pool_config_validate_params (config, state->caps, size, min,
+            max)) {
+      working_pool = gst_buffer_pool_set_config (pool, config);
+    } else {
+      gst_structure_free (config);
     }
-  } else {
-    GST_DEBUG_OBJECT (ffmpegdec,
-        "alignment or videometa not supported, disable direct rendering");
 
-    /* disable direct rendering. This will make us use the fallback ffmpeg
-     * picture allocation code with padding etc. We will then do the final
-     * copy (with cropping) into a buffer from our pool */
-    ffmpegdec->current_dr = FALSE;
+    if (!working_pool) {
+      gst_object_unref (pool);
+      pool = gst_video_buffer_pool_new ();
+      config = gst_buffer_pool_get_config (pool);
+      gst_buffer_pool_config_set_params (config, state->caps, size, min, max);
+      gst_buffer_pool_config_set_allocator (config, NULL, &params);
+      gst_buffer_pool_set_config (pool, config);
+      update_pool = TRUE;
+    }
   }
 
+done:
   /* and store */
-  gst_buffer_pool_set_config (pool, config);
-
   if (update_pool)
     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
 
@@ -1804,8 +2179,8 @@ gst_ffmpegviddec_propose_allocation (GstVideoDecoder * decoder,
 
   gst_allocation_params_init (&params);
   params.flags = GST_MEMORY_FLAG_ZERO_PADDED;
-  params.align = 15;
-  params.padding = FF_INPUT_BUFFER_PADDING_SIZE;
+  params.align = DEFAULT_STRIDE_ALIGN;
+  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);
@@ -1841,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;
@@ -1872,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;
@@ -1895,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
@@ -1926,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
@@ -1935,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);
@@ -1952,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 */
@@ -1990,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:
@@ -2018,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");