summaryrefslogtreecommitdiff
path: root/gst/dvdspu
diff options
context:
space:
mode:
authorArnaud Vrac <avrac@freebox.fr>2014-08-27 16:47:22 +0200
committerJan Schmidt <jan@centricular.com>2015-09-26 23:29:15 +1000
commit1899e2a099932a7a734e7784c20b624517237e18 (patch)
treeab6cfb93dde69dbc07f3363e44cee7ed407c7f99 /gst/dvdspu
parentaabb8a1a68372f750c63b9b6586e18e829745774 (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.c393
-rw-r--r--gst/dvdspu/gstdvdspu.h1
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;