assrender: Improve negotiation

This is mostly a copy/paste of the negotiation function in
basetextoverlay, which was improved recently to handle many more cases.

This will allow us to negotiate a window size with downstream.

https://bugzilla.gnome.org/show_bug.cgi?id=753824
This commit is contained in:
Arnaud Vrac 2014-09-04 18:16:00 +02:00 committed by Sebastian Dröge
parent 31c6902373
commit f021d32bef
2 changed files with 178 additions and 108 deletions

View file

@ -293,6 +293,15 @@ gst_ass_render_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (object); G_OBJECT_CLASS (parent_class)->finalize (object);
} }
static void
gst_ass_render_reset_composition (GstAssRender * render)
{
if (render->composition) {
gst_video_overlay_composition_unref (render->composition);
render->composition = NULL;
}
}
static void static void
gst_ass_render_set_property (GObject * object, guint prop_id, gst_ass_render_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec) const GValue * value, GParamSpec * pspec)
@ -387,12 +396,9 @@ gst_ass_render_change_state (GstElement * element, GstStateChange transition)
if (render->ass_track) if (render->ass_track)
ass_free_track (render->ass_track); ass_free_track (render->ass_track);
render->ass_track = NULL; render->ass_track = NULL;
if (render->composition) {
gst_video_overlay_composition_unref (render->composition);
render->composition = NULL;
}
render->track_init_ok = FALSE; render->track_init_ok = FALSE;
render->renderer_init_ok = FALSE; render->renderer_init_ok = FALSE;
gst_ass_render_reset_composition (render);
g_mutex_unlock (&render->ass_mutex); g_mutex_unlock (&render->ass_mutex);
break; break;
case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_READY_TO_PAUSED:
@ -774,125 +780,188 @@ gst_ass_render_can_handle_caps (GstCaps * incaps)
return ret; return ret;
} }
static gboolean
gst_ass_render_negotiate (GstAssRender * render, 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;
guint alloc_index;
GST_DEBUG_OBJECT (render, "performing negotiation");
/* Clear cached composition */
gst_ass_render_reset_composition (render);
/* Clear any pending reconfigure flag */
gst_pad_check_reconfigure (render->srcpad);
if (!caps)
caps = gst_pad_get_current_caps (render->video_sinkpad);
else
gst_caps_ref (caps);
if (!caps || gst_caps_is_empty (caps))
goto no_format;
/* 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 overlay composition in caps */
/* FIXME: We should probably check if downstream *prefers* the
* overlay 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 (render->srcpad, NULL);
caps_has_meta = gst_caps_can_intersect (peercaps, overlay_caps);
gst_caps_unref (peercaps);
GST_DEBUG ("caps have overlay 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 (render->srcpad, overlay_caps);
/* First check if the allocation meta has compositon */
query = gst_query_new_allocation (overlay_caps, FALSE);
if (!gst_pad_peer_query (render->srcpad, query)) {
/* no problem, we use the query defaults */
GST_DEBUG_OBJECT (render, "ALLOCATION query failed");
/* In case we were flushing, mark reconfigure and fail this method,
* will make it retry */
if (render->video_flushing)
ret = FALSE;
}
alloc_has_meta = gst_query_find_allocation_meta (query,
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, &alloc_index);
GST_DEBUG ("sink alloc has overlay 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_ass_render_can_handle_caps (caps);
}
} else {
ret = gst_ass_render_can_handle_caps (caps);
}
/* If we attach, then pick the overlay caps */
if (attach) {
GST_DEBUG_OBJECT (render, "Using caps %" GST_PTR_FORMAT, overlay_caps);
/* Caps where already sent */
} else if (ret) {
GST_DEBUG_OBJECT (render, "Using caps %" GST_PTR_FORMAT, caps);
ret = gst_pad_set_caps (render->srcpad, caps);
}
render->attach_compo_to_buffer = attach;
if (!ret) {
GST_DEBUG_OBJECT (render, "negotiation failed, schedule reconfigure");
gst_pad_mark_reconfigure (render->srcpad);
} else {
g_mutex_lock (&render->ass_mutex);
ass_set_frame_size (render->ass_renderer,
render->info.width, render->info.height);
ass_set_storage_size (render->ass_renderer,
render->info.width, render->info.height);
ass_set_pixel_aspect (render->ass_renderer,
(gdouble) render->info.par_n / (gdouble) render->info.par_d);
ass_set_font_scale (render->ass_renderer, 1.0);
ass_set_hinting (render->ass_renderer, ASS_HINTING_LIGHT);
ass_set_fonts (render->ass_renderer, "Arial", "sans-serif", 1, NULL, 1);
ass_set_fonts (render->ass_renderer, NULL, "Sans", 1, NULL, 1);
ass_set_margins (render->ass_renderer, 0, 0, 0, 0);
ass_set_use_margins (render->ass_renderer, 0);
g_mutex_unlock (&render->ass_mutex);
render->renderer_init_ok = TRUE;
GST_DEBUG_OBJECT (render, "ass renderer setup complete");
}
gst_caps_unref (overlay_caps);
gst_caps_unref (caps);
return ret;
no_format:
{
if (caps)
gst_caps_unref (caps);
return FALSE;
}
}
static gboolean static gboolean
gst_ass_render_setcaps_video (GstPad * pad, GstAssRender * render, gst_ass_render_setcaps_video (GstPad * pad, GstAssRender * render,
GstCaps * caps) GstCaps * caps)
{ {
GstQuery *query;
gboolean ret = FALSE;
GstVideoInfo info; GstVideoInfo info;
gboolean attach = FALSE; gboolean ret;
gboolean caps_has_meta = TRUE;
GstCapsFeatures *f;
GstCaps *original_caps = caps;
if (!gst_video_info_from_caps (&info, caps)) if (!gst_video_info_from_caps (&info, caps))
goto invalid_caps; goto invalid_caps;
render->info = info; render->info = info;
gst_caps_ref (caps);
/* Try to use the overlay meta if possible */ ret = gst_ass_render_negotiate (render, caps);
f = gst_caps_get_features (caps, 0);
/* if the caps doesn't have the overlay meta, we query if downstream GST_ASS_RENDER_LOCK (render);
* accepts it before trying the version without the meta
* If upstream already is using the meta then we can only use it */
if (!f
|| !gst_caps_features_contains (f,
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION)) {
GstCaps *overlay_caps;
/* In this case we added the meta, but we can work without it
* so preserve the original caps so we can use it as a fallback */
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);
ret = gst_pad_peer_query_accept_caps (render->srcpad, overlay_caps);
GST_DEBUG_OBJECT (render, "Downstream accepts the overlay meta: %d", ret);
if (ret) {
gst_caps_unref (caps);
caps = overlay_caps;
} else {
/* fallback to the original */
gst_caps_unref (overlay_caps);
caps_has_meta = FALSE;
}
if (!render->attach_compo_to_buffer &&
!gst_ass_render_can_handle_caps (caps)) {
GST_DEBUG_OBJECT (render, "unsupported caps %" GST_PTR_FORMAT, caps);
ret = FALSE;
} }
GST_DEBUG_OBJECT (render, "Using caps %" GST_PTR_FORMAT, caps); GST_ASS_RENDER_UNLOCK (render);
ret = gst_pad_set_caps (render->srcpad, caps);
gst_caps_unref (caps);
if (!ret)
goto out;
render->width = info.width;
render->height = info.height;
query = gst_query_new_allocation (caps, FALSE);
if (caps_has_meta && gst_pad_peer_query (render->srcpad, query)) {
if (gst_query_find_allocation_meta (query,
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL))
attach = TRUE;
}
gst_query_unref (query);
render->attach_compo_to_buffer = attach;
if (!attach) {
if (caps_has_meta) {
/* Some elements (fakesink) claim to accept the meta on caps but won't
put it in the allocation query result, this leads below
check to fail. Prevent this by removing the meta from caps */
caps = original_caps;
ret = gst_pad_set_caps (render->srcpad, caps);
if (!ret)
goto out;
}
if (!gst_ass_render_can_handle_caps (caps))
goto unsupported_caps;
}
g_mutex_lock (&render->ass_mutex);
ass_set_frame_size (render->ass_renderer, render->width, render->height);
ass_set_storage_size (render->ass_renderer,
render->info.width, render->info.height);
ass_set_pixel_aspect (render->ass_renderer,
(gdouble) render->info.par_n / (gdouble) render->info.par_d);
ass_set_font_scale (render->ass_renderer, 1.0);
ass_set_hinting (render->ass_renderer, ASS_HINTING_LIGHT);
ass_set_fonts (render->ass_renderer, "Arial", "sans-serif", 1, NULL, 1);
ass_set_fonts (render->ass_renderer, NULL, "Sans", 1, NULL, 1);
ass_set_margins (render->ass_renderer, 0, 0, 0, 0);
ass_set_use_margins (render->ass_renderer, 0);
g_mutex_unlock (&render->ass_mutex);
render->renderer_init_ok = TRUE;
GST_DEBUG_OBJECT (render, "ass renderer setup complete");
out:
return ret; return ret;
/* ERRORS */ /* ERRORS */
invalid_caps: invalid_caps:
{ {
GST_ERROR_OBJECT (render, "Can't parse caps: %" GST_PTR_FORMAT, caps); GST_ERROR_OBJECT (render, "could not parse caps");
ret = FALSE; return FALSE;
goto out;
}
unsupported_caps:
{
GST_ERROR_OBJECT (render, "Unsupported caps: %" GST_PTR_FORMAT, caps);
ret = FALSE;
goto out;
} }
} }
@ -1004,8 +1073,8 @@ gst_ass_render_composite_overlay (GstAssRender * render, ASS_Image * images)
max_y = image->dst_y + image->h; max_y = image->dst_y + image->h;
} }
width = MIN (max_x - min_x, render->width); width = MIN (max_x - min_x, render->info.width);
height = MIN (max_y - min_y, render->height); height = MIN (max_y - min_y, render->info.height);
GST_DEBUG_OBJECT (render, "render overlay rectangle %dx%d%+d%+d", GST_DEBUG_OBJECT (render, "render overlay rectangle %dx%d%+d%+d",
width, height, min_x, min_y); width, height, min_x, min_y);
@ -1079,6 +1148,9 @@ gst_ass_render_chain_video (GstPad * pad, GstObject * parent,
guint64 start, stop, clip_start = 0, clip_stop = 0; guint64 start, stop, clip_start = 0, clip_stop = 0;
ASS_Image *ass_image; ASS_Image *ass_image;
if (gst_pad_check_reconfigure (render->srcpad))
gst_ass_render_negotiate (render, NULL);
if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
goto missing_timestamp; goto missing_timestamp;
@ -1211,8 +1283,7 @@ wait_for_text_buf:
if ((!ass_image || changed) && render->composition) { if ((!ass_image || changed) && render->composition) {
GST_DEBUG_OBJECT (render, "release overlay (changed %d)", changed); GST_DEBUG_OBJECT (render, "release overlay (changed %d)", changed);
gst_video_overlay_composition_unref (render->composition); gst_ass_render_reset_composition (render);
render->composition = NULL;
} }
if (ass_image != NULL) { if (ass_image != NULL) {

View file

@ -82,7 +82,6 @@ struct _GstAssRender
/* overlay stuff */ /* overlay stuff */
GstVideoOverlayComposition *composition; GstVideoOverlayComposition *composition;
gint width, height;
gboolean attach_compo_to_buffer; gboolean attach_compo_to_buffer;
}; };