From 4872c7bf75c49eaa00195adfdcac1652cde2452c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 23 Mar 2012 11:07:49 +0100 Subject: [PATCH 01/12] playsink: Fix subtitle rendering if there's no video, no visualizations but a text-sink --- gst/playback/gstplaysink.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index a68b6ae5dd..4d1ec92e21 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -2583,7 +2583,7 @@ gst_play_sink_reconfigure (GstPlaySink * playsink) /* we have a text_pad and we need text rendering, in this case we need a * video_pad to combine the video with the text or visualizations */ - if (need_text && !need_video) { + if (need_text && !need_video && !playsink->text_sink) { if (playsink->video_pad) { need_video = TRUE; } else if (need_audio) { @@ -2947,25 +2947,27 @@ gst_play_sink_reconfigure (GstPlaySink * playsink) playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING); } - if (need_vis) { - GstPad *srcpad; + if (need_vis || need_video) { + if (need_vis) { + GstPad *srcpad; - srcpad = - gst_element_get_static_pad (playsink->vischain->chain.bin, "src"); - gst_pad_unlink (srcpad, playsink->videochain->sinkpad); - gst_pad_link_full (srcpad, playsink->textchain->videosinkpad, - GST_PAD_LINK_CHECK_NOTHING); - gst_object_unref (srcpad); - } else { - if (need_deinterlace) - gst_pad_link_full (playsink->videodeinterlacechain->srcpad, - playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING); - else - gst_pad_link_full (playsink->video_srcpad_stream_synchronizer, - playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING); + srcpad = + gst_element_get_static_pad (playsink->vischain->chain.bin, "src"); + gst_pad_unlink (srcpad, playsink->videochain->sinkpad); + gst_pad_link_full (srcpad, playsink->textchain->videosinkpad, + GST_PAD_LINK_CHECK_NOTHING); + gst_object_unref (srcpad); + } else { + if (need_deinterlace) + gst_pad_link_full (playsink->videodeinterlacechain->srcpad, + playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING); + else + gst_pad_link_full (playsink->video_srcpad_stream_synchronizer, + playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING); + } + gst_pad_link_full (playsink->textchain->srcpad, + playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING); } - gst_pad_link_full (playsink->textchain->srcpad, - playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING); activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE); } From f17b8cbbc3642b6d9358fb70b819cdff4ef5760b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 24 Mar 2012 00:17:33 +0000 Subject: [PATCH 02/12] riff: map ISBJ tag to GST_TAG_ALBUM_ARTIST http://www.bass.radio42.com/help/html/7e1a8908-88bd-d54b-77d7-f0d08466284c.htm --- gst-libs/gst/riff/riff-read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/riff/riff-read.c b/gst-libs/gst/riff/riff-read.c index c4ba3e823d..746ad93cd4 100644 --- a/gst-libs/gst/riff/riff-read.c +++ b/gst-libs/gst/riff/riff-read.c @@ -695,7 +695,7 @@ gst_riff_parse_info (GstElement * element, type = GST_TAG_ALBUM; break; case GST_RIFF_INFO_ISBJ: - type = NULL; /*"Subject"; */ + type = GST_TAG_ALBUM_ARTIST; break; case GST_RIFF_INFO_ISFT: type = GST_TAG_ENCODER; From 76c0881549e73efb4995ac8b38d596d51d1cc0fe Mon Sep 17 00:00:00 2001 From: Holger Kaelberer Date: Sat, 24 Mar 2012 19:31:29 +0000 Subject: [PATCH 03/12] video: overlay-composition: add support for global alpha multiplicator https://bugzilla.gnome.org/show_bug.cgi?id=668483 --- gst-libs/gst/video/video-blend.c | 7 +- gst-libs/gst/video/video-blend.h | 3 +- .../gst/video/video-overlay-composition.c | 219 +++++++++++++++++- .../gst/video/video-overlay-composition.h | 8 +- 4 files changed, 222 insertions(+), 15 deletions(-) diff --git a/gst-libs/gst/video/video-blend.c b/gst-libs/gst/video/video-blend.c index 2eb8ac0e31..9b6c172d73 100644 --- a/gst-libs/gst/video/video-blend.c +++ b/gst-libs/gst/video/video-blend.c @@ -1333,12 +1333,14 @@ video_blend_scale_linear_RGBA (GstBlendVideoFormatInfo * src, * @dest * @x: The x offset in pixel where the @src image should be blended * @y: the y offset in pixel where the @src image should be blended + * @global_alpha: the global_alpha each per-pixel alpha value is multiplied + * with * * Lets you blend the @src image into the @dest image */ gboolean video_blend (GstBlendVideoFormatInfo * dest, - GstBlendVideoFormatInfo * src, gint x, gint y) + GstBlendVideoFormatInfo * src, guint x, guint y, gfloat global_alpha) { guint i, j; guint8 alpha; @@ -1349,6 +1351,7 @@ video_blend (GstBlendVideoFormatInfo * dest, g_return_val_if_fail (dest, FALSE); g_return_val_if_fail (src, FALSE); + g_return_val_if_fail (global_alpha >= 0 && global_alpha <= 1, FALSE); /* we do no support writing to premultiplied alpha, though that should just be a matter of adding blenders below (BLEND01 and BLEND11) */ @@ -1414,7 +1417,7 @@ video_blend (GstBlendVideoFormatInfo * dest, #define BLENDLOOP(blender) \ do { \ for (j = 0; j < src->width * 4; j += 4) { \ - alpha = tmpsrcline[j]; \ + alpha = (guint8) tmpsrcline[j] * global_alpha; \ \ blender (tmpdestline[j + 1], alpha, tmpsrcline[j + 1], tmpdestline[j + 1]); \ blender (tmpdestline[j + 2], alpha, tmpsrcline[j + 2], tmpdestline[j + 2]); \ diff --git a/gst-libs/gst/video/video-blend.h b/gst-libs/gst/video/video-blend.h index 32e5d12028..7e4c50fcb7 100644 --- a/gst-libs/gst/video/video-blend.h +++ b/gst-libs/gst/video/video-blend.h @@ -71,6 +71,7 @@ void video_blend_scale_linear_RGBA (GstBlendVideoFormatInfo * src, gboolean video_blend (GstBlendVideoFormatInfo * dest, GstBlendVideoFormatInfo * src, - gint x, gint y); + guint x, guint y, + gfloat global_alpha); #endif diff --git a/gst-libs/gst/video/video-overlay-composition.c b/gst-libs/gst/video/video-overlay-composition.c index ad9788670f..a1de051a46 100644 --- a/gst-libs/gst/video/video-overlay-composition.c +++ b/gst-libs/gst/video/video-overlay-composition.c @@ -69,6 +69,7 @@ #include "video-overlay-composition.h" #include "video-blend.h" +#include struct _GstVideoOverlayComposition { @@ -134,6 +135,19 @@ struct _GstVideoOverlayRectangle * rectangles have expired. */ guint seq_num; + /* global alpha: global alpha value of the rectangle. Each each per-pixel + * alpha value of image-data will be multiplied with the global alpha value + * during blending. + * Can be used for efficient fading in/out of overlay rectangles. + * GstElements that render OverlayCompositions and don't support global alpha + * should simply ignore it.*/ + gfloat global_alpha; + + /* track alpha-values already applied: */ + gfloat applied_global_alpha; + /* store initial per-pixel alpha values: */ + guint8 *initial_alpha; + /* FIXME: we may also need a (private) way to cache converted/scaled * pixel blobs */ #if !GLIB_CHECK_VERSION (2, 31, 0) @@ -522,7 +536,7 @@ gst_video_overlay_composition_blend (GstVideoOverlayComposition * comp, video_blend_format_info_init (&rectangle_info, GST_BUFFER_DATA (rect->pixels), rect->height, rect->width, rect->format, - !!(rect->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)); + ! !(rect->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)); needs_scaling = gst_video_overlay_rectangle_needs_scaling (rect); if (needs_scaling) { @@ -530,7 +544,8 @@ gst_video_overlay_composition_blend (GstVideoOverlayComposition * comp, rect->render_width); } - ret = video_blend (&video_info, &rectangle_info, rect->x, rect->y); + ret = video_blend (&video_info, &rectangle_info, rect->x, rect->y, + rect->global_alpha); if (!ret) { GST_WARNING ("Could not blend overlay rectangle onto video buffer"); } @@ -683,6 +698,8 @@ gst_video_overlay_rectangle_finalize (GstMiniObject * mini_obj) rect->scaled_rectangles = g_list_delete_link (rect->scaled_rectangles, rect->scaled_rectangles); } + + g_free (rect->initial_alpha); #if !GLIB_CHECK_VERSION (2, 31, 0) g_static_mutex_free (&rect->lock); #else @@ -714,7 +731,8 @@ static inline gboolean gst_video_overlay_rectangle_check_flags (GstVideoOverlayFormatFlags flags) { /* Check flags only contains flags we know about */ - return (flags & ~(GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)) == 0; + return (flags & ~(GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA | + GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)) == 0; } static gboolean @@ -790,13 +808,18 @@ gst_video_overlay_rectangle_new_argb (GstBuffer * pixels, rect->render_width = render_width; rect->render_height = render_height; + rect->global_alpha = 1.0; + rect->applied_global_alpha = 1.0; + rect->initial_alpha = NULL; + rect->flags = flags; rect->seq_num = gst_video_overlay_get_seqnum (); GST_LOG ("new rectangle %p: %ux%u => %ux%u @ %u,%u, seq_num %u, format %u, " - "flags %x, pixels %p", rect, width, height, render_width, render_height, - render_x, render_y, rect->seq_num, rect->format, rect->flags, pixels); + "flags %x, pixels %p, global_alpha=%f", rect, width, height, render_width, + render_height, render_x, render_y, rect->seq_num, rect->format, + rect->flags, pixels, rect->global_alpha); return rect; } @@ -917,6 +940,70 @@ gst_video_overlay_rectangle_unpremultiply (GstBlendVideoFormatInfo * info) } } + +static gboolean +gst_video_overlay_rectangle_extract_alpha (GstVideoOverlayRectangle * rect) +{ + guint8 *src, *dst; + int offset = 0; + int alpha_size = rect->stride * rect->height / 4; + + g_free (rect->initial_alpha); + rect->initial_alpha = NULL; + + rect->initial_alpha = g_malloc (alpha_size); + src = GST_BUFFER_DATA (rect->pixels); + dst = rect->initial_alpha; + while (offset < alpha_size) { + dst[offset] = src[offset * 4 + ARGB_A]; + ++offset; + } + return TRUE; +} + + +static gboolean +gst_video_overlay_rectangle_apply_global_alpha (GstVideoOverlayRectangle * rect, + float global_alpha) +{ + guint8 *src, *dst; + guint offset = 0; + + g_return_val_if_fail (!(rect->applied_global_alpha != 1.0 + && rect->initial_alpha == NULL), FALSE); + + if (global_alpha == rect->applied_global_alpha) + return TRUE; + + if (rect->initial_alpha == NULL && + !gst_video_overlay_rectangle_extract_alpha (rect)) + return FALSE; + + src = rect->initial_alpha; + rect->pixels = gst_buffer_make_writable (rect->pixels); + dst = GST_BUFFER_DATA (rect->pixels); + while (offset < (rect->height * rect->stride / 4)) { + guint doff = offset * 4; + guint8 na = (guint8) src[offset] * global_alpha; + if (! !(rect->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)) { + dst[doff + ARGB_R] = + (guint8) ((double) (dst[doff + ARGB_R] * 255) / (double) dst[doff + + ARGB_A]) * na / 255; + dst[doff + ARGB_G] = + (guint8) ((double) (dst[doff + ARGB_G] * 255) / (double) dst[doff + + ARGB_A]) * na / 255; + dst[doff + ARGB_B] = + (guint8) ((double) (dst[doff + ARGB_B] * 255) / (double) dst[doff + + ARGB_A]) * na / 255; + } + dst[doff + ARGB_A] = na; + ++offset; + } + + rect->applied_global_alpha = global_alpha; + return TRUE; +} + static GstBuffer * gst_video_overlay_rectangle_get_pixels_argb_internal (GstVideoOverlayRectangle * rectangle, guint * stride, GstVideoOverlayFormatFlags flags, @@ -929,18 +1016,36 @@ gst_video_overlay_rectangle_get_pixels_argb_internal (GstVideoOverlayRectangle * GList *l; guint wanted_width = unscaled ? rectangle->width : rectangle->render_width; guint wanted_height = unscaled ? rectangle->height : rectangle->render_height; + gboolean apply_global_alpha; + gboolean revert_global_alpha; g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL); g_return_val_if_fail (stride != NULL, NULL); g_return_val_if_fail (gst_video_overlay_rectangle_check_flags (flags), NULL); + apply_global_alpha = + (! !(rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA) + && !(flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)); + revert_global_alpha = + (! !(rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA) + && ! !(flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)); + /* This assumes we don't need to adjust the format */ if (wanted_width == rectangle->width && wanted_height == rectangle->height && gst_video_overlay_rectangle_is_same_alpha_type (rectangle->flags, flags)) { - *stride = rectangle->stride; - return rectangle->pixels; + /* don't need to apply/revert global-alpha either: */ + if ((!apply_global_alpha + || rectangle->applied_global_alpha == rectangle->global_alpha) + && (!revert_global_alpha || rectangle->applied_global_alpha == 1.0)) { + *stride = rectangle->stride; + return rectangle->pixels; + } else { + /* only apply/revert global-alpha */ + scaled_rect = rectangle; + goto done; + } } /* see if we've got one cached already */ @@ -962,12 +1067,14 @@ gst_video_overlay_rectangle_get_pixels_argb_internal (GstVideoOverlayRectangle * if (scaled_rect != NULL) goto done; - /* not cached yet, do the scaling and put the result into our cache */ + /* not cached yet, do the preprocessing and put the result into our cache */ video_blend_format_info_init (&info, GST_BUFFER_DATA (rectangle->pixels), rectangle->height, rectangle->width, rectangle->format, - !!(rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)); + ! !(rectangle->flags & + GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)); if (wanted_width != rectangle->width || wanted_height != rectangle->height) { + /* we could check the cache for a scaled rect with global_alpha == 1 here */ video_blend_scale_linear_RGBA (&info, wanted_height, wanted_width); } else { /* if we don't have to scale, we have to modify the alpha values, so we @@ -994,7 +1101,9 @@ gst_video_overlay_rectangle_get_pixels_argb_internal (GstVideoOverlayRectangle * scaled_rect = gst_video_overlay_rectangle_new_argb (buf, wanted_width, wanted_height, info.stride[0], 0, 0, wanted_width, wanted_height, new_flags); - + if (rectangle->global_alpha != 1.0) + gst_video_overlay_rectangle_set_global_alpha (scaled_rect, + rectangle->global_alpha); gst_buffer_unref (buf); GST_RECTANGLE_LOCK (rectangle); @@ -1003,17 +1112,33 @@ gst_video_overlay_rectangle_get_pixels_argb_internal (GstVideoOverlayRectangle * GST_RECTANGLE_UNLOCK (rectangle); done: + if (apply_global_alpha + && scaled_rect->applied_global_alpha != rectangle->global_alpha) { + if (!gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, + rectangle->global_alpha)) + return NULL; /* return original data? */ + gst_video_overlay_rectangle_set_global_alpha (scaled_rect, + rectangle->global_alpha); + } else if (revert_global_alpha && scaled_rect->applied_global_alpha != 1.0) { + if (!gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, 1.0)) + return NULL; /* return original data? */ + } *stride = scaled_rect->stride; return scaled_rect->pixels; } + /** * gst_video_overlay_rectangle_get_pixels_argb: * @rectangle: a #GstVideoOverlayRectangle * @stride: (out) (allow-none): address of guint variable where to store the * row stride of the ARGB pixel data in the buffer * @flags: flags + * If a global_alpha value != 1 is set for the rectangle, the caller + * should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag + * if he wants to apply global-alpha himself. If the flag is not set + * global_alpha is applied internally before returning the pixel-data. * * Returns: (transfer none): a #GstBuffer holding the ARGB pixel data with * row stride @stride and width and height of the render dimensions as per @@ -1040,7 +1165,11 @@ gst_video_overlay_rectangle_get_pixels_argb (GstVideoOverlayRectangle * * rectangle in pixels * @stride: (out): address of guint variable where to store the row * stride of the ARGB pixel data in the buffer - * @flags: flags + * @flags: flags. + * If a global_alpha value != 1 is set for the rectangle, the caller + * should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag + * if he wants to apply global-alpha himself. If the flag is not set + * global_alpha is applied internally before returning the pixel-data. * * Retrieves the pixel data as it is. This is useful if the caller can * do the scaling itself when handling the overlaying. The rectangle will @@ -1092,6 +1221,61 @@ gst_video_overlay_rectangle_get_flags (GstVideoOverlayRectangle * rectangle) return rectangle->flags; } +/** + * gst_video_overlay_rectangle_get_global_alpha: + * @rectangle: a #GstVideoOverlayRectangle + * + * Retrieves the global-alpha value associated with a #GstVideoOverlayRectangle. + * + * Returns: the global-alpha value associated with the rectangle. + * + * Since: 0.10.37 + */ +gfloat +gst_video_overlay_rectangle_get_global_alpha (GstVideoOverlayRectangle * + rectangle) +{ + g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), -1); + + return rectangle->global_alpha; +} + +/** + * gst_video_overlay_rectangle_set_global_alpha: + * @rectangle: a #GstVideoOverlayRectangle + * + * Sets the global alpha value associated with a #GstVideoOverlayRectangle. Per- + * pixel alpha values are multiplied with this value. Valid + * values: 0 <= global_alpha <= 1; 1 to deactivate. + * + # @rectangle must be writable, meaning its refcount must be 1. You can + * make the rectangles inside a #GstVideoOverlayComposition writable using + * gst_video_overlay_composition_make_writable() or + * gst_video_overlay_composition_copy(). + * + * Since: 0.10.37 + */ +void +gst_video_overlay_rectangle_set_global_alpha (GstVideoOverlayRectangle * + rectangle, gfloat global_alpha) +{ + g_return_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle)); + g_return_if_fail (global_alpha >= 0 && global_alpha <= 1); + + if (rectangle->global_alpha != global_alpha) { + rectangle->global_alpha = global_alpha; + if (global_alpha != 1) + rectangle->flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA; + else + rectangle->flags &= ~GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA; + /* update seq_num automatically to signal the consumer, that data has changed + * note, that this might mislead renderers, that can handle global-alpha + * themselves, because what they want to know is whether the actual pixel data + * has changed. */ + rectangle->seq_num = gst_video_overlay_get_seqnum (); + } +} + /** * gst_video_overlay_rectangle_copy: * @rectangle: (transfer none): a #GstVideoOverlayRectangle to copy @@ -1117,6 +1301,9 @@ gst_video_overlay_rectangle_copy (GstVideoOverlayRectangle * rectangle) rectangle->width, rectangle->height, rectangle->stride, rectangle->x, rectangle->y, rectangle->render_width, rectangle->render_height, rectangle->flags); + if (rectangle->global_alpha != 1) + gst_video_overlay_rectangle_set_global_alpha (copy, + rectangle->global_alpha); return copy; } @@ -1130,6 +1317,16 @@ gst_video_overlay_rectangle_copy (GstVideoOverlayRectangle * rectangle) * (meaning there will never be a rectangle with the same sequence number as * a composition). * + * Using the sequence number of a rectangle as an indicator for changed + * pixel-data of a rectangle is dangereous. Some API calls, like e.g. + * gst_video_overlay_rectangle_set_global_alpha(), automatically update + * the per rectangle sequence number, which is misleading for renderers/ + * consumers, that handle global-alpha themselves. For them the + * pixel-data returned by gst_video_overlay_rectangle_get_pixels_*() + * wont be different for different global-alpha values. In this case a + * renderer could also use the GstBuffer pointers as a hint for changed + * pixel-data. + * * Returns: the sequence number of @rectangle * * Since: 0.10.36 diff --git a/gst-libs/gst/video/video-overlay-composition.h b/gst-libs/gst/video/video-overlay-composition.h index b91404ba91..65538e487c 100644 --- a/gst-libs/gst/video/video-overlay-composition.h +++ b/gst-libs/gst/video/video-overlay-composition.h @@ -96,6 +96,7 @@ gst_video_overlay_rectangle_unref (GstVideoOverlayRectangle * comp) * GstVideoOverlayFormatFlags: * @GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE: no flags * @GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA: RGB are premultiplied by A/255. Since: 0.10.37 + * @GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA: a global-alpha value != 1 is set. Since: 0.10.37 * * Overlay format flags. * @@ -103,7 +104,8 @@ gst_video_overlay_rectangle_unref (GstVideoOverlayRectangle * comp) */ typedef enum { GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE = 0, - GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA = 1 + GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA = 1, + GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA = 2 } GstVideoOverlayFormatFlags; GType gst_video_overlay_rectangle_get_type (void); @@ -142,6 +144,10 @@ GstBuffer * gst_video_overlay_rectangle_get_pixels_unscaled_arg GstVideoOverlayFormatFlags gst_video_overlay_rectangle_get_flags (GstVideoOverlayRectangle * rectangle); +gfloat gst_video_overlay_rectangle_get_global_alpha (GstVideoOverlayRectangle * rectangle); +void gst_video_overlay_rectangle_set_global_alpha (GstVideoOverlayRectangle * rectangle, + gfloat global_alpha); + /** * GstVideoOverlayComposition: * From 9d1b331004bb1d6dba91eaea294af6adcadc29a3 Mon Sep 17 00:00:00 2001 From: Holger Kaelberer Date: Sat, 24 Mar 2012 19:38:26 +0000 Subject: [PATCH 04/12] tests: add unit test for video overlay composition global alpha support https://bugzilla.gnome.org/show_bug.cgi?id=668483 --- tests/check/libs/video.c | 381 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 381 insertions(+) diff --git a/tests/check/libs/video.c b/tests/check/libs/video.c index bc2445110f..91a5e4b113 100644 --- a/tests/check/libs/video.c +++ b/tests/check/libs/video.c @@ -1037,6 +1037,386 @@ GST_START_TEST (test_overlay_composition_premultiplied_alpha) GST_END_TEST; + +GST_START_TEST (test_overlay_composition_global_alpha) +{ + GstVideoOverlayRectangle *rect1; + GstBuffer *pix1, *pix2, *pix3, *pix4, *pix5; + guint8 *data2, *data4, *data5; + guint w, h, stride, stride3, w4, h4, stride4, stride5; + guint seq1, seq2; + gfloat ga1, ga2; + GstVideoOverlayFormatFlags flags1; + + pix1 = gst_buffer_new_and_alloc (200 * sizeof (guint32) * 50); + memset (GST_BUFFER_DATA (pix1), 0x80, GST_BUFFER_SIZE (pix1)); + + rect1 = gst_video_overlay_rectangle_new_argb (pix1, 200, 50, 200 * 4, + 600, 50, 300, 50, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE); + gst_buffer_unref (pix1); + + /* same flags, unscaled, should be the same buffer */ + pix2 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w, &h, + &stride, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE); + fail_unless (pix1 == pix2); + + /* same flags, but scaled */ + pix3 = gst_video_overlay_rectangle_get_pixels_argb (rect1, &stride3, + GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE); + fail_if (pix3 == pix1 || pix3 == pix2); + + /* get unscaled premultiplied data, new cached rectangle should be created */ + pix4 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w4, &h4, + &stride4, GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA); + fail_if (pix4 == pix2 || pix4 == pix3); + fail_unless_equals_int (stride, stride4); + fail_unless_equals_int (w, w4); + fail_unless_equals_int (h, h4); + fail_unless_equals_int (GST_BUFFER_SIZE (pix2), GST_BUFFER_SIZE (pix4)); + data4 = GST_BUFFER_DATA (pix4); + fail_if (memcmp (data4, GST_BUFFER_DATA (pix1), GST_BUFFER_SIZE (pix1)) == 0); + /* make sure it actually did what we expected it to do (input=0x80808080) */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data4[0], 0x40); + fail_unless_equals_int (data4[1], 0x40); + fail_unless_equals_int (data4[2], 0x40); + fail_unless_equals_int (data4[3], 0x80); +#else + /* A - R - G - B */ + fail_unless_equals_int (data4[0], 0x80); + fail_unless_equals_int (data4[1], 0x40); + fail_unless_equals_int (data4[2], 0x40); + fail_unless_equals_int (data4[3], 0x40); +#endif + + /* now premultiplied and scaled, again a new cached rectangle should be cached */ + pix5 = gst_video_overlay_rectangle_get_pixels_argb (rect1, &stride5, + GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA); + fail_if (pix5 == pix2 || pix5 == pix3 || pix5 == pix4); + /* stride and size should be equal to the first scaled rect */ + fail_unless_equals_int (stride5, stride3); + fail_unless_equals_int (GST_BUFFER_SIZE (pix3), GST_BUFFER_SIZE (pix3)); + data5 = GST_BUFFER_DATA (pix5); + /* data should be different (premutliplied) though */ + fail_if (memcmp (data5, GST_BUFFER_DATA (pix3), GST_BUFFER_SIZE (pix3)) == 0); + /* make sure it actually did what we expected it to do (input=0x80808080) */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data5[0], 0x40); + fail_unless_equals_int (data5[1], 0x40); + fail_unless_equals_int (data5[2], 0x40); + fail_unless_equals_int (data5[3], 0x80); +#else + /* A - R - G - B */ + fail_unless_equals_int (data5[0], 0x80); + fail_unless_equals_int (data5[1], 0x40); + fail_unless_equals_int (data5[2], 0x40); + fail_unless_equals_int (data5[3], 0x40); +#endif + + /* global_alpha should initially be 1.0 */ + ga1 = gst_video_overlay_rectangle_get_global_alpha (rect1); + fail_unless_equals_float (ga1, 1.0); + + /* now set global_alpha */ + seq1 = gst_video_overlay_rectangle_get_seqnum (rect1); + gst_video_overlay_rectangle_set_global_alpha (rect1, 0.5); + ga2 = gst_video_overlay_rectangle_get_global_alpha (rect1); + fail_unless_equals_float (ga2, 0.5); + + /* seqnum should have changed */ + seq2 = gst_video_overlay_rectangle_get_seqnum (rect1); + fail_unless (seq1 < seq2); + + /* internal flags should have been set */ + flags1 = gst_video_overlay_rectangle_get_flags (rect1); + fail_unless_equals_int (flags1, GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA); + + /* request unscaled pixel-data, global-alpha not applied */ + pix2 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w, &h, + &stride, GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA); + /* this should just return the same buffer */ + fail_unless (pix2 == pix1); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix1), + GST_BUFFER_SIZE (pix1)) == 0); + /* make sure we got the initial data (input=0x80808080) */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#endif + + /* unscaled pixel-data, global-alpha applied */ + pix2 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w, &h, + &stride, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE); + /* this should be the same buffer with on-the-fly modified alpha-channel */ + fail_unless (pix2 == pix1); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix1), + GST_BUFFER_SIZE (pix1)) == 0); + /* make sure we got the initial data with adjusted alpha-channel */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x40); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x40); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#endif + + /* adjust global_alpha once more */ + gst_video_overlay_rectangle_set_global_alpha (rect1, 0.25); + ga2 = gst_video_overlay_rectangle_get_global_alpha (rect1); + fail_unless_equals_float (ga2, 0.25); + /* and again request unscaled pixel-data, global-alpha applied */ + pix2 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w, &h, + &stride, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE); + fail_unless (pix2 == pix1); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix1), + GST_BUFFER_SIZE (pix1)) == 0); + /* make sure we got the initial data with adjusted alpha-channel */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x20); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x20); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#endif + + /* again: unscaled pixel-data, global-alpha not applied, + * this should revert alpha-channel to initial values */ + pix2 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w, &h, + &stride, GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA); + fail_unless (pix2 == pix1); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix1), + GST_BUFFER_SIZE (pix1)) == 0); + /* make sure we got the initial data (input=0x80808080) */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#endif + + /* now scaled, global-alpha not applied */ + pix2 = gst_video_overlay_rectangle_get_pixels_argb (rect1, &stride, + GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA); + /* this should just return the rect/buffer, that was cached for these + * scaling dimensions */ + fail_unless (pix2 == pix3); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix3), + GST_BUFFER_SIZE (pix3)) == 0); + /* make sure we got the initial data (input=0x80808080) */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#endif + + /* scaled, global-alpha (0.25) applied */ + pix2 = gst_video_overlay_rectangle_get_pixels_argb (rect1, &stride, + GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE); + /* this should just return the rect/buffer, that was cached for these + * scaling dimensions with modified alpha channel */ + fail_unless (pix2 == pix3); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix3), + GST_BUFFER_SIZE (pix3)) == 0); + /* make sure we got the data we expect for global-alpha=0.25 */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x20); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x20); + fail_unless_equals_int (data2[1], 0x80); + fail_unless_equals_int (data2[2], 0x80); + fail_unless_equals_int (data2[3], 0x80); +#endif + + /* now unscaled premultiplied data, global-alpha not applied, + * is this really a valid use case?*/ + pix2 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w, &h, + &stride, GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA | + GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA); + /* this should just return the rect/buffer, that was cached for the + * premultiplied data */ + fail_unless (pix2 == pix4); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix4), + GST_BUFFER_SIZE (pix4)) == 0); + /* make sure we got what we expected */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x40); + fail_unless_equals_int (data2[1], 0x40); + fail_unless_equals_int (data2[2], 0x40); + fail_unless_equals_int (data2[3], 0x80); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x40); + fail_unless_equals_int (data2[2], 0x40); + fail_unless_equals_int (data2[3], 0x40); +#endif + + /* unscaled premultiplied data, global-alpha (0.25) applied */ + pix2 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w, &h, + &stride, GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA); + /* this should just return the rect/buffer, that was cached for the + * premultiplied data */ + fail_unless (pix2 == pix4); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix4), + GST_BUFFER_SIZE (pix4)) == 0); + /* make sure we got what we expected: + * (0x40 / (0x80/0xFF) * (0x20/0xFF) = 0x10 + * NOTE: unless we are using round() for the premultiplied case + * in gst_video_overlay_rectangle_apply_global_alpha() we get rounding + * error, i.e. 0x0F here */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x0F); + fail_unless_equals_int (data2[1], 0x0F); + fail_unless_equals_int (data2[2], 0x0F); + fail_unless_equals_int (data2[3], 0x20); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x20); + fail_unless_equals_int (data2[1], 0x0F); + fail_unless_equals_int (data2[2], 0x0F); + fail_unless_equals_int (data2[3], 0x0F); +#endif + + /* set global_alpha once more */ + gst_video_overlay_rectangle_set_global_alpha (rect1, 0.75); + /* and verify that also premultiplied data is adjusted + * correspondingly (though with increasing rounding errors) */ + pix2 = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect1, &w, &h, + &stride, GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA); + /* this should just return the rect/buffer, that was cached for the + * premultiplied data */ + fail_unless (pix2 == pix4); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix4), + GST_BUFFER_SIZE (pix4)) == 0); + /* make sure we got what we expected: + * (0x0F / (0x20/0xFF) * (0x60/0xFF) = 0x2D + * NOTE: using floats everywhere we would get 0x30 + * here we will actually end up with 0x2C */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x2C); + fail_unless_equals_int (data2[1], 0x2C); + fail_unless_equals_int (data2[2], 0x2C); + fail_unless_equals_int (data2[3], 0x60); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x60); + fail_unless_equals_int (data2[1], 0x2C); + fail_unless_equals_int (data2[2], 0x2C); + fail_unless_equals_int (data2[3], 0x2C); +#endif + + /* now scaled and premultiplied data, global-alpha not applied, + * is this really a valid use case?*/ + pix2 = gst_video_overlay_rectangle_get_pixels_argb (rect1, &stride, + GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA | + GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA); + /* this should just return the rect/buffer, that was cached for the + * first premultiplied+scaled rect*/ + fail_unless (pix2 == pix5); + fail_unless (stride == stride5); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix5), + GST_BUFFER_SIZE (pix5)) == 0); + /* make sure we got what we expected */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x40); + fail_unless_equals_int (data2[1], 0x40); + fail_unless_equals_int (data2[2], 0x40); + fail_unless_equals_int (data2[3], 0x80); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x80); + fail_unless_equals_int (data2[1], 0x40); + fail_unless_equals_int (data2[2], 0x40); + fail_unless_equals_int (data2[3], 0x40); +#endif + + /* scaled and premultiplied data, global-alpha applied */ + pix2 = gst_video_overlay_rectangle_get_pixels_argb (rect1, &stride, + GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA); + /* this should just return the rect/buffer, that was cached for the + * first premultiplied+scaled rect*/ + fail_unless (pix2 == pix5); + fail_unless (stride == stride5); + data2 = GST_BUFFER_DATA (pix2); + fail_unless (memcmp (data2, GST_BUFFER_DATA (pix5), + GST_BUFFER_SIZE (pix5)) == 0); + /* make sure we got what we expected; see above note about rounding errors! */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* B - G - R - A */ + fail_unless_equals_int (data2[0], 0x2F); + fail_unless_equals_int (data2[1], 0x2F); + fail_unless_equals_int (data2[2], 0x2F); + fail_unless_equals_int (data2[3], 0x60); +#else + /* A - R - G - B */ + fail_unless_equals_int (data2[0], 0x60); + fail_unless_equals_int (data2[1], 0x2F); + fail_unless_equals_int (data2[2], 0x2F); + fail_unless_equals_int (data2[3], 0x2F); +#endif + + gst_video_overlay_rectangle_unref (rect1); +} + +GST_END_TEST; + static Suite * video_suite (void) { @@ -1055,6 +1435,7 @@ video_suite (void) tcase_add_test (tc_chain, test_video_size_from_caps); tcase_add_test (tc_chain, test_overlay_composition); tcase_add_test (tc_chain, test_overlay_composition_premultiplied_alpha); + tcase_add_test (tc_chain, test_overlay_composition_global_alpha); return s; } From 32679e1826267a7b63a69bfbc7f0f4c9c6addb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 24 Mar 2012 19:47:10 +0000 Subject: [PATCH 05/12] video: overlay-composition: some minor clean-ups extract_alpha and apply_global alpha always return TRUE really, so just do away with the return value. Convert a g_return_if_fail() into a g_assert(), since this is only to check internal consistency and not a guard for public API. Add some locking. https://bugzilla.gnome.org/show_bug.cgi?id=668483 --- .../gst/video/video-overlay-composition.c | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/gst-libs/gst/video/video-overlay-composition.c b/gst-libs/gst/video/video-overlay-composition.c index a1de051a46..f9f90c5bb7 100644 --- a/gst-libs/gst/video/video-overlay-composition.c +++ b/gst-libs/gst/video/video-overlay-composition.c @@ -941,7 +941,7 @@ gst_video_overlay_rectangle_unpremultiply (GstBlendVideoFormatInfo * info) } -static gboolean +static void gst_video_overlay_rectangle_extract_alpha (GstVideoOverlayRectangle * rect) { guint8 *src, *dst; @@ -954,30 +954,29 @@ gst_video_overlay_rectangle_extract_alpha (GstVideoOverlayRectangle * rect) rect->initial_alpha = g_malloc (alpha_size); src = GST_BUFFER_DATA (rect->pixels); dst = rect->initial_alpha; + /* FIXME we're accessing possibly uninitialised bytes from the row padding */ while (offset < alpha_size) { dst[offset] = src[offset * 4 + ARGB_A]; ++offset; } - return TRUE; } -static gboolean +static void gst_video_overlay_rectangle_apply_global_alpha (GstVideoOverlayRectangle * rect, float global_alpha) { guint8 *src, *dst; guint offset = 0; - g_return_val_if_fail (!(rect->applied_global_alpha != 1.0 - && rect->initial_alpha == NULL), FALSE); + g_assert (!(rect->applied_global_alpha != 1.0 + && rect->initial_alpha == NULL)); if (global_alpha == rect->applied_global_alpha) - return TRUE; + return; - if (rect->initial_alpha == NULL && - !gst_video_overlay_rectangle_extract_alpha (rect)) - return FALSE; + if (rect->initial_alpha == NULL) + gst_video_overlay_rectangle_extract_alpha (rect); src = rect->initial_alpha; rect->pixels = gst_buffer_make_writable (rect->pixels); @@ -1001,7 +1000,6 @@ gst_video_overlay_rectangle_apply_global_alpha (GstVideoOverlayRectangle * rect, } rect->applied_global_alpha = global_alpha; - return TRUE; } static GstBuffer * @@ -1112,17 +1110,18 @@ gst_video_overlay_rectangle_get_pixels_argb_internal (GstVideoOverlayRectangle * GST_RECTANGLE_UNLOCK (rectangle); done: + + GST_RECTANGLE_LOCK (rectangle); if (apply_global_alpha && scaled_rect->applied_global_alpha != rectangle->global_alpha) { - if (!gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, - rectangle->global_alpha)) - return NULL; /* return original data? */ + gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, + rectangle->global_alpha); gst_video_overlay_rectangle_set_global_alpha (scaled_rect, rectangle->global_alpha); } else if (revert_global_alpha && scaled_rect->applied_global_alpha != 1.0) { - if (!gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, 1.0)) - return NULL; /* return original data? */ + gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, 1.0); } + GST_RECTANGLE_UNLOCK (rectangle); *stride = scaled_rect->stride; return scaled_rect->pixels; From 79953f27a86c9c550e2ccaa68eb37d56ab63575a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 25 Mar 2012 00:22:29 +0000 Subject: [PATCH 06/12] video: overlay-composition: try to avoid floating point maths in inner loop Try to avoid floating point maths for each pixel to be blended in inner loop, and try to avoid the multiplication entirely for the most common case of the global alpha being 1. Could probably be refactored a bit more. --- gst-libs/gst/video/video-blend.c | 42 +++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/gst-libs/gst/video/video-blend.c b/gst-libs/gst/video/video-blend.c index 9b6c172d73..07f521f198 100644 --- a/gst-libs/gst/video/video-blend.c +++ b/gst-libs/gst/video/video-blend.c @@ -1342,16 +1342,16 @@ gboolean video_blend (GstBlendVideoFormatInfo * dest, GstBlendVideoFormatInfo * src, guint x, guint y, gfloat global_alpha) { - guint i, j; - guint8 alpha; + guint i, j, global_alpha_val; GetPutLine getputdest, getputsrc; gint src_stride; guint8 *tmpdestline = NULL, *tmpsrcline = NULL; gboolean src_premultiplied_alpha; - g_return_val_if_fail (dest, FALSE); - g_return_val_if_fail (src, FALSE); - g_return_val_if_fail (global_alpha >= 0 && global_alpha <= 1, FALSE); + g_assert (dest != NULL); + g_assert (src != NULL); + + global_alpha_val = 256.0 * global_alpha; /* we do no support writing to premultiplied alpha, though that should just be a matter of adding blenders below (BLEND01 and BLEND11) */ @@ -1414,10 +1414,12 @@ video_blend (GstBlendVideoFormatInfo * dest, /* Here dest and src are both either in AYUV or ARGB * TODO: Make the orc version working properly*/ -#define BLENDLOOP(blender) \ +#define BLENDLOOP(blender,alpha_val,alpha_scale) \ do { \ for (j = 0; j < src->width * 4; j += 4) { \ - alpha = (guint8) tmpsrcline[j] * global_alpha; \ + guint8 alpha; \ + \ + alpha = (tmpsrcline[j] * alpha_val) / alpha_scale; \ \ blender (tmpdestline[j + 1], alpha, tmpsrcline[j + 1], tmpdestline[j + 1]); \ blender (tmpdestline[j + 2], alpha, tmpsrcline[j + 2], tmpdestline[j + 2]); \ @@ -1425,14 +1427,26 @@ video_blend (GstBlendVideoFormatInfo * dest, } \ } while(0) - if (src_premultiplied_alpha && dest->premultiplied_alpha) { - /* BLENDLOOP (BLEND11); */ - } else if (!src_premultiplied_alpha && dest->premultiplied_alpha) { - /* BLENDLOOP (BLEND01); */ - } else if (src_premultiplied_alpha && !dest->premultiplied_alpha) { - BLENDLOOP (BLEND10); + if (G_LIKELY (global_alpha == 1.0)) { + if (src_premultiplied_alpha && dest->premultiplied_alpha) { + /* BLENDLOOP (BLEND11, 1, 1); */ + } else if (!src_premultiplied_alpha && dest->premultiplied_alpha) { + /* BLENDLOOP (BLEND01, 1, 1); */ + } else if (src_premultiplied_alpha && !dest->premultiplied_alpha) { + BLENDLOOP (BLEND10, 1, 1); + } else { + BLENDLOOP (BLEND00, 1, 1); + } } else { - BLENDLOOP (BLEND00); + if (src_premultiplied_alpha && dest->premultiplied_alpha) { + /* BLENDLOOP (BLEND11, global_alpha_val, 256); */ + } else if (!src_premultiplied_alpha && dest->premultiplied_alpha) { + /* BLENDLOOP (BLEND01, global_alpha_val, 256); */ + } else if (src_premultiplied_alpha && !dest->premultiplied_alpha) { + BLENDLOOP (BLEND10, global_alpha_val, 256); + } else { + BLENDLOOP (BLEND00, global_alpha_val, 256); + } } #undef BLENDLOOP From 35a17ac152a3563875624988eb94d3209aa19e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 25 Mar 2012 00:31:41 +0000 Subject: [PATCH 07/12] video: overlay-composition: blending micro-optimisation --- gst-libs/gst/video/video-blend.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/gst-libs/gst/video/video-blend.c b/gst-libs/gst/video/video-blend.c index 07f521f198..78ef7eb6fa 100644 --- a/gst-libs/gst/video/video-blend.c +++ b/gst-libs/gst/video/video-blend.c @@ -1263,6 +1263,7 @@ void video_blend_scale_linear_RGBA (GstBlendVideoFormatInfo * src, gint dest_height, gint dest_width) { + const guint8 *src_pixels; int acc; int y_increment; int x_increment; @@ -1293,8 +1294,10 @@ video_blend_scale_linear_RGBA (GstBlendVideoFormatInfo * src, #define LINE(x) ((tmpbuf) + (dest_size)*((x)&1)) + src_pixels = src->pixels; + acc = 0; - orc_resample_bilinear_u32 (LINE (0), src->pixels, 0, x_increment, dest_width); + orc_resample_bilinear_u32 (LINE (0), src_pixels, 0, x_increment, dest_width); y1 = 0; for (i = 0; i < dest_height; i++) { j = acc >> 16; @@ -1305,12 +1308,12 @@ video_blend_scale_linear_RGBA (GstBlendVideoFormatInfo * src, } else { if (j > y1) { orc_resample_bilinear_u32 (LINE (j), - src->pixels + j * src_stride, 0, x_increment, dest_width); + src_pixels + j * src_stride, 0, x_increment, dest_width); y1++; } if (j >= y1) { orc_resample_bilinear_u32 (LINE (j + 1), - src->pixels + (j + 1) * src_stride, 0, x_increment, dest_width); + src_pixels + (j + 1) * src_stride, 0, x_increment, dest_width); y1++; } orc_merge_linear_u8 (dest_pixels + i * dest_stride, @@ -1342,7 +1345,7 @@ gboolean video_blend (GstBlendVideoFormatInfo * dest, GstBlendVideoFormatInfo * src, guint x, guint y, gfloat global_alpha) { - guint i, j, global_alpha_val; + guint i, j, global_alpha_val, src_width, src_height; GetPutLine getputdest, getputsrc; gint src_stride; guint8 *tmpdestline = NULL, *tmpsrcline = NULL; @@ -1404,19 +1407,22 @@ video_blend (GstBlendVideoFormatInfo * dest, if (y + src->height > dest->height) src->height = dest->height - y; + src_width = src->width; + src_height = src->height; + /* Mainloop doing the needed conversions, and blending */ - for (i = y; i < y + src->height; i++) { + for (i = y; i < y + src_height; i++) { getputdest.getline (tmpdestline, dest, x, i); getputsrc.getline (tmpsrcline, src, 0, (i - y)); - getputsrc.matrix (tmpsrcline, src->width); + getputsrc.matrix (tmpsrcline, src_width); /* Here dest and src are both either in AYUV or ARGB * TODO: Make the orc version working properly*/ #define BLENDLOOP(blender,alpha_val,alpha_scale) \ do { \ - for (j = 0; j < src->width * 4; j += 4) { \ + for (j = 0; j < src_width * 4; j += 4) { \ guint8 alpha; \ \ alpha = (tmpsrcline[j] * alpha_val) / alpha_scale; \ From 3242f5586195d20855563e922deaa1aad45ad3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 25 Mar 2012 13:35:23 +0100 Subject: [PATCH 08/12] test: fix leak in video overlay composition unit test gst_buffer_set_qdata() will leak the structure passed to it when called incorrectly (e.g. on a non-metadata-writable buffer). This is expected, but we must avoid doing that in valgrind. --- gst-libs/gst/video/video-blend.c | 1 + tests/check/libs/video.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/video/video-blend.c b/gst-libs/gst/video/video-blend.c index 78ef7eb6fa..140e2bbb79 100644 --- a/gst-libs/gst/video/video-blend.c +++ b/gst-libs/gst/video/video-blend.c @@ -1259,6 +1259,7 @@ G_STMT_START { \ ret = v0 + (v1 * (255 - alpha)) / 255; \ } G_STMT_END +/* returns newly-allocated pixels in src->pixels, which caller must g_free() */ void video_blend_scale_linear_RGBA (GstBlendVideoFormatInfo * src, gint dest_height, gint dest_width) diff --git a/tests/check/libs/video.c b/tests/check/libs/video.c index 91a5e4b113..4abd0b37ab 100644 --- a/tests/check/libs/video.c +++ b/tests/check/libs/video.c @@ -25,6 +25,10 @@ #include "config.h" #endif +#ifdef HAVE_VALGRIND +# include +#endif + #include #include @@ -905,8 +909,13 @@ GST_START_TEST (test_overlay_composition) fail_unless (gst_video_buffer_get_overlay_composition (buf) == NULL); gst_buffer_ref (buf); - /* buffer now has refcount of 2, so its metadata is not writable */ - ASSERT_CRITICAL (gst_video_buffer_set_overlay_composition (buf, comp1)); + /* buffer now has refcount of 2, so its metadata is not writable. + * only check this if we are not running in valgrind, as it leaks */ +#ifdef HAVE_VALGRIND + if (!RUNNING_ON_VALGRIND) { + ASSERT_CRITICAL (gst_video_buffer_set_overlay_composition (buf, comp1)); + } +#endif gst_buffer_unref (buf); gst_video_buffer_set_overlay_composition (buf, comp1); fail_unless (gst_video_buffer_get_overlay_composition (buf) == comp1); From 07d41dd346b12fe4b84206877a26340aa9dcce30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 26 Mar 2012 09:11:49 +0200 Subject: [PATCH 09/12] playback-test: Rename advanced playback to advanced seeking It's about seeking, not general playback. --- tests/examples/playback/playback-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/examples/playback/playback-test.c b/tests/examples/playback/playback-test.c index b01022d572..1a1994ca72 100644 --- a/tests/examples/playback/playback-test.c +++ b/tests/examples/playback/playback-test.c @@ -2613,7 +2613,7 @@ create_ui (PlaybackApp * app) gtk_grid_attach (GTK_GRID (flagtable), rate_label, 4, 0, 1, 1); gtk_grid_attach (GTK_GRID (flagtable), rate_spinbutton, 4, 1, 1, 1); - advanced_seek = gtk_frame_new ("Advanced Playback"); + advanced_seek = gtk_frame_new ("Advanced Seeking"); advanced_seek_grid = gtk_grid_new (); gtk_grid_set_row_spacing (GTK_GRID (advanced_seek_grid), 2); gtk_grid_set_row_homogeneous (GTK_GRID (advanced_seek_grid), FALSE); @@ -2631,7 +2631,7 @@ create_ui (PlaybackApp * app) gtk_grid_attach (GTK_GRID (advanced_seek_grid), app->seek_entry, 0, 1, 1, 1); - seek_button = gtk_button_new_with_label ("Playback"); + seek_button = gtk_button_new_with_label ("Seek"); g_signal_connect (G_OBJECT (seek_button), "clicked", G_CALLBACK (advanced_seek_button_cb), app); gtk_grid_attach (GTK_GRID (advanced_seek_grid), seek_button, 1, 0, 1, 1); From 5e872479374a9d3b99737c6fe505b359c2276e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 26 Mar 2012 09:13:20 +0200 Subject: [PATCH 10/12] playback-test: Make grid children sizes non homogeneous This only takes space for no good reason and doesn't even look good. --- tests/examples/playback/playback-test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/examples/playback/playback-test.c b/tests/examples/playback/playback-test.c index 1a1994ca72..a993c72a53 100644 --- a/tests/examples/playback/playback-test.c +++ b/tests/examples/playback/playback-test.c @@ -2553,7 +2553,7 @@ create_ui (PlaybackApp * app) gtk_grid_set_row_spacing (GTK_GRID (flagtable), 2); gtk_grid_set_row_homogeneous (GTK_GRID (flagtable), FALSE); gtk_grid_set_column_spacing (GTK_GRID (flagtable), 2); - gtk_grid_set_column_homogeneous (GTK_GRID (flagtable), TRUE); + gtk_grid_set_column_homogeneous (GTK_GRID (flagtable), FALSE); accurate_checkbox = gtk_check_button_new_with_label ("Accurate Playback"); key_checkbox = gtk_check_button_new_with_label ("Key-unit Playback"); @@ -2725,7 +2725,7 @@ create_ui (PlaybackApp * app) gtk_grid_set_row_spacing (GTK_GRID (grid), 2); gtk_grid_set_row_homogeneous (GTK_GRID (grid), FALSE); gtk_grid_set_column_spacing (GTK_GRID (grid), 2); - gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE); + gtk_grid_set_column_homogeneous (GTK_GRID (grid), FALSE); navigation_button = gtk_button_new_with_label ("Menu 1"); g_signal_connect (G_OBJECT (navigation_button), "clicked", @@ -2951,7 +2951,7 @@ create_ui (PlaybackApp * app) gtk_grid_set_row_spacing (GTK_GRID (boxes), 2); gtk_grid_set_row_homogeneous (GTK_GRID (boxes), FALSE); gtk_grid_set_column_spacing (GTK_GRID (boxes), 2); - gtk_grid_set_column_homogeneous (GTK_GRID (boxes), TRUE); + gtk_grid_set_column_homogeneous (GTK_GRID (boxes), FALSE); app->video_checkbox = gtk_check_button_new_with_label ("Video"); app->audio_checkbox = gtk_check_button_new_with_label ("Audio"); @@ -3060,7 +3060,7 @@ create_ui (PlaybackApp * app) gtk_grid_set_row_spacing (GTK_GRID (boxes3), 2); gtk_grid_set_row_homogeneous (GTK_GRID (boxes3), FALSE); gtk_grid_set_column_spacing (GTK_GRID (boxes3), 2); - gtk_grid_set_column_homogeneous (GTK_GRID (boxes3), TRUE); + gtk_grid_set_column_homogeneous (GTK_GRID (boxes3), FALSE); label = gtk_label_new ("Video sink"); gtk_grid_attach (GTK_GRID (boxes3), label, 0, 0, 1, 1); From affd142d33249cf236a9dd5db1539b4af513410e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 26 Mar 2012 09:15:18 +0200 Subject: [PATCH 11/12] playback-test: Some minor grid layout improvements --- tests/examples/playback/playback-test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/examples/playback/playback-test.c b/tests/examples/playback/playback-test.c index a993c72a53..720e54ee9f 100644 --- a/tests/examples/playback/playback-test.c +++ b/tests/examples/playback/playback-test.c @@ -2984,9 +2984,9 @@ create_ui (PlaybackApp * app) gtk_grid_attach (GTK_GRID (boxes), app->soft_colorbalance_checkbox, 4, 1, 1, 1); - gtk_grid_attach (GTK_GRID (boxes), app->mute_checkbox, 7, 0, 2, 1); - gtk_grid_attach (GTK_GRID (boxes), volume_label, 6, 1, 1, 1); - gtk_grid_attach (GTK_GRID (boxes), app->volume_spinbutton, 7, 1, 1, 1); + gtk_grid_attach (GTK_GRID (boxes), app->mute_checkbox, 6, 0, 1, 1); + gtk_grid_attach (GTK_GRID (boxes), volume_label, 5, 1, 1, 1); + gtk_grid_attach (GTK_GRID (boxes), app->volume_spinbutton, 6, 1, 1, 1); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->video_checkbox), TRUE); From b47350b6e97db908448921e9700e5ff7a8764f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 26 Mar 2012 13:52:41 +0200 Subject: [PATCH 12/12] pbutils: Add some more subtitle format descriptions --- gst-libs/gst/pbutils/descriptions.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/pbutils/descriptions.c b/gst-libs/gst/pbutils/descriptions.c index 63f5aa6c50..a58de312f7 100644 --- a/gst-libs/gst/pbutils/descriptions.c +++ b/gst-libs/gst/pbutils/descriptions.c @@ -234,13 +234,18 @@ static const FormatInfo formats[] = { {"image/vnd.wap.wbmp", "Wireless Bitmap", 0}, /* subtitle formats with static descriptions */ - {"application/x-ass", "ASS", 0}, + {"application/x-ssa", "SubStation Alpha", 0}, + {"application/x-ass", "Advanced SubStation Alpha", 0}, + /* FIXME: add variant field to typefinder? */ + {"application/x-subtitle", N_("Subtitle"), 0}, + {"application/x-subtitle-mpl2", N_("MPL2 subtitle format"), 0}, + {"application/x-subtitle-dks", N_("DKS subtitle format"), 0}, + {"application/x-subtitle-qttext", N_("QTtext subtitle format"), 0}, {"application/x-subtitle-sami", N_("Sami subtitle format"), 0}, {"application/x-subtitle-tmplayer", N_("TMPlayer subtitle format"), 0}, {"application/x-kate", "Kate", 0}, {"subtitle/x-kate", N_("Kate subtitle format"), 0}, {"subpicture/x-dvb", "DVB subtitles", 0}, - /* add variant field to typefinder? { "application/x-subtitle", N_("subtitle"), 0}, */ /* non-audio/video/container formats */ {"hdv/aux-v", "HDV AUX-V", 0},