vaapidecode: plug a memory leak.
[platform/upstream/gstreamer-vaapi.git] / gst / vaapi / gstvaapidecode.c
index 68cc58f..da422e2 100644 (file)
@@ -2,7 +2,9 @@
  *  gstvaapidecode.c - VA-API video decoder
  *
  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
- *  Copyright (C) 2011-2013 Intel Corporation
+ *    Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
+ *  Copyright (C) 2011-2014 Intel Corporation
+ *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public License
 
 #include "gst/vaapi/sysdeps.h"
 #include <gst/vaapi/gstvaapidisplay.h>
-#include <gst/video/videocontext.h>
 
 #include "gstvaapidecode.h"
 #include "gstvaapipluginutil.h"
 #include "gstvaapivideobuffer.h"
+#if GST_CHECK_VERSION(1,1,0) && USE_GLX
+#include "gstvaapivideometa_texture.h"
+#endif
 #if GST_CHECK_VERSION(1,0,0)
 #include "gstvaapivideobufferpool.h"
 #include "gstvaapivideomemory.h"
 #include <gst/vaapi/gstvaapidecoder_mpeg2.h>
 #include <gst/vaapi/gstvaapidecoder_mpeg4.h>
 #include <gst/vaapi/gstvaapidecoder_vc1.h>
+#include <gst/vaapi/gstvaapidecoder_vp8.h>
 
 #define GST_PLUGIN_NAME "vaapidecode"
 #define GST_PLUGIN_DESC "A VA-API based video decoder"
 
+#define GST_VAAPI_DECODE_FLOW_PARSE_DATA        GST_FLOW_CUSTOM_SUCCESS_2
+
 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapidecode);
 #define GST_CAT_DEFAULT gst_debug_vaapidecode
 
@@ -63,11 +70,20 @@ static const char gst_vaapidecode_sink_caps_str[] =
     GST_CAPS_CODEC("video/x-h263")
     GST_CAPS_CODEC("video/x-h264")
     GST_CAPS_CODEC("video/x-wmv")
+    GST_CAPS_CODEC("video/x-vp8")
     GST_CAPS_CODEC("image/jpeg")
     ;
 
 static const char gst_vaapidecode_src_caps_str[] =
+#if GST_CHECK_VERSION(1,1,0)
+    GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
+        GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, NV12, I420, YV12 }") ";"
+    GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
+        GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA") ";"
+    GST_VIDEO_CAPS_MAKE("{ NV12, I420, YV12 }");
+#else
     GST_VAAPI_SURFACE_CAPS;
+#endif
 
 static GstStaticPadTemplate gst_vaapidecode_sink_factory =
     GST_STATIC_PAD_TEMPLATE(
@@ -83,50 +99,11 @@ static GstStaticPadTemplate gst_vaapidecode_src_factory =
         GST_PAD_ALWAYS,
         GST_STATIC_CAPS(gst_vaapidecode_src_caps_str));
 
-/* GstImplementsInterface interface */
-#if !GST_CHECK_VERSION(1,0,0)
-static gboolean
-gst_vaapidecode_implements_interface_supported(
-    GstImplementsInterface *iface,
-    GType                   type
-)
-{
-    return (type == GST_TYPE_VIDEO_CONTEXT);
-}
-
-static void
-gst_vaapidecode_implements_iface_init(GstImplementsInterfaceClass *iface)
-{
-    iface->supported = gst_vaapidecode_implements_interface_supported;
-}
-#endif
-
-/* GstVideoContext interface */
-static void
-gst_vaapidecode_set_video_context(GstVideoContext *context, const gchar *type,
-    const GValue *value)
-{
-    GstVaapiDecode *decode = GST_VAAPIDECODE (context);
-    gst_vaapi_set_display (type, value, &decode->display);
-}
-
-static void
-gst_video_context_interface_init(GstVideoContextInterface *iface)
-{
-    iface->set_context = gst_vaapidecode_set_video_context;
-}
-
-#define GstVideoContextClass GstVideoContextInterface
 G_DEFINE_TYPE_WITH_CODE(
     GstVaapiDecode,
     gst_vaapidecode,
     GST_TYPE_VIDEO_DECODER,
-#if !GST_CHECK_VERSION(1,0,0)
-    G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
-                          gst_vaapidecode_implements_iface_init);
-#endif
-    G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
-                          gst_video_context_interface_init))
+    GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES)
 
 static gboolean
 gst_vaapidecode_update_src_caps(GstVaapiDecode *decode,
@@ -137,10 +114,12 @@ gst_vaapi_decoder_state_changed(GstVaapiDecoder *decoder,
     const GstVideoCodecState *codec_state, gpointer user_data)
 {
     GstVaapiDecode * const decode = GST_VAAPIDECODE(user_data);
+    GstVideoDecoder * const vdec = GST_VIDEO_DECODER(decode);
 
     g_assert(decode->decoder == decoder);
 
     gst_vaapidecode_update_src_caps(decode, codec_state);
+    gst_video_decoder_negotiate(vdec);
 }
 
 static inline gboolean
@@ -157,6 +136,14 @@ gst_vaapidecode_update_src_caps(GstVaapiDecode *decode,
     GstVideoDecoder * const vdec = GST_VIDEO_DECODER(decode);
     GstVideoCodecState *state;
     GstVideoInfo *vi, vis;
+#if GST_CHECK_VERSION(1,1,0)
+    GstCapsFeatures *features = NULL;
+    GstVaapiCapsFeature feature;
+
+    feature = gst_vaapi_find_preferred_caps_feature(
+        GST_VIDEO_DECODER_SRC_PAD(vdec),
+        GST_VIDEO_INFO_FORMAT(&ref_state->info));
+#endif
 
     state = gst_video_decoder_set_output_state(vdec,
         GST_VIDEO_INFO_FORMAT(&ref_state->info),
@@ -174,6 +161,35 @@ gst_vaapidecode_update_src_caps(GstVaapiDecode *decode,
     }
     gst_video_codec_state_unref(state);
 
+#if GST_CHECK_VERSION(1,1,0)
+    vis = *vi;
+    switch (feature) {
+    case GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META:
+        gst_video_info_set_format(&vis, GST_VIDEO_FORMAT_RGBA,
+            GST_VIDEO_INFO_WIDTH(vi), GST_VIDEO_INFO_HEIGHT(vi));
+        features = gst_caps_features_new(
+            GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, NULL);
+        break;
+    default:
+        if (GST_VIDEO_INFO_FORMAT(vi) == GST_VIDEO_FORMAT_ENCODED) {
+            /* XXX: this is a workaround until auto-plugging is fixed when
+            format=ENCODED + memory:VASurface caps feature are provided.
+            Meanwhile, providing a random format here works but this is
+            a terribly wrong thing per se. */
+            gst_video_info_set_format(&vis, GST_VIDEO_FORMAT_NV12,
+                GST_VIDEO_INFO_WIDTH(vi), GST_VIDEO_INFO_HEIGHT(vi));
+#if GST_CHECK_VERSION(1,3,0)
+            if (feature == GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE)
+                features = gst_caps_features_new(
+                    GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, NULL);
+#endif
+        }
+        break;
+    }
+    state->caps = gst_video_info_to_caps(&vis);
+    if (features)
+        gst_caps_set_features(state->caps, 0, features);
+#else
     /* XXX: gst_video_info_to_caps() from GStreamer 0.10 does not
        reconstruct suitable caps for "encoded" video formats */
     state->caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
@@ -189,11 +205,8 @@ gst_vaapidecode_update_src_caps(GstVaapiDecode *decode,
         "pixel-aspect-ratio", GST_TYPE_FRACTION, vi->par_n, vi->par_d,
         NULL);
 
-    if (GST_VIDEO_INFO_IS_INTERLACED(vi)) {
-        GstStructure * const structure =
-            gst_caps_get_structure(state->caps, 0);
-        gst_structure_set_interlaced(structure, TRUE);
-    }
+    gst_caps_set_interlaced(state->caps, vi);
+#endif
     gst_caps_replace(&decode->srcpad_caps, state->caps);
     return TRUE;
 }
@@ -212,45 +225,36 @@ gst_vaapidecode_decode_frame(GstVideoDecoder *vdec, GstVideoCodecFrame *frame)
     GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
     GstVaapiDecoderStatus status;
     GstFlowReturn ret;
-    gint64 end_time;
-
-    if (decode->render_time_base)
-        end_time = decode->render_time_base;
-    else
-        end_time = g_get_monotonic_time();
-    end_time += GST_TIME_AS_USECONDS(decode->last_buffer_time);
-    end_time += G_TIME_SPAN_SECOND;
 
     /* Decode current frame */
     for (;;) {
         status = gst_vaapi_decoder_decode(decode->decoder, frame);
         if (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE) {
-            gboolean was_signalled;
             GST_VIDEO_DECODER_STREAM_UNLOCK(vdec);
             g_mutex_lock(&decode->decoder_mutex);
-            was_signalled = g_cond_wait_until(
-                &decode->decoder_ready,
-                &decode->decoder_mutex,
-                end_time
-            );
+            g_cond_wait(&decode->decoder_ready, &decode->decoder_mutex);
             g_mutex_unlock(&decode->decoder_mutex);
             GST_VIDEO_DECODER_STREAM_LOCK(vdec);
-            if (was_signalled)
-                continue;
-            goto error_decode_timeout;
+            if (decode->decoder_loop_status < 0)
+                goto error_decode_loop;
+            continue;
         }
         if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
             goto error_decode;
         break;
     }
-    return GST_FLOW_OK;
+
+    /* Try to report back early any error that occured in the decode task */
+    GST_VIDEO_DECODER_STREAM_UNLOCK(vdec);
+    GST_VIDEO_DECODER_STREAM_LOCK(vdec);
+    return decode->decoder_loop_status;
 
     /* ERRORS */
-error_decode_timeout:
+error_decode_loop:
     {
-        GST_WARNING("decode timeout. Decoder required a VA surface but none "
-                    "got available within one second");
-        return GST_FLOW_EOS;
+        GST_ERROR("decode loop error %d", decode->decoder_loop_status);
+        gst_video_decoder_drop_frame(vdec, frame);
+        return decode->decoder_loop_status;
     }
 error_decode:
     {
@@ -262,7 +266,7 @@ error_decode:
             ret = GST_FLOW_NOT_SUPPORTED;
             break;
         default:
-            ret = GST_FLOW_EOS;
+            ret = GST_FLOW_ERROR;
             break;
         }
         gst_video_decoder_drop_frame(vdec, frame);
@@ -271,12 +275,11 @@ error_decode:
 }
 
 static GstFlowReturn
-gst_vaapidecode_push_decoded_frame(GstVideoDecoder *vdec)
+gst_vaapidecode_push_decoded_frame(GstVideoDecoder *vdec,
+    GstVideoCodecFrame *out_frame)
 {
     GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
     GstVaapiSurfaceProxy *proxy;
-    GstVaapiDecoderStatus status;
-    GstVideoCodecFrame *out_frame;
     GstFlowReturn ret;
 #if GST_CHECK_VERSION(1,0,0)
     const GstVaapiRectangle *crop_rect;
@@ -284,11 +287,6 @@ gst_vaapidecode_push_decoded_frame(GstVideoDecoder *vdec)
     guint flags;
 #endif
 
-    status = gst_vaapi_decoder_get_frame_with_timeout(decode->decoder,
-        &out_frame, 100000);
-    if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
-        return GST_VIDEO_DECODER_FLOW_NEED_DATA;
-
     if (!GST_VIDEO_CODEC_FRAME_IS_DECODE_ONLY(out_frame)) {
         proxy = gst_video_codec_frame_get_user_data(out_frame);
 
@@ -328,6 +326,11 @@ gst_vaapidecode_push_decoded_frame(GstVideoDecoder *vdec)
                 crop_meta->height = crop_rect->height;
             }
         }
+
+#if GST_CHECK_VERSION(1,1,0) && USE_GLX
+        if (decode->has_texture_upload_meta)
+            gst_buffer_ensure_texture_upload_meta(out_frame->output_buffer);
+#endif
 #else
         out_frame->output_buffer =
             gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
@@ -340,22 +343,6 @@ gst_vaapidecode_push_decoded_frame(GstVideoDecoder *vdec)
     if (ret != GST_FLOW_OK)
         goto error_commit_buffer;
 
-    /* Estimate when this frame would no longer be needed for rendering */
-    if (GST_CLOCK_TIME_IS_VALID(out_frame->pts)) {
-        if (!decode->render_time_base)
-            decode->render_time_base = g_get_monotonic_time() -
-                GST_TIME_AS_USECONDS(out_frame->pts);
-        decode->last_buffer_time = out_frame->pts;
-        if (GST_CLOCK_TIME_IS_VALID(out_frame->duration))
-            decode->last_buffer_time += out_frame->duration;
-        else
-            decode->last_buffer_time += GST_SECOND;
-    }
-    else {
-        decode->render_time_base = 0;
-        decode->last_buffer_time = 0;
-    }
-
     gst_video_codec_frame_unref(out_frame);
     return GST_FLOW_OK;
 
@@ -370,7 +357,7 @@ error_create_buffer:
                   GST_VAAPI_ID_ARGS(surface_id));
         gst_video_decoder_drop_frame(vdec, out_frame);
         gst_video_codec_frame_unref(out_frame);
