mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-29 21:21:12 +00:00
subtitleoverlay: Handle video sink pad CAPS query earlier
The internal elements are only created when caps on both video and subtitle pads are known. Prior to that, a GST_QUERY_CAPS on a video sink pad would just return ANY instead of giving a hint of what downstream can actually handle and prefers. This could result in upstream elements (such as decoders) deciding on chosing (in the best cases) a non-optimal caps or (in the worst case) caps that couldn't be handled by the elements downstream of subtitleoverlay. In order to fix that, we assume that all subtitle "elements" handle the subtitle overlay composition feature/meta and handle `GST_QUERY_CAPS` ourselves if the internal elements aren't present yet. Fixes #3176 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5834>
This commit is contained in:
parent
b13ea85140
commit
87bb3a8e2e
1 changed files with 188 additions and 0 deletions
|
@ -55,6 +55,10 @@ GST_DEBUG_CATEGORY_STATIC (subtitle_overlay_debug);
|
||||||
#define IS_VIDEO_CHAIN_IGNORE_ERROR(flow) \
|
#define IS_VIDEO_CHAIN_IGNORE_ERROR(flow) \
|
||||||
G_UNLIKELY (flow == GST_FLOW_ERROR)
|
G_UNLIKELY (flow == GST_FLOW_ERROR)
|
||||||
|
|
||||||
|
/* We have colorspace conversions so we can accept any known video formats */
|
||||||
|
#define SUBTITLE_OVERLAY_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)
|
||||||
|
static GstStaticCaps sw_template_caps = GST_STATIC_CAPS (SUBTITLE_OVERLAY_CAPS);
|
||||||
|
|
||||||
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
|
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
GST_PAD_SRC,
|
GST_PAD_SRC,
|
||||||
GST_PAD_ALWAYS,
|
GST_PAD_ALWAYS,
|
||||||
|
@ -1796,6 +1800,186 @@ done:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_subtitle_overlay_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_subtitle_overlay_add_feature_and_intersect (GstCaps * caps,
|
||||||
|
const gchar * feature, GstCaps * filter)
|
||||||
|
{
|
||||||
|
int i, caps_size;
|
||||||
|
GstCaps *new_caps;
|
||||||
|
|
||||||
|
new_caps = gst_caps_copy (caps);
|
||||||
|
|
||||||
|
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_subtitle_overlay_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_subtitle_overlay_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_caps_unref (simple_caps);
|
||||||
|
gst_caps_append (new_caps, filtered_caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_subtitle_overlay_get_videosink_caps (GstSubtitleOverlay * overlay,
|
||||||
|
GstPad * pad, GstCaps * filter)
|
||||||
|
{
|
||||||
|
GstPad *srcpad = overlay->srcpad;
|
||||||
|
GstCaps *peer_caps = NULL, *caps = NULL, *overlay_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);
|
||||||
|
overlay_filter = gst_subtitle_overlay_add_feature_and_intersect (filter,
|
||||||
|
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
|
||||||
|
gst_caps_unref (sw_caps);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (overlay, "overlay filter %" GST_PTR_FORMAT,
|
||||||
|
overlay_filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
peer_caps = gst_pad_peer_query_caps (srcpad, overlay_filter);
|
||||||
|
|
||||||
|
if (overlay_filter)
|
||||||
|
gst_caps_unref (overlay_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_subtitle_overlay_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 = intersection;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
|
||||||
|
|
||||||
|
return caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_subtitle_overlay_video_sink_query (GstPad * pad, GstObject * parent,
|
||||||
|
GstQuery * query)
|
||||||
|
{
|
||||||
|
GstSubtitleOverlay *overlay = (GstSubtitleOverlay *) parent;
|
||||||
|
gboolean res = FALSE;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (pad, "query %" GST_PTR_FORMAT, query);
|
||||||
|
|
||||||
|
switch (GST_QUERY_TYPE (query)) {
|
||||||
|
case GST_QUERY_CAPS:
|
||||||
|
{
|
||||||
|
GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad));
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
/* If we have a peer, we let the default handler take care of it */
|
||||||
|
res = gst_pad_query_default (pad, parent, query);
|
||||||
|
gst_object_unref (target);
|
||||||
|
} else {
|
||||||
|
GstCaps *filter, *caps;
|
||||||
|
/* We haven't instantiated the internal elements yet, we handle it ourselves */
|
||||||
|
gst_query_parse_caps (query, &filter);
|
||||||
|
caps = gst_subtitle_overlay_get_videosink_caps (overlay, pad, filter);
|
||||||
|
gst_query_set_caps_result (query, caps);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
res = TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
res = gst_pad_query_default (pad, parent, query);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstObject * parent,
|
gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstObject * parent,
|
||||||
GstBuffer * buffer)
|
GstBuffer * buffer)
|
||||||
|
@ -2023,6 +2207,8 @@ gst_subtitle_overlay_subtitle_sink_query (GstPad * pad, GstObject * parent,
|
||||||
{
|
{
|
||||||
gboolean ret;
|
gboolean ret;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (pad, "got query %" GST_PTR_FORMAT, query);
|
||||||
|
|
||||||
switch (GST_QUERY_TYPE (query)) {
|
switch (GST_QUERY_TYPE (query)) {
|
||||||
case GST_QUERY_ACCEPT_CAPS:
|
case GST_QUERY_ACCEPT_CAPS:
|
||||||
{
|
{
|
||||||
|
@ -2078,6 +2264,8 @@ gst_subtitle_overlay_init (GstSubtitleOverlay * self)
|
||||||
gst_object_unref (templ);
|
gst_object_unref (templ);
|
||||||
gst_pad_set_event_function (self->video_sinkpad,
|
gst_pad_set_event_function (self->video_sinkpad,
|
||||||
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_event));
|
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_event));
|
||||||
|
gst_pad_set_query_function (self->video_sinkpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_query));
|
||||||
gst_pad_set_chain_function (self->video_sinkpad,
|
gst_pad_set_chain_function (self->video_sinkpad,
|
||||||
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_chain));
|
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_chain));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue