vaapipostproc: rework plug-in element.
authorGwenole Beauchesne <gwenole.beauchesne@intel.com>
Thu, 3 Oct 2013 17:04:07 +0000 (19:04 +0200)
committerGwenole Beauchesne <gwenole.beauchesne@intel.com>
Wed, 9 Oct 2013 16:30:06 +0000 (18:30 +0200)
Rewrite the vaapipostproc plug-in element so that it derives from
GstBaseTransform, thus simplifying the caps negotiation process.

gst-libs/gst/vaapi/gstcompat.h
gst/vaapi/gstvaapipostproc.c
gst/vaapi/gstvaapipostproc.h
gst/vaapi/gstvaapivideobuffer.c
gst/vaapi/gstvaapivideobuffer.h

index 4407648..c3a8ff8 100644 (file)
@@ -84,6 +84,11 @@ typedef enum {
 } GstMemoryFlags;
 
 typedef enum {
+    GST_BUFFER_COPY_META        = GST_BUFFER_COPY_QDATA,
+    GST_BUFFER_COPY_MEMORY      = 0,
+} GstBufferCopyFlags1_0;
+
+typedef enum {
     GST_MAP_READ        = 1 << 0,
     GST_MAP_WRITE       = 1 << 1
 } GstMapFlags;
@@ -110,6 +115,9 @@ typedef struct {
 #undef  gst_buffer_extract
 #define gst_buffer_extract(buffer, offset, dest, size) \
     gst_compat_buffer_extract(buffer, offset, dest, size)
+#undef  gst_buffer_copy_into
+#define gst_buffer_copy_into(dest, src, flags, offset, size) \
+    gst_compat_buffer_copy_into(dest, src, flags, offset, size)
 
 static inline GstBuffer *
 gst_compat_buffer_new_wrapped_full(GstMemoryFlags flags, gpointer data,
@@ -166,6 +174,16 @@ gst_compat_buffer_extract(GstBuffer *buffer, gsize offset, gpointer dest,
     return esize;
 }
 
+static inline void
+gst_compat_buffer_copy_into(GstBuffer *dest, GstBuffer *src,
+    GstBufferCopyFlags flags, gsize offset, gsize size)
+{
+    g_return_if_fail(offset == 0);
+    g_return_if_fail(size == (gsize)-1);
+
+    gst_buffer_copy_metadata(dest, src, flags);
+}
+
 /* GstAdapter */
 #include <gst/base/gstadapter.h>
 
index ad082cb..c22e3b8 100755 (executable)
@@ -106,7 +106,7 @@ gst_video_context_interface_init(GstVideoContextInterface *iface)
 G_DEFINE_TYPE_WITH_CODE(
     GstVaapiPostproc,
     gst_vaapipostproc,
-    GST_TYPE_ELEMENT,
+    GST_TYPE_BASE_TRANSFORM,
 #if !GST_CHECK_VERSION(1,0,0)
     G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
                           gst_vaapipostproc_implements_iface_init);
@@ -149,12 +149,6 @@ gst_vaapi_deinterlace_mode_get_type(void)
     return deinterlace_mode_type;
 }
 
-static inline GstVaapiPostproc *
-get_vaapipostproc_from_pad(GstPad *pad)
-{
-    return GST_VAAPIPOSTPROC(gst_pad_get_parent_element(pad));
-}
-
 static inline gboolean
 gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
 {
@@ -163,111 +157,127 @@ gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
 }
 
 static gboolean
-gst_vaapipostproc_create(GstVaapiPostproc *postproc, GstCaps *caps)
+gst_vaapipostproc_create(GstVaapiPostproc *postproc)
 {
     if (!gst_vaapipostproc_ensure_display(postproc))
         return FALSE;
-
-    gst_caps_replace(&postproc->postproc_caps, caps);
     return TRUE;
 }
 
 static void
 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
 {
-    gst_caps_replace(&postproc->postproc_caps, NULL);
-
     gst_vaapi_display_replace(&postproc->display, NULL);
+
+    gst_caps_replace(&postproc->sinkpad_caps, NULL);
+    gst_caps_replace(&postproc->srcpad_caps,  NULL);
+    gst_caps_replace(&postproc->allowed_caps, NULL);
 }
 
 static gboolean
-gst_vaapipostproc_reset(GstVaapiPostproc *postproc, GstCaps *caps)
+gst_vaapipostproc_reset(GstVaapiPostproc *postproc)
 {
-    if (postproc->postproc_caps &&
-        gst_caps_is_always_compatible(caps, postproc->postproc_caps))
-        return TRUE;
-
     gst_vaapipostproc_destroy(postproc);
-    return gst_vaapipostproc_create(postproc, caps);
+    return gst_vaapipostproc_create(postproc);
 }
 
 static gboolean
-gst_vaapipostproc_start(GstVaapiPostproc *postproc)
+gst_vaapipostproc_start(GstBaseTransform *trans)
 {
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+
     if (!gst_vaapipostproc_ensure_display(postproc))
         return FALSE;
     return TRUE;
 }
 
 static gboolean
-gst_vaapipostproc_stop(GstVaapiPostproc *postproc)
+gst_vaapipostproc_stop(GstBaseTransform *trans)
 {
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+
     gst_vaapi_display_replace(&postproc->display, NULL);
     return TRUE;
 }
 
+static GstBuffer *
+create_output_buffer(GstVaapiPostproc *postproc)
+{
+    GstBuffer *outbuf;
+
+    /* Create a raw VA video buffer without GstVaapiVideoMeta attached
+       to it yet, as this will be done next in the transform() hook */
+    outbuf = gst_vaapi_video_buffer_new_empty();
+    if (!outbuf)
+        goto error_create_buffer;
+
+#if !GST_CHECK_VERSION(1,0,0)
+    gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
+#endif
+    return outbuf;
+
+    /* ERRORS */
+error_create_buffer:
+    {
+        GST_ERROR("failed to create output video buffer");
+        return NULL;
+    }
+}
+
+static inline void
+append_output_buffer_metadata(GstBuffer *outbuf, GstBuffer *inbuf, guint flags)
+{
+    gst_buffer_copy_into(outbuf, inbuf, flags |
+        GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_META | GST_BUFFER_COPY_MEMORY,
+        0, -1);
+}
+
 static GstFlowReturn
-gst_vaapipostproc_process(GstVaapiPostproc *postproc, GstBuffer *buf)
+gst_vaapipostproc_process(GstBaseTransform *trans, GstBuffer *inbuf,
+    GstBuffer *outbuf)
 {
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
     GstVaapiVideoMeta *meta;
-    GstVaapiSurfaceProxy *proxy;
     GstClockTime timestamp;
     GstFlowReturn ret;
-    GstBuffer *outbuf;
-    guint outbuf_flags, flags;
+    GstBuffer *fieldbuf;
+    guint fieldbuf_flags, outbuf_flags, flags;
     gboolean tff;
 
-    meta = gst_buffer_get_vaapi_video_meta(buf);
+    meta = gst_buffer_get_vaapi_video_meta(inbuf);
     if (!meta)
         goto error_invalid_buffer;
 
-    /* Deinterlacing disabled, push frame */
-    if (!postproc->deinterlace) {
-        outbuf = gst_buffer_ref(buf);
-        if (!outbuf)
-            goto error_create_buffer;
-        ret = gst_pad_push(postproc->srcpad, outbuf);
-        if (ret != GST_FLOW_OK)
-            goto error_push_buffer;
-        gst_buffer_unref(buf);
-        return GST_FLOW_OK;
-    }
-
-    timestamp  = GST_BUFFER_TIMESTAMP(buf);
-    proxy      = gst_vaapi_video_meta_get_surface_proxy(meta);
-    tff        = GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_TFF);
+    timestamp  = GST_BUFFER_TIMESTAMP(inbuf);
+    tff        = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
 
     flags = gst_vaapi_video_meta_get_render_flags(meta) &
         ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
           GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
 
     /* First field */
-    outbuf = gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
-    if (!outbuf)
+    fieldbuf = create_output_buffer(postproc);
+    if (!fieldbuf)
         goto error_create_buffer;
+    append_output_buffer_metadata(fieldbuf, inbuf, 0);
 
-    meta = gst_buffer_get_vaapi_video_meta(outbuf);
-    outbuf_flags = flags;
-    outbuf_flags |= postproc->deinterlace ? (
+    meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
+    fieldbuf_flags = flags;
+    fieldbuf_flags |= postproc->deinterlace ? (
         tff ?
         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
-    gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
+    gst_vaapi_video_meta_set_render_flags(meta, fieldbuf_flags);
 
-    GST_BUFFER_TIMESTAMP(outbuf) = timestamp;
-    GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
-#if !GST_CHECK_VERSION(1,0,0)
-    gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
-#endif
-    ret = gst_pad_push(postproc->srcpad, outbuf);
+    GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
+    GST_BUFFER_DURATION(fieldbuf)  = postproc->field_duration;
+    ret = gst_pad_push(trans->srcpad, fieldbuf);
     if (ret != GST_FLOW_OK)
         goto error_push_buffer;
 
     /* Second field */
-    outbuf = gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
-    if (!outbuf)
-        goto error_create_buffer;
+    append_output_buffer_metadata(outbuf, inbuf, 0);
 
     meta = gst_buffer_get_vaapi_video_meta(outbuf);
     outbuf_flags = flags;
@@ -280,118 +290,118 @@ gst_vaapipostproc_process(GstVaapiPostproc *postproc, GstBuffer *buf)
 
     GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
     GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
-#if !GST_CHECK_VERSION(1,0,0)
-    gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
-#endif
-    ret = gst_pad_push(postproc->srcpad, outbuf);
-    if (ret != GST_FLOW_OK)
-        goto error_push_buffer;
-
-    gst_buffer_unref(buf);
     return GST_FLOW_OK;
 
     /* ERRORS */
 error_invalid_buffer:
     {
-        GST_ERROR("failed to receive a valid video buffer");
-        gst_buffer_unref(buf);
-        return GST_FLOW_EOS;
+        GST_ERROR("failed to validate source buffer");
+        return GST_FLOW_ERROR;
     }
 error_create_buffer:
     {
         GST_ERROR("failed to create output buffer");
-        gst_buffer_unref(buf);
         return GST_FLOW_EOS;
     }
 error_push_buffer:
     {
         if (ret != GST_FLOW_FLUSHING)
             GST_ERROR("failed to push output buffer to video sink");
-        gst_buffer_unref(buf);
         return GST_FLOW_EOS;
     }
 }
 
-static gboolean
-gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps)
+static GstFlowReturn
+gst_vaapipostproc_passthrough(GstBaseTransform *trans, GstBuffer *inbuf,
+    GstBuffer *outbuf)
 {
-    GstVideoInfo vi;
+    GstVaapiVideoMeta *meta;
 
-    if (!gst_video_info_from_caps(&vi, caps))
-        return FALSE;
-    postproc->fps_n = GST_VIDEO_INFO_FPS_N(&vi);
-    postproc->fps_d = GST_VIDEO_INFO_FPS_D(&vi);
+    /* No video processing needed, simply copy buffer metadata */
+    meta = gst_buffer_get_vaapi_video_meta(inbuf);
+    if (!meta)
+        goto error_invalid_buffer;
+
+    append_output_buffer_metadata(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
+    return GST_FLOW_OK;
+
+    /* ERRORS */
+error_invalid_buffer:
+    {
+        GST_ERROR("failed to validate source buffer");
+        return GST_FLOW_ERROR;
+    }
+}
+
+static gboolean
+is_deinterlace_enabled(GstVaapiPostproc *postproc, GstVideoInfo *vip)
+{
+    gboolean deinterlace;
 
     switch (postproc->deinterlace_mode) {
     case GST_VAAPI_DEINTERLACE_MODE_AUTO:
-        postproc->deinterlace = GST_VIDEO_INFO_IS_INTERLACED(&vi);
+        deinterlace = GST_VIDEO_INFO_IS_INTERLACED(vip);
         break;
     case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
-        postproc->deinterlace = TRUE;
+        deinterlace = TRUE;
         break;
-    case GST_VAAPI_DEINTERLACE_MODE_DISABLED:
-        postproc->deinterlace = FALSE;
+    default:
+        deinterlace = FALSE;
         break;
     }
+    return deinterlace;
+}
 
-    postproc->field_duration = gst_util_uint64_scale(
-        GST_SECOND,
-        postproc->fps_d,
-        (1 + postproc->deinterlace) * postproc->fps_n
-    );
-
-    gst_caps_replace(&postproc->sinkpad_caps, caps);
-    return TRUE;
+static gboolean
+video_info_changed(GstVideoInfo *old_vip, GstVideoInfo *new_vip)
+{
+    if (GST_VIDEO_INFO_FORMAT(old_vip) != GST_VIDEO_INFO_FORMAT(new_vip))
+        return TRUE;
+    if (GST_VIDEO_INFO_INTERLACE_MODE(old_vip) !=
+        GST_VIDEO_INFO_INTERLACE_MODE(new_vip))
+        return TRUE;
+    if (GST_VIDEO_INFO_WIDTH(old_vip) != GST_VIDEO_INFO_WIDTH(new_vip))
+        return TRUE;
+    if (GST_VIDEO_INFO_HEIGHT(old_vip) != GST_VIDEO_INFO_HEIGHT(new_vip))
+        return TRUE;
+    return FALSE;
 }
 
 static gboolean
-gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps)
+gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps,
+    gboolean *caps_changed_ptr)
 {
-    GstCaps *src_caps;
-    GstStructure *structure;
-    const GValue *v_width, *v_height, *v_par;
-    gint fps_n, fps_d;
+    GstVideoInfo vi;
 
-    if (postproc->srcpad_caps)
-        src_caps = gst_caps_make_writable(postproc->srcpad_caps);
-    else
-        src_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
-    if (!src_caps)
+    if (!gst_video_info_from_caps(&vi, caps))
         return FALSE;
-    postproc->srcpad_caps = src_caps;
 
-    structure    = gst_caps_get_structure(caps, 0);
-    v_width      = gst_structure_get_value(structure, "width");
-    v_height     = gst_structure_get_value(structure, "height");
-    v_par        = gst_structure_get_value(structure, "pixel-aspect-ratio");
+    if (video_info_changed(&vi, &postproc->sinkpad_info))
+        postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
 
-    structure = gst_caps_get_structure(src_caps, 0);
-    if (v_width && v_height) {
-        gst_structure_set_value(structure, "width", v_width);
-        gst_structure_set_value(structure, "height", v_height);
-    }
-    if (v_par)
-        gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
+    postproc->deinterlace = is_deinterlace_enabled(postproc, &vi);
+    postproc->field_duration = gst_util_uint64_scale(
+        GST_SECOND, GST_VIDEO_INFO_FPS_D(&vi),
+        (1 + postproc->deinterlace) * GST_VIDEO_INFO_FPS_N(&vi));
+    return TRUE;
+}
 