-        return GST_FLOW_EOS;
+        return GST_FLOW_ERROR;
     }
 #if GST_CHECK_VERSION(1,0,0)
 error_get_meta:
@@ -378,7 +365,7 @@ error_get_meta:
         GST_ERROR("failed to get vaapi video meta attached to video buffer");
         gst_video_decoder_drop_frame(vdec, out_frame);
         gst_video_codec_frame_unref(out_frame);
-        return GST_FLOW_EOS;
+        return GST_FLOW_ERROR;
     }
 #endif
 error_commit_buffer:
@@ -386,7 +373,7 @@ error_commit_buffer:
         if (ret != GST_FLOW_FLUSHING)
             GST_ERROR("video sink rejected the video buffer (error %d)", ret);
         gst_video_codec_frame_unref(out_frame);
-        return GST_FLOW_EOS;
+        return ret;
     }
 }
 
@@ -407,9 +394,28 @@ static void
 gst_vaapidecode_decode_loop(GstVaapiDecode *decode)
 {
     GstVideoDecoder * const vdec = GST_VIDEO_DECODER(decode);
+    GstVaapiDecoderStatus status;
+    GstVideoCodecFrame *out_frame;
     GstFlowReturn ret;
 
-    ret = gst_vaapidecode_push_decoded_frame(vdec);
+    status = gst_vaapi_decoder_get_frame_with_timeout(decode->decoder,
+        &out_frame, 100000);
+
+    GST_VIDEO_DECODER_STREAM_LOCK(vdec);
+    switch (status) {
+    case GST_VAAPI_DECODER_STATUS_SUCCESS:
+        ret = gst_vaapidecode_push_decoded_frame(vdec, out_frame);
+        break;
+    case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA:
+        ret = GST_VIDEO_DECODER_FLOW_NEED_DATA;
+        break;
+    default:
+        ret = GST_FLOW_ERROR;
+        break;
+    }
+    decode->decoder_loop_status = ret;
+    GST_VIDEO_DECODER_STREAM_UNLOCK(vdec);
+
     if (ret == GST_FLOW_OK)
         return;
 
@@ -425,23 +431,43 @@ gst_vaapidecode_decode_loop(GstVaapiDecode *decode)
 
     /* Suspend the task if an error occurred */
     if (ret != GST_VIDEO_DECODER_FLOW_NEED_DATA)
-        gst_pad_pause_task(decode->srcpad);
+        gst_pad_pause_task(GST_VAAPI_PLUGIN_BASE_SRC_PAD(decode));
 }
 
