gstreamer/gst/videomixer/blend.c

1060 lines
36 KiB
C
Raw Normal View History

/*
* Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
* Copyright (C) 2006 Mindfruit Bv.
* Author: Sjoerd Simons <sjoerd@luon.net>
* Author: Alex Ugarte <alexugarte@gmail.com>
* Copyright (C) 2009 Alex Ugarte <augarte@vicomtech.org>
* Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "blend.h"
#include "blendorc.h"
#include <string.h>
#include <gst/video/video.h>
#define BLEND(D,S,alpha) (((D) * (256 - (alpha)) + (S) * (alpha)) >> 8)
GST_DEBUG_CATEGORY_STATIC (gst_videomixer_blend_debug);
#define GST_CAT_DEFAULT gst_videomixer_blend_debug
/* Below are the implementations of everything */
/* A32 is for AYUV, ARGB and BGRA */
videomixer2: Add transparent background option for alpha channel formats This option allows the videomixer2 element to output a valid alpha channel when the inputs contain a valid alpha channel. This allows mixing to occur in multiple stages serially. The following pipeline shows an example of such a pipeline: gst-launch videotestsrc background-color=0x000000 pattern=ball ! video/x-raw-yuv,format=\(fourcc\)AYUV ! videomixer2 background=transparent name=mix1 ! videomixer2 name=mix2 ! ffmpegcolorspace ! autovideosink videotestsrc ! video/x-raw-yuv,format=\(fourcc\)AYUV ! mix2. The first videotestsrc in this pipeline creates a moving ball on a transparent background. It is then passed to the first videomixer2. Previously, this videomixer2 would have forced the alpha channel to 1.0 and given a background of checker, black, or white to the stream. With this patch, however, you can now specify the background as transparent, and the alpha channel of the input will be preserved. This allows for further mixing downstream, as is shown in the above pipeline where the a second videomixer2 is used to mix in a background of an smpte videotestsrc. So the result is a ball hovering over the smpte test source. This could, of course, have been accomplished with a single mixer element, but staged mixing is useful when it is not convenient to mix all video at once (e.g. a pipeline where a foreground and background bin exist and are mixed at the final output, but the foreground bin needs an internal mixer to create transitions between clips). Fixes bug #639994.
2011-01-19 19:07:17 +00:00
#define BLEND_A32(name, method, LOOP) \
static void \
2012-01-25 17:00:52 +00:00
method##_ ##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
gdouble src_alpha, GstVideoFrame * destframe) \
{ \
guint s_alpha; \
gint src_stride, dest_stride; \
2012-01-25 17:00:52 +00:00
gint dest_width, dest_height; \
guint8 *src, *dest; \
gint src_width, src_height; \
\
src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
2012-01-25 17:00:52 +00:00
src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
dest_width = GST_VIDEO_FRAME_COMP_WIDTH (destframe, 0); \
dest_height = GST_VIDEO_FRAME_COMP_HEIGHT (destframe, 0); \
\
s_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
\
/* If it's completely transparent... we just return */ \
if (G_UNLIKELY (s_alpha == 0)) \
return; \
\
/* adjust src pointers for negative sizes */ \
if (xpos < 0) { \
src += -xpos * 4; \
src_width -= -xpos; \
xpos = 0; \
} \
if (ypos < 0) { \
src += -ypos * src_stride; \
src_height -= -ypos; \
ypos = 0; \
} \
/* adjust width/height if the src is bigger than dest */ \
if (xpos + src_width > dest_width) { \
src_width = dest_width - xpos; \
} \
if (ypos + src_height > dest_height) { \
src_height = dest_height - ypos; \
} \
\
dest = dest + 4 * xpos + (ypos * dest_stride); \
\
LOOP (dest, src, src_height, src_width, src_stride, dest_stride, s_alpha); \
}
videomixer2: Add transparent background option for alpha channel formats This option allows the videomixer2 element to output a valid alpha channel when the inputs contain a valid alpha channel. This allows mixing to occur in multiple stages serially. The following pipeline shows an example of such a pipeline: gst-launch videotestsrc background-color=0x000000 pattern=ball ! video/x-raw-yuv,format=\(fourcc\)AYUV ! videomixer2 background=transparent name=mix1 ! videomixer2 name=mix2 ! ffmpegcolorspace ! autovideosink videotestsrc ! video/x-raw-yuv,format=\(fourcc\)AYUV ! mix2. The first videotestsrc in this pipeline creates a moving ball on a transparent background. It is then passed to the first videomixer2. Previously, this videomixer2 would have forced the alpha channel to 1.0 and given a background of checker, black, or white to the stream. With this patch, however, you can now specify the background as transparent, and the alpha channel of the input will be preserved. This allows for further mixing downstream, as is shown in the above pipeline where the a second videomixer2 is used to mix in a background of an smpte videotestsrc. So the result is a ball hovering over the smpte test source. This could, of course, have been accomplished with a single mixer element, but staged mixing is useful when it is not convenient to mix all video at once (e.g. a pipeline where a foreground and background bin exist and are mixed at the final output, but the foreground bin needs an internal mixer to create transitions between clips). Fixes bug #639994.
2011-01-19 19:07:17 +00:00
#define BLEND_A32_LOOP(name, method) \
static inline void \
videomixer2: Add transparent background option for alpha channel formats This option allows the videomixer2 element to output a valid alpha channel when the inputs contain a valid alpha channel. This allows mixing to occur in multiple stages serially. The following pipeline shows an example of such a pipeline: gst-launch videotestsrc background-color=0x000000 pattern=ball ! video/x-raw-yuv,format=\(fourcc\)AYUV ! videomixer2 background=transparent name=mix1 ! videomixer2 name=mix2 ! ffmpegcolorspace ! autovideosink videotestsrc ! video/x-raw-yuv,format=\(fourcc\)AYUV ! mix2. The first videotestsrc in this pipeline creates a moving ball on a transparent background. It is then passed to the first videomixer2. Previously, this videomixer2 would have forced the alpha channel to 1.0 and given a background of checker, black, or white to the stream. With this patch, however, you can now specify the background as transparent, and the alpha channel of the input will be preserved. This allows for further mixing downstream, as is shown in the above pipeline where the a second videomixer2 is used to mix in a background of an smpte videotestsrc. So the result is a ball hovering over the smpte test source. This could, of course, have been accomplished with a single mixer element, but staged mixing is useful when it is not convenient to mix all video at once (e.g. a pipeline where a foreground and background bin exist and are mixed at the final output, but the foreground bin needs an internal mixer to create transitions between clips). Fixes bug #639994.
2011-01-19 19:07:17 +00:00
_##method##_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \
gint src_width, gint src_stride, gint dest_stride, guint s_alpha) \
{ \
s_alpha = MIN (255, s_alpha); \
video_mixer_orc_##method##_##name (dest, dest_stride, src, src_stride, \
s_alpha, src_width, src_height); \
}
videomixer2: Add transparent background option for alpha channel formats This option allows the videomixer2 element to output a valid alpha channel when the inputs contain a valid alpha channel. This allows mixing to occur in multiple stages serially. The following pipeline shows an example of such a pipeline: gst-launch videotestsrc background-color=0x000000 pattern=ball ! video/x-raw-yuv,format=\(fourcc\)AYUV ! videomixer2 background=transparent name=mix1 ! videomixer2 name=mix2 ! ffmpegcolorspace ! autovideosink videotestsrc ! video/x-raw-yuv,format=\(fourcc\)AYUV ! mix2. The first videotestsrc in this pipeline creates a moving ball on a transparent background. It is then passed to the first videomixer2. Previously, this videomixer2 would have forced the alpha channel to 1.0 and given a background of checker, black, or white to the stream. With this patch, however, you can now specify the background as transparent, and the alpha channel of the input will be preserved. This allows for further mixing downstream, as is shown in the above pipeline where the a second videomixer2 is used to mix in a background of an smpte videotestsrc. So the result is a ball hovering over the smpte test source. This could, of course, have been accomplished with a single mixer element, but staged mixing is useful when it is not convenient to mix all video at once (e.g. a pipeline where a foreground and background bin exist and are mixed at the final output, but the foreground bin needs an internal mixer to create transitions between clips). Fixes bug #639994.
2011-01-19 19:07:17 +00:00
BLEND_A32_LOOP (argb, blend);
BLEND_A32_LOOP (bgra, blend);
BLEND_A32_LOOP (argb, overlay);
BLEND_A32_LOOP (bgra, overlay);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
videomixer2: Add transparent background option for alpha channel formats This option allows the videomixer2 element to output a valid alpha channel when the inputs contain a valid alpha channel. This allows mixing to occur in multiple stages serially. The following pipeline shows an example of such a pipeline: gst-launch videotestsrc background-color=0x000000 pattern=ball ! video/x-raw-yuv,format=\(fourcc\)AYUV ! videomixer2 background=transparent name=mix1 ! videomixer2 name=mix2 ! ffmpegcolorspace ! autovideosink videotestsrc ! video/x-raw-yuv,format=\(fourcc\)AYUV ! mix2. The first videotestsrc in this pipeline creates a moving ball on a transparent background. It is then passed to the first videomixer2. Previously, this videomixer2 would have forced the alpha channel to 1.0 and given a background of checker, black, or white to the stream. With this patch, however, you can now specify the background as transparent, and the alpha channel of the input will be preserved. This allows for further mixing downstream, as is shown in the above pipeline where the a second videomixer2 is used to mix in a background of an smpte videotestsrc. So the result is a ball hovering over the smpte test source. This could, of course, have been accomplished with a single mixer element, but staged mixing is useful when it is not convenient to mix all video at once (e.g. a pipeline where a foreground and background bin exist and are mixed at the final output, but the foreground bin needs an internal mixer to create transitions between clips). Fixes bug #639994.
2011-01-19 19:07:17 +00:00
BLEND_A32 (argb, blend, _blend_loop_argb);
BLEND_A32 (bgra, blend, _blend_loop_bgra);
BLEND_A32 (argb, overlay, _overlay_loop_argb);
BLEND_A32 (bgra, overlay, _overlay_loop_bgra);
#else
videomixer2: Add transparent background option for alpha channel formats This option allows the videomixer2 element to output a valid alpha channel when the inputs contain a valid alpha channel. This allows mixing to occur in multiple stages serially. The following pipeline shows an example of such a pipeline: gst-launch videotestsrc background-color=0x000000 pattern=ball ! video/x-raw-yuv,format=\(fourcc\)AYUV ! videomixer2 background=transparent name=mix1 ! videomixer2 name=mix2 ! ffmpegcolorspace ! autovideosink videotestsrc ! video/x-raw-yuv,format=\(fourcc\)AYUV ! mix2. The first videotestsrc in this pipeline creates a moving ball on a transparent background. It is then passed to the first videomixer2. Previously, this videomixer2 would have forced the alpha channel to 1.0 and given a background of checker, black, or white to the stream. With this patch, however, you can now specify the background as transparent, and the alpha channel of the input will be preserved. This allows for further mixing downstream, as is shown in the above pipeline where the a second videomixer2 is used to mix in a background of an smpte videotestsrc. So the result is a ball hovering over the smpte test source. This could, of course, have been accomplished with a single mixer element, but staged mixing is useful when it is not convenient to mix all video at once (e.g. a pipeline where a foreground and background bin exist and are mixed at the final output, but the foreground bin needs an internal mixer to create transitions between clips). Fixes bug #639994.
2011-01-19 19:07:17 +00:00
BLEND_A32 (argb, blend, _blend_loop_bgra);
BLEND_A32 (bgra, blend, _blend_loop_argb);
BLEND_A32 (argb, overlay, _overlay_loop_bgra);
BLEND_A32 (bgra, overlay, _overlay_loop_argb);
#endif
#define A32_CHECKER_C(name, RGB, A, C1, C2, C3) \
static void \
2012-01-25 17:00:52 +00:00
fill_checker_##name##_c (GstVideoFrame * frame) \
{ \
gint i, j; \
gint val; \
static const gint tab[] = { 80, 160, 80, 160 }; \
2012-01-25 17:00:52 +00:00
gint width, height; \
guint8 *dest; \
\
dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
\
if (!RGB) { \
for (i = 0; i < height; i++) { \
for (j = 0; j < width; j++) { \
dest[A] = 0xff; \
dest[C1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
dest[C2] = 128; \
dest[C3] = 128; \
dest += 4; \
} \
} \
} else { \
for (i = 0; i < height; i++) { \
for (j = 0; j < width; j++) { \
val = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
dest[A] = 0xFF; \
dest[C1] = val; \
dest[C2] = val; \
dest[C3] = val; \
dest += 4; \
} \
} \
} \
}
A32_CHECKER_C (argb, TRUE, 0, 1, 2, 3);
A32_CHECKER_C (bgra, TRUE, 3, 2, 1, 0);
A32_CHECKER_C (ayuv, FALSE, 0, 1, 2, 3);
#define YUV_TO_R(Y,U,V) (CLAMP (1.164 * (Y - 16) + 1.596 * (V - 128), 0, 255))
#define YUV_TO_G(Y,U,V) (CLAMP (1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128), 0, 255))
#define YUV_TO_B(Y,U,V) (CLAMP (1.164 * (Y - 16) + 2.018 * (U - 128), 0, 255))
#define A32_COLOR(name, RGB, A, C1, C2, C3) \
static void \
2012-01-25 17:00:52 +00:00
fill_color_##name (GstVideoFrame * frame, gint Y, gint U, gint V) \
{ \
gint c1, c2, c3; \
guint32 val; \
2012-01-25 17:00:52 +00:00
gint width, height; \
guint8 *dest; \
\
dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
\
if (RGB) { \
c1 = YUV_TO_R (Y, U, V); \
c2 = YUV_TO_G (Y, U, V); \
c3 = YUV_TO_B (Y, U, V); \
} else { \
c1 = Y; \
c2 = U; \
c3 = V; \
} \
val = GUINT32_FROM_BE ((0xff << A) | (c1 << C1) | (c2 << C2) | (c3 << C3)); \
\
video_mixer_orc_splat_u32 ((guint32 *) dest, val, height * width); \
}
A32_COLOR (argb, TRUE, 24, 16, 8, 0);
A32_COLOR (bgra, TRUE, 0, 8, 16, 24);
A32_COLOR (abgr, TRUE, 24, 0, 8, 16);
A32_COLOR (rgba, TRUE, 0, 24, 16, 8);
A32_COLOR (ayuv, FALSE, 24, 16, 8, 0);
/* Y444, Y42B, I420, YV12, Y41B */
#define PLANAR_YUV_BLEND(format_name,format_enum,x_round,y_round,MEMCPY,BLENDLOOP) \
inline static void \
_blend_##format_name (const guint8 * src, guint8 * dest, \
gint src_stride, gint dest_stride, gint src_width, gint src_height, \
gdouble src_alpha) \
{ \
gint i; \
gint b_alpha; \
\
/* If it's completely transparent... we just return */ \
if (G_UNLIKELY (src_alpha == 0.0)) { \
GST_INFO ("Fast copy (alpha == 0.0)"); \
return; \
} \
\
/* If it's completely opaque, we do a fast copy */ \
if (G_UNLIKELY (src_alpha == 1.0)) { \
GST_INFO ("Fast copy (alpha == 1.0)"); \
for (i = 0; i < src_height; i++) { \
MEMCPY (dest, src, src_width); \
src += src_stride; \
dest += dest_stride; \
} \
return; \
} \
\
b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
\
BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \
} \
\
static void \
2012-01-25 17:00:52 +00:00
blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
gdouble src_alpha, GstVideoFrame * destframe) \
{ \
const guint8 *b_src; \
guint8 *b_dest; \
gint b_src_width; \
gint b_src_height; \
gint xoffset = 0; \
gint yoffset = 0; \
gint src_comp_rowstride, dest_comp_rowstride; \
gint src_comp_height; \
gint src_comp_width; \
gint comp_ypos, comp_xpos; \
gint comp_yoffset, comp_xoffset; \
2012-01-25 17:00:52 +00:00
gint dest_width, dest_height; \
const GstVideoFormatInfo *info; \
gint src_width, src_height; \
\
src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
2012-01-25 17:00:52 +00:00
\
info = srcframe->info.finfo; \
dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
\
xpos = x_round (xpos); \
ypos = y_round (ypos); \
\
b_src_width = src_width; \
b_src_height = src_height; \
\
/* adjust src pointers for negative sizes */ \
if (xpos < 0) { \
xoffset = -xpos; \
b_src_width -= -xpos; \
xpos = 0; \
} \
if (ypos < 0) { \
yoffset += -ypos; \
b_src_height -= -ypos; \
ypos = 0; \
} \
/* If x or y offset are larger then the source it's outside of the picture */ \
if (xoffset > src_width || yoffset > src_height) { \
return; \
} \
\
/* adjust width/height if the src is bigger than dest */ \
if (xpos + src_width > dest_width) { \
b_src_width = dest_width - xpos; \
} \
if (ypos + src_height > dest_height) { \
b_src_height = dest_height - ypos; \
} \
if (b_src_width < 0 || b_src_height < 0) { \
return; \
} \
\
/* First mix Y, then U, then V */ \
2012-01-25 17:00:52 +00:00
b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \
b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \
src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \
src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \
comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \
comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \
comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \
comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \
_blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
src_comp_rowstride, \
dest_comp_rowstride, src_comp_width, src_comp_height, \
src_alpha); \
\
2012-01-25 17:00:52 +00:00
b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 1); \
b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 1); \
src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \
dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \
src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \
src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \
comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \
comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, ypos); \
comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \
comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, yoffset); \
_blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
src_comp_rowstride, \
dest_comp_rowstride, src_comp_width, src_comp_height, \
src_alpha); \
\
2012-01-25 17:00:52 +00:00
b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 2); \
b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 2); \
src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 2); \
dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 2); \
src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 2, b_src_width); \
src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 2, b_src_height); \
comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xpos); \
comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 2, ypos); \
comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xoffset); \
comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 2, yoffset); \
_blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
src_comp_rowstride, \
dest_comp_rowstride, src_comp_width, src_comp_height, \
src_alpha); \
}
#define PLANAR_YUV_FILL_CHECKER(format_name, format_enum, MEMSET) \
static void \
2012-01-25 17:00:52 +00:00
fill_checker_##format_name (GstVideoFrame * frame) \
{ \
gint i, j; \
static const int tab[] = { 80, 160, 80, 160 }; \
guint8 *p; \
gint comp_width, comp_height; \
gint rowstride; \
\
2012-01-25 17:00:52 +00:00
p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
\
for (i = 0; i < comp_height; i++) { \
for (j = 0; j < comp_width; j++) { \
*p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
} \
p += rowstride - comp_width; \
} \
\
2012-01-25 17:00:52 +00:00
p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
\
for (i = 0; i < comp_height; i++) { \
MEMSET (p, 0x80, comp_width); \
p += rowstride; \
} \
\
2012-01-25 17:00:52 +00:00
p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \
\
for (i = 0; i < comp_height; i++) { \
MEMSET (p, 0x80, comp_width); \
p += rowstride; \
} \
}
#define PLANAR_YUV_FILL_COLOR(format_name,format_enum,MEMSET) \
static void \
2012-01-25 17:00:52 +00:00
fill_color_##format_name (GstVideoFrame * frame, \
gint colY, gint colU, gint colV) \
{ \
guint8 *p; \
gint comp_width, comp_height; \
gint rowstride; \
gint i; \
\
2012-01-25 17:00:52 +00:00
p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
\
for (i = 0; i < comp_height; i++) { \
MEMSET (p, colY, comp_width); \
p += rowstride; \
} \
\
2012-01-25 17:00:52 +00:00
p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
\
for (i = 0; i < comp_height; i++) { \
MEMSET (p, colU, comp_width); \
p += rowstride; \
} \
\
2012-01-25 17:00:52 +00:00
p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \
\
for (i = 0; i < comp_height; i++) { \
MEMSET (p, colV, comp_width); \
p += rowstride; \
} \
}
#define GST_ROUND_UP_1(x) (x)
PLANAR_YUV_BLEND (i420, GST_VIDEO_FORMAT_I420, GST_ROUND_UP_2,
GST_ROUND_UP_2, memcpy, video_mixer_orc_blend_u8);
PLANAR_YUV_FILL_CHECKER (i420, GST_VIDEO_FORMAT_I420, memset);
PLANAR_YUV_FILL_COLOR (i420, GST_VIDEO_FORMAT_I420, memset);
PLANAR_YUV_FILL_COLOR (yv12, GST_VIDEO_FORMAT_YV12, memset);
PLANAR_YUV_BLEND (y444, GST_VIDEO_FORMAT_Y444, GST_ROUND_UP_1,
GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
PLANAR_YUV_FILL_CHECKER (y444, GST_VIDEO_FORMAT_Y444, memset);
PLANAR_YUV_FILL_COLOR (y444, GST_VIDEO_FORMAT_Y444, memset);
PLANAR_YUV_BLEND (y42b, GST_VIDEO_FORMAT_Y42B, GST_ROUND_UP_2,
GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
PLANAR_YUV_FILL_CHECKER (y42b, GST_VIDEO_FORMAT_Y42B, memset);
PLANAR_YUV_FILL_COLOR (y42b, GST_VIDEO_FORMAT_Y42B, memset);
PLANAR_YUV_BLEND (y41b, GST_VIDEO_FORMAT_Y41B, GST_ROUND_UP_4,
GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
PLANAR_YUV_FILL_CHECKER (y41b, GST_VIDEO_FORMAT_Y41B, memset);
PLANAR_YUV_FILL_COLOR (y41b, GST_VIDEO_FORMAT_Y41B, memset);
/* NV12, NV21 */
#define NV_YUV_BLEND(format_name,first_component,MEMCPY,BLENDLOOP) \
inline static void \
_blend_##format_name (const guint8 * src, guint8 * dest, \
gint src_stride, gint dest_stride, gint src_width, gint src_height, \
gdouble src_alpha) \
{ \
gint i; \
gint b_alpha; \
\
/* If it's completely transparent... we just return */ \
if (G_UNLIKELY (src_alpha == 0.0)) { \
GST_INFO ("Fast copy (alpha == 0.0)"); \
return; \
} \
\
/* If it's completely opaque, we do a fast copy */ \
if (G_UNLIKELY (src_alpha == 1.0)) { \
GST_INFO ("Fast copy (alpha == 1.0)"); \
for (i = 0; i < src_height; i++) { \
MEMCPY (dest, src, src_width); \
src += src_stride; \
dest += dest_stride; \
} \
return; \
} \
\
b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
\
BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \
} \
\
static void \
blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
gdouble src_alpha, GstVideoFrame * destframe) \
{ \
const guint8 *b_src; \
guint8 *b_dest; \
gint b_src_width; \
gint b_src_height; \
gint xoffset = 0; \
gint yoffset = 0; \
gint src_comp_rowstride, dest_comp_rowstride; \
gint src_comp_height; \
gint src_comp_width; \
gint comp_ypos, comp_xpos; \
gint comp_yoffset, comp_xoffset; \
gint dest_width, dest_height; \
const GstVideoFormatInfo *info; \
gint src_width, src_height; \
\
src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
\
info = srcframe->info.finfo; \
dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
\
xpos = GST_ROUND_UP_2 (xpos); \
ypos = GST_ROUND_UP_2 (ypos); \
\
b_src_width = src_width; \
b_src_height = src_height; \
\
/* adjust src pointers for negative sizes */ \
if (xpos < 0) { \
xoffset = -xpos; \
b_src_width -= -xpos; \
xpos = 0; \
} \
if (ypos < 0) { \
yoffset += -ypos; \
b_src_height -= -ypos; \
ypos = 0; \
} \
/* If x or y offset are larger then the source it's outside of the picture */ \
if (xoffset > src_width || yoffset > src_height) { \
return; \
} \
\
/* adjust width/height if the src is bigger than dest */ \
if (xpos + src_width > dest_width) { \
b_src_width = dest_width - xpos; \
} \
if (ypos + src_height > dest_height) { \
b_src_height = dest_height - ypos; \
} \
if (b_src_width < 0 || b_src_height < 0) { \
return; \
} \
\
/* First mix Y, then UV */ \
b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \
b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \
src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \
src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \
comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \
comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \
comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \
comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \
_blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
src_comp_rowstride, \
dest_comp_rowstride, src_comp_width, src_comp_height, \
src_alpha); \
\
b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, first_component); \
b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, first_component); \
src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \
dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \
src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \
src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \
comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \
comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, ypos); \
comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \
comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, yoffset); \
_blend_##format_name (b_src + comp_xoffset * 2 + comp_yoffset * src_comp_rowstride, \
b_dest + comp_xpos * 2 + comp_ypos * dest_comp_rowstride, \
src_comp_rowstride, \
dest_comp_rowstride, 2 * src_comp_width, src_comp_height, \
src_alpha); \
}
#define NV_YUV_FILL_CHECKER(format_name, first_component, MEMSET) \
static void \
fill_checker_##format_name (GstVideoFrame * frame) \
{ \
gint i, j; \
static const int tab[] = { 80, 160, 80, 160 }; \
guint8 *p; \
gint comp_width, comp_height; \
gint rowstride; \
\
p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
\
for (i = 0; i < comp_height; i++) { \
for (j = 0; j < comp_width; j++) { \
*p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
} \
p += rowstride - comp_width; \
} \
\
p = GST_VIDEO_FRAME_COMP_DATA (frame, first_component); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
\
for (i = 0; i < comp_height; i++) { \
MEMSET (p, 0x80, comp_width * 2); \
p += rowstride; \
} \
}
#define NV_YUV_FILL_COLOR(format_name,MEMSET) \
static void \
fill_color_##format_name (GstVideoFrame * frame, \
gint colY, gint colU, gint colV) \
{ \
guint8 *y, *u, *v; \
gint comp_width, comp_height; \
gint rowstride; \
gint i, j; \
\
y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
\
for (i = 0; i < comp_height; i++) { \
MEMSET (y, colY, comp_width); \
y += rowstride; \
} \
\
u = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
v = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
\
for (i = 0; i < comp_height; i++) { \
for (j = 0; j < comp_width; j++) { \
u[j*2] = colU; \
v[j*2] = colV; \
} \
u += rowstride; \
v += rowstride; \
} \
}
NV_YUV_BLEND (nv12, 1, memcpy, video_mixer_orc_blend_u8);
NV_YUV_FILL_CHECKER (nv12, 1, memset);
NV_YUV_FILL_COLOR (nv12, memset);
NV_YUV_BLEND (nv21, 2, memcpy, video_mixer_orc_blend_u8);
NV_YUV_FILL_CHECKER (nv21, 2, memset);
/* RGB, BGR, xRGB, xBGR, RGBx, BGRx */
#define RGB_BLEND(name, bpp, MEMCPY, BLENDLOOP) \
static void \
2012-01-25 17:00:52 +00:00
blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
gdouble src_alpha, GstVideoFrame * destframe) \
{ \
gint b_alpha; \
gint i; \
gint src_stride, dest_stride; \
2012-01-25 17:00:52 +00:00
gint dest_width, dest_height; \
guint8 *dest, *src; \
gint src_width, src_height; \
\
src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
2012-01-25 17:00:52 +00:00
\
src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
\
dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
\
2012-01-25 17:00:52 +00:00
src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
\
b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
\
/* adjust src pointers for negative sizes */ \
if (xpos < 0) { \
src += -xpos * bpp; \
src_width -= -xpos; \
xpos = 0; \
} \
if (ypos < 0) { \
src += -ypos * src_stride; \
src_height -= -ypos; \
ypos = 0; \
} \
/* adjust width/height if the src is bigger than dest */ \
if (xpos + src_width > dest_width) { \
src_width = dest_width - xpos; \
} \
if (ypos + src_height > dest_height) { \
src_height = dest_height - ypos; \
} \
\
dest = dest + bpp * xpos + (ypos * dest_stride); \
/* If it's completely transparent... we just return */ \
if (G_UNLIKELY (src_alpha == 0.0)) { \
GST_INFO ("Fast copy (alpha == 0.0)"); \
return; \
} \
\
/* If it's completely opaque, we do a fast copy */ \
if (G_UNLIKELY (src_alpha == 1.0)) { \
GST_INFO ("Fast copy (alpha == 1.0)"); \
for (i = 0; i < src_height; i++) { \
MEMCPY (dest, src, bpp * src_width); \
src += src_stride; \
dest += dest_stride; \
} \
return; \
} \
\
BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width * bpp, src_height); \
}
#define RGB_FILL_CHECKER_C(name, bpp, r, g, b) \
static void \
2012-01-25 17:00:52 +00:00
fill_checker_##name##_c (GstVideoFrame * frame) \
{ \
gint i, j; \
static const int tab[] = { 80, 160, 80, 160 }; \
2012-01-25 17:00:52 +00:00
gint stride, dest_add, width, height; \
guint8 *dest; \
\
width = GST_VIDEO_FRAME_WIDTH (frame); \
height = GST_VIDEO_FRAME_HEIGHT (frame); \
dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
dest_add = stride - width * bpp; \
\
for (i = 0; i < height; i++) { \
for (j = 0; j < width; j++) { \
dest[r] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* red */ \
dest[g] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* green */ \
dest[b] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* blue */ \
dest += bpp; \
} \
dest += dest_add; \
} \
}
#define RGB_FILL_COLOR(name, bpp, MEMSET_RGB) \
static void \
2012-01-25 17:00:52 +00:00
fill_color_##name (GstVideoFrame * frame, \
gint colY, gint colU, gint colV) \
{ \
gint red, green, blue; \
gint i; \
2012-01-25 17:00:52 +00:00
gint dest_stride; \
gint width, height; \
guint8 *dest; \
\
width = GST_VIDEO_FRAME_WIDTH (frame); \
height = GST_VIDEO_FRAME_HEIGHT (frame); \
dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
\
red = YUV_TO_R (colY, colU, colV); \
green = YUV_TO_G (colY, colU, colV); \
blue = YUV_TO_B (colY, colU, colV); \
\
for (i = 0; i < height; i++) { \
MEMSET_RGB (dest, red, green, blue, width); \
dest += dest_stride; \
} \
}
#define MEMSET_RGB_C(name, r, g, b) \
static inline void \
_memset_##name##_c (guint8* dest, gint red, gint green, gint blue, gint width) { \
gint j; \
\
for (j = 0; j < width; j++) { \
dest[r] = red; \
dest[g] = green; \
dest[b] = blue; \
dest += 3; \
} \
}
#define MEMSET_XRGB(name, r, g, b) \
static inline void \
_memset_##name (guint8* dest, gint red, gint green, gint blue, gint width) { \
guint32 val; \
\
val = GUINT32_FROM_BE ((red << r) | (green << g) | (blue << b)); \
video_mixer_orc_splat_u32 ((guint32 *) dest, val, width); \
}
#define _orc_memcpy_u32(dest,src,len) video_mixer_orc_memcpy_u32((guint32 *) dest, (const guint32 *) src, len/4)
RGB_BLEND (rgb, 3, memcpy, video_mixer_orc_blend_u8);
RGB_FILL_CHECKER_C (rgb, 3, 0, 1, 2);
MEMSET_RGB_C (rgb, 0, 1, 2);
RGB_FILL_COLOR (rgb_c, 3, _memset_rgb_c);
MEMSET_RGB_C (bgr, 2, 1, 0);
RGB_FILL_COLOR (bgr_c, 3, _memset_bgr_c);
RGB_BLEND (xrgb, 4, _orc_memcpy_u32, video_mixer_orc_blend_u8);
RGB_FILL_CHECKER_C (xrgb, 4, 1, 2, 3);
MEMSET_XRGB (xrgb, 24, 16, 0);
RGB_FILL_COLOR (xrgb, 4, _memset_xrgb);
MEMSET_XRGB (xbgr, 0, 16, 24);
RGB_FILL_COLOR (xbgr, 4, _memset_xbgr);
MEMSET_XRGB (rgbx, 24, 16, 8);
RGB_FILL_COLOR (rgbx, 4, _memset_rgbx);
MEMSET_XRGB (bgrx, 8, 16, 24);
RGB_FILL_COLOR (bgrx, 4, _memset_bgrx);
/* YUY2, YVYU, UYVY */
#define PACKED_422_BLEND(name, MEMCPY, BLENDLOOP) \
static void \
2012-01-25 17:00:52 +00:00
blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
gdouble src_alpha, GstVideoFrame * destframe) \
{ \
gint b_alpha; \
gint i; \
gint src_stride, dest_stride; \
2012-01-25 17:00:52 +00:00
gint dest_width, dest_height; \
guint8 *src, *dest; \
gint src_width, src_height; \
\
src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
2012-01-25 17:00:52 +00:00
\
dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
\
src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
\
2012-01-25 17:00:52 +00:00
src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
\
b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
\
xpos = GST_ROUND_UP_2 (xpos); \
\
/* adjust src pointers for negative sizes */ \
if (xpos < 0) { \
src += -xpos * 2; \
src_width -= -xpos; \
xpos = 0; \
} \
if (ypos < 0) { \
src += -ypos * src_stride; \
src_height -= -ypos; \
ypos = 0; \
} \
\
/* adjust width/height if the src is bigger than dest */ \
if (xpos + src_width > dest_width) { \
src_width = dest_width - xpos; \
} \
if (ypos + src_height > dest_height) { \
src_height = dest_height - ypos; \
} \
\
dest = dest + 2 * xpos + (ypos * dest_stride); \
/* If it's completely transparent... we just return */ \
if (G_UNLIKELY (src_alpha == 0.0)) { \
GST_INFO ("Fast copy (alpha == 0.0)"); \
return; \
} \
\
/* If it's completely opaque, we do a fast copy */ \
if (G_UNLIKELY (src_alpha == 1.0)) { \
GST_INFO ("Fast copy (alpha == 1.0)"); \
for (i = 0; i < src_height; i++) { \
MEMCPY (dest, src, 2 * src_width); \
src += src_stride; \
dest += dest_stride; \
} \
return; \
} \
\
BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, 2 * src_width, src_height); \
}
#define PACKED_422_FILL_CHECKER_C(name, Y1, U, Y2, V) \
static void \
2012-01-25 17:00:52 +00:00
fill_checker_##name##_c (GstVideoFrame * frame) \
{ \
gint i, j; \
static const int tab[] = { 80, 160, 80, 160 }; \
gint dest_add; \
2012-01-25 17:00:52 +00:00
gint width, height; \
guint8 *dest; \
\
2012-01-25 17:00:52 +00:00
width = GST_VIDEO_FRAME_WIDTH (frame); \
width = GST_ROUND_UP_2 (width); \
2012-01-25 17:00:52 +00:00
height = GST_VIDEO_FRAME_HEIGHT (frame); \
dest = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
dest_add = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0) - width * 2; \
width /= 2; \
\
for (i = 0; i < height; i++) { \
for (j = 0; j < width; j++) { \
dest[Y1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
dest[Y2] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
dest[U] = 128; \
dest[V] = 128; \
dest += 4; \
} \
dest += dest_add; \
} \
}
#define PACKED_422_FILL_COLOR(name, Y1, U, Y2, V) \
static void \
2012-01-25 17:00:52 +00:00
fill_color_##name (GstVideoFrame * frame, \
gint colY, gint colU, gint colV) \
{ \
gint i; \
gint dest_stride; \
guint32 val; \
2012-01-25 17:00:52 +00:00
gint width, height; \
guint8 *dest; \
\
2012-01-25 17:00:52 +00:00
width = GST_VIDEO_FRAME_WIDTH (frame); \
width = GST_ROUND_UP_2 (width); \
2012-01-25 17:00:52 +00:00
height = GST_VIDEO_FRAME_HEIGHT (frame); \
dest = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
width /= 2; \
\
val = GUINT32_FROM_BE ((colY << Y1) | (colY << Y2) | (colU << U) | (colV << V)); \
\
for (i = 0; i < height; i++) { \
video_mixer_orc_splat_u32 ((guint32 *) dest, val, width); \
dest += dest_stride; \
} \
}
PACKED_422_BLEND (yuy2, memcpy, video_mixer_orc_blend_u8);
PACKED_422_FILL_CHECKER_C (yuy2, 0, 1, 2, 3);
PACKED_422_FILL_CHECKER_C (uyvy, 1, 0, 3, 2);
PACKED_422_FILL_COLOR (yuy2, 24, 16, 8, 0);
PACKED_422_FILL_COLOR (yvyu, 24, 0, 8, 16);
PACKED_422_FILL_COLOR (uyvy, 16, 24, 0, 8);
/* Init function */
BlendFunction gst_video_mixer_blend_argb;
BlendFunction gst_video_mixer_blend_bgra;
videomixer2: Add transparent background option for alpha channel formats This option allows the videomixer2 element to output a valid alpha channel when the inputs contain a valid alpha channel. This allows mixing to occur in multiple stages serially. The following pipeline shows an example of such a pipeline: gst-launch videotestsrc background-color=0x000000 pattern=ball ! video/x-raw-yuv,format=\(fourcc\)AYUV ! videomixer2 background=transparent name=mix1 ! videomixer2 name=mix2 ! ffmpegcolorspace ! autovideosink videotestsrc ! video/x-raw-yuv,format=\(fourcc\)AYUV ! mix2. The first videotestsrc in this pipeline creates a moving ball on a transparent background. It is then passed to the first videomixer2. Previously, this videomixer2 would have forced the alpha channel to 1.0 and given a background of checker, black, or white to the stream. With this patch, however, you can now specify the background as transparent, and the alpha channel of the input will be preserved. This allows for further mixing downstream, as is shown in the above pipeline where the a second videomixer2 is used to mix in a background of an smpte videotestsrc. So the result is a ball hovering over the smpte test source. This could, of course, have been accomplished with a single mixer element, but staged mixing is useful when it is not convenient to mix all video at once (e.g. a pipeline where a foreground and background bin exist and are mixed at the final output, but the foreground bin needs an internal mixer to create transitions between clips). Fixes bug #639994.
2011-01-19 19:07:17 +00:00
BlendFunction gst_video_mixer_overlay_argb;
BlendFunction gst_video_mixer_overlay_bgra;
/* AYUV/ABGR is equal to ARGB, RGBA is equal to BGRA */
BlendFunction gst_video_mixer_blend_y444;
BlendFunction gst_video_mixer_blend_y42b;
BlendFunction gst_video_mixer_blend_i420;
/* I420 is equal to YV12 */
BlendFunction gst_video_mixer_blend_nv12;
BlendFunction gst_video_mixer_blend_nv21;
BlendFunction gst_video_mixer_blend_y41b;
BlendFunction gst_video_mixer_blend_rgb;
/* BGR is equal to RGB */
BlendFunction gst_video_mixer_blend_rgbx;
/* BGRx, xRGB, xBGR are equal to RGBx */
BlendFunction gst_video_mixer_blend_yuy2;
/* YVYU and UYVY are equal to YUY2 */
FillCheckerFunction gst_video_mixer_fill_checker_argb;
FillCheckerFunction gst_video_mixer_fill_checker_bgra;
/* ABGR is equal to ARGB, RGBA is equal to BGRA */
FillCheckerFunction gst_video_mixer_fill_checker_ayuv;
FillCheckerFunction gst_video_mixer_fill_checker_y444;
FillCheckerFunction gst_video_mixer_fill_checker_y42b;
FillCheckerFunction gst_video_mixer_fill_checker_i420;
/* I420 is equal to YV12 */
FillCheckerFunction gst_video_mixer_fill_checker_nv12;
FillCheckerFunction gst_video_mixer_fill_checker_nv21;
FillCheckerFunction gst_video_mixer_fill_checker_y41b;
FillCheckerFunction gst_video_mixer_fill_checker_rgb;
/* BGR is equal to RGB */
FillCheckerFunction gst_video_mixer_fill_checker_xrgb;
/* BGRx, xRGB, xBGR are equal to RGBx */
FillCheckerFunction gst_video_mixer_fill_checker_yuy2;
/* YVYU is equal to YUY2 */
FillCheckerFunction gst_video_mixer_fill_checker_uyvy;
FillColorFunction gst_video_mixer_fill_color_argb;
FillColorFunction gst_video_mixer_fill_color_bgra;
FillColorFunction gst_video_mixer_fill_color_abgr;
FillColorFunction gst_video_mixer_fill_color_rgba;
FillColorFunction gst_video_mixer_fill_color_ayuv;
FillColorFunction gst_video_mixer_fill_color_y444;
FillColorFunction gst_video_mixer_fill_color_y42b;
FillColorFunction gst_video_mixer_fill_color_i420;
FillColorFunction gst_video_mixer_fill_color_yv12;
FillColorFunction gst_video_mixer_fill_color_nv12;
/* NV21 is equal to NV12 */
FillColorFunction gst_video_mixer_fill_color_y41b;
FillColorFunction gst_video_mixer_fill_color_rgb;
FillColorFunction gst_video_mixer_fill_color_bgr;
FillColorFunction gst_video_mixer_fill_color_xrgb;
FillColorFunction gst_video_mixer_fill_color_xbgr;
FillColorFunction gst_video_mixer_fill_color_rgbx;
FillColorFunction gst_video_mixer_fill_color_bgrx;
FillColorFunction gst_video_mixer_fill_color_yuy2;
FillColorFunction gst_video_mixer_fill_color_yvyu;
FillColorFunction gst_video_mixer_fill_color_uyvy;
void
gst_video_mixer_init_blend (void)
{
GST_DEBUG_CATEGORY_INIT (gst_videomixer_blend_debug, "videomixer_blend", 0,
"video mixer blending functions");
gst_video_mixer_blend_argb = blend_argb;
gst_video_mixer_blend_bgra = blend_bgra;
videomixer2: Add transparent background option for alpha channel formats This option allows the videomixer2 element to output a valid alpha channel when the inputs contain a valid alpha channel. This allows mixing to occur in multiple stages serially. The following pipeline shows an example of such a pipeline: gst-launch videotestsrc background-color=0x000000 pattern=ball ! video/x-raw-yuv,format=\(fourcc\)AYUV ! videomixer2 background=transparent name=mix1 ! videomixer2 name=mix2 ! ffmpegcolorspace ! autovideosink videotestsrc ! video/x-raw-yuv,format=\(fourcc\)AYUV ! mix2. The first videotestsrc in this pipeline creates a moving ball on a transparent background. It is then passed to the first videomixer2. Previously, this videomixer2 would have forced the alpha channel to 1.0 and given a background of checker, black, or white to the stream. With this patch, however, you can now specify the background as transparent, and the alpha channel of the input will be preserved. This allows for further mixing downstream, as is shown in the above pipeline where the a second videomixer2 is used to mix in a background of an smpte videotestsrc. So the result is a ball hovering over the smpte test source. This could, of course, have been accomplished with a single mixer element, but staged mixing is useful when it is not convenient to mix all video at once (e.g. a pipeline where a foreground and background bin exist and are mixed at the final output, but the foreground bin needs an internal mixer to create transitions between clips). Fixes bug #639994.
2011-01-19 19:07:17 +00:00
gst_video_mixer_overlay_argb = overlay_argb;
gst_video_mixer_overlay_bgra = overlay_bgra;
gst_video_mixer_blend_i420 = blend_i420;
gst_video_mixer_blend_nv12 = blend_nv12;
gst_video_mixer_blend_nv21 = blend_nv21;
gst_video_mixer_blend_y444 = blend_y444;
gst_video_mixer_blend_y42b = blend_y42b;
gst_video_mixer_blend_y41b = blend_y41b;
gst_video_mixer_blend_rgb = blend_rgb;
gst_video_mixer_blend_xrgb = blend_xrgb;
gst_video_mixer_blend_yuy2 = blend_yuy2;
gst_video_mixer_fill_checker_argb = fill_checker_argb_c;
gst_video_mixer_fill_checker_bgra = fill_checker_bgra_c;
gst_video_mixer_fill_checker_ayuv = fill_checker_ayuv_c;
gst_video_mixer_fill_checker_i420 = fill_checker_i420;
gst_video_mixer_fill_checker_nv12 = fill_checker_nv12;
gst_video_mixer_fill_checker_nv21 = fill_checker_nv21;
gst_video_mixer_fill_checker_y444 = fill_checker_y444;
gst_video_mixer_fill_checker_y42b = fill_checker_y42b;
gst_video_mixer_fill_checker_y41b = fill_checker_y41b;
gst_video_mixer_fill_checker_rgb = fill_checker_rgb_c;
gst_video_mixer_fill_checker_xrgb = fill_checker_xrgb_c;
gst_video_mixer_fill_checker_yuy2 = fill_checker_yuy2_c;
gst_video_mixer_fill_checker_uyvy = fill_checker_uyvy_c;
gst_video_mixer_fill_color_argb = fill_color_argb;
gst_video_mixer_fill_color_bgra = fill_color_bgra;
gst_video_mixer_fill_color_abgr = fill_color_abgr;
gst_video_mixer_fill_color_rgba = fill_color_rgba;
gst_video_mixer_fill_color_ayuv = fill_color_ayuv;
gst_video_mixer_fill_color_i420 = fill_color_i420;
gst_video_mixer_fill_color_yv12 = fill_color_yv12;
gst_video_mixer_fill_color_nv12 = fill_color_nv12;
gst_video_mixer_fill_color_y444 = fill_color_y444;
gst_video_mixer_fill_color_y42b = fill_color_y42b;
gst_video_mixer_fill_color_y41b = fill_color_y41b;
gst_video_mixer_fill_color_rgb = fill_color_rgb_c;
gst_video_mixer_fill_color_bgr = fill_color_bgr_c;
gst_video_mixer_fill_color_xrgb = fill_color_xrgb;
gst_video_mixer_fill_color_xbgr = fill_color_xbgr;
gst_video_mixer_fill_color_rgbx = fill_color_rgbx;
gst_video_mixer_fill_color_bgrx = fill_color_bgrx;
gst_video_mixer_fill_color_yuy2 = fill_color_yuy2;
gst_video_mixer_fill_color_yvyu = fill_color_yvyu;
gst_video_mixer_fill_color_uyvy = fill_color_uyvy;
}