vaapipostproc: add support for advanced deinterlacing.
authorGwenole Beauchesne <gwenole.beauchesne@intel.com>
Thu, 21 Nov 2013 18:52:56 +0000 (19:52 +0100)
committerGwenole Beauchesne <gwenole.beauchesne@intel.com>
Thu, 21 Nov 2013 22:08:02 +0000 (23:08 +0100)
Add initial support for advanced deinterlacing. The history buffer
size is arbitrarily set to 3 references for now.

gst/vaapi/gstvaapipostproc.c
gst/vaapi/gstvaapipostproc.h

index 6bcda3ffda434b3461cc3cef2f07a2064610deed..06ea5286c4d3c25985c29f5c0398bb13713e05ab 100755 (executable)
@@ -371,6 +371,51 @@ append_output_buffer_metadata(GstBuffer *outbuf, GstBuffer *inbuf, guint flags)
         0, -1);
 }
 
+static void
+ds_reset(GstVaapiDeinterlaceState *ds)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++)
+        gst_buffer_replace(&ds->buffers[i], NULL);
+    ds->buffers_index = 0;
+    ds->num_surfaces = 0;
+    ds->deint = FALSE;
+    ds->tff = FALSE;
+}
+
+static void
+ds_add_buffer(GstVaapiDeinterlaceState *ds, GstBuffer *buf)
+{
+    gst_buffer_replace(&ds->buffers[ds->buffers_index], buf);
+    ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS(ds->buffers);
+}
+
+static inline GstBuffer *
+ds_get_buffer(GstVaapiDeinterlaceState *ds, guint index)
+{
+    const guint n = ds->buffers_index + G_N_ELEMENTS(ds->buffers) - index - 1;
+    return ds->buffers[n % G_N_ELEMENTS(ds->buffers)];
+}
+
+static void
+ds_set_surfaces(GstVaapiDeinterlaceState *ds)
+{
+    GstVaapiVideoMeta *meta;
+    guint i;
+
+    ds->num_surfaces = 0;
+    for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++) {
+        GstBuffer * const buf = ds_get_buffer(ds, i);
+        if (!buf)
+            break;
+
+        meta = gst_buffer_get_vaapi_video_meta(buf);
+        ds->surfaces[ds->num_surfaces++] =
+            gst_vaapi_video_meta_get_surface(meta);
+    }
+}
+
 static GstVaapiDeinterlaceMethod
 get_next_deint_method(GstVaapiDeinterlaceMethod deint_method)
 {
@@ -409,6 +454,7 @@ gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
     GstBuffer *outbuf)
 {
     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+    GstVaapiDeinterlaceState * const ds = &postproc->deinterlace_state;
     GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
     GstVaapiSurface *inbuf_surface, *outbuf_surface;
     GstVaapiFilterStatus status;
@@ -433,6 +479,12 @@ gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
     tff        = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
     deint      = is_interlaced_buffer(postproc, inbuf);
 
+    /* Drop references if deinterlacing conditions changed */
+    if (deint != ds->deint || (ds->num_surfaces > 0 && tff != ds->tff))
+        ds_reset(ds);
+    ds->deint = deint;
+    ds->tff = tff;
+
     flags = gst_vaapi_video_meta_get_render_flags(inbuf_meta) &
         ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
           GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
@@ -451,8 +503,16 @@ gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
 
         if (deint) {
             deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
+            if (tff)
+                deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
             if (!set_best_deint_method(postproc, deint_flags, &deint_method))
                 goto error_op_deinterlace;
+
+            ds_set_surfaces(ds);
+            if (!gst_vaapi_filter_set_deinterlacing_references(postproc->filter,
+                    ds->surfaces, ds->num_surfaces, NULL, 0))
+                goto error_op_deinterlace;
+
             if (deint_method != postproc->deinterlace_method) {
                 GST_DEBUG("unsupported deinterlace-method %u. Using %u instead",
                           postproc->deinterlace_method, deint_method);
@@ -484,9 +544,15 @@ gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
 
     if (deint) {
         deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
+        if (tff)
+            deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
         if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
                 deint_method, deint_flags))
             goto error_op_deinterlace;
+
+        if (!gst_vaapi_filter_set_deinterlacing_references(postproc->filter,
+                ds->surfaces, ds->num_surfaces, NULL, 0))
+            goto error_op_deinterlace;
     }
 
     status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
@@ -502,6 +568,7 @@ gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
     }
     gst_buffer_set_vaapi_video_meta(outbuf, outbuf_meta);
     gst_vaapi_video_meta_unref(outbuf_meta);