-static GstFlowReturn
-gst_vaapidecode_finish(GstVideoDecoder *vdec)
+static gboolean
+gst_vaapidecode_flush(GstVideoDecoder *vdec)
 {
     GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
     GstVaapiDecoderStatus status;
 
     /* If there is something in GstVideoDecoder's output adapter, then
        submit the frame for decoding */
-    if (decode->current_frame_size)
+    if (decode->current_frame_size) {
         gst_video_decoder_have_frame(vdec);
+        decode->current_frame_size = 0;
+    }
 
     status = gst_vaapi_decoder_flush(decode->decoder);
     if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
         goto error_flush;
+    return TRUE;
+
+    /* ERRORS */
+error_flush:
+    {
+        GST_ERROR("failed to flush decoder (status %d)", status);
+        return FALSE;
+    }
+}
+
+static GstFlowReturn
+gst_vaapidecode_finish(GstVideoDecoder *vdec)
+{
+    GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
+    GstFlowReturn ret = GST_FLOW_OK;
+
+    if (!gst_vaapidecode_flush(vdec))
+        ret = GST_FLOW_OK;
 
     /* Make sure the decode loop function has a chance to return, thus
        possibly unlocking gst_video_decoder_finish_frame() */
@@ -450,16 +476,9 @@ gst_vaapidecode_finish(GstVideoDecoder *vdec)
     decode->decoder_finish = TRUE;
     g_cond_wait(&decode->decoder_finish_done, &decode->decoder_mutex);
     g_mutex_unlock(&decode->decoder_mutex);
-    gst_pad_stop_task(decode->srcpad);
+    gst_pad_stop_task(GST_VAAPI_PLUGIN_BASE_SRC_PAD(decode));
     GST_VIDEO_DECODER_STREAM_LOCK(vdec);
-    return GST_FLOW_OK;
-
-    /* ERRORS */
-error_flush:
-    {
-        GST_ERROR("failed to flush decoder (status %d)", status);
-        return GST_FLOW_EOS;
-    }
+    return ret;
 }
 
 #if GST_CHECK_VERSION(1,0,0)
@@ -473,19 +492,45 @@ gst_vaapidecode_decide_allocation(GstVideoDecoder *vdec, GstQuery *query)
     GstVideoInfo vi;
     guint size, min, max;
     gboolean need_pool, update_pool;
+    gboolean has_video_meta = FALSE;
+    GstVideoCodecState *state;
+#if GST_CHECK_VERSION(1,1,0) && USE_GLX
+    GstCapsFeatures *features, *features2;
+#endif
 
     gst_query_parse_allocation(query, &caps, &need_pool);
 
     if (!caps)
         goto error_no_caps;
 
+    state = gst_video_decoder_get_output_state(vdec);
+
+    decode->has_texture_upload_meta = FALSE;
+    has_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
+#if GST_CHECK_VERSION(1,1,0) && USE_GLX
+    if (has_video_meta)
+        decode->has_texture_upload_meta = gst_query_find_allocation_meta(query,
+            GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL);
+
+    features = gst_caps_get_features(state->caps, 0);
+    features2 = gst_caps_features_new(GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, NULL);
+
+    /* Update src caps if feature is not handled downstream */
+    if (!decode->has_texture_upload_meta &&
+        gst_caps_features_is_equal(features, features2))
+        gst_vaapidecode_update_src_caps (decode, state);
+    gst_caps_features_free(features2);
+#endif
+
+    gst_video_codec_state_unref(state);
+
     gst_video_info_init(&vi);
     gst_video_info_from_caps(&vi, caps);
     if (GST_VIDEO_INFO_FORMAT(&vi) == GST_VIDEO_FORMAT_ENCODED)
         gst_video_info_set_format(&vi, GST_VIDEO_FORMAT_NV12,
             GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
 
-    g_return_val_if_fail(decode->display != NULL, FALSE);
+    g_return_val_if_fail(GST_VAAPI_PLUGIN_BASE_DISPLAY(decode) != NULL, FALSE);
 
     if (gst_query_get_n_allocation_pools(query) > 0) {
         gst_query_parse_nth_allocation_pool(query, 0, &pool, &size, &min, &max);
@@ -503,7 +548,10 @@ gst_vaapidecode_decide_allocation(GstVideoDecoder *vdec, GstQuery *query)
             GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META)) {
         GST_INFO("no pool or doesn't support GstVaapiVideoMeta, "
             "making new pool");
-        pool = gst_vaapi_video_buffer_pool_new(decode->display);
+        if (pool)
+            gst_object_unref(pool);
+        pool = gst_vaapi_video_buffer_pool_new(
+            GST_VAAPI_PLUGIN_BASE_DISPLAY(decode));
         if (!pool)
             goto error_create_pool;
 
@@ -514,6 +562,18 @@ gst_vaapidecode_decide_allocation(GstVideoDecoder *vdec, GstQuery *query)
         gst_buffer_pool_set_config(pool, config);
     }
 
+    if (has_video_meta) {
+        config = gst_buffer_pool_get_config(pool);
+        gst_buffer_pool_config_add_option(config,
+            GST_BUFFER_POOL_OPTION_VIDEO_META);
+#if GST_CHECK_VERSION(1,1,0) && USE_GLX
+        if (decode->has_texture_upload_meta)
+            gst_buffer_pool_config_add_option(config,
+                GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META);
+#endif
+        gst_buffer_pool_set_config(pool, config);
+    }
+
     if (update_pool)
         gst_query_set_nth_allocation_pool(query, 0, pool, size, min, max);
     else
@@ -539,8 +599,7 @@ error_create_pool:
 static inline gboolean
 gst_vaapidecode_ensure_display(GstVaapiDecode *decode)
 {
-    return gst_vaapi_ensure_display(decode, GST_VAAPI_DISPLAY_TYPE_ANY,
-        &decode->display);
+    return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(decode));
 }
 
 static inline guint
@@ -556,7 +615,7 @@ gst_vaapidecode_create(GstVaapiDecode *decode, GstCaps *caps)
 
     if (!gst_vaapidecode_ensure_display(decode))
         return FALSE;
-    dpy = decode->display;
+    dpy = GST_VAAPI_PLUGIN_BASE_DISPLAY(decode);
 
     switch (gst_vaapi_codec_from_caps(caps)) {
     case GST_VAAPI_CODEC_MPEG2:
@@ -578,6 +637,11 @@ gst_vaapidecode_create(GstVaapiDecode *decode, GstCaps *caps)
         decode->decoder = gst_vaapi_decoder_jpeg_new(dpy, caps);
         break;
 #endif
+#if USE_VP8_DECODER
+    case GST_VAAPI_CODEC_VP8:
+        decode->decoder = gst_vaapi_decoder_vp8_new(dpy, caps);
+        break;
+#endif
     default:
         decode->decoder = NULL;
         break;
@@ -589,14 +653,14 @@ gst_vaapidecode_create(GstVaapiDecode *decode, GstCaps *caps)
         gst_vaapi_decoder_state_changed, decode);
 
     decode->decoder_caps = gst_caps_ref(caps);
