diff options
author | Arnaud Vrac <avrac@freebox.fr> | 2014-08-27 16:47:22 +0200 |
---|---|---|
committer | Jan Schmidt <jan@centricular.com> | 2015-09-26 23:29:15 +1000 |
commit | 1899e2a099932a7a734e7784c20b624517237e18 (patch) | |
tree | ab6cfb93dde69dbc07f3363e44cee7ed407c7f99 /gst/dvdspu | |
parent | aabb8a1a68372f750c63b9b6586e18e829745774 (diff) |
dvdspu: improve negotiation of overlay composition
Support negotiating GstVideoOverlayComposition downstream
while not providing it upstream.
https://bugzilla.gnome.org/show_bug.cgi?id=663750
Diffstat (limited to 'gst/dvdspu')
-rw-r--r-- | gst/dvdspu/gstdvdspu.c | 393 | ||||
-rw-r--r-- | gst/dvdspu/gstdvdspu.h | 1 |
2 files changed, 344 insertions, 50 deletions
diff --git a/gst/dvdspu/gstdvdspu.c b/gst/dvdspu/gstdvdspu.c index cf0a63b93..50edc920a 100644 --- a/gst/dvdspu/gstdvdspu.c +++ b/gst/dvdspu/gstdvdspu.c @@ -58,17 +58,23 @@ enum #define VIDEO_FORMATS GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS +#define DVDSPU_CAPS GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS) +#define DVDSPU_ALL_CAPS DVDSPU_CAPS ";" \ + GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL) + +static GstStaticCaps sw_template_caps = GST_STATIC_CAPS (DVDSPU_CAPS); + static GstStaticPadTemplate video_sink_factory = GST_STATIC_PAD_TEMPLATE ("video", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS)) + GST_STATIC_CAPS (DVDSPU_ALL_CAPS) ); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS)) + GST_STATIC_CAPS (DVDSPU_ALL_CAPS) ); static GstStaticPadTemplate subpic_sink_factory = @@ -90,8 +96,10 @@ static gboolean gst_dvd_spu_src_event (GstPad * pad, GstObject * parent, GstEvent * event); static gboolean gst_dvd_spu_src_query (GstPad * pad, GstObject * parent, GstQuery * query); +static GstCaps *gst_dvd_spu_src_get_caps (GstDVDSpu * dvdspu, GstPad * pad, + GstCaps * filter); -static GstCaps *gst_dvd_spu_video_proxy_getcaps (GstPad * pad, +static GstCaps *gst_dvd_spu_video_get_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * filter); static gboolean gst_dvd_spu_video_set_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * caps); @@ -110,7 +118,7 @@ static gboolean gst_dvd_spu_subpic_event (GstPad * pad, GstObject * parent, GstEvent * event); static gboolean gst_dvd_spu_subpic_set_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * caps); -static gboolean gst_dvd_spu_negotiate (GstDVDSpu * dvdspu); +static gboolean gst_dvd_spu_negotiate (GstDVDSpu * dvdspu, GstCaps * caps); static void gst_dvd_spu_clear (GstDVDSpu * dvdspu); static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu, @@ -302,6 +310,7 @@ gst_dvd_spu_src_event (GstPad * pad, GstObject * parent, GstEvent * event) static gboolean gst_dvd_spu_src_query (GstPad * pad, GstObject * parent, GstQuery * query) { + GstDVDSpu *dvdspu = GST_DVD_SPU (parent); gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { @@ -310,7 +319,7 @@ gst_dvd_spu_src_query (GstPad * pad, GstObject * parent, GstQuery * query) GstCaps *filter, *caps; gst_query_parse_caps (query, &filter); - caps = gst_dvd_spu_video_proxy_getcaps (pad, filter); + caps = gst_dvd_spu_src_get_caps (dvdspu, pad, filter); gst_query_set_caps_result (query, caps); gst_caps_unref (caps); res = TRUE; @@ -325,53 +334,238 @@ gst_dvd_spu_src_query (GstPad * pad, GstObject * parent, GstQuery * query) } static gboolean +gst_dvd_spu_can_handle_caps (GstCaps * caps) +{ + GstCaps *sw_caps; + gboolean ret; + + sw_caps = gst_static_caps_get (&sw_template_caps); + ret = gst_caps_is_subset (caps, sw_caps); + gst_caps_unref (sw_caps); + + return ret; +} + +static gboolean gst_dvd_spu_video_set_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * caps) { - gboolean res = FALSE; GstVideoInfo info; - SpuState *state; + gboolean ret = FALSE; if (!gst_video_info_from_caps (&info, caps)) - goto done; + goto invalid_caps; - res = gst_pad_set_caps (dvdspu->srcpad, caps); - if (res) { - DVD_SPU_LOCK (dvdspu); - state = &dvdspu->spu_state; - state->info = info; - gst_dvd_spu_negotiate (dvdspu); - DVD_SPU_UNLOCK (dvdspu); + dvdspu->spu_state.info = info; + + ret = gst_dvd_spu_negotiate (dvdspu, caps); + + DVD_SPU_LOCK (dvdspu); + if (!dvdspu->attach_compo_to_buffer && !gst_dvd_spu_can_handle_caps (caps)) { + GST_DEBUG_OBJECT (dvdspu, "unsupported caps %" GST_PTR_FORMAT, caps); + ret = FALSE; } -done: - return res; + DVD_SPU_UNLOCK (dvdspu); + + return ret; + + /* ERRORS */ +invalid_caps: + { + GST_DEBUG_OBJECT (dvdspu, "could not parse caps"); + return FALSE; + } } + +/** + * gst_dvd_spu_add_feature_and_intersect: + * + * Creates a new #GstCaps containing the (given caps + + * given caps feature) + (given caps intersected by the + * given filter). + * + * Returns: the new #GstCaps + */ static GstCaps * -gst_dvd_spu_video_proxy_getcaps (GstPad * pad, GstCaps * filter) +gst_dvd_spu_add_feature_and_intersect (GstCaps * caps, + const gchar * feature, GstCaps * filter) { - GstDVDSpu *dvdspu = GST_DVD_SPU (gst_pad_get_parent (pad)); - GstCaps *caps; - GstPad *otherpad; + int i, caps_size; + GstCaps *new_caps; - /* Proxy the getcaps between videosink and the srcpad, ignoring the - * subpicture sink pad */ - otherpad = (pad == dvdspu->srcpad) ? dvdspu->videosinkpad : dvdspu->srcpad; + new_caps = gst_caps_copy (caps); - caps = gst_pad_peer_query_caps (otherpad, filter); - if (caps) { - GstCaps *temp, *templ; + caps_size = gst_caps_get_size (new_caps); + for (i = 0; i < caps_size; i++) { + GstCapsFeatures *features = gst_caps_get_features (new_caps, i); + if (!gst_caps_features_is_any (features)) { + gst_caps_features_add (features, feature); + } + } + + gst_caps_append (new_caps, gst_caps_intersect_full (caps, + filter, GST_CAPS_INTERSECT_FIRST)); + + return new_caps; +} + +/** + * gst_dvd_spu_intersect_by_feature: + * + * Creates a new #GstCaps based on the following filtering rule. + * + * For each individual caps contained in given caps, if the + * caps uses the given caps feature, keep a version of the caps + * with the feature and an another one without. Otherwise, intersect + * the caps with the given filter. + * + * Returns: the new #GstCaps + */ +static GstCaps * +gst_dvd_spu_intersect_by_feature (GstCaps * caps, + const gchar * feature, GstCaps * filter) +{ + int i, caps_size; + GstCaps *new_caps; + + new_caps = gst_caps_new_empty (); + + caps_size = gst_caps_get_size (caps); + for (i = 0; i < caps_size; i++) { + GstStructure *caps_structure = gst_caps_get_structure (caps, i); + GstCapsFeatures *caps_features = + gst_caps_features_copy (gst_caps_get_features (caps, i)); + GstCaps *filtered_caps; + GstCaps *simple_caps = + gst_caps_new_full (gst_structure_copy (caps_structure), NULL); + gst_caps_set_features (simple_caps, 0, caps_features); + + if (gst_caps_features_contains (caps_features, feature)) { + gst_caps_append (new_caps, gst_caps_copy (simple_caps)); + + gst_caps_features_remove (caps_features, feature); + filtered_caps = gst_caps_ref (simple_caps); + } else { + filtered_caps = gst_caps_intersect_full (simple_caps, filter, + GST_CAPS_INTERSECT_FIRST); + } - templ = gst_pad_get_pad_template_caps (otherpad); - temp = gst_caps_intersect (caps, templ); - gst_caps_unref (templ); + gst_caps_unref (simple_caps); + gst_caps_append (new_caps, filtered_caps); + } + + return new_caps; +} + +static GstCaps * +gst_dvd_spu_video_get_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * filter) +{ + GstPad *srcpad = dvdspu->srcpad; + GstCaps *peer_caps = NULL, *caps = NULL, *dvdspu_filter = NULL; + + if (filter) { + /* filter caps + composition feature + filter caps + * filtered by the software caps. */ + GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps); + dvdspu_filter = gst_dvd_spu_add_feature_and_intersect (filter, + GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps); + gst_caps_unref (sw_caps); + + GST_DEBUG_OBJECT (dvdspu, "dvdspu filter %" GST_PTR_FORMAT, dvdspu_filter); + } + + peer_caps = gst_pad_peer_query_caps (srcpad, dvdspu_filter); + + if (dvdspu_filter) + gst_caps_unref (dvdspu_filter); + + if (peer_caps) { + GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, peer_caps); + + if (gst_caps_is_any (peer_caps)) { + /* if peer returns ANY caps, return filtered src pad template caps */ + caps = gst_caps_copy (gst_pad_get_pad_template_caps (srcpad)); + } else { + /* duplicate caps which contains the composition into one version with + * the meta and one without. Filter the other caps by the software caps */ + GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps); + caps = gst_dvd_spu_intersect_by_feature (peer_caps, + GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps); + gst_caps_unref (sw_caps); + } + + gst_caps_unref (peer_caps); + + } else { + /* no peer, our padtemplate is enough then */ + caps = gst_pad_get_pad_template_caps (pad); + } + + if (filter) { + GstCaps *intersection = gst_caps_intersect_full (filter, caps, + GST_CAPS_INTERSECT_FIRST); gst_caps_unref (caps); - caps = temp; + caps = intersection; + } + + GST_DEBUG_OBJECT (dvdspu, "returning %" GST_PTR_FORMAT, caps); + + return caps; +} + +static GstCaps * +gst_dvd_spu_src_get_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * filter) +{ + GstPad *sinkpad = dvdspu->videosinkpad; + GstCaps *peer_caps = NULL, *caps = NULL, *dvdspu_filter = NULL; + + if (filter) { + /* duplicate filter caps which contains the composition into one version + * with the meta and one without. Filter the other caps by the software + * caps */ + GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps); + dvdspu_filter = gst_dvd_spu_intersect_by_feature (filter, + GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps); + gst_caps_unref (sw_caps); + } + + peer_caps = gst_pad_peer_query_caps (sinkpad, dvdspu_filter); + + if (dvdspu_filter) + gst_caps_unref (dvdspu_filter); + + if (peer_caps) { + GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, peer_caps); + + if (gst_caps_is_any (peer_caps)) { + /* if peer returns ANY caps, return filtered sink pad template caps */ + caps = gst_caps_copy (gst_pad_get_pad_template_caps (sinkpad)); + } else { + /* return upstream caps + composition feature + upstream caps + * filtered by the software caps. */ + GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps); + caps = gst_dvd_spu_add_feature_and_intersect (peer_caps, + GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps); + gst_caps_unref (sw_caps); + } + + gst_caps_unref (peer_caps); + } else { + /* no peer, our padtemplate is enough then */ caps = gst_pad_get_pad_template_caps (pad); } - gst_object_unref (dvdspu); + if (filter) { + GstCaps *intersection = gst_caps_intersect_full (filter, caps, + GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (caps); + caps = intersection; + } + + GST_DEBUG_OBJECT (dvdspu, "returning %" GST_PTR_FORMAT, caps); + return caps; } @@ -500,12 +694,16 @@ gst_dvd_spu_video_event (GstPad * pad, GstObject * parent, GstEvent * event) break; } case GST_EVENT_FLUSH_START: + DVD_SPU_LOCK (dvdspu); + dvdspu->video_flushing = TRUE; + DVD_SPU_UNLOCK (dvdspu); res = gst_pad_event_default (pad, parent, event); goto done; case GST_EVENT_FLUSH_STOP: res = gst_pad_event_default (pad, parent, event); DVD_SPU_LOCK (dvdspu); + dvdspu->video_flushing = FALSE; gst_segment_init (&dvdspu->video_seg, GST_FORMAT_UNDEFINED); gst_buffer_replace (&dvdspu->ref_frame, NULL); gst_buffer_replace (&dvdspu->pending_frame, NULL); @@ -529,6 +727,7 @@ error: static gboolean gst_dvd_spu_video_query (GstPad * pad, GstObject * parent, GstQuery * query) { + GstDVDSpu *dvdspu = GST_DVD_SPU (parent); gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { @@ -537,7 +736,7 @@ gst_dvd_spu_video_query (GstPad * pad, GstObject * parent, GstQuery * query) GstCaps *filter, *caps; gst_query_parse_caps (query, &filter); - caps = gst_dvd_spu_video_proxy_getcaps (pad, filter); + caps = gst_dvd_spu_video_get_caps (dvdspu, pad, filter); gst_query_set_caps_result (query, caps); gst_caps_unref (caps); res = TRUE; @@ -560,7 +759,7 @@ gst_dvd_spu_video_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) g_return_val_if_fail (dvdspu != NULL, GST_FLOW_ERROR); if (gst_pad_check_reconfigure (dvdspu->srcpad)) - gst_dvd_spu_negotiate (dvdspu); + gst_dvd_spu_negotiate (dvdspu, NULL); GST_LOG_OBJECT (dvdspu, "video buffer %p with TS %" GST_TIME_FORMAT, buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); @@ -1062,40 +1261,134 @@ submit_new_spu_packet (GstDVDSpu * dvdspu, GstBuffer * buf) } static gboolean -gst_dvd_spu_negotiate (GstDVDSpu * dvdspu) +gst_dvd_spu_negotiate (GstDVDSpu * dvdspu, GstCaps * caps) { - GstQuery *query; - GstCaps *caps; + gboolean upstream_has_meta = FALSE; + gboolean caps_has_meta = FALSE; + gboolean alloc_has_meta = FALSE; gboolean attach = FALSE; + gboolean ret = TRUE; + GstCapsFeatures *f; + GstCaps *overlay_caps; + GstQuery *query; + + GST_DEBUG_OBJECT (dvdspu, "performing negotiation"); + /* Clear the cached composition */ gst_dvd_spu_reset_composition (dvdspu); - GST_DEBUG_OBJECT (dvdspu, "performing negotiation"); + /* Clear any pending reconfigure to avoid negotiating twice */ + gst_pad_check_reconfigure (dvdspu->srcpad); + + if (!caps) + caps = gst_pad_get_current_caps (dvdspu->videosinkpad); + else + gst_caps_ref (caps); - caps = gst_pad_get_current_caps (dvdspu->srcpad); if (!caps || gst_caps_is_empty (caps)) goto no_format; - query = gst_query_new_allocation (caps, FALSE); + /* Check if upstream caps have meta */ + if ((f = gst_caps_get_features (caps, 0))) { + upstream_has_meta = gst_caps_features_contains (f, + GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION); + } + + if (upstream_has_meta) { + overlay_caps = gst_caps_ref (caps); + } else { + GstCaps *peercaps; + + /* BaseTransform requires caps for the allocation query to work */ + overlay_caps = gst_caps_copy (caps); + f = gst_caps_get_features (overlay_caps, 0); + gst_caps_features_add (f, + GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION); + + /* Then check if downstream accept dvdspu composition in caps */ + /* FIXME: We should probably check if downstream *prefers* the + * dvdspu meta, and only enforce usage of it if we can't handle + * the format ourselves and thus would have to drop the overlays. + * Otherwise we should prefer what downstream wants here. + */ + peercaps = gst_pad_peer_query_caps (dvdspu->srcpad, NULL); + caps_has_meta = gst_caps_can_intersect (peercaps, overlay_caps); + gst_caps_unref (peercaps); + + GST_DEBUG ("caps have dvdspu meta %d", caps_has_meta); + } + + if (upstream_has_meta || caps_has_meta) { + /* Send caps immediatly, it's needed by GstBaseTransform to get a reply + * from allocation query */ + ret = gst_pad_set_caps (dvdspu->srcpad, overlay_caps); + + /* First check if the allocation meta has compositon */ + query = gst_query_new_allocation (overlay_caps, FALSE); - if (!gst_pad_peer_query (dvdspu->srcpad, query)) - GST_DEBUG_OBJECT (dvdspu, "ALLOCATION query failed"); + if (!gst_pad_peer_query (dvdspu->srcpad, query)) { + /* no problem, we use the query defaults */ + GST_DEBUG_OBJECT (dvdspu, "ALLOCATION query failed"); - if (gst_query_find_allocation_meta (query, - GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL)) + /* In case we were flushing, mark reconfigure and fail this method, + * will make it retry */ + if (dvdspu->video_flushing) + ret = FALSE; + } + + alloc_has_meta = gst_query_find_allocation_meta (query, + GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL); + + GST_DEBUG ("sink alloc has dvdspu meta %d", alloc_has_meta); + + gst_query_unref (query); + } + + /* For backward compatbility, we will prefer bliting if downstream + * allocation does not support the meta. In other case we will prefer + * attaching, and will fail the negotiation in the unlikely case we are + * force to blit, but format isn't supported. */ + + if (upstream_has_meta) { attach = TRUE; + } else if (caps_has_meta) { + if (alloc_has_meta) { + attach = TRUE; + } else { + /* Don't attach unless we cannot handle the format */ + attach = !gst_dvd_spu_can_handle_caps (caps); + } + } else { + ret = gst_dvd_spu_can_handle_caps (caps); + } - gst_query_unref (query); - gst_caps_unref (caps); + /* If we attach, then pick the dvdspu caps */ + if (attach) { + GST_DEBUG_OBJECT (dvdspu, "Using caps %" GST_PTR_FORMAT, overlay_caps); + /* Caps where already sent */ + } else if (ret) { + GST_DEBUG_OBJECT (dvdspu, "Using caps %" GST_PTR_FORMAT, caps); + ret = gst_pad_set_caps (dvdspu->srcpad, caps); + } dvdspu->attach_compo_to_buffer = attach; - return TRUE; + if (!ret) { + GST_DEBUG_OBJECT (dvdspu, "negotiation failed, schedule reconfigure"); + gst_pad_mark_reconfigure (dvdspu->srcpad); + } + + gst_caps_unref (overlay_caps); + gst_caps_unref (caps); + + return ret; no_format: - if (caps) - gst_caps_unref (caps); - return FALSE; + { + if (caps) + gst_caps_unref (caps); + return FALSE; + } } static GstFlowReturn diff --git a/gst/dvdspu/gstdvdspu.h b/gst/dvdspu/gstdvdspu.h index 6fa700cb3..61d3e9c42 100644 --- a/gst/dvdspu/gstdvdspu.h +++ b/gst/dvdspu/gstdvdspu.h @@ -94,6 +94,7 @@ struct _GstDVDSpu { /* Mutex to protect state we access from different chain funcs */ GMutex spu_lock; + gboolean video_flushing; GstSegment video_seg; GstSegment subp_seg; |