-    gst_structure_set(structure, "type", G_TYPE_STRING, "vaapi", NULL);
-    gst_structure_set(structure, "opengl", G_TYPE_BOOLEAN, USE_GLX, NULL);
+static gboolean
+gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps,
+    gboolean *caps_changed_ptr)
+{
+    GstVideoInfo vi;
 
-    if (postproc->deinterlace) {
-        /* Set double framerate in interlaced mode */
-        if (!gst_util_fraction_multiply(postproc->fps_n, postproc->fps_d,
-                                        2, 1,
-                                        &fps_n, &fps_d))
-            return FALSE;
+    if (!gst_video_info_from_caps(&vi, caps))
+        return FALSE;
 
-        gst_structure_set(structure, "framerate",
-            GST_TYPE_FRACTION, fps_n, fps_d, NULL);
-    }
-    gst_caps_set_interlaced(src_caps, NULL);
-    return gst_pad_set_caps(postproc->srcpad, src_caps);
+    if (video_info_changed(&vi, &postproc->srcpad_info))
+        postproc->srcpad_info = vi, *caps_changed_ptr = TRUE;
+    return TRUE;
 }
 
 static gboolean
-gst_vaapipostproc_ensure_allowed_caps(GstVaapiPostproc *postproc)
+ensure_allowed_caps(GstVaapiPostproc *postproc)
 {
     if (postproc->allowed_caps)
         return TRUE;
@@ -406,116 +416,161 @@ gst_vaapipostproc_ensure_allowed_caps(GstVaapiPostproc *postproc)
 }
 
 static GstCaps *