-    return gst_pad_start_task(decode->srcpad,
+    return gst_pad_start_task(GST_VAAPI_PLUGIN_BASE_SRC_PAD(decode),
         (GstTaskFunction)gst_vaapidecode_decode_loop, decode, NULL);
 }
 
 static void
 gst_vaapidecode_destroy(GstVaapiDecode *decode)
 {
-    gst_pad_stop_task(decode->srcpad);
+    gst_pad_stop_task(GST_VAAPI_PLUGIN_BASE_SRC_PAD(decode));
     gst_vaapi_decoder_replace(&decode->decoder, NULL);
     gst_caps_replace(&decode->decoder_caps, NULL);
     gst_vaapidecode_release(decode);
@@ -607,10 +671,28 @@ gst_vaapidecode_reset_full(GstVaapiDecode *decode, GstCaps *caps, gboolean hard)
 {
     GstVaapiCodec codec;
 
+    decode->has_texture_upload_meta = FALSE;
+
+    /* Reset tracked frame size */
+    decode->current_frame_size = 0;
+
     /* Reset timers if hard reset was requested (e.g. seek) */
     if (hard) {
-        decode->render_time_base = 0;
-        decode->last_buffer_time = 0;
+        GstVideoDecoder * const vdec = GST_VIDEO_DECODER(decode);
+        GstVideoCodecFrame *out_frame = NULL;
+
+        gst_vaapi_decoder_flush(decode->decoder);
+        GST_VIDEO_DECODER_STREAM_UNLOCK(vdec);
+        gst_pad_stop_task(GST_VAAPI_PLUGIN_BASE_SRC_PAD(decode));
+        GST_VIDEO_DECODER_STREAM_LOCK(vdec);
+        decode->decoder_loop_status = GST_FLOW_OK;
+
+        /* Purge all decoded frames as we don't need them (e.g. seek) */
+        while (gst_vaapi_decoder_get_frame_with_timeout(decode->decoder,
+                   &out_frame, 0) == GST_VAAPI_DECODER_STATUS_SUCCESS) {
+            gst_video_codec_frame_unref(out_frame);
+            out_frame = NULL;
+        }
     }
 
     /* Only reset decoder if codec type changed */
@@ -622,9 +704,6 @@ gst_vaapidecode_reset_full(GstVaapiDecode *decode, GstCaps *caps, gboolean hard)
             return TRUE;
     }
 
-    /* Reset tracked frame size */
-    decode->current_frame_size = 0;
-
     gst_vaapidecode_destroy(decode);
     return gst_vaapidecode_create(decode, caps);
 }
@@ -638,12 +717,11 @@ gst_vaapidecode_finalize(GObject *object)
     gst_caps_replace(&decode->srcpad_caps,  NULL);
     gst_caps_replace(&decode->allowed_caps, NULL);
 
-    gst_vaapi_display_replace(&decode->display, NULL);
-
     g_cond_clear(&decode->decoder_finish_done);
     g_cond_clear(&decode->decoder_ready);
     g_mutex_clear(&decode->decoder_mutex);
 
+    gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(object));
     G_OBJECT_CLASS(gst_vaapidecode_parent_class)->finalize(object);
 }
 
@@ -651,15 +729,18 @@ static gboolean
 gst_vaapidecode_open(GstVideoDecoder *vdec)
 {
     GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
-    GstVaapiDisplay * const old_display = decode->display;
+    GstVaapiDisplay * const old_display = GST_VAAPI_PLUGIN_BASE_DISPLAY(decode);
     gboolean success;
 
+    if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(decode)))
+        return FALSE;
+
     /* Let GstVideoContext ask for a proper display to its neighbours */
     /* Note: steal old display that may be allocated from get_caps()
        so that to retain a reference to it, thus avoiding extra
        initialization steps if we turn out to simply re-use the
        existing (cached) VA display */
-    decode->display = NULL;
+    GST_VAAPI_PLUGIN_BASE_DISPLAY(decode) = NULL;
     success = gst_vaapidecode_ensure_display(decode);
     if (old_display)
         gst_vaapi_display_unref(old_display);
@@ -672,7 +753,7 @@ gst_vaapidecode_close(GstVideoDecoder *vdec)
     GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
 
     gst_vaapidecode_destroy(decode);
-    gst_vaapi_display_replace(&decode->display, NULL);
+    gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(decode));
     return TRUE;
 }
 
@@ -681,25 +762,34 @@ gst_vaapidecode_reset(GstVideoDecoder *vdec, gboolean hard)
 {
     GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
 
+    /* In GStreamer 1.0 context, this means a flush */
+    if (decode->decoder && !hard && !gst_vaapidecode_flush(vdec))
+        return FALSE;
     return gst_vaapidecode_reset_full(decode, decode->sinkpad_caps, hard);
 }
 
 static gboolean
 gst_vaapidecode_set_format(GstVideoDecoder *vdec, GstVideoCodecState *state)
 {
+    GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(vdec);
     GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
 
     if (!gst_vaapidecode_update_sink_caps(decode, state->caps))
         return FALSE;
     if (!gst_vaapidecode_update_src_caps(decode, state))
         return FALSE;
+    if (!gst_video_decoder_negotiate(vdec))
+        return FALSE;
+    if (!gst_vaapi_plugin_base_set_caps(plugin, decode->sinkpad_caps,
+            decode->srcpad_caps))
+        return FALSE;
     if (!gst_vaapidecode_reset_full(decode, decode->sinkpad_caps, FALSE))
         return FALSE;
     return TRUE;
 }
 
 static GstFlowReturn
