/*
* gstvaapipostproc.c - VA-API video postprocessing
*
- * Copyright (C) 2012 Intel Corporation
+ * Copyright (C) 2012-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
* implemented.
*/
-#include "config.h"
+#include "gst/vaapi/sysdeps.h"
#include <gst/video/video.h>
-#include <gst/video/videocontext.h>
-#include <gst/vaapi/gstvaapivideosink.h>
-#include <gst/vaapi/gstvaapivideobuffer.h>
-#include "gstvaapipluginutil.h"
#include "gstvaapipostproc.h"
+#include "gstvaapipluginutil.h"
+#include "gstvaapivideobuffer.h"
+#if GST_CHECK_VERSION(1,0,0)
+#include "gstvaapivideobufferpool.h"
+#include "gstvaapivideomemory.h"
+#endif
#define GST_PLUGIN_NAME "vaapipostproc"
#define GST_PLUGIN_DESC "A video postprocessing filter"
GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapipostproc);
#define GST_CAT_DEFAULT gst_debug_vaapipostproc
-/* ElementFactory information */
-static const GstElementDetails gst_vaapipostproc_details =
- GST_ELEMENT_DETAILS(
- "VA-API video postprocessing",
- "Filter/Converter/Video",
- GST_PLUGIN_DESC,
- "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
-
/* Default templates */
static const char gst_vaapipostproc_sink_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 }") ", "
+#else
GST_VAAPI_SURFACE_CAPS ", "
- "interlaced = (boolean) { true, false }";
+#endif
+ GST_CAPS_INTERLACED_MODES "; "
+#if GST_CHECK_VERSION(1,0,0)
+ GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
+#else
+ "video/x-raw-yuv, "
+ "width = " GST_VIDEO_SIZE_RANGE ", "
+ "height = " GST_VIDEO_SIZE_RANGE ", "
+ "framerate = " GST_VIDEO_FPS_RANGE ", "
+#endif
+ GST_CAPS_INTERLACED_MODES;
static const char gst_vaapipostproc_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 }") ", "
+#else
GST_VAAPI_SURFACE_CAPS ", "
- "interlaced = (boolean) false";
+#endif
+ GST_CAPS_INTERLACED_FALSE;
static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
GST_STATIC_PAD_TEMPLATE(
GST_PAD_ALWAYS,
GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
-#define GstVideoContextClass GstVideoContextInterface
-GST_BOILERPLATE_WITH_INTERFACE(
+G_DEFINE_TYPE_WITH_CODE(
GstVaapiPostproc,
gst_vaapipostproc,
- GstElement,
- GST_TYPE_ELEMENT,
- GstVideoContext,
- GST_TYPE_VIDEO_CONTEXT,
- gst_video_context);
+ GST_TYPE_BASE_TRANSFORM,
+ GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES)
enum {
PROP_0,
+ PROP_FORMAT,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_FORCE_ASPECT_RATIO,
PROP_DEINTERLACE_MODE,
PROP_DEINTERLACE_METHOD,
+ PROP_DENOISE,
+ PROP_SHARPEN,
+ PROP_HUE,
+ PROP_SATURATION,
+ PROP_BRIGHTNESS,
+ PROP_CONTRAST,
};
+#define DEFAULT_FORMAT GST_VIDEO_FORMAT_ENCODED
#define DEFAULT_DEINTERLACE_MODE GST_VAAPI_DEINTERLACE_MODE_AUTO
#define DEFAULT_DEINTERLACE_METHOD GST_VAAPI_DEINTERLACE_METHOD_BOB
-#define GST_TYPE_VAAPI_DEINTERLACE_MODES \
- gst_vaapi_deinterlace_modes_get_type()
+#define GST_VAAPI_TYPE_DEINTERLACE_MODE \
+ gst_vaapi_deinterlace_mode_get_type()
static GType
-gst_vaapi_deinterlace_modes_get_type(void)
+gst_vaapi_deinterlace_mode_get_type(void)
{
- static GType deinterlace_modes_type = 0;
+ static GType deinterlace_mode_type = 0;
- static const GEnumValue modes_types[] = {
+ static const GEnumValue mode_types[] = {
{ GST_VAAPI_DEINTERLACE_MODE_AUTO,
"Auto detection", "auto" },
{ GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
{ 0, NULL, NULL },
};
- if (!deinterlace_modes_type) {
- deinterlace_modes_type =
- g_enum_register_static("GstVaapiDeinterlaceModes", modes_types);
+ if (!deinterlace_mode_type) {
+ deinterlace_mode_type =
+ g_enum_register_static("GstVaapiDeinterlaceMode", mode_types);
}
- return deinterlace_modes_type;
+ return deinterlace_mode_type;
}
-#define GST_TYPE_VAAPI_DEINTERLACE_METHODS \
- gst_vaapi_deinterlace_methods_get_type()
-
-static GType
-gst_vaapi_deinterlace_methods_get_type(void)
-{
- static GType deinterlace_methods_type = 0;
-
- static const GEnumValue methods_types[] = {
- { GST_VAAPI_DEINTERLACE_METHOD_BOB,
- "Bob deinterlacing", "bob" },
-#if 0
- /* VA/VPP */
- { GST_VAAPI_DEINTERLACE_METHOD_WEAVE,
- "Weave deinterlacing", "weave" },
- { GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE,
- "Motion adaptive deinterlacing", "motion-adaptive" },
- { GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED,
- "Motion compensated deinterlacing", "motion-compensated" },
-#endif
- { 0, NULL, NULL },
- };
-
- if (!deinterlace_methods_type) {
- deinterlace_methods_type =
- g_enum_register_static("GstVaapiDeinterlaceMethods", methods_types);
- }
- return deinterlace_methods_type;
+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 inline GstVaapiPostproc *
-get_vaapipostproc_from_pad(GstPad *pad)
+static void
+ds_add_buffer(GstVaapiDeinterlaceState *ds, GstBuffer *buf)
{
- return GST_VAAPIPOSTPROC(gst_pad_get_parent_element(pad));
+ gst_buffer_replace(&ds->buffers[ds->buffers_index], buf);
+ ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS(ds->buffers);
}
-/* GstVideoContext interface */
+static inline GstBuffer *
+ds_get_buffer(GstVaapiDeinterlaceState *ds, guint index)
+{
+ /* Note: the index increases towards older buffers.
+ i.e. buffer at index 0 means the immediately preceding buffer
+ in the history, buffer at index 1 means the one preceding the
+ surface at index 0, etc. */
+ const guint n = ds->buffers_index + G_N_ELEMENTS(ds->buffers) - index - 1;
+ return ds->buffers[n % G_N_ELEMENTS(ds->buffers)];
+}
static void
-gst_vaapipostproc_set_video_context(
- GstVideoContext *context,
- const gchar *type,
- const GValue *value
-)
+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 GstVaapiFilterOpInfo *
+find_filter_op(GPtrArray *filter_ops, GstVaapiFilterOp op)
{
- GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(context);
+ guint i;
+
+ if (filter_ops) {
+ for (i = 0; i < filter_ops->len; i++) {
+ GstVaapiFilterOpInfo * const filter_op =
+ g_ptr_array_index(filter_ops, i);
+ if (filter_op->op == op)
+ return filter_op;
+ }
+ }
+ return NULL;
+}
- gst_vaapi_set_display(type, value, &postproc->display);
+static inline gboolean
+gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
+{
+ return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(postproc));
}
static gboolean
-gst_video_context_supported(GstVaapiPostproc *postproc, GType iface_type)
+gst_vaapipostproc_ensure_uploader(GstVaapiPostproc *postproc)
{
- return (iface_type == GST_TYPE_VIDEO_CONTEXT);
+ if (!gst_vaapipostproc_ensure_display(postproc))
+ return FALSE;
+ if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(postproc)))
+ return FALSE;
+ return TRUE;
}
-static void
-gst_video_context_interface_init(GstVideoContextInterface *iface)
+static gboolean
+gst_vaapipostproc_ensure_filter(GstVaapiPostproc *postproc)
{
- iface->set_context = gst_vaapipostproc_set_video_context;
+ if (postproc->filter)
+ return TRUE;
+
+ if (!gst_vaapipostproc_ensure_display(postproc))
+ return FALSE;
+
+ postproc->filter = gst_vaapi_filter_new(
+ GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
+ if (!postproc->filter)
+ return FALSE;
+ return TRUE;
}
static gboolean
-gst_vaapipostproc_create(GstVaapiPostproc *postproc, GstCaps *caps)
+gst_vaapipostproc_ensure_filter_caps(GstVaapiPostproc *postproc)
{
- if (!gst_vaapi_ensure_display(postproc, &postproc->display))
+ if (!gst_vaapipostproc_ensure_filter(postproc))
return FALSE;
- gst_caps_replace(&postproc->postproc_caps, caps);
+ postproc->filter_ops = gst_vaapi_filter_get_operations(postproc->filter);
+ if (!postproc->filter_ops)
+ return FALSE;
+
+ postproc->filter_formats = gst_vaapi_filter_get_formats(postproc->filter);
+ if (!postproc->filter_formats)
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean
+gst_vaapipostproc_create(GstVaapiPostproc *postproc)
+{
+ if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
+ return FALSE;
+ if (!gst_vaapipostproc_ensure_display(postproc))
+ return FALSE;
+ if (!gst_vaapipostproc_ensure_uploader(postproc))
+ return FALSE;
+ if (gst_vaapipostproc_ensure_filter(postproc))
+ postproc->use_vpp = TRUE;
return TRUE;
}
static void
-gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
+gst_vaapipostproc_destroy_filter(GstVaapiPostproc *postproc)
{
- gst_caps_replace(&postproc->postproc_caps, NULL);
+ if (postproc->filter_formats) {
+ g_array_unref(postproc->filter_formats);
+ postproc->filter_formats = NULL;
+ }
- if (postproc->display) {
- g_object_unref(postproc->display);
- postproc->display = NULL;
+ if (postproc->filter_ops) {
+ g_ptr_array_unref(postproc->filter_ops);
+ postproc->filter_ops = NULL;
}
+ gst_vaapi_filter_replace(&postproc->filter, NULL);
+ gst_vaapi_video_pool_replace(&postproc->filter_pool, NULL);
}
-static gboolean
-gst_vaapipostproc_reset(GstVaapiPostproc *postproc, GstCaps *caps)
+static void
+gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
{
- if (postproc->postproc_caps &&
- gst_caps_is_always_compatible(caps, postproc->postproc_caps))
- return TRUE;
+ ds_reset(&postproc->deinterlace_state);
+ gst_vaapipostproc_destroy_filter(postproc);
- gst_vaapipostproc_destroy(postproc);
- return gst_vaapipostproc_create(postproc, caps);
+ gst_caps_replace(&postproc->allowed_sinkpad_caps, NULL);
+ gst_caps_replace(&postproc->allowed_srcpad_caps, NULL);
+ gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
}
static gboolean
-gst_vaapipostproc_start(GstVaapiPostproc *postproc)
+gst_vaapipostproc_start(GstBaseTransform *trans)
{
- if (!gst_vaapi_ensure_display(postproc, &postproc->display))
+ GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+
+ ds_reset(&postproc->deinterlace_state);
+ if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
+ return FALSE;
+ if (!gst_vaapipostproc_ensure_display(postproc))
return FALSE;
return TRUE;
}
static gboolean
-gst_vaapipostproc_stop(GstVaapiPostproc *postproc)
+gst_vaapipostproc_stop(GstBaseTransform *trans)
{
- if (postproc->display) {
- g_object_unref(postproc->display);
- postproc->display = NULL;
- }
+ GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+
+ ds_reset(&postproc->deinterlace_state);
+ gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
return TRUE;
}
+static gboolean
+should_deinterlace_buffer(GstVaapiPostproc *postproc, GstBuffer *buf)
+{
+ if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
+ postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
+ return FALSE;
+
+ if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
+ return TRUE;
+
+ g_assert(postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
+
+ switch (GST_VIDEO_INFO_INTERLACE_MODE(&postproc->sinkpad_info)) {
+ case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
+ return TRUE;
+ case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
+ return FALSE;
+ case GST_VIDEO_INTERLACE_MODE_MIXED:
+#if GST_CHECK_VERSION(1,0,0)
+ if (GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
+ return TRUE;
+#else
+ if (!GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_PROGRESSIVE))
+ return TRUE;
+#endif
+ break;
+ default:
+ GST_ERROR("unhandled \"interlace-mode\", disabling deinterlacing" );
+ break;
+ }
+ return FALSE;
+}
+
+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, GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAPS(postproc));
+#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 gboolean
+deint_method_is_advanced(GstVaapiDeinterlaceMethod deint_method)
+{
+ gboolean is_advanced;
+
+ switch (deint_method) {
+ case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
+ case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
+ is_advanced = TRUE;
+ break;
+ default:
+ is_advanced = FALSE;
+ break;
+ }
+ return is_advanced;
+}
+
+static GstVaapiDeinterlaceMethod
+get_next_deint_method(GstVaapiDeinterlaceMethod deint_method)
+{
+ switch (deint_method) {
+ case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
+ deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
+ break;
+ default:
+ /* Default to basic "bob" for all others */
+ deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
+ break;
+ }
+ return deint_method;
+}
+
+static gboolean
+set_best_deint_method(GstVaapiPostproc *postproc, guint flags,
+ GstVaapiDeinterlaceMethod *deint_method_ptr)
+{
+ GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
+ gboolean success;
+
+ for (;;) {
+ success = gst_vaapi_filter_set_deinterlacing(postproc->filter,
+ deint_method, flags);
+ if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
+ break;
+ deint_method = get_next_deint_method(deint_method);
+ }
+ *deint_method_ptr = deint_method;
+ return success;
+}
+
static GstFlowReturn
-gst_vaapipostproc_process(GstVaapiPostproc *postproc, GstBuffer *buf)
+gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
+ GstBuffer *outbuf)
{
- GstVaapiVideoBuffer *vbuf = GST_VAAPI_VIDEO_BUFFER(buf);
- GstVaapiSurfaceProxy *proxy;
+ GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+ GstVaapiDeinterlaceState * const ds = &postproc->deinterlace_state;
+ GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
+ GstVaapiSurface *inbuf_surface, *outbuf_surface;
+ GstVaapiFilterStatus status;
GstClockTime timestamp;
GstFlowReturn ret;
- GstBuffer *outbuf = NULL;
- guint outbuf_flags, flags = 0;
- gboolean tff;
-
- /* Deinterlacing disabled, push frame */
- if (!postproc->deinterlace) {
- gst_vaapi_video_buffer_set_render_flags(vbuf, flags);
- ret = gst_pad_push(postproc->srcpad, buf);
+ GstBuffer *fieldbuf;
+ GstVaapiDeinterlaceMethod deint_method;
+ guint flags, deint_flags;
+ gboolean tff, deint, deint_refs, deint_changed;
+ GstVaapiRectangle *crop_rect = NULL;
+
+ /* Validate filters */
+ if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
+ !gst_vaapi_filter_set_format(postproc->filter, postproc->format))
+ return GST_FLOW_NOT_SUPPORTED;
+
+ if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) &&
+ !gst_vaapi_filter_set_denoising_level(postproc->filter,
+ postproc->denoise_level))
+ return GST_FLOW_NOT_SUPPORTED;
+
+ if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) &&
+ !gst_vaapi_filter_set_sharpening_level(postproc->filter,
+ postproc->sharpen_level))
+ return GST_FLOW_NOT_SUPPORTED;
+
+ if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) &&
+ !gst_vaapi_filter_set_hue(postproc->filter,
+ postproc->hue))
+ return GST_FLOW_NOT_SUPPORTED;
+
+ if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) &&
+ !gst_vaapi_filter_set_saturation(postproc->filter,
+ postproc->saturation))
+ return GST_FLOW_NOT_SUPPORTED;
+
+ if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) &&
+ !gst_vaapi_filter_set_brightness(postproc->filter,
+ postproc->brightness))
+ return GST_FLOW_NOT_SUPPORTED;
+
+ if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) &&
+ !gst_vaapi_filter_set_contrast(postproc->filter,
+ postproc->contrast))
+ return GST_FLOW_NOT_SUPPORTED;
+
+ inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
+ if (!inbuf_meta)
+ goto error_invalid_buffer;
+ inbuf_surface = gst_vaapi_video_meta_get_surface(inbuf_meta);
+
+#if GST_CHECK_VERSION(1,0,0)
+ GstVideoCropMeta * const crop_meta =
+ gst_buffer_get_video_crop_meta(inbuf);
+ if (crop_meta) {
+ GstVaapiRectangle tmp_rect;
+ crop_rect = &tmp_rect;
+ crop_rect->x = crop_meta->x;
+ crop_rect->y = crop_meta->y;
+ crop_rect->width = crop_meta->width;
+ crop_rect->height = crop_meta->height;
+ }
+#endif
+ if (!crop_rect)
+ crop_rect = (GstVaapiRectangle *)
+ gst_vaapi_video_meta_get_render_rect(inbuf_meta);
+
+ timestamp = GST_BUFFER_TIMESTAMP(inbuf);
+ tff = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
+ deint = should_deinterlace_buffer(postproc, inbuf);
+
+ /* Drop references if deinterlacing conditions changed */
+ deint_changed = deint != ds->deint;
+ if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
+ ds_reset(ds);
+
+ deint_method = postproc->deinterlace_method;
+ deint_refs = deint_method_is_advanced(deint_method);
+ if (deint_refs) {
+ GstBuffer * const prev_buf = ds_get_buffer(ds, 0);
+ GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP(inbuf);
+ /* Reset deinterlacing state when there is a discontinuity */
+ if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP(prev_buf)) != pts) {
+ const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF(prev_pts, pts);
+ if (pts_diff < 0 || pts_diff > postproc->field_duration * 2)
+ ds_reset(ds);
+ }
+ }
+
+ ds->deint = deint;
+ ds->tff = tff;
+
+ flags = gst_vaapi_video_meta_get_render_flags(inbuf_meta) &
+ ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
+
+ /* First field */
+ if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
+ fieldbuf = create_output_buffer(postproc);
+ if (!fieldbuf)
+ goto error_create_buffer;
+
+ outbuf_meta = gst_vaapi_video_meta_new_from_pool(postproc->filter_pool);
+ if (!outbuf_meta)
+ goto error_create_meta;
+ outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
+
+ 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;
+
+ if (deint_method != postproc->deinterlace_method) {
+ GST_DEBUG("unsupported deinterlace-method %u. Using %u instead",
+ postproc->deinterlace_method, deint_method);
+ postproc->deinterlace_method = deint_method;
+ deint_refs = deint_method_is_advanced(deint_method);
+ }
+
+ if (deint_refs) {
+ ds_set_surfaces(ds);
+ if (!gst_vaapi_filter_set_deinterlacing_references(
+ postproc->filter, ds->surfaces, ds->num_surfaces,
+ NULL, 0))
+ goto error_op_deinterlace;
+ }
+ }
+ else if (deint_changed) {
+ // Reset internal filter to non-deinterlacing mode
+ deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
+ if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
+ deint_method, 0))
+ goto error_op_deinterlace;
+ }
+
+ gst_vaapi_filter_set_cropping_rectangle(postproc->filter, crop_rect);
+ status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
+ outbuf_surface, flags);
+ if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
+ goto error_process_vpp;
+
+ gst_buffer_set_vaapi_video_meta(fieldbuf, outbuf_meta);
+ gst_vaapi_video_meta_unref(outbuf_meta);
+
+ 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;
- return GST_FLOW_OK;
}
+ fieldbuf = NULL;
- timestamp = GST_BUFFER_TIMESTAMP(buf);
- proxy = gst_vaapi_video_buffer_get_surface_proxy(vbuf);
- tff = gst_vaapi_surface_proxy_get_tff(proxy);
+ /* Second field */
+ outbuf_meta = gst_vaapi_video_meta_new_from_pool(postproc->filter_pool);
+ if (!outbuf_meta)
+ goto error_create_meta;
+ outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
+
+ 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 (deint_refs && !gst_vaapi_filter_set_deinterlacing_references(
+ postproc->filter, ds->surfaces, ds->num_surfaces, NULL, 0))
+ goto error_op_deinterlace;
+ }
+ else if (deint_changed && !gst_vaapi_filter_set_deinterlacing(
+ postproc->filter, deint_method, 0))
+ goto error_op_deinterlace;
+
+ gst_vaapi_filter_set_cropping_rectangle(postproc->filter, crop_rect);
+ status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
+ outbuf_surface, flags);
+ if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
+ goto error_process_vpp;
+
+ if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
+ gst_buffer_copy_into(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
+ else {
+ GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
+ GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
+ }
+ gst_buffer_set_vaapi_video_meta(outbuf, outbuf_meta);
+ gst_vaapi_video_meta_unref(outbuf_meta);
+
+ if (deint && deint_refs)
+ ds_add_buffer(ds, inbuf);
+ return GST_FLOW_OK;
+
+ /* ERRORS */
+error_invalid_buffer:
+ {
+ GST_ERROR("failed to validate source buffer");
+ return GST_FLOW_ERROR;
+ }
+error_create_buffer:
+ {
+ GST_ERROR("failed to create output buffer");
+ return GST_FLOW_ERROR;
+ }
+error_create_meta:
+ {
+ GST_ERROR("failed to create new output buffer meta");
+ gst_buffer_replace(&fieldbuf, NULL);
+ gst_vaapi_video_meta_unref(outbuf_meta);
+ return GST_FLOW_ERROR;
+ }
+error_op_deinterlace:
+ {
+ GST_ERROR("failed to apply deinterlacing filter");
+ gst_buffer_replace(&fieldbuf, NULL);
+ gst_vaapi_video_meta_unref(outbuf_meta);
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+error_process_vpp:
+ {
+ GST_ERROR("failed to apply VPP filters (error %d)", status);
+ gst_buffer_replace(&fieldbuf, NULL);
+ gst_vaapi_video_meta_unref(outbuf_meta);
+ return GST_FLOW_ERROR;
+ }
+error_push_buffer:
+ {
+ if (ret != GST_FLOW_FLUSHING)
+ GST_ERROR("failed to push output buffer to video sink");
+ return GST_FLOW_ERROR;
+ }
+}
+
+static GstFlowReturn
+gst_vaapipostproc_process(GstBaseTransform *trans, GstBuffer *inbuf,
+ GstBuffer *outbuf)
+{
+ GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+ GstVaapiVideoMeta *meta;
+ GstClockTime timestamp;
+ GstFlowReturn ret;
+ GstBuffer *fieldbuf;
+ guint fieldbuf_flags, outbuf_flags, flags;
+ gboolean tff, deint;
+
+ meta = gst_buffer_get_vaapi_video_meta(inbuf);
+ if (!meta)
+ goto error_invalid_buffer;
+
+ timestamp = GST_BUFFER_TIMESTAMP(inbuf);
+ tff = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
+ deint = should_deinterlace_buffer(postproc, inbuf);
+
+ flags = gst_vaapi_video_meta_get_render_flags(meta) &
+ ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
/* 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);
- vbuf = GST_VAAPI_VIDEO_BUFFER(outbuf);
- outbuf_flags = flags;
- outbuf_flags |= tff ?
+ meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
+ fieldbuf_flags = flags;
+ fieldbuf_flags |= deint ? (
+ tff ?
GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
- GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD;
- gst_vaapi_video_buffer_set_render_flags(vbuf, outbuf_flags);
+ GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
+ GST_VAAPI_PICTURE_STRUCTURE_FRAME;
+ gst_vaapi_video_meta_set_render_flags(meta, fieldbuf_flags);
- GST_BUFFER_TIMESTAMP(outbuf) = timestamp;
- GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
- gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
- 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);
- vbuf = GST_VAAPI_VIDEO_BUFFER(outbuf);
+ meta = gst_buffer_get_vaapi_video_meta(outbuf);
outbuf_flags = flags;
- outbuf_flags |= tff ?
+ outbuf_flags |= deint ? (
+ tff ?
GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
- GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD;
- gst_vaapi_video_buffer_set_render_flags(vbuf, outbuf_flags);
+ GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
+ GST_VAAPI_PICTURE_STRUCTURE_FRAME;
+ gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
- gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
- 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 validate source buffer");
+ return GST_FLOW_ERROR;
+ }
error_create_buffer:
{
GST_ERROR("failed to create output buffer");
- gst_buffer_unref(buf);
- return GST_FLOW_UNEXPECTED;
+ return GST_FLOW_EOS;
}
error_push_buffer:
{
- if (ret != GST_FLOW_WRONG_STATE)
+ if (ret != GST_FLOW_FLUSHING)
GST_ERROR("failed to push output buffer to video sink");
- gst_buffer_unref(buf);
- return GST_FLOW_UNEXPECTED;
+ 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)
{
- gint fps_n, fps_d;
- gboolean interlaced;
+ GstVaapiVideoMeta *meta;
- if (!gst_video_parse_caps_framerate(caps, &fps_n, &fps_d))
- return FALSE;
- postproc->fps_n = fps_n;
- postproc->fps_d = fps_d;
+ /* 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:
- if (!gst_video_format_parse_caps_interlaced(caps, &interlaced))
- return FALSE;
- postproc->deinterlace = interlaced;
+ 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;
+}
+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_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps,
+ gboolean *caps_changed_ptr)
+{
+ GstVideoInfo vi;
+ gboolean deinterlace;
+
+ if (!gst_video_info_from_caps(&vi, caps))
+ return FALSE;
+
+ if (video_info_changed(&vi, &postproc->sinkpad_info))
+ postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
+
+ deinterlace = is_deinterlace_enabled(postproc, &vi);
+ if (deinterlace)
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
postproc->field_duration = gst_util_uint64_scale(
- GST_SECOND,
- postproc->fps_d,
- (1 + postproc->deinterlace) * postproc->fps_n
- );
+ GST_SECOND, GST_VIDEO_INFO_FPS_D(&vi),
+ (1 + deinterlace) * GST_VIDEO_INFO_FPS_N(&vi));
- gst_caps_replace(&postproc->sinkpad_caps, caps);
+ postproc->is_raw_yuv = GST_VIDEO_INFO_IS_YUV(&vi);
return TRUE;
}
static gboolean
-gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps)
+gst_vaapipostproc_update_src_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->srcpad_info))
+ postproc->srcpad_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 (postproc->format != GST_VIDEO_INFO_FORMAT(&postproc->sinkpad_info))
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
+
+ if ((postproc->width || postproc->height) &&
+ postproc->width != GST_VIDEO_INFO_WIDTH(&postproc->sinkpad_info) &&
+ postproc->height != GST_VIDEO_INFO_HEIGHT(&postproc->sinkpad_info))
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
+ return TRUE;
+}
+
+static gboolean
+ensure_allowed_sinkpad_caps(GstVaapiPostproc *postproc)
+{
+ GstCaps *out_caps, *yuv_caps;
+
+ if (postproc->allowed_sinkpad_caps)
+ return TRUE;
+
+ /* Create VA caps */
+#if GST_CHECK_VERSION(1,1,0)
+ out_caps = gst_static_pad_template_get_caps(
+ &gst_vaapipostproc_sink_factory);
+#else
+ out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS ", "
+ GST_CAPS_INTERLACED_MODES);
+#endif
+ if (!out_caps) {
+ GST_ERROR("failed to create VA sink caps");
+ return FALSE;
}
- if (v_par)
- gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
- gst_structure_set(structure, "type", G_TYPE_STRING, "vaapi", NULL);
- gst_structure_set(structure, "opengl", G_TYPE_BOOLEAN, USE_VAAPI_GLX, NULL);
+ /* Append YUV caps */
+ if (gst_vaapipostproc_ensure_uploader(postproc)) {
+ yuv_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(postproc);
+ if (yuv_caps) {
+ out_caps = gst_caps_make_writable(out_caps);
+ gst_caps_append(out_caps, gst_caps_copy(yuv_caps));
+ }
+ else
+ GST_WARNING("failed to create YUV sink caps");
+ }
+ postproc->allowed_sinkpad_caps = out_caps;
- if (!postproc->deinterlace)
- gst_structure_remove_field(structure, "interlaced");
- else {
- /* 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;
+ /* XXX: append VA/VPP filters */
+ return TRUE;
+}
- gst_structure_set(
- structure,
- "interlaced", G_TYPE_BOOLEAN, FALSE,
- "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
- NULL
- );
+/* Fixup output caps so that to reflect the supported set of pixel formats */
+static GstCaps *
+expand_allowed_srcpad_caps(GstVaapiPostproc *postproc, GstCaps *caps)
+{
+ GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
+ guint i, num_structures;
+ gboolean had_filter;
+
+ had_filter = postproc->filter != NULL;
+ if (!had_filter && !gst_vaapipostproc_ensure_filter(postproc))
+ goto cleanup;
+ if (!gst_vaapipostproc_ensure_filter_caps(postproc))
+ goto cleanup;
+
+ /* Reset "format" field for each structure */
+ if (!gst_vaapi_value_set_format_list(&value, postproc->filter_formats))
+ goto cleanup;
+ if (gst_vaapi_value_set_format(&v_format, GST_VIDEO_FORMAT_ENCODED)) {
+ gst_value_list_prepend_value(&value, &v_format);
+ g_value_unset(&v_format);
}
- return gst_pad_set_caps(postproc->srcpad, src_caps);
+
+ num_structures = gst_caps_get_size(caps);
+ for (i = 0; i < num_structures; i++) {
+ GstStructure * const structure = gst_caps_get_structure(caps, i);
+ if (!structure)
+ continue;
+ gst_structure_set_value(structure, "format", &value);
+ }
+ g_value_unset(&value);
+
+cleanup:
+ if (!had_filter)
+ gst_vaapipostproc_destroy_filter(postproc);
+ return caps;
}
static gboolean
-gst_vaapipostproc_ensure_allowed_caps(GstVaapiPostproc *postproc)
+ensure_allowed_srcpad_caps(GstVaapiPostproc *postproc)
{
- if (postproc->allowed_caps)
+ GstCaps *out_caps;
+
+ if (postproc->allowed_srcpad_caps)
return TRUE;
- postproc->allowed_caps =
- gst_caps_from_string(gst_vaapipostproc_sink_caps_str);
- if (!postproc->allowed_caps)
+ /* Create initial caps from pad template */
+ out_caps = gst_caps_from_string(gst_vaapipostproc_src_caps_str);
+ if (!out_caps) {
+ GST_ERROR("failed to create VA src caps");
return FALSE;
+ }
- /* XXX: append VA/VPP filters */
- return TRUE;
+ postproc->allowed_srcpad_caps =
+ expand_allowed_srcpad_caps(postproc, out_caps);
+ return postproc->allowed_srcpad_caps != NULL;
+}
+
+static void
+find_best_size(GstVaapiPostproc *postproc, GstVideoInfo *vip,
+ guint *width_ptr, guint *height_ptr)
+{
+ guint width, height;
+
+ width = GST_VIDEO_INFO_WIDTH(vip);
+ height = GST_VIDEO_INFO_HEIGHT(vip);
+ if (postproc->width && postproc->height) {
+ width = postproc->width;
+ height = postproc->height;
+ }
+ else if (postproc->keep_aspect) {
+ const gdouble ratio = (gdouble)width / height;
+ if (postproc->width) {
+ width = postproc->width;
+ height = postproc->width / ratio;
+ }
+ else if (postproc->height) {
+ height = postproc->height;
+ width = postproc->height * ratio;
+ }
+ }
+ else if (postproc->width)
+ width = postproc->width;
+ else if (postproc->height)
+ height = postproc->height;
+
+ *width_ptr = width;
+ *height_ptr = height;
}
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;
+ guint width, height;
- if (gst_vaapipostproc_ensure_allowed_caps(postproc))
- out_caps = gst_caps_ref(postproc->allowed_caps);
- else
- out_caps = gst_caps_new_empty();
+ /* Generate the sink pad caps, that could be fixated afterwards */
+ if (direction == GST_PAD_SRC) {
+ if (!ensure_allowed_sinkpad_caps(postproc))
+ return NULL;
+ return gst_caps_ref(postproc->allowed_sinkpad_caps);
+ }
+
+ /* Generate complete set of src pad caps if non-fixated sink pad
+ caps are provided */
+ if (!gst_caps_is_fixed(caps)) {
+ if (!ensure_allowed_srcpad_caps(postproc))
+ return NULL;
+ return gst_caps_ref(postproc->allowed_srcpad_caps);
+ }
- gst_object_unref(postproc);
+ /* Generate the expected src pad caps, from the current fixated
+ sink pad caps */
+ if (!gst_video_info_from_caps(&vi, caps))
+ return NULL;
+
+ // Set double framerate in interlaced mode
+ if (is_deinterlace_enabled(postproc, &vi)) {
+ gint fps_n = GST_VIDEO_INFO_FPS_N(&vi);
+ gint fps_d = GST_VIDEO_INFO_FPS_D(&vi);
+ if (!gst_util_fraction_multiply(fps_n, fps_d, 2, 1, &fps_n, &fps_d))
+ return NULL;
+ GST_VIDEO_INFO_FPS_N(&vi) = fps_n;
+ GST_VIDEO_INFO_FPS_D(&vi) = fps_d;
+ }
+
+ // Signal the other pad that we only generate progressive frames
+ GST_VIDEO_INFO_INTERLACE_MODE(&vi) = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
+
+ // Update size from user-specified parameters
+#if GST_CHECK_VERSION(1,1,0)
+ format = postproc->format;
+#else
+ format = GST_VIDEO_FORMAT_ENCODED;
+#endif
+ find_best_size(postproc, &vi, &width, &height);
+ gst_video_info_set_format(&vi, format, width, height);
+
+#if GST_CHECK_VERSION(1,1,0)
+ out_caps = gst_video_info_to_caps(&vi);
+ if (!out_caps)
+ return NULL;
+
+ gst_caps_set_features(out_caps, 0,
+ gst_caps_features_new(GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, NULL));
+#else
+ /* 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;
+
+ 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, GST_VIDEO_INFO_FPS_N(&vi),
+ GST_VIDEO_INFO_FPS_D(&vi),
+ "pixel-aspect-ratio", GST_TYPE_FRACTION, GST_VIDEO_INFO_PAR_N(&vi),
+ GST_VIDEO_INFO_PAR_D(&vi),
+ NULL);
+
+ gst_caps_set_interlaced(out_caps, &vi);
+#endif
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;
+ GstCaps *out_caps;
- g_return_val_if_fail(pad == postproc->sinkpad, FALSE);
+ 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
- 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;
+#if GST_CHECK_VERSION(1,0,0)
+typedef gsize GstBaseTransformSizeType;
+#else
+typedef guint GstBaseTransformSizeType;
+#endif
+
+static gboolean
+gst_vaapipostproc_transform_size(GstBaseTransform *trans,
+ GstPadDirection direction, GstCaps *caps, GstBaseTransformSizeType size,
+ GstCaps *othercaps, GstBaseTransformSizeType *othersize)
+{
+ GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+
+ if (direction == GST_PAD_SINK || !postproc->is_raw_yuv)
+ *othersize = 0;
+ else
+ *othersize = size;
+ return TRUE;
}
static GstFlowReturn
-gst_vaapipostproc_chain(GstPad *pad, GstBuffer *buf)
+gst_vaapipostproc_transform(GstBaseTransform *trans, GstBuffer *inbuf,
+ GstBuffer *outbuf)
{
- GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+ GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+ GstBuffer *buf;
GstFlowReturn ret;
- ret = gst_vaapipostproc_process(postproc, buf);
- gst_object_unref(postproc);
+ ret = gst_vaapi_plugin_base_get_input_buffer(
+ GST_VAAPI_PLUGIN_BASE(postproc), inbuf, &buf);
+ if (ret != GST_FLOW_OK)
+ return GST_FLOW_ERROR;
+
+ ret = GST_FLOW_NOT_SUPPORTED;
+ if (postproc->flags) {
+ /* Use VA/VPP extensions to process this frame */
+ if (postproc->use_vpp &&
+ postproc->flags != GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
+ ret = gst_vaapipostproc_process_vpp(trans, buf, outbuf);
+ if (ret != GST_FLOW_NOT_SUPPORTED)
+ goto done;
+ GST_WARNING("unsupported VPP filters. Disabling");
+ postproc->use_vpp = FALSE;
+ }
+
+ /* Only append picture structure meta data (top/bottom field) */
+ if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
+ ret = gst_vaapipostproc_process(trans, buf, outbuf);
+ if (ret != GST_FLOW_NOT_SUPPORTED)
+ goto done;
+ }
+ }
+
+ /* Fallback: passthrough to the downstream element as is */
+ ret = gst_vaapipostproc_passthrough(trans, buf, outbuf);
+
+done:
+ gst_buffer_unref(buf);
return ret;
}
+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 = GST_VAAPIPOSTPROC(trans);
+
+ *outbuf_ptr = create_output_buffer(postproc);
+ return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
+}
+
static gboolean
-gst_vaapipostproc_sink_event(GstPad *pad, GstEvent *event)
+ensure_srcpad_buffer_pool(GstVaapiPostproc *postproc, GstCaps *caps)
{
- GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
- gboolean success;
+ GstVideoInfo vi;
+ GstVaapiVideoPool *pool;
- GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
+ gst_video_info_init(&vi);
+ gst_video_info_from_caps(&vi, caps);
+ gst_video_info_set_format(&vi, postproc->format,
+ GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
- /* Propagate event downstream */
- success = gst_pad_push_event(postproc->srcpad, event);
- gst_object_unref(postproc);
- return success;
+ if (!video_info_changed(&vi, &postproc->filter_pool_info))
+ return TRUE;
+ postproc->filter_pool_info = vi;
+
+ pool = gst_vaapi_surface_pool_new(GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc),
+ &postproc->filter_pool_info);
+ if (!pool)
+ return FALSE;
+
+ gst_vaapi_video_pool_replace(&postproc->filter_pool, pool);
+ gst_vaapi_video_pool_unref(pool);
+ return TRUE;
}
static gboolean
-gst_vaapipostproc_src_event(GstPad *pad, GstEvent *event)
+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) {
+ gst_vaapipostproc_destroy(postproc);
+ if (!gst_vaapipostproc_create(postproc))
+ return FALSE;
+ if (!gst_vaapi_plugin_base_set_caps(GST_VAAPI_PLUGIN_BASE(trans),
+ caps, out_caps))
+ return FALSE;
+ }
+
+ if (!ensure_srcpad_buffer_pool(postproc, out_caps))
+ return FALSE;
+ return TRUE;
}
static gboolean
-gst_vaapipostproc_query(GstPad *pad, GstQuery *query)
+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;
- else
- success = gst_pad_query_default(pad, query);
+ if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc))) {
+ GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
+ return TRUE;
+ }
- gst_object_unref(postproc);
- return success;
+ return GST_BASE_TRANSFORM_CLASS(gst_vaapipostproc_parent_class)->query(
+ trans, direction, query);
+}
+
+#if GST_CHECK_VERSION(1,0,0)
+static gboolean
+gst_vaapipostproc_propose_allocation(GstBaseTransform *trans,
+ GstQuery *decide_query, GstQuery *query)
+{
+ GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
+ GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(trans);
+
+ /* Let vaapidecode allocate the video buffers */
+ if (!postproc->is_raw_yuv)
+ return FALSE;
+ if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
+ return FALSE;
+ return TRUE;
}
+#endif
static void
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(parent_class)->finalize(object);
+ gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(postproc));
+ G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
}
static void
GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
switch (prop_id) {
+ case PROP_FORMAT:
+ postproc->format = g_value_get_enum(value);
+ break;
+ case PROP_WIDTH:
+ postproc->width = g_value_get_uint(value);
+ break;
+ case PROP_HEIGHT:
+ postproc->height = g_value_get_uint(value);
+ break;
+ case PROP_FORCE_ASPECT_RATIO:
+ postproc->keep_aspect = g_value_get_boolean(value);
+ break;
case PROP_DEINTERLACE_MODE:
postproc->deinterlace_mode = g_value_get_enum(value);
break;
case PROP_DEINTERLACE_METHOD:
postproc->deinterlace_method = g_value_get_enum(value);
break;
+ case PROP_DENOISE:
+ postproc->denoise_level = g_value_get_float(value);
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
+ break;
+ case PROP_SHARPEN:
+ postproc->sharpen_level = g_value_get_float(value);
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
+ break;
+ case PROP_HUE:
+ postproc->hue = g_value_get_float(value);
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
+ break;
+ case PROP_SATURATION:
+ postproc->saturation = g_value_get_float(value);
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
+ break;
+ case PROP_BRIGHTNESS:
+ postproc->brightness = g_value_get_float(value);
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
+ break;
+ case PROP_CONTRAST:
+ postproc->contrast = g_value_get_float(value);
+ postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
switch (prop_id) {
+ case PROP_FORMAT:
+ g_value_set_enum(value, postproc->format);
+ break;
+ case PROP_WIDTH:
+ g_value_set_uint(value, postproc->width);
+ break;
+ case PROP_HEIGHT:
+ g_value_set_uint(value, postproc->height);
+ break;
+ case PROP_FORCE_ASPECT_RATIO:
+ g_value_set_boolean(value, postproc->keep_aspect);
+ break;
case PROP_DEINTERLACE_MODE:
g_value_set_enum(value, postproc->deinterlace_mode);
break;
case PROP_DEINTERLACE_METHOD:
g_value_set_enum(value, postproc->deinterlace_method);
break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ case PROP_DENOISE:
+ g_value_set_float(value, postproc->denoise_level);
break;
- }
-}
-
-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;
+ case PROP_SHARPEN:
+ g_value_set_float(value, postproc->sharpen_level);
break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
+ case PROP_HUE:
+ g_value_set_float(value, postproc->hue);
break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
- if (ret != GST_STATE_CHANGE_SUCCESS)
- return ret;
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ case PROP_SATURATION:
+ g_value_set_float(value, postproc->saturation);
break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
+ case PROP_BRIGHTNESS:
+ g_value_set_float(value, postproc->brightness);
break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- if (!gst_vaapipostproc_stop(postproc))
- return GST_STATE_CHANGE_FAILURE;
+ case PROP_CONTRAST:
+ g_value_set_float(value, postproc->contrast);
break;
default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
- return GST_STATE_CHANGE_SUCCESS;
}
static void
{
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;
+ GPtrArray *filter_ops;
+ GstVaapiFilterOpInfo *filter_op;
GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
+ gst_vaapi_plugin_base_class_init(GST_VAAPI_PLUGIN_BASE_CLASS(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_size = gst_vaapipostproc_transform_size;
+ trans_class->transform = gst_vaapipostproc_transform;
+ trans_class->set_caps = gst_vaapipostproc_set_caps;
+ trans_class->query = gst_vaapipostproc_query;
+
+#if GST_CHECK_VERSION(1,0,0)
+ trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
+#endif
- 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",
+ "Filter/Converter/Video",
+ GST_PLUGIN_DESC,
+ "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
+
+ /* sink pad */
+ pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
+ gst_element_class_add_pad_template(element_class, pad_template);
+
+ /* src pad */
+ pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
+ gst_element_class_add_pad_template(element_class, pad_template);
/**
- * GstVaapiSink:deinterlace-mode:
+ * GstVaapiPostproc:deinterlace-mode:
*
* This selects whether the deinterlacing should always be applied or if
* they should only be applied on content that has the "interlaced" flag
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_TYPE_VAAPI_DEINTERLACE_MODES,
+ GST_VAAPI_TYPE_DEINTERLACE_MODE,
DEFAULT_DEINTERLACE_MODE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
- * GstVaapiSink:deinterlace-method:
+ * GstVaapiPostproc:deinterlace-method:
*
* This selects the deinterlacing method to apply.
*/
g_param_spec_enum("deinterlace-method",
"Deinterlace method",
"Deinterlace method to use",
- GST_TYPE_VAAPI_DEINTERLACE_METHODS,
+ GST_VAAPI_TYPE_DEINTERLACE_METHOD,
DEFAULT_DEINTERLACE_METHOD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-}
-static void
-gst_vaapipostproc_base_init(gpointer klass)
-{
- GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
- GstPadTemplate *pad_template;
+ filter_ops = gst_vaapi_filter_get_operations(NULL);
+ if (!filter_ops)
+ return;
+
+ /**
+ * GstVaapiPostproc:format:
+ *
+ * The forced output pixel format, expressed as a #GstVideoFormat.
+ */
+ filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
+ if (filter_op)
+ g_object_class_install_property(object_class,
+ PROP_FORMAT, filter_op->pspec);
- gst_element_class_set_details(element_class, &gst_vaapipostproc_details);
+ /**
+ * GstVaapiPostproc:width:
+ *
+ * The forced output width in pixels. If set to zero, the width is
+ * calculated from the height if aspect ration is preserved, or
+ * inherited from the sink caps width
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_WIDTH,
+ g_param_spec_uint("width",
+ "Width",
+ "Forced output width",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /* sink pad */
- pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
- gst_element_class_add_pad_template(element_class, pad_template);
- gst_object_unref(pad_template);
+ /**
+ * GstVaapiPostproc:height:
+ *
+ * The forced output height in pixels. If set to zero, the height
+ * is calculated from the width if aspect ration is preserved, or
+ * inherited from the sink caps height
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_HEIGHT,
+ g_param_spec_uint("height",
+ "Height",
+ "Forced output height",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /* src pad */
- pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
- gst_element_class_add_pad_template(element_class, pad_template);
- gst_object_unref(pad_template);
+ /**
+ * GstVaapiPostproc:force-aspect-ratio:
+ *
+ * When enabled, scaling respects video aspect ratio; when
+ * disabled, the video is distorted to fit the width and height
+ * properties.
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_FORCE_ASPECT_RATIO,
+ g_param_spec_boolean("force-aspect-ratio",
+ "Force aspect ratio",
+ "When enabled, scaling will respect original aspect ratio",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstVaapiPostproc:denoise:
+ *
+ * The level of noise reduction to apply.
+ */
+ filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
+ if (filter_op)
+ g_object_class_install_property(object_class,
+ PROP_DENOISE, filter_op->pspec);
+
+ /**
+ * GstVaapiPostproc:sharpen:
+ *
+ * The level of sharpening to apply for positive values, or the
+ * level of blurring for negative values.
+ */
+ filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
+ if (filter_op)
+ g_object_class_install_property(object_class,
+ PROP_SHARPEN, filter_op->pspec);
+
+ /**
+ * GstVaapiPostproc:hue:
+ *
+ * The color hue, expressed as a float value. Range is -180.0 to
+ * 180.0. Default value is 0.0 and represents no modification.
+ */
+ filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_HUE);
+ if (filter_op)
+ g_object_class_install_property(object_class,
+ PROP_HUE, filter_op->pspec);
+
+ /**
+ * GstVaapiPostproc:saturation:
+ *
+ * The color saturation, expressed as a float value. Range is 0.0
+ * to 2.0. Default value is 1.0 and represents no modification.
+ */
+ filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
+ if (filter_op)
+ g_object_class_install_property(object_class,
+ PROP_SATURATION, filter_op->pspec);
+
+ /**
+ * GstVaapiPostproc:brightness:
+ *
+ * The color brightness, expressed as a float value. Range is -1.0
+ * to 1.0. Default value is 0.0 and represents no modification.
+ */
+ filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
+ if (filter_op)
+ g_object_class_install_property(object_class,
+ PROP_BRIGHTNESS, filter_op->pspec);
+
+ /**
+ * GstVaapiPostproc:contrast:
+ *
+ * The color contrast, expressed as a float value. Range is 0.0 to
+ * 2.0. Default value is 1.0 and represents no modification.
+ */
+ filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
+ if (filter_op)
+ g_object_class_install_property(object_class,
+ PROP_CONTRAST, filter_op->pspec);
+
+ g_ptr_array_unref(filter_ops);
}
static void
-gst_vaapipostproc_init(GstVaapiPostproc *postproc, GstVaapiPostprocClass *klass)
+gst_vaapipostproc_init(GstVaapiPostproc *postproc)
{
- GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
+ gst_vaapi_plugin_base_init(GST_VAAPI_PLUGIN_BASE(postproc), GST_CAT_DEFAULT);
- postproc->allowed_caps = NULL;
- postproc->postproc_caps = NULL;
- postproc->display = NULL;
- postproc->surface_width = 0;
- postproc->surface_height = 0;
- postproc->deinterlace = FALSE;
+ postproc->format = DEFAULT_FORMAT;
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;
-
- gst_pad_set_getcaps_function(postproc->sinkpad, gst_vaapipostproc_get_caps);
- gst_pad_set_setcaps_function(postproc->sinkpad, gst_vaapipostproc_set_caps);
- 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);
+ postproc->keep_aspect = TRUE;
+
+ gst_video_info_init(&postproc->sinkpad_info);
+ gst_video_info_init(&postproc->srcpad_info);
+ gst_video_info_init(&postproc->filter_pool_info);
}