-gst_vaapipostproc_get_caps(GstPad *pad)
+gst_vaapipostproc_transform_caps_impl(GstBaseTransform *trans,
+    GstPadDirection direction, GstCaps *caps)
 {
-    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+    GstVideoInfo vi;
+    GstVideoFormat format;
     GstCaps *out_caps;
+    gint fps_n, fps_d, par_n, par_d;
 
-    if (gst_vaapipostproc_ensure_allowed_caps(postproc))
-        out_caps = gst_caps_ref(postproc->allowed_caps);
-    else
-        out_caps = gst_caps_new_empty();
+    if (!gst_caps_is_fixed(caps)) {
+        if (direction == GST_PAD_SINK)
+            return gst_caps_from_string(gst_vaapipostproc_src_caps_str);
 
-    gst_object_unref(postproc);
+        /* Generate the allowed set of caps on the sink pad */
+        if (!ensure_allowed_caps(postproc))
+            return NULL;
+        return gst_caps_ref(postproc->allowed_caps);
+    }
+
+    /* Generate the other pad caps, based on the current pad caps, as
+       specified by the direction argument */
+    if (!gst_video_info_from_caps(&vi, caps))
+        return NULL;
+
+    format = GST_VIDEO_INFO_FORMAT(&vi);
+    if (format == GST_VIDEO_FORMAT_UNKNOWN)
+        return NULL;
+
+    fps_n = GST_VIDEO_INFO_FPS_N(&vi);
+    fps_d = GST_VIDEO_INFO_FPS_D(&vi);
+    if (direction == GST_PAD_SINK) {
+        if (is_deinterlace_enabled(postproc, &vi)) {
+            /* Set double framerate in interlaced mode */
+            if (!gst_util_fraction_multiply(fps_n, fps_d, 2, 1, &fps_n, &fps_d))
+                return NULL;
+
+            /* Signal the other pad that we generate only progressive frames */
+            GST_VIDEO_INFO_INTERLACE_MODE(&vi) =
+                GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
+        }
+    }
+    else {
+        if (is_deinterlace_enabled(postproc, &vi)) {
+            /* Set half framerate in interlaced mode */
+            if (!gst_util_fraction_multiply(fps_n, fps_d, 1, 2, &fps_n, &fps_d))
+                return NULL;
+        }
+    }
+
+    /* XXX: gst_video_info_to_caps() from GStreamer 0.10 does not
+       reconstruct suitable caps for "encoded" video formats */
+    out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
+    if (!out_caps)
+        return NULL;
+
+    par_n = GST_VIDEO_INFO_PAR_N(&vi);
+    par_d = GST_VIDEO_INFO_PAR_D(&vi);
+    gst_caps_set_simple(out_caps,
+        "type", G_TYPE_STRING, "vaapi",
+        "opengl", G_TYPE_BOOLEAN, USE_GLX,
+        "width", G_TYPE_INT, GST_VIDEO_INFO_WIDTH(&vi),
+        "height", G_TYPE_INT, GST_VIDEO_INFO_HEIGHT(&vi),
+        "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
+        "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
+        NULL);
+
+    gst_caps_set_interlaced(out_caps, &vi);
     return out_caps;
 }
 
