From 945516c9c7ef5120d643c95fbb1efc1d2f4b2311 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Wed, 17 Apr 2013 10:18:45 +0200 Subject: [PATCH] vaapipostproc: port to GStreamer 1.0. Add support for interlaced streams with GStreamer 1.0 too. Basically, this enables vaapipostproc, though it is not auto-plugged yet. We also make sure to reply to CAPS queries, and happily handle CAPS events. --- gst-libs/gst/vaapi/gstcompat.h | 19 +++++++- gst/vaapi/Makefile.am | 4 +- gst/vaapi/gstvaapi.c | 2 - gst/vaapi/gstvaapidecode.c | 22 +++++++-- gst/vaapi/gstvaapipluginutil.h | 39 ++++++++++++++++ gst/vaapi/gstvaapipostproc.c | 85 ++++++++++++++++++++++------------ 6 files changed, 132 insertions(+), 39 deletions(-) diff --git a/gst-libs/gst/vaapi/gstcompat.h b/gst-libs/gst/vaapi/gstcompat.h index 16b3c71f58..a921fa69e4 100644 --- a/gst-libs/gst/vaapi/gstcompat.h +++ b/gst-libs/gst/vaapi/gstcompat.h @@ -58,7 +58,11 @@ gst_compat_structure_get_fourcc(const GstStructure *structure, typedef const guint8 *(*GstCompatTypeFindPeekFunction)(gpointer, gint64, guint); typedef void (*GstCompatTypeFindSuggestFunction)(gpointer, guint, GstCaps *); -/* GstQuery */ +/* GstPad */ +#define GST_PAD_CHAIN_FUNCTION_ARGS \ + GstPad *pad, GstObject *parent, GstBuffer *buffer +#define GST_PAD_EVENT_FUNCTION_ARGS \ + GstPad *pad, GstObject *parent, GstEvent *event #define GST_PAD_QUERY_FUNCTION_ARGS \ GstPad *pad, GstObject *parent, GstQuery *query #define GST_PAD_QUERY_FUNCTION_CALL(func, pad, parent, query) \ @@ -228,9 +232,22 @@ gst_compat_video_overlay_rectangle_get_pixels_unscaled_raw( &width, &height, &stride, flags); } +typedef enum { + GST_VIDEO_BUFFER_FLAG_TFF = GST_VIDEO_BUFFER_TFF, + GST_VIDEO_BUFFER_FLAG_RFF = GST_VIDEO_BUFFER_RFF, + GST_VIDEO_BUFFER_FLAG_ONEFIELD = GST_VIDEO_BUFFER_ONEFIELD +} GstVideoBufferFlags; + /* GstPad */ #undef GST_FLOW_EOS #define GST_FLOW_EOS GST_FLOW_UNEXPECTED +#undef GST_FLOW_FLUSHING +#define GST_FLOW_FLUSHING GST_FLOW_WRONG_STATE + +#define GST_PAD_CHAIN_FUNCTION_ARGS \ + GstPad *pad, GstBuffer *buffer +#define GST_PAD_EVENT_FUNCTION_ARGS \ + GstPad *pad, GstEvent *event /* GstElement */ #undef gst_element_class_set_static_metadata diff --git a/gst/vaapi/Makefile.am b/gst/vaapi/Makefile.am index d73bbadc33..bd9dedd4d8 100644 --- a/gst/vaapi/Makefile.am +++ b/gst/vaapi/Makefile.am @@ -33,6 +33,7 @@ libgstvaapi_source_c = \ gstvaapi.c \ gstvaapidecode.c \ gstvaapipluginutil.c \ + gstvaapipostproc.c \ gstvaapisink.c \ gstvaapiuploader.c \ gstvaapivideobuffer.c \ @@ -42,6 +43,7 @@ libgstvaapi_source_c = \ libgstvaapi_source_h = \ gstvaapidecode.h \ gstvaapipluginutil.h \ + gstvaapipostproc.h \ gstvaapisink.h \ gstvaapiuploader.h \ gstvaapivideobuffer.h \ @@ -74,13 +76,11 @@ endif if USE_GST_API_0_10 libgstvaapi_0_10_source_c = \ gstvaapidownload.c \ - gstvaapipostproc.c \ gstvaapiupload.c \ $(NULL) libgstvaapi_0_10_source_h = \ gstvaapidownload.h \ - gstvaapipostproc.h \ gstvaapiupload.h \ $(NULL) endif diff --git a/gst/vaapi/gstvaapi.c b/gst/vaapi/gstvaapi.c index 600632d60f..3000b2296f 100644 --- a/gst/vaapi/gstvaapi.c +++ b/gst/vaapi/gstvaapi.c @@ -47,11 +47,9 @@ plugin_init (GstPlugin *plugin) gst_element_register(plugin, "vaapidecode", GST_RANK_PRIMARY, GST_TYPE_VAAPIDECODE); -#if !GST_CHECK_VERSION(1,0,0) gst_element_register(plugin, "vaapipostproc", GST_RANK_PRIMARY, GST_TYPE_VAAPIPOSTPROC); -#endif gst_element_register(plugin, "vaapisink", GST_RANK_PRIMARY, GST_TYPE_VAAPISINK); diff --git a/gst/vaapi/gstvaapidecode.c b/gst/vaapi/gstvaapidecode.c index f0e5654a0f..cd773f7502 100644 --- a/gst/vaapi/gstvaapidecode.c +++ b/gst/vaapi/gstvaapidecode.c @@ -188,10 +188,11 @@ gst_vaapidecode_update_src_caps(GstVaapiDecode *decode, "pixel-aspect-ratio", GST_TYPE_FRACTION, vi->par_n, vi->par_d, NULL); - if (GST_VIDEO_INFO_IS_INTERLACED(vi)) - gst_caps_set_simple(state->caps, "interlaced", G_TYPE_BOOLEAN, - TRUE, NULL); - + if (GST_VIDEO_INFO_IS_INTERLACED(vi)) { + GstStructure * const structure = + gst_caps_get_structure(state->caps, 0); + gst_structure_set_interlaced(structure, TRUE); + } gst_caps_replace(&decode->srcpad_caps, state->caps); return TRUE; } @@ -274,6 +275,7 @@ gst_vaapidecode_push_decoded_frames(GstVideoDecoder *vdec) GstVaapiVideoMeta *meta; GstVideoCodecFrame *out_frame; GstFlowReturn ret; + guint flags; /* Output all decoded frames */ for (;;) { @@ -296,6 +298,18 @@ gst_vaapidecode_push_decoded_frames(GstVideoDecoder *vdec) if (!meta) goto error_get_meta; gst_vaapi_video_meta_set_surface_proxy(meta, proxy); + + flags = gst_vaapi_surface_proxy_get_flags(proxy); + if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_INTERLACED) { + guint out_flags = GST_VIDEO_BUFFER_FLAG_INTERLACED; + if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_TFF) + out_flags |= GST_VIDEO_BUFFER_FLAG_TFF; + if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_RFF) + out_flags |= GST_VIDEO_BUFFER_FLAG_RFF; + if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_ONEFIELD) + out_flags |= GST_VIDEO_BUFFER_FLAG_ONEFIELD; + GST_BUFFER_FLAG_SET(out_frame->output_buffer, out_flags); + } #else out_frame->output_buffer = gst_vaapi_video_buffer_new_with_surface_proxy(proxy); diff --git a/gst/vaapi/gstvaapipluginutil.h b/gst/vaapi/gstvaapipluginutil.h index 8cb70d17dd..6212a9d648 100644 --- a/gst/vaapi/gstvaapipluginutil.h +++ b/gst/vaapi/gstvaapipluginutil.h @@ -61,4 +61,43 @@ gst_vaapi_apply_composition(GstVaapiSurface *surface, GstBuffer *buffer); } while (0) #endif +/* Helpers to handle interlaced contents */ +#if GST_CHECK_VERSION(1,0,0) +# define GST_CAPS_INTERLACED_MODES \ + "interlace-mode = (string){ progressive, interleaved }" +# define GST_CAPS_INTERLACED_FALSE \ + "interlace-mode = (string)progressive" + +static inline void +gst_structure_remove_interlaced_field(GstStructure *structure) +{ + gst_structure_remove_field(structure, "interlace-mode"); +} + +static inline void +gst_structure_set_interlaced(GstStructure *structure, gboolean interlaced) +{ + gst_structure_set(structure, "interlace-mode", + G_TYPE_STRING, interlaced ? "interleaved" : "progressive", NULL); +} +#else +# define GST_CAPS_INTERLACED_MODES \ + "interlaced = (boolean){ true, false }" +# define GST_CAPS_INTERLACED_FALSE \ + "interlaced = (boolean)false" + +static inline void +gst_structure_remove_interlaced_field(GstStructure *structure) +{ + gst_structure_remove_field(structure, "interlaced"); +} + +static inline void +gst_structure_set_interlaced(GstStructure *structure, gboolean interlaced) +{ + gst_structure_set(structure, "interlaced", + G_TYPE_BOOLEAN, interlaced, NULL); +} +#endif + #endif /* GST_VAAPI_PLUGIN_UTIL_H */ diff --git a/gst/vaapi/gstvaapipostproc.c b/gst/vaapi/gstvaapipostproc.c index 534017e693..28a9cc3574 100644 --- a/gst/vaapi/gstvaapipostproc.c +++ b/gst/vaapi/gstvaapipostproc.c @@ -45,11 +45,11 @@ GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapipostproc); /* Default templates */ static const char gst_vaapipostproc_sink_caps_str[] = GST_VAAPI_SURFACE_CAPS ", " - "interlaced = (boolean) { true, false }"; + GST_CAPS_INTERLACED_MODES; static const char gst_vaapipostproc_src_caps_str[] = GST_VAAPI_SURFACE_CAPS ", " - "interlaced = (boolean) false"; + GST_CAPS_INTERLACED_FALSE; static GstStaticPadTemplate gst_vaapipostproc_sink_factory = GST_STATIC_PAD_TEMPLATE( @@ -66,6 +66,7 @@ static GstStaticPadTemplate gst_vaapipostproc_src_factory = GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str)); /* GstImplementsInterface interface */ +#if !GST_CHECK_VERSION(1,0,0) static gboolean gst_vaapipostproc_implements_interface_supported( GstImplementsInterface *iface, @@ -80,6 +81,7 @@ gst_vaapipostproc_implements_iface_init(GstImplementsInterfaceClass *iface) { iface->supported = gst_vaapipostproc_implements_interface_supported; } +#endif /* GstVideoContext interface */ static void @@ -105,8 +107,10 @@ G_DEFINE_TYPE_WITH_CODE( GstVaapiPostproc, gst_vaapipostproc, GST_TYPE_ELEMENT, +#if !GST_CHECK_VERSION(1,0,0) G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE, gst_vaapipostproc_implements_iface_init); +#endif G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT, gst_video_context_interface_init)) @@ -263,7 +267,7 @@ gst_vaapipostproc_process(GstVaapiPostproc *postproc, GstBuffer *buf) timestamp = GST_BUFFER_TIMESTAMP(buf); proxy = gst_vaapi_video_meta_get_surface_proxy(meta); - tff = GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_TFF); + tff = GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_TFF); flags &= ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD| GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD); @@ -284,7 +288,9 @@ gst_vaapipostproc_process(GstVaapiPostproc *postproc, GstBuffer *buf) 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); if (ret != GST_FLOW_OK) goto error_push_buffer; @@ -305,7 +311,9 @@ 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; @@ -318,39 +326,36 @@ error_invalid_buffer: { GST_ERROR("failed to receive a valid video buffer"); gst_buffer_unref(buf); - return GST_FLOW_UNEXPECTED; + return GST_FLOW_EOS; } 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) { - gint fps_n, fps_d; - gboolean interlaced; + GstVideoInfo vi; - if (!gst_video_parse_caps_framerate(caps, &fps_n, &fps_d)) + if (!gst_video_info_from_caps(&vi, caps)) return FALSE; - postproc->fps_n = fps_n; - postproc->fps_d = fps_d; + postproc->fps_n = GST_VIDEO_INFO_FPS_N(&vi); + postproc->fps_d = GST_VIDEO_INFO_FPS_D(&vi); switch (postproc->deinterlace_mode) { case GST_VAAPI_DEINTERLACE_MODE_AUTO: - if (!gst_video_format_parse_caps_interlaced(caps, &interlaced)) - return FALSE; - postproc->deinterlace = interlaced; + postproc->deinterlace = GST_VIDEO_INFO_IS_INTERLACED(&vi); break; case GST_VAAPI_DEINTERLACE_MODE_INTERLACED: postproc->deinterlace = TRUE; @@ -403,7 +408,7 @@ gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps) gst_structure_set(structure, "opengl", G_TYPE_BOOLEAN, USE_GLX, NULL); if (!postproc->deinterlace) - gst_structure_remove_field(structure, "interlaced"); + gst_structure_remove_interlaced_field(structure); else { /* Set double framerate in interlaced mode */ if (!gst_util_fraction_multiply(postproc->fps_n, postproc->fps_d, @@ -411,12 +416,9 @@ gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps) &fps_n, &fps_d)) return FALSE; - gst_structure_set( - structure, - "interlaced", G_TYPE_BOOLEAN, FALSE, - "framerate", GST_TYPE_FRACTION, fps_n, fps_d, - NULL - ); + gst_structure_set(structure, "framerate", + GST_TYPE_FRACTION, fps_n, fps_d, NULL); + gst_structure_set_interlaced(structure, FALSE); } return gst_pad_set_caps(postproc->srcpad, src_caps); } @@ -473,32 +475,44 @@ gst_vaapipostproc_set_caps(GstPad *pad, GstCaps *caps) } static GstFlowReturn -gst_vaapipostproc_chain(GstPad *pad, GstBuffer *buf) +gst_vaapipostproc_chain(GST_PAD_CHAIN_FUNCTION_ARGS) { GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad); GstFlowReturn ret; - ret = gst_vaapipostproc_process(postproc, buf); + ret = gst_vaapipostproc_process(postproc, buffer); gst_object_unref(postproc); return ret; } static gboolean -gst_vaapipostproc_sink_event(GstPad *pad, GstEvent *event) +gst_vaapipostproc_sink_event(GST_PAD_EVENT_FUNCTION_ARGS) { GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad); gboolean success; GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event)); - /* Propagate event downstream */ - success = gst_pad_push_event(postproc->srcpad, event); + 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; } static gboolean -gst_vaapipostproc_src_event(GstPad *pad, GstEvent *event) +gst_vaapipostproc_src_event(GST_PAD_EVENT_FUNCTION_ARGS) { GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad); gboolean success; @@ -512,7 +526,7 @@ gst_vaapipostproc_src_event(GstPad *pad, GstEvent *event) } static gboolean -gst_vaapipostproc_query(GstPad *pad, GstQuery *query) +gst_vaapipostproc_query(GST_PAD_QUERY_FUNCTION_ARGS) { GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad); gboolean success; @@ -521,8 +535,17 @@ gst_vaapipostproc_query(GstPad *pad, GstQuery *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; + } +#endif else - success = gst_pad_query_default(pad, query); + success = GST_PAD_QUERY_FUNCTION_CALL(gst_pad_query_default, pad, + parent, query); gst_object_unref(postproc); return success; @@ -714,8 +737,10 @@ gst_vaapipostproc_init(GstVaapiPostproc *postproc) ); 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);