-gst_vaapidecode_parse(GstVideoDecoder *vdec,
+gst_vaapidecode_parse_frame(GstVideoDecoder *vdec,
     GstVideoCodecFrame *frame, GstAdapter *adapter, gboolean at_eos)
 {
     GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
@@ -722,7 +812,7 @@ gst_vaapidecode_parse(GstVideoDecoder *vdec,
             decode->current_frame_size = 0;
         }
         else
-            ret = GST_FLOW_OK;
+            ret = GST_VAAPI_DECODE_FLOW_PARSE_DATA;
         break;
     case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA:
         ret = GST_VIDEO_DECODER_FLOW_NEED_DATA;
@@ -743,6 +833,34 @@ gst_vaapidecode_parse(GstVideoDecoder *vdec,
     return ret;
 }
 
+static GstFlowReturn
+gst_vaapidecode_parse(GstVideoDecoder *vdec,
+    GstVideoCodecFrame *frame, GstAdapter *adapter, gboolean at_eos)
+{
+    GstFlowReturn ret;
+
+    do {
+        ret = gst_vaapidecode_parse_frame(vdec, frame, adapter, at_eos);
+    } while (ret == GST_VAAPI_DECODE_FLOW_PARSE_DATA);
+    return ret;
+}
+
+static GstStateChangeReturn
+gst_vaapidecode_change_state (GstElement * element, GstStateChange transition)
+{
+    GstVaapiDecode * const decode = GST_VAAPIDECODE(element);
+
+    switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+        gst_pad_stop_task(GST_VAAPI_PLUGIN_BASE_SRC_PAD(decode));
+        break;
+    default:
+        break;
+    }
+    return GST_ELEMENT_CLASS(gst_vaapidecode_parent_class)->change_state(
+        element, transition);
+}
+
 static void
 gst_vaapidecode_class_init(GstVaapiDecodeClass *klass)
 {
@@ -754,8 +872,13 @@ gst_vaapidecode_class_init(GstVaapiDecodeClass *klass)
     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapidecode,
                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
 
+    gst_vaapi_plugin_base_class_init(GST_VAAPI_PLUGIN_BASE_CLASS(klass));
+
     object_class->finalize   = gst_vaapidecode_finalize;
 
+    element_class->change_state =
+        GST_DEBUG_FUNCPTR(gst_vaapidecode_change_state);
+
     vdec_class->open         = GST_DEBUG_FUNCPTR(gst_vaapidecode_open);
     vdec_class->close        = GST_DEBUG_FUNCPTR(gst_vaapidecode_close);
     vdec_class->set_format   = GST_DEBUG_FUNCPTR(gst_vaapidecode_set_format);
@@ -787,8 +910,9 @@ gst_vaapidecode_class_init(GstVaapiDecodeClass *klass)
 static gboolean
 gst_vaapidecode_ensure_allowed_caps(GstVaapiDecode *decode)
 {
-    GstCaps *decode_caps;
-    guint i, n_decode_caps;
+    GstCaps *caps, *allowed_caps;
+    GArray *profiles;
+    guint i;
 
     if (decode->allowed_caps)
         return TRUE;
@@ -796,29 +920,32 @@ gst_vaapidecode_ensure_allowed_caps(GstVaapiDecode *decode)
     if (!gst_vaapidecode_ensure_display(decode))
         goto error_no_display;
 
-    decode_caps = gst_vaapi_display_get_decode_caps(decode->display);
-    if (!decode_caps)
-        goto error_no_decode_caps;
-    n_decode_caps = gst_caps_get_size(decode_caps);
+    profiles = gst_vaapi_display_get_decode_profiles(
+        GST_VAAPI_PLUGIN_BASE_DISPLAY(decode));
+    if (!profiles)
+        goto error_no_profiles;
 
-    decode->allowed_caps = gst_caps_new_empty();
-    if (!decode->allowed_caps)
+    allowed_caps = gst_caps_new_empty();
+    if (!allowed_caps)
         goto error_no_memory;
 
-    for (i = 0; i < n_decode_caps; i++) {
-        GstStructure *structure;
-        structure = gst_caps_get_structure(decode_caps, i);
-        if (!structure)
+    for (i = 0; i < profiles->len; i++) {
+        const GstVaapiProfile profile =
+            g_array_index(profiles, GstVaapiProfile, i);
+        const gchar *media_type_name;
+
+        media_type_name = gst_vaapi_profile_get_media_type_name(profile);
+        if (!media_type_name)
             continue;
-        structure = gst_structure_copy(structure);
-        if (!structure)
+
+        caps = gst_caps_from_string(media_type_name);
+        if (!caps)
             continue;
-        gst_structure_remove_field(structure, "profile");
-        decode->allowed_caps =
-            gst_caps_merge_structure(decode->allowed_caps, structure);
+        allowed_caps = gst_caps_merge(allowed_caps, caps);
     }
+    decode->allowed_caps = allowed_caps;
 
-    gst_caps_unref(decode_caps);
+    g_array_unref(profiles);
     return TRUE;
 
     /* ERRORS */
@@ -827,15 +954,15 @@ error_no_display:
         GST_ERROR("failed to retrieve VA display");
         return FALSE;
     }
-error_no_decode_caps:
+error_no_profiles:
     {
-        GST_ERROR("failed to retrieve VA decode caps");
+        GST_ERROR("failed to retrieve VA decode profiles");
         return FALSE;
     }
 error_no_memory:
     {
         GST_ERROR("failed to allocate allowed-caps set");
-        gst_caps_unref(decode_caps);
+        g_array_unref(profiles);
         return FALSE;
     }
 }
@@ -856,12 +983,15 @@ gst_vaapidecode_query(GST_PAD_QUERY_FUNCTION_ARGS)
 {
     GstVaapiDecode * const decode =
         GST_VAAPIDECODE(gst_pad_get_parent_element(pad));
+    GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(decode);
     gboolean res;
 
-    GST_DEBUG("sharing display %p", decode->display);
+    GST_INFO_OBJECT(decode, "query type %s", GST_QUERY_TYPE_NAME(query));
 
-    if (gst_vaapi_reply_to_query(query, decode->display))
+    if (gst_vaapi_reply_to_query(query, plugin->display)) {
+        GST_DEBUG("sharing display %p", plugin->display);
         res = TRUE;
+    }
     else if (GST_PAD_IS_SINK(pad)) {
         switch (GST_QUERY_TYPE(query)) {
 #if GST_CHECK_VERSION(1,0,0)
@@ -874,14 +1004,14 @@ gst_vaapidecode_query(GST_PAD_QUERY_FUNCTION_ARGS)
         }
 #endif
         default:
-            res = GST_PAD_QUERY_FUNCTION_CALL(decode->sinkpad_query,
-                decode->sinkpad, parent, query);
+            res = GST_PAD_QUERY_FUNCTION_CALL(plugin->sinkpad_query, pad,
+                parent, query);
             break;
         }
     }
     else
-        res = GST_PAD_QUERY_FUNCTION_CALL(decode->srcpad_query,
-            decode->srcpad, parent, query);
+        res = GST_PAD_QUERY_FUNCTION_CALL(plugin->srcpad_query, pad,
+            parent, query);
 
     gst_object_unref(decode);
     return res;
@@ -891,13 +1021,14 @@ static void
 gst_vaapidecode_init(GstVaapiDecode *decode)
 {
     GstVideoDecoder * const vdec = GST_VIDEO_DECODER(decode);
+    GstPad *pad;
+
+    gst_vaapi_plugin_base_init(GST_VAAPI_PLUGIN_BASE(decode), GST_CAT_DEFAULT);
 
-    decode->display             = NULL;
     decode->decoder             = NULL;
     decode->decoder_caps        = NULL;
     decode->allowed_caps        = NULL;
-    decode->render_time_base    = 0;
-    decode->last_buffer_time    = 0;
+    decode->decoder_loop_status = GST_FLOW_OK;
 
     g_mutex_init(&decode->decoder_mutex);
     g_cond_init(&decode->decoder_ready);
@@ -906,15 +1037,13 @@ gst_vaapidecode_init(GstVaapiDecode *decode)
     gst_video_decoder_set_packetized(vdec, FALSE);
 
     /* Pad through which data comes in to the element */
-    decode->sinkpad = GST_VIDEO_DECODER_SINK_PAD(vdec);
-    decode->sinkpad_query = GST_PAD_QUERYFUNC(decode->sinkpad);
-    gst_pad_set_query_function(decode->sinkpad, gst_vaapidecode_query);
+    pad = GST_VAAPI_PLUGIN_BASE_SINK_PAD(decode);
+    gst_pad_set_query_function(pad, gst_vaapidecode_query);
 #if !GST_CHECK_VERSION(1,0,0)
-    gst_pad_set_getcaps_function(decode->sinkpad, gst_vaapidecode_get_caps);
+    gst_pad_set_getcaps_function(pad, gst_vaapidecode_get_caps);
 #endif
 
     /* Pad through which data goes out of the element */
-    decode->srcpad = GST_VIDEO_DECODER_SRC_PAD(vdec);
-    decode->srcpad_query = GST_PAD_QUERYFUNC(decode->srcpad);
-    gst_pad_set_query_function(decode->srcpad, gst_vaapidecode_query);
+    pad = GST_VAAPI_PLUGIN_BASE_SRC_PAD(decode);
+    gst_pad_set_query_function(pad, gst_vaapidecode_query);
 }