-static gboolean
-gst_vaapipostproc_set_caps(GstPad *pad, GstCaps *caps)
+#if GST_CHECK_VERSION(1,0,0)
+static GstCaps *
+gst_vaapipostproc_transform_caps(GstBaseTransform *trans,
+    GstPadDirection direction, GstCaps *caps, GstCaps *filter)
 {
-    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
-    gboolean success = FALSE;
-
-    g_return_val_if_fail(pad == postproc->sinkpad, FALSE);
-
-    do {
-        if (!gst_vaapipostproc_update_sink_caps(postproc, caps))
-            break;
-        if (!gst_vaapipostproc_update_src_caps(postproc, caps))
-            break;
-        if (!gst_vaapipostproc_reset(postproc, postproc->sinkpad_caps))
-            break;
-        success = TRUE;
-    } while (0);
-    gst_object_unref(postproc);
-    return success;
+    GstCaps *out_caps;
+
+    caps = gst_vaapipostproc_transform_caps_impl(trans, direction, caps);
+    if (caps && filter) {
+        out_caps = gst_caps_intersect_full(caps, filter,
+            GST_CAPS_INTERSECT_FIRST);
+        gst_caps_unref(caps);
+        return out_caps;
+    }
+    return caps;
 }
+#else
+#define gst_vaapipostproc_transform_caps \
+    gst_vaapipostproc_transform_caps_impl
+#endif
 
 static GstFlowReturn