+    ds_add_buffer(ds, inbuf);
     return GST_FLOW_OK;
 
     /* ERRORS */
index b82f751748bb81a8a59ccbbab0e0fe0a316abc2a..7340c3f23a017cbda1018950a2ff1d796f044d8b 100755 (executable)
@@ -57,6 +57,7 @@ G_BEGIN_DECLS
 
 typedef struct _GstVaapiPostproc                GstVaapiPostproc;
 typedef struct _GstVaapiPostprocClass           GstVaapiPostprocClass;
+typedef struct _GstVaapiDeinterlaceState        GstVaapiDeinterlaceState;
 
 /**
  * GstVaapiDeinterlaceMode:
@@ -70,6 +71,18 @@ typedef enum {
     GST_VAAPI_DEINTERLACE_MODE_DISABLED,
 } GstVaapiDeinterlaceMode;
 
+/*
+ * GST_VAAPI_DEINTERLACE_MAX_REFERENCES:
+ *
+ * This represents the maximum number of VA surfaces we could keep as
+ * references for advanced deinterlacing.
+ *
+ * Note: if the upstream element is vaapidecode, then the maximum
+ * number of allowed surfaces used as references shall be less than
+ * the actual number of scratch surfaces used for decoding (4).
+ */
+#define GST_VAAPI_DEINTERLACE_MAX_REFERENCES 3
+
 /**
  * GstVaapiPostprocFlags:
  * @GST_VAAPI_POSTPROC_FLAG_FORMAT: Pixel format conversion.
@@ -99,6 +112,26 @@ typedef enum {
     GST_VAAPI_POSTPROC_FLAG_SIZE        = GST_VAAPI_POSTPROC_FLAG_CUSTOM,
 } GstVaapiPostprocFlags;
 
+/*
+ * GstVaapiDeinterlaceState:
+ * @buffers: history buffer, maintained as a cyclic array
+ * @buffers_index: next free slot in the history buffer
+ * @surfaces: array of surfaces used as references
+ * @num_surfaces: number of active surfaces in that array
+ * @deint: flag: previous buffers were interlaced?
+ * @tff: flag: previous buffers were organized as top-field-first?
+ *
+ * Context used to maintain deinterlacing state.
+ */
+struct _GstVaapiDeinterlaceState {
+    GstBuffer                  *buffers[GST_VAAPI_DEINTERLACE_MAX_REFERENCES];
+    guint                       buffers_index;
+    GstVaapiSurface            *surfaces[GST_VAAPI_DEINTERLACE_MAX_REFERENCES];
+    guint                       num_surfaces;
+    guint                       deint           : 1;
+    guint                       tff             : 1;
+};
+
 struct _GstVaapiPostproc {
     /*< private >*/
     GstBaseTransform            parent_instance;
@@ -129,6 +162,7 @@ struct _GstVaapiPostproc {
     /* Deinterlacing */
     GstVaapiDeinterlaceMode     deinterlace_mode;
     GstVaapiDeinterlaceMethod   deinterlace_method;
+    GstVaapiDeinterlaceState    deinterlace_state;
     GstClockTime                field_duration;
 
     guint                       is_raw_yuv      : 1;