*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/**
* </itemizedlist>
* </para>
* </refsect2>
- *
- * Since: 0.10.36
*/
/* TODO:
#include "video-overlay-composition.h"
#include "video-blend.h"
+#include "gstvideometa.h"
+#include <string.h>
struct _GstVideoOverlayComposition
{
guint seq_num;
};
-struct _GstVideoOverlayCompositionClass
-{
- GstMiniObjectClass parent_class;
-};
-
struct _GstVideoOverlayRectangle
{
GstMiniObject parent;
gint x, y;
guint render_width, render_height;
- /* Dimensions of overlay pixels */
- guint width, height, stride;
-
- /* The format of the data in pixels */
- GstVideoFormat format;
+ /* Info on overlay pixels (format, width, height) */
+ GstVideoInfo info;
/* The flags associated to this rectangle */
GstVideoOverlayFormatFlags flags;
* 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)
- GStaticMutex lock;
-#else
GMutex lock;
-#endif
GList *scaled_rectangles;
};
-struct _GstVideoOverlayRectangleClass
-{
- GstMiniObjectClass parent_class;
-};
-
-#if !GLIB_CHECK_VERSION (2, 31, 0)
-#define GST_RECTANGLE_LOCK(rect) g_static_mutex_lock(&rect->lock)
-#define GST_RECTANGLE_UNLOCK(rect) g_static_mutex_unlock(&rect->lock)
-#else
#define GST_RECTANGLE_LOCK(rect) g_mutex_lock(&rect->lock)
#define GST_RECTANGLE_UNLOCK(rect) g_mutex_unlock(&rect->lock)
-#endif
-
-static void gst_video_overlay_composition_class_init (GstMiniObjectClass * k);
-static void gst_video_overlay_composition_finalize (GstMiniObject * comp);
-static void gst_video_overlay_rectangle_class_init (GstMiniObjectClass * klass);
-static void gst_video_overlay_rectangle_finalize (GstMiniObject * rect);
/* --------------------------- utility functions --------------------------- */
{
static gint seqnum; /* 0 */
-#if GLIB_CHECK_VERSION(2,29,5)
return (guint) g_atomic_int_add (&seqnum, 1);
-#else
- return (guint) g_atomic_int_exchange_and_add (&seqnum, 1);
-#endif
}
-#define GST_OVERLAY_COMPOSITION_QUARK gst_overlay_composition_quark_get()
-static GQuark
-gst_overlay_composition_quark_get (void)
+static void
+gst_video_overlay_composition_meta_free (GstMeta * meta, GstBuffer * buf)
+{
+ GstVideoOverlayCompositionMeta *ometa;
+
+ ometa = (GstVideoOverlayCompositionMeta *) meta;
+
+ if (ometa->overlay)
+ gst_video_overlay_composition_unref (ometa->overlay);
+}
+
+static gboolean
+gst_video_overlay_composition_meta_transform (GstBuffer * dest, GstMeta * meta,
+ GstBuffer * buffer, GQuark type, gpointer data)
{
- static gsize quark_gonce = 0;
+ GstVideoOverlayCompositionMeta *dmeta, *smeta;
- if (g_once_init_enter (&quark_gonce)) {
- gsize quark;
+ smeta = (GstVideoOverlayCompositionMeta *) meta;
- quark = (gsize) g_quark_from_static_string ("GstVideoOverlayComposition");
+ if (GST_META_TRANSFORM_IS_COPY (type)) {
+ GstMetaTransformCopy *copy = data;
- g_once_init_leave (&quark_gonce, quark);
- }
+ if (!copy->region) {
+ GST_DEBUG ("copy video overlay composition metadata");
- return (GQuark) quark_gonce;
+ /* only copy if the complete data is copied as well */
+ dmeta =
+ (GstVideoOverlayCompositionMeta *) gst_buffer_add_meta (dest,
+ GST_VIDEO_OVERLAY_COMPOSITION_META_INFO, NULL);
+ dmeta->overlay = gst_video_overlay_composition_ref (smeta->overlay);
+ }
+ }
+ return TRUE;
}
-#define COMPOSITION_QUARK composition_quark_get()
-static GQuark
-composition_quark_get (void)
+GType
+gst_video_overlay_composition_meta_api_get_type (void)
{
- static gsize quark_gonce = 0;
-
- if (g_once_init_enter (&quark_gonce)) {
- gsize quark;
-
- quark = (gsize) g_quark_from_static_string ("composition");
+ static volatile GType type = 0;
+ static const gchar *tags[] = { NULL };
- g_once_init_leave (&quark_gonce, quark);
+ if (g_once_init_enter (&type)) {
+ GType _type =
+ gst_meta_api_type_register ("GstVideoOverlayCompositionMetaAPI", tags);
+ g_once_init_leave (&type, _type);
}
+ return type;
+}
- return (GQuark) quark_gonce;
+/* video overlay composition metadata */
+const GstMetaInfo *
+gst_video_overlay_composition_meta_get_info (void)
+{
+ static const GstMetaInfo *video_overlay_composition_meta_info = NULL;
+
+ if (g_once_init_enter (&video_overlay_composition_meta_info)) {
+ const GstMetaInfo *meta =
+ gst_meta_register (GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE,
+ "GstVideoOverlayCompositionMeta",
+ sizeof (GstVideoOverlayCompositionMeta), (GstMetaInitFunction) NULL,
+ (GstMetaFreeFunction) gst_video_overlay_composition_meta_free,
+ (GstMetaTransformFunction)
+ gst_video_overlay_composition_meta_transform);
+ g_once_init_leave (&video_overlay_composition_meta_info, meta);
+ }
+ return video_overlay_composition_meta_info;
}
/**
- * gst_video_buffer_set_overlay_composition:
+ * gst_buffer_add_video_overlay_composition_meta:
* @buf: a #GstBuffer
- * @comp: (allow-none): a #GstVideoOverlayComposition, or NULL to clear a
- * previously-set composition
+ * @comp: (allow-none): a #GstVideoOverlayComposition
*
* Sets an overlay composition on a buffer. The buffer will obtain its own
* reference to the composition, meaning this function does not take ownership
* of @comp.
- *
- * Since: 0.10.36
*/
-void
-gst_video_buffer_set_overlay_composition (GstBuffer * buf,
+GstVideoOverlayCompositionMeta *
+gst_buffer_add_video_overlay_composition_meta (GstBuffer * buf,
GstVideoOverlayComposition * comp)
{
- gst_buffer_set_qdata (buf, GST_OVERLAY_COMPOSITION_QUARK,
- gst_structure_id_new (GST_OVERLAY_COMPOSITION_QUARK,
- COMPOSITION_QUARK, GST_TYPE_VIDEO_OVERLAY_COMPOSITION, comp, NULL));
-}
+ GstVideoOverlayCompositionMeta *ometa;
-/**
- * gst_video_buffer_get_overlay_composition:
- * @buf: a #GstBuffer
- *
- * Get the overlay composition that has previously been attached to a buffer
- * with gst_video_buffer_get_overlay_composition(), usually by another element
- * upstream.
- *
- * Returns: (transfer none): the #GstVideoOverlayComposition attached to
- * this buffer, or NULL. Does not return a reference to the composition,
- * caller must obtain her own ref via gst_video_overlay_composition_ref()
- * if needed.
- *
- * Since: 0.10.36
- */
-GstVideoOverlayComposition *
-gst_video_buffer_get_overlay_composition (GstBuffer * buf)
-{
- const GstStructure *s;
- const GValue *val;
+ g_return_val_if_fail (gst_buffer_is_writable (buf), NULL);
- s = gst_buffer_get_qdata (buf, GST_OVERLAY_COMPOSITION_QUARK);
- if (s == NULL)
- return NULL;
+ ometa = (GstVideoOverlayCompositionMeta *)
+ gst_buffer_add_meta (buf, GST_VIDEO_OVERLAY_COMPOSITION_META_INFO, NULL);
- val = gst_structure_id_get_value (s, COMPOSITION_QUARK);
- if (val == NULL)
- return NULL;
+ ometa->overlay = gst_video_overlay_composition_ref (comp);
- return GST_VIDEO_OVERLAY_COMPOSITION (gst_value_get_mini_object (val));
+ return ometa;
}
/* ------------------------------ composition ------------------------------ */
#define RECTANGLE_ARRAY_STEP 4 /* premature optimization */
-GType
-gst_video_overlay_composition_get_type (void)
-{
- static volatile gsize type_id = 0;
-
- if (g_once_init_enter (&type_id)) {
- GType new_type_id = g_type_register_static_simple (GST_TYPE_MINI_OBJECT,
- g_intern_static_string ("GstVideoOverlayComposition"),
- sizeof (GstVideoOverlayCompositionClass),
- (GClassInitFunc) gst_video_overlay_composition_class_init,
- sizeof (GstVideoOverlayComposition),
- NULL,
- (GTypeFlags) 0);
-
- g_once_init_leave (&type_id, new_type_id);
- }
-
- return type_id;
-}
+GST_DEFINE_MINI_OBJECT_TYPE (GstVideoOverlayComposition,
+ gst_video_overlay_composition);
static void
-gst_video_overlay_composition_finalize (GstMiniObject * mini_obj)
+gst_video_overlay_composition_free (GstMiniObject * mini_obj)
{
GstVideoOverlayComposition *comp = (GstVideoOverlayComposition *) mini_obj;
guint num;
comp->rectangles = NULL;
comp->num_rectangles = 0;
- /* not chaining up to GstMiniObject's finalize for now, we know it's empty */
-}
-
-static void
-gst_video_overlay_composition_class_init (GstMiniObjectClass * klass)
-{
- klass->finalize = gst_video_overlay_composition_finalize;
- klass->copy = (GstMiniObjectCopyFunction) gst_video_overlay_composition_copy;
+ g_slice_free (GstVideoOverlayComposition, comp);
}
/**
*
* Returns: (transfer full): a new #GstVideoOverlayComposition. Unref with
* gst_video_overlay_composition_unref() when no longer needed.
- *
- * Since: 0.10.36
*/
GstVideoOverlayComposition *
gst_video_overlay_composition_new (GstVideoOverlayRectangle * rectangle)
* an empty new + _add() in a loop is easier? */
g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
- comp = (GstVideoOverlayComposition *)
- gst_mini_object_new (GST_TYPE_VIDEO_OVERLAY_COMPOSITION);
+ comp = g_slice_new0 (GstVideoOverlayComposition);
+
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (comp), 0,
+ GST_TYPE_VIDEO_OVERLAY_COMPOSITION,
+ (GstMiniObjectCopyFunction) gst_video_overlay_composition_copy,
+ NULL, (GstMiniObjectFreeFunction) gst_video_overlay_composition_free);
comp->rectangles = g_new0 (GstVideoOverlayRectangle *, RECTANGLE_ARRAY_STEP);
comp->rectangles[0] = gst_video_overlay_rectangle_ref (rectangle);
*
* Adds an overlay rectangle to an existing overlay composition object. This
* must be done right after creating the overlay composition.
- *
- * Since: 0.10.36
*/
void
gst_video_overlay_composition_add_rectangle (GstVideoOverlayComposition * comp,
* Returns the number of #GstVideoOverlayRectangle<!-- -->s contained in @comp.
*
* Returns: the number of rectangles
- *
- * Since: 0.10.36
*/
guint
gst_video_overlay_composition_n_rectangles (GstVideoOverlayComposition * comp)
* bounds. Will not return a new reference, the caller will need to
* obtain her own reference using gst_video_overlay_rectangle_ref()
* if needed.
- *
- * Since: 0.10.36
*/
GstVideoOverlayRectangle *
gst_video_overlay_composition_get_rectangle (GstVideoOverlayComposition * comp,
static gboolean
gst_video_overlay_rectangle_needs_scaling (GstVideoOverlayRectangle * r)
{
- return (r->width != r->render_width || r->height != r->render_height);
+ return (GST_VIDEO_INFO_WIDTH (&r->info) != r->render_width ||
+ GST_VIDEO_INFO_HEIGHT (&r->info) != r->render_height);
}
/**
* gst_video_overlay_composition_blend:
* @comp: a #GstVideoOverlayComposition
- * @video_buf: a #GstBuffer containing raw video data in a supported format
+ * @video_buf: a #GstVideoFrame containing raw video data in a supported format
*
* Blends the overlay rectangles in @comp on top of the raw video data
- * contained in @video_buf. The data in @video_buf must be writable. If
- * needed, use gst_buffer_make_writable() before calling this function to
- * ensure a buffer is writable. @video_buf must also have valid raw video
- * caps set on it.
- *
- * Since: 0.10.36
+ * contained in @video_buf. The data in @video_buf must be writable and
+ * mapped appropriately.
+ */
+/* FIXME: formats with more than 8 bit per component which get unpacked into
+ * ARGB64 or AYUV64 (such as v210, v216, UYVP, GRAY16_LE and GRAY16_BE)
+ * are not supported yet by the code in video-blend.c.
*/
gboolean
gst_video_overlay_composition_blend (GstVideoOverlayComposition * comp,
- GstBuffer * video_buf)
+ GstVideoFrame * video_buf)
{
- GstBlendVideoFormatInfo video_info, rectangle_info;
+ GstVideoInfo scaled_info;
+ GstVideoInfo *vinfo;
+ GstVideoFrame rectangle_frame;
GstVideoFormat fmt;
+ GstBuffer *pixels = NULL;
gboolean ret = TRUE;
guint n, num;
int w, h;
g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), FALSE);
- g_return_val_if_fail (GST_IS_BUFFER (video_buf), FALSE);
- g_return_val_if_fail (gst_buffer_is_writable (video_buf), FALSE);
- g_return_val_if_fail (GST_BUFFER_CAPS (video_buf) != NULL, FALSE);
-
- if (!gst_video_format_parse_caps (GST_BUFFER_CAPS (video_buf), &fmt, &w, &h)) {
- gchar *str = gst_caps_to_string (GST_BUFFER_CAPS (video_buf));
- g_warning ("%s: could not parse video buffer caps '%s'", GST_FUNCTION, str);
- g_free (str);
- return FALSE;
- }
+ g_return_val_if_fail (video_buf != NULL, FALSE);
- video_blend_format_info_init (&video_info, GST_BUFFER_DATA (video_buf),
- h, w, fmt, FALSE);
+ w = GST_VIDEO_FRAME_WIDTH (video_buf);
+ h = GST_VIDEO_FRAME_HEIGHT (video_buf);
+ fmt = GST_VIDEO_FRAME_FORMAT (video_buf);
num = comp->num_rectangles;
GST_LOG ("Blending composition %p with %u rectangles onto video buffer %p "
rect = comp->rectangles[n];
- GST_LOG (" rectangle %u %p: %ux%u, format %u", n, rect, rect->height,
- rect->width, rect->format);
-
- 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));
+ GST_LOG (" rectangle %u %p: %ux%u, format %u", n, rect,
+ GST_VIDEO_INFO_WIDTH (&rect->info), GST_VIDEO_INFO_HEIGHT (&rect->info),
+ GST_VIDEO_INFO_FORMAT (&rect->info));
needs_scaling = gst_video_overlay_rectangle_needs_scaling (rect);
if (needs_scaling) {
- video_blend_scale_linear_RGBA (&rectangle_info, rect->render_height,
- rect->render_width);
+ gst_video_blend_scale_linear_RGBA (&rect->info, rect->pixels,
+ rect->render_height, rect->render_width, &scaled_info, &pixels);
+ vinfo = &scaled_info;
+ } else {
+ pixels = gst_buffer_ref (rect->pixels);
+ vinfo = &rect->info;
}
- ret = video_blend (&video_info, &rectangle_info, rect->x, rect->y);
+ gst_video_frame_map (&rectangle_frame, vinfo, pixels, GST_MAP_READ);
+
+ ret = gst_video_blend (video_buf, &rectangle_frame, rect->x, rect->y,
+ rect->global_alpha);
+ gst_video_frame_unmap (&rectangle_frame);
if (!ret) {
GST_WARNING ("Could not blend overlay rectangle onto video buffer");
}
/* FIXME: should cache scaled pixels in the rectangle struct */
- if (needs_scaling)
- g_free (rectangle_info.pixels);
+ gst_buffer_unref (pixels);
}
return ret;
*
* Returns: (transfer full): a new #GstVideoOverlayComposition equivalent
* to @comp.
- *
- * Since: 0.10.36
*/
GstVideoOverlayComposition *
gst_video_overlay_composition_copy (GstVideoOverlayComposition * comp)
*
* Returns: (transfer full): a writable #GstVideoOverlayComposition
* equivalent to @comp.
- *
- * Since: 0.10.36
*/
GstVideoOverlayComposition *
gst_video_overlay_composition_make_writable (GstVideoOverlayComposition * comp)
* a composition).
*
* Returns: the sequence number of @comp
- *
- * Since: 0.10.36
*/
guint
gst_video_overlay_composition_get_seqnum (GstVideoOverlayComposition * comp)
/* ------------------------------ rectangles ------------------------------ -*/
-static void gst_video_overlay_rectangle_instance_init (GstMiniObject * miniobj);
-
-GType
-gst_video_overlay_rectangle_get_type (void)
-{
- static volatile gsize type_id = 0;
-
- if (g_once_init_enter (&type_id)) {
- GType new_type_id = g_type_register_static_simple (GST_TYPE_MINI_OBJECT,
- g_intern_static_string ("GstVideoOverlayRectangle"),
- sizeof (GstVideoOverlayRectangleClass),
- (GClassInitFunc) gst_video_overlay_rectangle_class_init,
- sizeof (GstVideoOverlayRectangle),
- (GInstanceInitFunc) gst_video_overlay_rectangle_instance_init,
- (GTypeFlags) 0);
-
- g_once_init_leave (&type_id, new_type_id);
- }
-
- return type_id;
-}
+GST_DEFINE_MINI_OBJECT_TYPE (GstVideoOverlayRectangle,
+ gst_video_overlay_rectangle);
static void
-gst_video_overlay_rectangle_finalize (GstMiniObject * mini_obj)
+gst_video_overlay_rectangle_free (GstMiniObject * mini_obj)
{
GstVideoOverlayRectangle *rect = (GstVideoOverlayRectangle *) mini_obj;
rect->scaled_rectangles =
g_list_delete_link (rect->scaled_rectangles, rect->scaled_rectangles);
}
-#if !GLIB_CHECK_VERSION (2, 31, 0)
- g_static_mutex_free (&rect->lock);
-#else
- g_mutex_clear (&rect->lock);
-#endif
- /* not chaining up to GstMiniObject's finalize for now, we know it's empty */
-}
-
-static void
-gst_video_overlay_rectangle_class_init (GstMiniObjectClass * klass)
-{
- klass->finalize = gst_video_overlay_rectangle_finalize;
- klass->copy = (GstMiniObjectCopyFunction) gst_video_overlay_rectangle_copy;
-}
-static void
-gst_video_overlay_rectangle_instance_init (GstMiniObject * mini_obj)
-{
- GstVideoOverlayRectangle *rect = (GstVideoOverlayRectangle *) mini_obj;
+ g_free (rect->initial_alpha);
+ g_mutex_clear (&rect->lock);
-#if !GLIB_CHECK_VERSION (2, 31, 0)
- g_static_mutex_init (&rect->lock);
-#else
- g_mutex_init (&rect->lock);
-#endif
+ g_slice_free (GstVideoOverlayRectangle, rect);
}
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
/**
- * gst_video_overlay_rectangle_new_argb:
+ * gst_video_overlay_rectangle_new_raw:
* @pixels: (transfer none): a #GstBuffer pointing to the pixel memory
- * @width: the width of the rectangle in @pixels
- * @height: the height of the rectangle in @pixels
- * @stride: the stride of the rectangle in @pixels in bytes (>= 4*width)
- * @x: the X co-ordinate on the video where the top-left corner of this
+ * @render_x: the X co-ordinate on the video where the top-left corner of this
* overlay rectangle should be rendered to
- * @y: the Y co-ordinate on the video where the top-left corner of this
+ * @render_y: the Y co-ordinate on the video where the top-left corner of this
* overlay rectangle should be rendered to
* @render_width: the render width of this rectangle on the video
* @render_height: the render height of this rectangle on the video
* @flags: flags
*
- * Creates a new video overlay rectangle with ARGB pixel data. The layout
- * of the components in memory is B-G-R-A on little-endian platforms
+ * Creates a new video overlay rectangle with ARGB or AYUV pixel data.
+ * The layout in case of ARGB of the components in memory is B-G-R-A
+ * on little-endian platforms
* (corresponding to #GST_VIDEO_FORMAT_BGRA) and A-R-G-B on big-endian
* platforms (corresponding to #GST_VIDEO_FORMAT_ARGB). In other words,
* pixels are treated as 32-bit words and the lowest 8 bits then contain
* component value. Unless specified in the flags, the RGB values are
* non-premultiplied. This is the format that is used by most hardware,
* and also many rendering libraries such as Cairo, for example.
+ * The pixel data buffer must have #GstVideoMeta set.
*
* Returns: (transfer full): a new #GstVideoOverlayRectangle. Unref with
* gst_video_overlay_rectangle_unref() when no longer needed.
- *
- * Since: 0.10.36
*/
GstVideoOverlayRectangle *
-gst_video_overlay_rectangle_new_argb (GstBuffer * pixels,
- guint width, guint height, guint stride, gint render_x, gint render_y,
- guint render_width, guint render_height, GstVideoOverlayFormatFlags flags)
+gst_video_overlay_rectangle_new_raw (GstBuffer * pixels,
+ gint render_x, gint render_y, guint render_width, guint render_height,
+ GstVideoOverlayFormatFlags flags)
{
GstVideoOverlayRectangle *rect;
+ GstVideoMeta *vmeta;
+ GstVideoFormat format;
+ guint width, height;
g_return_val_if_fail (GST_IS_BUFFER (pixels), NULL);
- /* technically ((height-1)*stride)+width might be okay too */
- g_return_val_if_fail (GST_BUFFER_SIZE (pixels) >= height * stride, NULL);
- g_return_val_if_fail (stride >= (4 * width), NULL);
- g_return_val_if_fail (height > 0 && width > 0, NULL);
g_return_val_if_fail (render_height > 0 && render_width > 0, NULL);
g_return_val_if_fail (gst_video_overlay_rectangle_check_flags (flags), NULL);
- rect = (GstVideoOverlayRectangle *)
- gst_mini_object_new (GST_TYPE_VIDEO_OVERLAY_RECTANGLE);
+ /* buffer must have video meta with some expected settings */
+ vmeta = gst_buffer_get_video_meta (pixels);
+ g_return_val_if_fail (vmeta, NULL);
+ g_return_val_if_fail (vmeta->format ==
+ GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB ||
+ vmeta->format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV, NULL);
+ g_return_val_if_fail (vmeta->flags == GST_VIDEO_FRAME_FLAG_NONE, NULL);
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
- rect->format = GST_VIDEO_FORMAT_BGRA;
-#else
- rect->format = GST_VIDEO_FORMAT_ARGB;
-#endif
+ format = vmeta->format;
+ width = vmeta->width;
+ height = vmeta->height;
+
+ /* technically ((height-1)*stride)+width might be okay too */
+ g_return_val_if_fail (gst_buffer_get_size (pixels) >= height * width * 4,
+ NULL);
+ g_return_val_if_fail (height > 0 && width > 0, NULL);
+
+ rect = g_slice_new0 (GstVideoOverlayRectangle);
+
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (rect), 0,
+ GST_TYPE_VIDEO_OVERLAY_RECTANGLE,
+ (GstMiniObjectCopyFunction) gst_video_overlay_rectangle_copy,
+ NULL, (GstMiniObjectFreeFunction) gst_video_overlay_rectangle_free);
+
+ g_mutex_init (&rect->lock);
rect->pixels = gst_buffer_ref (pixels);
+ rect->scaled_rectangles = NULL;
- rect->width = width;
- rect->height = height;
- rect->stride = stride;
+ gst_video_info_init (&rect->info);
+ gst_video_info_set_format (&rect->info, format, width, height);
+ if (flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
+ rect->info.flags |= GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA;
rect->x = render_x;
rect->y = render_y;
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, format,
+ rect->flags, pixels, rect->global_alpha);
return rect;
}
* rectangle on the video.
*
* Returns: TRUE if valid render dimensions were retrieved.
- *
- * Since: 0.10.36
*/
gboolean
gst_video_overlay_rectangle_get_render_rectangle (GstVideoOverlayRectangle *
* make the rectangles inside a #GstVideoOverlayComposition writable using
* gst_video_overlay_composition_make_writable() or
* gst_video_overlay_composition_copy().
- *
- * Since: 0.10.36
*/
void
gst_video_overlay_rectangle_set_render_rectangle (GstVideoOverlayRectangle *
rectangle->render_height = render_height;
}
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-# define ARGB_A 3
-# define ARGB_R 2
-# define ARGB_G 1
-# define ARGB_B 0
-#else
-# define ARGB_A 0
-# define ARGB_R 1
-# define ARGB_G 2
-# define ARGB_B 3
-#endif
-
/* FIXME: orc-ify */
static void
-gst_video_overlay_rectangle_premultiply (GstBlendVideoFormatInfo * info)
+gst_video_overlay_rectangle_premultiply_0 (GstVideoFrame * frame)
{
int i, j;
- for (j = 0; j < info->height; ++j) {
- guint8 *line = info->pixels + info->stride[0] * j;
- for (i = 0; i < info->width; ++i) {
- int a = line[ARGB_A];
- line[ARGB_R] = line[ARGB_R] * a / 255;
- line[ARGB_G] = line[ARGB_G] * a / 255;
- line[ARGB_B] = line[ARGB_B] * a / 255;
+ for (j = 0; j < GST_VIDEO_FRAME_HEIGHT (frame); ++j) {
+ guint8 *line;
+
+ line = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+ line += GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) * j;
+ for (i = 0; i < GST_VIDEO_FRAME_WIDTH (frame); ++i) {
+ int a = line[0];
+ line[1] = line[1] * a / 255;
+ line[2] = line[2] * a / 255;
+ line[3] = line[3] * a / 255;
line += 4;
}
}
}
+static void
+gst_video_overlay_rectangle_premultiply_3 (GstVideoFrame * frame)
+{
+ int i, j;
+ for (j = 0; j < GST_VIDEO_FRAME_HEIGHT (frame); ++j) {
+ guint8 *line;
+
+ line = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+ line += GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) * j;
+ for (i = 0; i < GST_VIDEO_FRAME_WIDTH (frame); ++i) {
+ int a = line[3];
+ line[0] = line[0] * a / 255;
+ line[1] = line[1] * a / 255;
+ line[2] = line[2] * a / 255;
+ line += 4;
+ }
+ }
+}
+
+static void
+gst_video_overlay_rectangle_premultiply (GstVideoFrame * frame)
+{
+ gint alpha_offset;
+
+ alpha_offset = GST_VIDEO_FRAME_COMP_POFFSET (frame, 3);
+ switch (alpha_offset) {
+ case 0:
+ gst_video_overlay_rectangle_premultiply_0 (frame);
+ break;
+ case 3:
+ gst_video_overlay_rectangle_premultiply_3 (frame);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
/* FIXME: orc-ify */
static void
-gst_video_overlay_rectangle_unpremultiply (GstBlendVideoFormatInfo * info)
+gst_video_overlay_rectangle_unpremultiply_0 (GstVideoFrame * frame)
{
int i, j;
- for (j = 0; j < info->height; ++j) {
- guint8 *line = info->pixels + info->stride[0] * j;
- for (i = 0; i < info->width; ++i) {
- int a = line[ARGB_A];
+ for (j = 0; j < GST_VIDEO_FRAME_HEIGHT (frame); ++j) {
+ guint8 *line;
+
+ line = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+ line += GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) * j;
+ for (i = 0; i < GST_VIDEO_FRAME_WIDTH (frame); ++i) {
+ int a = line[0];
if (a) {
- line[ARGB_R] = MIN ((line[ARGB_R] * 255 + a / 2) / a, 255);
- line[ARGB_G] = MIN ((line[ARGB_G] * 255 + a / 2) / a, 255);
- line[ARGB_B] = MIN ((line[ARGB_B] * 255 + a / 2) / a, 255);
+ line[1] = MIN ((line[1] * 255 + a / 2) / a, 255);
+ line[2] = MIN ((line[2] * 255 + a / 2) / a, 255);
+ line[3] = MIN ((line[3] * 255 + a / 2) / a, 255);
}
line += 4;
}
}
}
+static void
+gst_video_overlay_rectangle_unpremultiply_3 (GstVideoFrame * frame)
+{
+ int i, j;
+ for (j = 0; j < GST_VIDEO_FRAME_HEIGHT (frame); ++j) {
+ guint8 *line;
+
+ line = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+ line += GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) * j;
+ for (i = 0; i < GST_VIDEO_FRAME_WIDTH (frame); ++i) {
+ int a = line[3];
+ if (a) {
+ line[0] = MIN ((line[0] * 255 + a / 2) / a, 255);
+ line[1] = MIN ((line[1] * 255 + a / 2) / a, 255);
+ line[2] = MIN ((line[2] * 255 + a / 2) / a, 255);
+ }
+ line += 4;
+ }
+ }
+}
+
+static void
+gst_video_overlay_rectangle_unpremultiply (GstVideoFrame * frame)
+{
+ gint alpha_offset;
+
+ alpha_offset = GST_VIDEO_FRAME_COMP_POFFSET (frame, 3);
+ switch (alpha_offset) {
+ case 0:
+ gst_video_overlay_rectangle_unpremultiply_0 (frame);
+ break;
+ case 3:
+ gst_video_overlay_rectangle_unpremultiply_3 (frame);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+
+static void
+gst_video_overlay_rectangle_extract_alpha (GstVideoOverlayRectangle * rect)
+{
+ guint8 *src, *dst;
+ GstVideoFrame frame;
+ gint i, j, w, h, stride, alpha_offset;
+
+ alpha_offset = GST_VIDEO_INFO_COMP_POFFSET (&rect->info, 3);
+ g_return_if_fail (alpha_offset == 0 || alpha_offset == 3);
+
+ gst_video_frame_map (&frame, &rect->info, rect->pixels, GST_MAP_READ);
+ src = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
+ w = GST_VIDEO_INFO_WIDTH (&rect->info);
+ h = GST_VIDEO_INFO_HEIGHT (&rect->info);
+ stride = GST_VIDEO_INFO_PLANE_STRIDE (&rect->info, 0);
+
+ g_free (rect->initial_alpha);
+ rect->initial_alpha = g_malloc (w * h);
+ dst = rect->initial_alpha;
+
+ for (i = 0; i < h; i++) {
+ for (j = 0; j < w; j++) {
+ *dst = src[alpha_offset];
+ dst++;
+ src += 4;
+ }
+ src += stride - 4 * w;
+ }
+ gst_video_frame_unmap (&frame);
+}
+
+
+static void
+gst_video_overlay_rectangle_apply_global_alpha (GstVideoOverlayRectangle * rect,
+ float global_alpha)
+{
+ guint8 *src, *dst;
+ GstVideoFrame frame;
+ gint i, j, w, h, stride;
+ gint argb_a, argb_r, argb_g, argb_b;
+
+ g_assert (!(rect->applied_global_alpha != 1.0
+ && rect->initial_alpha == NULL));
+
+ if (global_alpha == rect->applied_global_alpha)
+ return;
+
+ if (rect->initial_alpha == NULL)
+ gst_video_overlay_rectangle_extract_alpha (rect);
+
+ src = rect->initial_alpha;
+ rect->pixels = gst_buffer_make_writable (rect->pixels);
+
+ gst_video_frame_map (&frame, &rect->info, rect->pixels, GST_MAP_READ);
+ dst = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
+ w = GST_VIDEO_INFO_WIDTH (&rect->info);
+ h = GST_VIDEO_INFO_HEIGHT (&rect->info);
+ stride = GST_VIDEO_INFO_PLANE_STRIDE (&rect->info, 0);
+
+ argb_a = GST_VIDEO_INFO_COMP_POFFSET (&rect->info, 3);
+ argb_r = (argb_a + 1) % 4;
+ argb_g = (argb_a + 2) % 4;
+ argb_b = (argb_a + 3) % 4;
+
+ for (i = 0; i < h; i++) {
+ for (j = 0; j < w; j++) {
+ guint8 na = (guint8) (*src * global_alpha);
+
+ if (! !(rect->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)) {
+ dst[argb_r] =
+ (guint8) ((double) (dst[argb_r] * 255) / (double) dst[argb_a]) *
+ na / 255;
+ dst[argb_g] =
+ (guint8) ((double) (dst[argb_g] * 255) / (double) dst[argb_a]) *
+ na / 255;
+ dst[argb_b] =
+ (guint8) ((double) (dst[argb_b] * 255) / (double) dst[argb_a]) *
+ na / 255;
+ }
+ dst[argb_a] = na;
+ src++;
+ dst += 4;
+ }
+ dst += stride - 4 * w;
+ }
+ gst_video_frame_unmap (&frame);
+
+ rect->applied_global_alpha = global_alpha;
+}
+
+static void
+gst_video_overlay_rectangle_convert (GstVideoInfo * src, GstBuffer * src_buffer,
+ GstVideoFormat dest_format, GstVideoInfo * dest, GstBuffer ** dest_buffer)
+{
+ gint width, height, stride;
+ GstVideoFrame src_frame, dest_frame;
+ GstVideoFormat format;
+ gint k, l;
+ guint8 *sdata, *ddata;
+
+ format = GST_VIDEO_INFO_FORMAT (src);
+
+ width = GST_VIDEO_INFO_WIDTH (src);
+ height = GST_VIDEO_INFO_HEIGHT (src);
+
+ gst_video_info_init (dest);
+ gst_video_info_set_format (dest, dest_format, width, height);
+
+ *dest_buffer = gst_buffer_new_and_alloc (GST_VIDEO_INFO_SIZE (dest));
+
+ gst_video_frame_map (&src_frame, src, src_buffer, GST_MAP_READ);
+ gst_video_frame_map (&dest_frame, dest, *dest_buffer, GST_MAP_WRITE);
+
+ sdata = GST_VIDEO_FRAME_PLANE_DATA (&src_frame, 0);
+ ddata = GST_VIDEO_FRAME_PLANE_DATA (&dest_frame, 0);
+ stride = GST_VIDEO_FRAME_PLANE_STRIDE (&src_frame, 0);
+
+ if (format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV &&
+ dest_format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB) {
+ gint ayuv;
+ gint a, y, u, v, r, g, b;
+
+ for (k = 0; k < height; k++) {
+ for (l = 0; l < width; l++) {
+ ayuv = GST_READ_UINT32_BE (sdata);
+ a = ayuv >> 24;
+ y = (ayuv >> 16) & 0xff;
+ u = (ayuv >> 8) & 0xff;
+ v = (ayuv & 0xff);
+
+ r = (298 * y + 459 * v - 63514) >> 8;
+ g = (298 * y - 55 * u - 136 * v + 19681) >> 8;
+ b = (298 * y + 541 * u - 73988) >> 8;
+
+ r = CLAMP (r, 0, 255);
+ g = CLAMP (g, 0, 255);
+ b = CLAMP (b, 0, 255);
+
+ /* native endian ARGB */
+ *(guint32 *) ddata = ((a << 24) | (r << 16) | (g << 8) | b);
+
+ sdata += 4;
+ ddata += 4;
+ }
+ sdata += stride - 4 * width;
+ }
+ } else if (format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB &&
+ dest_format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV) {
+ gint argb;
+ gint a, y, u, v, r, g, b;
+
+ for (k = 0; k < height; k++) {
+ for (l = 0; l < width; l++) {
+ /* native endian ARGB */
+ argb = *(guint32 *) sdata;
+ a = argb >> 24;
+ r = (argb >> 16) & 0xff;
+ g = (argb >> 8) & 0xff;
+ b = (argb & 0xff);
+
+ y = (47 * r + 157 * g + 16 * b + 4096) >> 8;
+ u = (-26 * r - 87 * g + 112 * b + 32768) >> 8;
+ v = (112 * r - 102 * g - 10 * b + 32768) >> 8;
+
+ y = CLAMP (y, 0, 255);
+ u = CLAMP (u, 0, 255);
+ v = CLAMP (v, 0, 255);
+
+ GST_WRITE_UINT32_BE (ddata, ((a << 24) | (y << 16) | (u << 8) | v));
+
+ sdata += 4;
+ ddata += 4;
+ }
+ sdata += stride - 4 * width;
+ }
+ } else {
+ GST_ERROR ("unsupported conversion");
+ g_assert_not_reached ();
+ }
+
+ gst_video_frame_unmap (&src_frame);
+ gst_video_frame_unmap (&dest_frame);
+}
+
static GstBuffer *
-gst_video_overlay_rectangle_get_pixels_argb_internal (GstVideoOverlayRectangle *
- rectangle, guint * stride, GstVideoOverlayFormatFlags flags,
- gboolean unscaled)
+gst_video_overlay_rectangle_get_pixels_raw_internal (GstVideoOverlayRectangle *
+ rectangle, GstVideoOverlayFormatFlags flags, gboolean unscaled,
+ GstVideoFormat wanted_format)
{
GstVideoOverlayFormatFlags new_flags;
- GstVideoOverlayRectangle *scaled_rect = NULL;
- GstBlendVideoFormatInfo info;
+ GstVideoOverlayRectangle *scaled_rect = NULL, *conv_rect = NULL;
+ GstVideoInfo info;
+ GstVideoFrame frame;
GstBuffer *buf;
GList *l;
- guint wanted_width = unscaled ? rectangle->width : rectangle->render_width;
- guint wanted_height = unscaled ? rectangle->height : rectangle->render_height;
+ guint width, height;
+ guint wanted_width;
+ guint wanted_height;
+ gboolean apply_global_alpha;
+ gboolean revert_global_alpha;
+ GstVideoFormat format;
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);
+ width = GST_VIDEO_INFO_WIDTH (&rectangle->info);
+ height = GST_VIDEO_INFO_HEIGHT (&rectangle->info);
+ wanted_width = unscaled ? width : rectangle->render_width;
+ wanted_height = unscaled ? height : rectangle->render_height;
+ format = GST_VIDEO_INFO_FORMAT (&rectangle->info);
+
+ 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 &&
+ if (wanted_width == width &&
+ wanted_height == height &&
+ wanted_format == format &&
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)) {
+ return rectangle->pixels;
+ } else {
+ /* only apply/revert global-alpha */
+ scaled_rect = rectangle;
+ goto done;
+ }
}
/* see if we've got one cached already */
for (l = rectangle->scaled_rectangles; l != NULL; l = l->next) {
GstVideoOverlayRectangle *r = l->data;
- if (r->width == wanted_width &&
- r->height == wanted_height &&
+ if (GST_VIDEO_INFO_WIDTH (&r->info) == wanted_width &&
+ GST_VIDEO_INFO_HEIGHT (&r->info) == wanted_height &&
+ GST_VIDEO_INFO_FORMAT (&r->info) == wanted_format &&
gst_video_overlay_rectangle_is_same_alpha_type (r->flags, flags)) {
/* we'll keep these rectangles around until finalize, so it's ok not
* to take our own ref here */
if (scaled_rect != NULL)
goto done;
- /* not cached yet, do the scaling 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));
-
- if (wanted_width != rectangle->width || wanted_height != rectangle->height) {
- video_blend_scale_linear_RGBA (&info, wanted_height, wanted_width);
+ /* maybe have one in the right format though */
+ if (format != wanted_format) {
+ GST_RECTANGLE_LOCK (rectangle);
+ for (l = rectangle->scaled_rectangles; l != NULL; l = l->next) {
+ GstVideoOverlayRectangle *r = l->data;
+
+ if (GST_VIDEO_INFO_FORMAT (&r->info) == wanted_format &&
+ gst_video_overlay_rectangle_is_same_alpha_type (r->flags, flags)) {
+ /* we'll keep these rectangles around until finalize, so it's ok not
+ * to take our own ref here */
+ conv_rect = r;
+ break;
+ }
+ }
+ GST_RECTANGLE_UNLOCK (rectangle);
} else {
+ conv_rect = rectangle;
+ }
+
+ if (conv_rect == NULL) {
+ GstVideoInfo conv_info;
+
+ gst_video_overlay_rectangle_convert (&rectangle->info, rectangle->pixels,
+ wanted_format, &conv_info, &buf);
+ gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
+ GST_VIDEO_INFO_FORMAT (&conv_info), width, height);
+ conv_rect = gst_video_overlay_rectangle_new_raw (buf,
+ 0, 0, width, height, rectangle->flags);
+ if (rectangle->global_alpha != 1.0)
+ gst_video_overlay_rectangle_set_global_alpha (scaled_rect,
+ rectangle->global_alpha);
+ gst_buffer_unref (buf);
+ /* keep this converted one around as well in any case */
+ GST_RECTANGLE_LOCK (rectangle);
+ rectangle->scaled_rectangles =
+ g_list_prepend (rectangle->scaled_rectangles, conv_rect);
+ GST_RECTANGLE_UNLOCK (rectangle);
+ }
+
+ /* now we continue from conv_rect */
+ width = GST_VIDEO_INFO_WIDTH (&conv_rect->info);
+ height = GST_VIDEO_INFO_HEIGHT (&conv_rect->info);
+ format = GST_VIDEO_INFO_FORMAT (&conv_rect->info);
+
+ /* not cached yet, do the preprocessing and put the result into our cache */
+ if (wanted_width != width || wanted_height != height) {
+ GstVideoInfo scaled_info;
+
+ /* we could check the cache for a scaled rect with global_alpha == 1 here */
+ gst_video_blend_scale_linear_RGBA (&conv_rect->info, conv_rect->pixels,
+ wanted_height, wanted_width, &scaled_info, &buf);
+ info = scaled_info;
+ gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
+ GST_VIDEO_INFO_FORMAT (&conv_rect->info), wanted_width, wanted_height);
+ } else if (!gst_video_overlay_rectangle_is_same_alpha_type (conv_rect->flags,
+ flags)) {
/* if we don't have to scale, we have to modify the alpha values, so we
* need to make a copy of the pixel memory (and we take ownership below) */
- info.pixels = g_memdup (info.pixels, info.size);
+ buf = gst_buffer_copy (conv_rect->pixels);
+ info = conv_rect->info;
+ } else {
+ /* do not need to scale or modify alpha values, almost done then */
+ scaled_rect = conv_rect;
+ goto done;
}
- new_flags = rectangle->flags;
- if (!gst_video_overlay_rectangle_is_same_alpha_type (rectangle->flags, flags)) {
+ new_flags = conv_rect->flags;
+ gst_video_frame_map (&frame, &info, buf, GST_MAP_READWRITE);
+ if (!gst_video_overlay_rectangle_is_same_alpha_type (conv_rect->flags, flags)) {
if (rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA) {
- gst_video_overlay_rectangle_unpremultiply (&info);
+ gst_video_overlay_rectangle_unpremultiply (&frame);
new_flags &= ~GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
} else {
- gst_video_overlay_rectangle_premultiply (&info);
+ gst_video_overlay_rectangle_premultiply (&frame);
new_flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
}
}
+ gst_video_frame_unmap (&frame);
- buf = gst_buffer_new ();
- GST_BUFFER_DATA (buf) = info.pixels;
- GST_BUFFER_MALLOCDATA (buf) = info.pixels;
- GST_BUFFER_SIZE (buf) = info.size;
-
- scaled_rect = gst_video_overlay_rectangle_new_argb (buf,
- wanted_width, wanted_height, info.stride[0],
+ scaled_rect = gst_video_overlay_rectangle_new_raw (buf,
0, 0, wanted_width, wanted_height, new_flags);
-
+ if (conv_rect->global_alpha != 1.0)
+ gst_video_overlay_rectangle_set_global_alpha (scaled_rect,
+ conv_rect->global_alpha);
gst_buffer_unref (buf);
GST_RECTANGLE_LOCK (rectangle);
done:
- *stride = scaled_rect->stride;
+ GST_RECTANGLE_LOCK (rectangle);
+ if (apply_global_alpha
+ && scaled_rect->applied_global_alpha != rectangle->global_alpha) {
+ 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) {
+ gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, 1.0);
+ }
+ GST_RECTANGLE_UNLOCK (rectangle);
+
return scaled_rect->pixels;
}
+
+/**
+ * gst_video_overlay_rectangle_get_pixels_raw:
+ * @rectangle: a #GstVideoOverlayRectangle
+ * @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 pixel data with
+ * format as originally provided and specified in video meta with
+ * width and height of the render dimensions as per
+ * gst_video_overlay_rectangle_get_render_rectangle(). This function does
+ * not return a reference, the caller should obtain a reference of her own
+ * with gst_buffer_ref() if needed.
+ */
+GstBuffer *
+gst_video_overlay_rectangle_get_pixels_raw (GstVideoOverlayRectangle *
+ rectangle, GstVideoOverlayFormatFlags flags)
+{
+ return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
+ flags, FALSE, GST_VIDEO_INFO_FORMAT (&rectangle->info));
+}
+
/**
* 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
+ * width and height of the render dimensions as per
* gst_video_overlay_rectangle_get_render_rectangle(). This function does
* not return a reference, the caller should obtain a reference of her own
* with gst_buffer_ref() if needed.
- *
- * Since: 0.10.36
*/
GstBuffer *
gst_video_overlay_rectangle_get_pixels_argb (GstVideoOverlayRectangle *
- rectangle, guint * stride, GstVideoOverlayFormatFlags flags)
+ rectangle, GstVideoOverlayFormatFlags flags)
{
- return gst_video_overlay_rectangle_get_pixels_argb_internal (rectangle,
- stride, flags, FALSE);
+ return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
+ flags, FALSE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB);
}
/**
- * gst_video_overlay_rectangle_get_pixels_unscaled_argb:
+ * gst_video_overlay_rectangle_get_pixels_ayuv:
* @rectangle: a #GstVideoOverlayRectangle
- * @width: (out): address where to store the width of the unscaled
- * rectangle in pixels
- * @width: (out): address where to store the height of the unscaled
- * 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
+ * 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 AYUV pixel data with
+ * width and height of the render dimensions as per
+ * gst_video_overlay_rectangle_get_render_rectangle(). This function does
+ * not return a reference, the caller should obtain a reference of her own
+ * with gst_buffer_ref() if needed.
+ */
+GstBuffer *
+gst_video_overlay_rectangle_get_pixels_ayuv (GstVideoOverlayRectangle *
+ rectangle, GstVideoOverlayFormatFlags flags)
+{
+ return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
+ flags, FALSE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV);
+}
+
+/**
+ * gst_video_overlay_rectangle_get_pixels_unscaled_raw:
+ * @rectangle: a #GstVideoOverlayRectangle
+ * @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
* need to be scaled to the render dimensions, which can be retrieved using
* gst_video_overlay_rectangle_get_render_rectangle().
*
- * Returns: (transfer none): a #GstBuffer holding the ARGB pixel data with
- * row stride @stride. This function does not return a reference, the caller
+ * Returns: (transfer none): a #GstBuffer holding the pixel data with
+ * #GstVideoMeta set. This function does not return a reference, the caller
* should obtain a reference of her own with gst_buffer_ref() if needed.
+ */
+GstBuffer *
+gst_video_overlay_rectangle_get_pixels_unscaled_raw (GstVideoOverlayRectangle *
+ rectangle, GstVideoOverlayFormatFlags flags)
+{
+ g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
+
+ return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
+ flags, TRUE, GST_VIDEO_INFO_FORMAT (&rectangle->info));
+}
+
+/**
+ * gst_video_overlay_rectangle_get_pixels_unscaled_argb:
+ * @rectangle: a #GstVideoOverlayRectangle
+ * @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
+ * need to be scaled to the render dimensions, which can be retrieved using
+ * gst_video_overlay_rectangle_get_render_rectangle().
*
- * Since: 0.10.36
+ * Returns: (transfer none): a #GstBuffer holding the ARGB pixel data with
+ * #GstVideoMeta set. This function does not return a reference, the caller
+ * should obtain a reference of her own with gst_buffer_ref() if needed.
*/
GstBuffer *
gst_video_overlay_rectangle_get_pixels_unscaled_argb (GstVideoOverlayRectangle *
- rectangle, guint * width, guint * height, guint * stride,
- GstVideoOverlayFormatFlags flags)
+ rectangle, GstVideoOverlayFormatFlags flags)
{
g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
- g_return_val_if_fail (width != NULL, NULL);
- g_return_val_if_fail (height != NULL, NULL);
- g_return_val_if_fail (stride != NULL, NULL);
-
- *width = rectangle->width;
- *height = rectangle->height;
- return gst_video_overlay_rectangle_get_pixels_argb_internal (rectangle,
- stride, flags, TRUE);
+
+ return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
+ flags, TRUE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB);
+}
+
+/**
+ * gst_video_overlay_rectangle_get_pixels_unscaled_ayuv:
+ * @rectangle: a #GstVideoOverlayRectangle
+ * @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
+ * need to be scaled to the render dimensions, which can be retrieved using
+ * gst_video_overlay_rectangle_get_render_rectangle().
+ *
+ * Returns: (transfer none): a #GstBuffer holding the AYUV pixel data with
+ * #GstVideoMeta set. This function does not return a reference, the caller
+ * should obtain a reference of her own with gst_buffer_ref() if needed.
+ */
+GstBuffer *
+gst_video_overlay_rectangle_get_pixels_unscaled_ayuv (GstVideoOverlayRectangle *
+ rectangle, GstVideoOverlayFormatFlags flags)
+{
+ g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
+
+ return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
+ flags, TRUE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV);
}
/**
* it is stored in, to avoid unnecessary conversion.
*
* Returns: the #GstVideoOverlayFormatFlags associated with the rectangle.
- *
- * Since: 0.10.37
*/
GstVideoOverlayFormatFlags
gst_video_overlay_rectangle_get_flags (GstVideoOverlayRectangle * rectangle)
}
/**
+ * 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.
+ */
+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().
+ */
+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
*
*
* Returns: (transfer full): a new #GstVideoOverlayRectangle equivalent
* to @rectangle.
- *
- * Since: 0.10.36
*/
GstVideoOverlayRectangle *
gst_video_overlay_rectangle_copy (GstVideoOverlayRectangle * rectangle)
g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
- copy = gst_video_overlay_rectangle_new_argb (rectangle->pixels,
- rectangle->width, rectangle->height, rectangle->stride,
+ copy = gst_video_overlay_rectangle_new_raw (rectangle->pixels,
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;
}
* (meaning there will never be a rectangle with the same sequence number as
* a composition).
*
- * Returns: the sequence number of @rectangle
+ * 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.
*
- * Since: 0.10.36
+ * Returns: the sequence number of @rectangle
*/
guint
gst_video_overlay_rectangle_get_seqnum (GstVideoOverlayRectangle * rectangle)