-gst_vaapipostproc_chain(GST_PAD_CHAIN_FUNCTION_ARGS)
+gst_vaapipostproc_transform(GstBaseTransform *trans, GstBuffer *inbuf,
+    GstBuffer *outbuf)
 {
-    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
     GstFlowReturn ret;
 
-    ret = gst_vaapipostproc_process(postproc, buffer);
-    gst_object_unref(postproc);
+    if (postproc->deinterlace)
+        ret = gst_vaapipostproc_process(trans, inbuf, outbuf);
+    else
+        ret = gst_vaapipostproc_passthrough(trans, inbuf, outbuf);
     return ret;
 }
 
-static gboolean
-gst_vaapipostproc_sink_event(GST_PAD_EVENT_FUNCTION_ARGS)
+static GstFlowReturn
+gst_vaapipostproc_prepare_output_buffer(GstBaseTransform *trans,
+    GstBuffer *inbuf,
+#if !GST_CHECK_VERSION(1,0,0)
+    gint size, GstCaps *caps,
+#endif
+    GstBuffer **outbuf_ptr)
 {
-    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
-    gboolean success;
-
-    GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
 
-    switch (GST_EVENT_TYPE(event)) {
-#if GST_CHECK_VERSION(1,0,0)
-    case GST_EVENT_CAPS: {
-        GstCaps *caps;
-        gst_event_parse_caps(event, &caps);
-        success = gst_vaapipostproc_set_caps(pad, caps);
-        break;
-    }
-#endif
-    default:
-        /* Propagate event downstream */
-        success = gst_pad_push_event(postproc->srcpad, event);
-        break;
-    }
-    gst_object_unref(postproc);
-    return success;
+    *outbuf_ptr = create_output_buffer(postproc);
+    return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
 }
 
 static gboolean
-gst_vaapipostproc_src_event(GST_PAD_EVENT_FUNCTION_ARGS)
+gst_vaapipostproc_set_caps(GstBaseTransform *trans, GstCaps *caps,
+    GstCaps *out_caps)
 {
-    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
-    gboolean success;
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+    gboolean caps_changed = FALSE;
 
-    GST_DEBUG("handle src event '%s'", GST_EVENT_TYPE_NAME(event));
+    if (!gst_vaapipostproc_update_sink_caps(postproc, caps, &caps_changed))
+        return FALSE;
+    if (!gst_vaapipostproc_update_src_caps(postproc, out_caps, &caps_changed))
+        return FALSE;
 
-    /* Propagate event upstream */
-    success = gst_pad_push_event(postproc->sinkpad, event);
-    gst_object_unref(postproc);
-    return success;
+    if (caps_changed) {
+        if (!gst_vaapipostproc_reset(postproc))
+            return FALSE;
+        gst_caps_replace(&postproc->sinkpad_caps, caps);
+        gst_caps_replace(&postproc->srcpad_caps, out_caps);
+    }
+    return TRUE;
 }
 
 static gboolean
-gst_vaapipostproc_query(GST_PAD_QUERY_FUNCTION_ARGS)
+gst_vaapipostproc_query(GstBaseTransform *trans, GstPadDirection direction,
+    GstQuery *query)
 {
-    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
-    gboolean success;
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
 
-    GST_DEBUG("sharing display %p", postproc->display);
+    GST_INFO_OBJECT(trans, "query type `%s'", GST_QUERY_TYPE_NAME(query));
 
-    if (gst_vaapi_reply_to_query(query, postproc->display))
-        success = TRUE;
-#if GST_CHECK_VERSION(1,0,0)
-    else if (GST_PAD_IS_SINK(pad) && GST_QUERY_TYPE(query) == GST_QUERY_CAPS) {
-        GstCaps * const caps = gst_vaapipostproc_get_caps(pad);
-        gst_query_set_caps_result(query, caps);
-        gst_caps_unref(caps);
-        success = TRUE;
+    if (gst_vaapi_reply_to_query(query, postproc->display)) {
+        GST_DEBUG("sharing display %p", postproc->display);
+        return TRUE;
     }
-#endif
-    else
-        success = GST_PAD_QUERY_FUNCTION_CALL(gst_pad_query_default, pad,
-            parent, query);
 
-    gst_object_unref(postproc);
-    return success;
+    return GST_BASE_TRANSFORM_CLASS(gst_vaapipostproc_parent_class)->query(
+        trans, direction, query);
 }
 
 static void
@@ -525,10 +580,6 @@ gst_vaapipostproc_finalize(GObject *object)
 
     gst_vaapipostproc_destroy(postproc);
 
-    gst_caps_replace(&postproc->sinkpad_caps, NULL);
-    gst_caps_replace(&postproc->srcpad_caps,  NULL);
-    gst_caps_replace(&postproc->allowed_caps, NULL);
-
     G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
 }
 
@@ -578,49 +629,12 @@ gst_vaapipostproc_get_property(
     }
 }
 
-static GstStateChangeReturn
-gst_vaapipostproc_change_state(GstElement *element, GstStateChange transition)
-{
-    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(element);
-    GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
-
-    switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-        if (!gst_vaapipostproc_start(postproc))
-            return GST_STATE_CHANGE_FAILURE;
-        break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-        break;
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-        break;
-    default:
-        break;
-    }
-
-    ret = GST_ELEMENT_CLASS(gst_vaapipostproc_parent_class)->change_state(element, transition);
-    if (ret != GST_STATE_CHANGE_SUCCESS)
-        return ret;
-
-    switch (transition) {
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
-        break;
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-        break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
-        if (!gst_vaapipostproc_stop(postproc))
-            return GST_STATE_CHANGE_FAILURE;
-        break;
-    default:
-        break;
-    }
-    return GST_STATE_CHANGE_SUCCESS;
-}
-
 static void
 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
 {
     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
+    GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
     GstPadTemplate *pad_template;
 
     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
@@ -629,8 +643,15 @@ gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
     object_class->finalize      = gst_vaapipostproc_finalize;
     object_class->set_property  = gst_vaapipostproc_set_property;
     object_class->get_property  = gst_vaapipostproc_get_property;
+    trans_class->start          = gst_vaapipostproc_start;
+    trans_class->stop           = gst_vaapipostproc_stop;
+    trans_class->transform_caps = gst_vaapipostproc_transform_caps;
+    trans_class->transform      = gst_vaapipostproc_transform;
+    trans_class->set_caps       = gst_vaapipostproc_set_caps;
+    trans_class->query          = gst_vaapipostproc_query;
 
-    element_class->change_state = gst_vaapipostproc_change_state;
+    trans_class->prepare_output_buffer =
+        gst_vaapipostproc_prepare_output_buffer;
 
     gst_element_class_set_static_metadata(element_class,
         "VA-API video postprocessing",
@@ -656,8 +677,8 @@ gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
     g_object_class_install_property
         (object_class,
          PROP_DEINTERLACE_MODE,
-         g_param_spec_enum("deinterlace",
-                           "Deinterlace",
+         g_param_spec_enum("deinterlace-mode",
+                           "Deinterlace mode",
                            "Deinterlace mode to use",
                            GST_VAAPI_TYPE_DEINTERLACE_MODE,
                            DEFAULT_DEINTERLACE_MODE,
@@ -682,45 +703,11 @@ gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
 static void
 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
 {
-    GstVaapiPostprocClass *klass = GST_VAAPIPOSTPROC_GET_CLASS(postproc);
-    GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
-
-    postproc->allowed_caps              = NULL;
-    postproc->postproc_caps             = NULL;
-    postproc->display                   = NULL;
-    postproc->surface_width             = 0;
-    postproc->surface_height            = 0;
     postproc->deinterlace               = FALSE;
     postproc->deinterlace_mode          = DEFAULT_DEINTERLACE_MODE;
     postproc->deinterlace_method        = DEFAULT_DEINTERLACE_METHOD;
     postproc->field_duration            = GST_CLOCK_TIME_NONE;
-    postproc->fps_n                     = 0;
-    postproc->fps_d                     = 0;
-
-    /* Pad through which data comes in to the element */
-    postproc->sinkpad = gst_pad_new_from_template(
-        gst_element_class_get_pad_template(element_class, "sink"),
-        "sink"
-    );
-    postproc->sinkpad_caps = NULL;
 
-#if !GST_CHECK_VERSION(1,0,0)
-    gst_pad_set_getcaps_function(postproc->sinkpad, gst_vaapipostproc_get_caps);
-    gst_pad_set_setcaps_function(postproc->sinkpad, gst_vaapipostproc_set_caps);
-#endif
-    gst_pad_set_chain_function(postproc->sinkpad, gst_vaapipostproc_chain);
-    gst_pad_set_event_function(postproc->sinkpad, gst_vaapipostproc_sink_event);
-    gst_pad_set_query_function(postproc->sinkpad, gst_vaapipostproc_query);
-    gst_element_add_pad(GST_ELEMENT(postproc), postproc->sinkpad);
-
-    /* Pad through which data goes out of the element */
-    postproc->srcpad = gst_pad_new_from_template(
-        gst_element_class_get_pad_template(element_class, "src"),
-        "src"
-    );
-    postproc->srcpad_caps = NULL;
-
-    gst_pad_set_event_function(postproc->srcpad, gst_vaapipostproc_src_event);
-    gst_pad_set_query_function(postproc->srcpad, gst_vaapipostproc_query);
-    gst_element_add_pad(GST_ELEMENT(postproc), postproc->srcpad);
+    gst_video_info_init(&postproc->sinkpad_info);
+    gst_video_info_init(&postproc->srcpad_info);
 }
index b33e724..480753c 100755 (executable)
@@ -22,7 +22,7 @@
 #ifndef GST_VAAPIPOSTPROC_H
 #define GST_VAAPIPOSTPROC_H
 
-#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
 #include <gst/vaapi/gstvaapidisplay.h>
 #include <gst/vaapi/gstvaapisurface.h>
 #include <gst/vaapi/gstvaapisurfacepool.h>
@@ -71,31 +71,25 @@ typedef enum {
 
 struct _GstVaapiPostproc {
     /*< private >*/
-    GstElement                  parent_instance;
+    GstBaseTransform            parent_instance;
 
-    GstPad                     *sinkpad;
+    GstVaapiDisplay            *display;
+    GstCaps                    *allowed_caps;
     GstCaps                    *sinkpad_caps;
-    GstPad                     *srcpad;
+    GstVideoInfo                sinkpad_info;
     GstCaps                    *srcpad_caps;
-    GstCaps                    *allowed_caps;
-    GstCaps                    *postproc_caps;
-
-    GstVaapiDisplay            *display;
-    guint                       surface_width;
-    guint                       surface_height;
+    GstVideoInfo                srcpad_info;
 
     /* Deinterlacing */
     gboolean                    deinterlace;
     GstVaapiDeinterlaceMode     deinterlace_mode;
     GstVaapiDeinterlaceMethod   deinterlace_method;
     GstClockTime                field_duration;
-    gint                        fps_n;
-    gint                        fps_d;
 };
 
 struct _GstVaapiPostprocClass {
     /*< private >*/
-    GstElementClass             parent_class;
+    GstBaseTransformClass       parent_class;
 };
 
 GType
index 40c11e8..e03dd69 100644 (file)
@@ -312,6 +312,12 @@ gst_vaapi_video_buffer_new(GstVaapiVideoMeta *meta)
 }
 
 GstBuffer *
+gst_vaapi_video_buffer_new_empty(void)
+{
+    return gst_surface_buffer_new();
+}
+
+GstBuffer *
 gst_vaapi_video_buffer_new_from_pool(GstVaapiVideoPool *pool)
 {
     return new_vbuffer(gst_vaapi_video_meta_new_from_pool(pool));
index f4457ab..d172f44 100644 (file)
@@ -35,6 +35,10 @@ gst_vaapi_video_buffer_new(GstVaapiVideoMeta *meta);
 
 G_GNUC_INTERNAL
 GstBuffer *
+gst_vaapi_video_buffer_new_empty(void);
+
+G_GNUC_INTERNAL
+GstBuffer *
 gst_vaapi_video_buffer_new_from_pool(GstVaapiVideoPool *pool);
 
 G_GNUC_INTERNAL