gstreamer/subprojects/gst-plugins-good/gst/videobox/gstvideobox.c
Vivienne Watermeier 6c2f6c3bd4 all: Use new navigation interface and API
Use and implement the new navigation interface in all relevant sink elements,
and use API functions everywhere instead of directy accessing the event structure.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1633>
2022-03-23 13:14:52 +00:00

3336 lines
107 KiB
C

/* GStreamer
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* Copyright (C) 2010 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.
*/
/**
* SECTION:element-videobox
* @title: videobox
* @see_also: #GstVideoCrop
*
* This plugin crops or enlarges the image. It takes 4 values as input, a
* top, bottom, left and right offset. Positive values will crop that much
* pixels from the respective border of the image, negative values will add
* that much pixels. When pixels are added, you can specify their color.
* Some predefined colors are usable with an enum property.
*
* The plugin is alpha channel aware and will try to negotiate with a format
* that supports alpha channels first. When alpha channel is active two
* other properties, alpha and border_alpha can be used to set the alpha
* values of the inner picture and the border respectively. an alpha value of
* 0.0 means total transparency, 1.0 is opaque.
*
* The videobox plugin has many uses such as doing a mosaic of pictures,
* letterboxing video, cutting out pieces of video, picture in picture, etc..
*
* Setting autocrop to true changes the behavior of the plugin so that
* caps determine crop properties rather than the other way around: given
* input and output dimensions, the crop values are selected so that the
* smaller frame is effectively centered in the larger frame. This
* involves either cropping or padding.
*
* If you use autocrop there is little point in setting the other
* properties manually because they will be overridden if the caps change,
* but nothing stops you from doing so.
*
* Sample pipeline:
* |[
* gst-launch-1.0 videotestsrc ! videobox autocrop=true ! \
* "video/x-raw, width=600, height=400" ! videoconvert ! ximagesink
* ]|
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstvideobox.h"
#include "gstvideoboxorc.h"
#include <math.h>
#include <string.h>
GST_DEBUG_CATEGORY_STATIC (videobox_debug);
#define GST_CAT_DEFAULT videobox_debug
/* From videotestsrc.c */
static const guint8 yuv_sdtv_colors_Y[VIDEO_BOX_FILL_LAST] =
{ 16, 145, 41, 81, 210, 235 };
static const guint8 yuv_sdtv_colors_U[VIDEO_BOX_FILL_LAST] =
{ 128, 54, 240, 90, 16, 128 };
static const guint8 yuv_sdtv_colors_V[VIDEO_BOX_FILL_LAST] =
{ 128, 34, 110, 240, 146, 128 };
static const guint8 yuv_hdtv_colors_Y[VIDEO_BOX_FILL_LAST] =
{ 16, 173, 32, 63, 219, 235 };
static const guint8 yuv_hdtv_colors_U[VIDEO_BOX_FILL_LAST] =
{ 128, 42, 240, 102, 16, 128 };
static const guint8 yuv_hdtv_colors_V[VIDEO_BOX_FILL_LAST] =
{ 128, 26, 118, 240, 138, 128 };
static const guint8 rgb_colors_R[VIDEO_BOX_FILL_LAST] =
{ 0, 0, 0, 255, 255, 255 };
static const guint8 rgb_colors_G[VIDEO_BOX_FILL_LAST] =
{ 0, 255, 0, 0, 255, 255 };
static const guint8 rgb_colors_B[VIDEO_BOX_FILL_LAST] =
{ 0, 0, 255, 0, 0, 255 };
/* Generated by -bad/ext/cog/generate_tables */
static const int cog_ycbcr_to_rgb_matrix_8bit_hdtv[] = {
298, 0, 459, -63514,
298, -55, -136, 19681,
298, 541, 0, -73988,
};
static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
298, 0, 409, -57068,
298, -100, -208, 34707,
298, 516, 0, -70870,
};
static const gint cog_rgb_to_ycbcr_matrix_8bit_hdtv[] = {
47, 157, 16, 4096,
-26, -87, 112, 32768,
112, -102, -10, 32768,
};
static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
66, 129, 25, 4096,
-38, -74, 112, 32768,
112, -94, -18, 32768,
};
static const gint cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit[] = {
256, -30, -53, 10600,
0, 261, 29, -4367,
0, 19, 262, -3289,
};
static const gint cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit[] = {
256, 25, 49, -9536,
0, 253, -28, 3958,
0, -19, 252, 2918,
};
static const gint cog_identity_matrix_8bit[] = {
256, 0, 0, 0,
0, 256, 0, 0,
0, 0, 256, 0,
};
#define APPLY_MATRIX(m,o,v1,v2,v3) ((m[o*4] * v1 + m[o*4+1] * v2 + m[o*4+2] * v3 + m[o*4+3]) >> 8)
static void
fill_ayuv (GstVideoBoxFill fill_type, guint b_alpha,
GstVideoFrame * frame, gboolean sdtv)
{
guint32 empty_pixel;
guint8 *dest;
gint width, height;
gint stride;
width = GST_VIDEO_FRAME_WIDTH (frame);
height = GST_VIDEO_FRAME_HEIGHT (frame);
b_alpha = MIN (b_alpha, 255);
if (sdtv)
empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
(yuv_sdtv_colors_Y[fill_type] << 16) |
(yuv_sdtv_colors_U[fill_type] << 8) | yuv_sdtv_colors_V[fill_type]);
else
empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
(yuv_hdtv_colors_Y[fill_type] << 16) |
(yuv_hdtv_colors_U[fill_type] << 8) | yuv_hdtv_colors_V[fill_type]);
dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
if (G_LIKELY (stride == 4 * width))
video_box_orc_splat_u32 ((guint32 *) dest, empty_pixel, width * height);
else if (height) {
for (; height; --height) {
video_box_orc_splat_u32 ((guint32 *) dest, empty_pixel, width);
dest += stride;
}
}
}
static void
copy_ayuv_ayuv (guint i_alpha, GstVideoFrame * dest_frame,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
gint src_stride;
gint dest_stride;
guint8 *dest, *src;
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
dest = dest + dest_y * dest_stride + dest_x * 4;
src = src + src_y * src_stride + src_x * 4;
w *= 4;
if (dest_sdtv != src_sdtv) {
gint matrix[12];
gint y, u, v;
memcpy (matrix,
dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
/* ORC FIXME */
dest[j] = (src[j] * i_alpha) >> 8;
y = src[j + 1];
u = src[j + 2];
v = src[j + 3];
dest[j + 1] = APPLY_MATRIX (matrix, 0, y, u, v);
dest[j + 2] = APPLY_MATRIX (matrix, 1, y, u, v);
dest[j + 3] = APPLY_MATRIX (matrix, 2, y, u, v);
}
dest += dest_stride;
src += src_stride;
}
} else {
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
/* ORC FIXME */
dest[j] = (src[j] * i_alpha) >> 8;
dest[j + 1] = src[j + 1];
dest[j + 2] = src[j + 2];
dest[j + 3] = src[j + 3];
}
dest += dest_stride;
src += src_stride;
}
}
}
static void
copy_ayuv_i420 (guint i_alpha, GstVideoFrame * dest_frame,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
guint8 *destY, *destY2, *destU, *destV;
gint dest_strideY, dest_strideU, dest_strideV;
const guint8 *src2;
gint src_stride;
gint y_idx, uv_idx;
gint y1, y2, y3, y4;
gint u1, u2, u3, u4;
gint v1, v2, v3, v4;
gint matrix[12];
guint8 *src;
gint dest_height, src_height, dest_width;
dest_height = GST_VIDEO_FRAME_HEIGHT (dest_frame);
dest_width = GST_VIDEO_FRAME_WIDTH (dest_frame);
src_height = GST_VIDEO_FRAME_HEIGHT (src_frame);
dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest_frame, 0);
dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest_frame, 1);
dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest_frame, 2);
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
destY = GST_VIDEO_FRAME_COMP_DATA (dest_frame, 0);
destU = GST_VIDEO_FRAME_COMP_DATA (dest_frame, 1);
destV = GST_VIDEO_FRAME_COMP_DATA (dest_frame, 2);
destY = destY + dest_y * dest_strideY + dest_x;
destY2 = (dest_y < dest_height) ? destY + dest_strideY : destY;
destU = destU + (dest_y / 2) * dest_strideU + dest_x / 2;
destV = destV + (dest_y / 2) * dest_strideV + dest_x / 2;
src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
src = src + src_y * src_stride + src_x * 4;
src2 = (src_y < src_height) ? src + src_stride : src;
h = dest_y + h;
w = dest_x + w;
if (src_sdtv != dest_sdtv)
memcpy (matrix,
dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
else
memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
/* 1. Handle the first destination scanline specially if it
* doesn't start at the macro pixel boundary, i.e. blend
* with the background! */
if (dest_y % 2 == 1) {
/* 1.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 2 == 1) {
y1 = src[4 * 0 + 1];
u1 = src[4 * 0 + 2];
v1 = src[4 * 0 + 3];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[0] =
CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
255);
destV[0] =
CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
255);
j = dest_x + 1;
y_idx = uv_idx = 1;
} else {
j = dest_x;
y_idx = uv_idx = 0;
}
/* 1.2. Copy all macro pixels from the source to the destination
* but blend with the background because we're only filling
* the lower part of the macro pixels. */
for (; j < w - 1; j += 2) {
y1 = src[4 * y_idx + 1];
y2 = src[4 * y_idx + 4 + 1];
u1 = src[4 * y_idx + 2];
u2 = src[4 * y_idx + 4 + 2];
v1 = src[4 * y_idx + 3];
v2 = src[4 * y_idx + 4 + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[uv_idx] = CLAMP (
(2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
destV[uv_idx] = CLAMP (
(2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
y_idx += 2;
uv_idx++;
}
/* 1.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = src[4 * y_idx + 1];
u1 = src[4 * y_idx + 2];
v1 = src[4 * y_idx + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[uv_idx] = CLAMP (
(destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
destV[uv_idx] = CLAMP (
(destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
} else if (j == w - 1) {
y1 = src[4 * y_idx + 1];
u1 = src[4 * y_idx + 2];
v1 = src[4 * y_idx + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[uv_idx] = CLAMP (
(3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
255);
destV[uv_idx] =
CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4,
0, 255);
}
destY += dest_strideY;
destY2 += dest_strideY;
destU += dest_strideU;
destV += dest_strideV;
src += src_stride;
src2 += src_stride;
i = dest_y + 1;
} else {
i = dest_y;
}
/* 2. Copy all macro pixel scanlines, the destination scanline
* now starts at macro pixel boundary. */
for (; i < h - 1; i += 2) {
/* 2.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 2 == 1) {
y1 = src[4 * 0 + 1];
y2 = src2[4 * 0 + 1];
u1 = src[4 * 0 + 2];
u2 = src2[4 * 0 + 2];
v1 = src[4 * 0 + 3];
v2 = src2[4 * 0 + 3];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY2[0] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[0] = CLAMP (
(2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
destV[0] = CLAMP (
(2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
j = dest_x + 1;
y_idx = uv_idx = 1;
} else {
j = dest_x;
y_idx = uv_idx = 0;
}
/* 2.2. Copy all macro pixels from the source to the destination.
* All pixels now start at macro pixel boundary, i.e. no
* blending with the background is necessary. */
for (; j < w - 1; j += 2) {
y1 = src[4 * y_idx + 1];
y2 = src[4 * y_idx + 4 + 1];
y3 = src2[4 * y_idx + 1];
y4 = src2[4 * y_idx + 4 + 1];
u1 = src[4 * y_idx + 2];
u2 = src[4 * y_idx + 4 + 2];
u3 = src2[4 * y_idx + 2];
u4 = src2[4 * y_idx + 4 + 2];
v1 = src[4 * y_idx + 3];
v2 = src[4 * y_idx + 4 + 3];
v3 = src2[4 * y_idx + 3];
v4 = src2[4 * y_idx + 4 + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
destY2[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
destU[uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
destV[uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
y_idx += 2;
uv_idx++;
}
/* 2.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = src[4 * y_idx + 1];
y2 = src2[4 * y_idx + 1];
u1 = src[4 * y_idx + 2];
u2 = src2[4 * y_idx + 2];
v1 = src[4 * y_idx + 3];
v2 = src2[4 * y_idx + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
u2, v2)) / 2, 0, 255);
destV[uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
u2, v2)) / 2, 0, 255);
} else if (j == w - 1) {
y1 = src[4 * y_idx + 1];
y2 = src2[4 * y_idx + 1];
u1 = src[4 * y_idx + 2];
u2 = src2[4 * y_idx + 2];
v1 = src[4 * y_idx + 3];
v2 = src2[4 * y_idx + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[uv_idx] = CLAMP (
(2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
destV[uv_idx] = CLAMP (
(2 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
}
destY += 2 * dest_strideY;
destY2 += 2 * dest_strideY;
destU += dest_strideU;
destV += dest_strideV;
src += 2 * src_stride;
src2 += 2 * src_stride;
}
/* 3. Handle the last scanline if one exists. This again
* doesn't start at macro pixel boundary but should
* only fill the upper part of the macro pixels. */
if (i == h - 1 && i == dest_height - 1) {
/* 3.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 2 == 1) {
y1 = src[4 * 0 + 1];
u1 = src[4 * 0 + 2];
v1 = src[4 * 0 + 3];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[0] =
CLAMP ((destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
destV[0] =
CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
j = dest_x + 1;
y_idx = uv_idx = 1;
} else {
j = dest_x;
y_idx = uv_idx = 0;
}
/* 3.2. Copy all macro pixels from the source to the destination
* but blend with the background because we're only filling
* the upper part of the macro pixels. */
for (; j < w - 1; j += 2) {
y1 = src[4 * y_idx + 1];
y2 = src[4 * y_idx + 4 + 1];
u1 = src[4 * y_idx + 2];
u2 = src[4 * y_idx + 4 + 2];
v1 = src[4 * y_idx + 3];
v2 = src[4 * y_idx + 4 + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[uv_idx] = CLAMP (
(2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
destV[uv_idx] = CLAMP (
(2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
y_idx += 2;
uv_idx++;
}
/* 3.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = src[4 * y_idx + 1];
u1 = src[4 * y_idx + 2];
v1 = src[4 * y_idx + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[uv_idx] = CLAMP (
(destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
destV[uv_idx] = CLAMP (
(destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
} else if (j == w - 1) {
y1 = src[4 * y_idx + 1];
u1 = src[4 * y_idx + 2];
v1 = src[4 * y_idx + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[uv_idx] = CLAMP (
(3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
255);
destV[uv_idx] =
CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
0, 255);
}
} else if (i == h - 1) {
/* 3.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 2 == 1) {
y1 = src[4 * 0 + 1];
u1 = src[4 * 0 + 2];
v1 = src[4 * 0 + 3];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[0] =
CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
255);
destV[0] =
CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
255);
j = dest_x + 1;
y_idx = uv_idx = 1;
} else {
j = dest_x;
y_idx = uv_idx = 0;
}
/* 3.2. Copy all macro pixels from the source to the destination
* but blend with the background because we're only filling
* the upper part of the macro pixels. */
for (; j < w - 1; j += 2) {
y1 = src[4 * y_idx + 1];
y2 = src[4 * y_idx + 4 + 1];
u1 = src[4 * y_idx + 2];
u2 = src[4 * y_idx + 4 + 2];
v1 = src[4 * y_idx + 3];
v2 = src[4 * y_idx + 4 + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[uv_idx] = CLAMP (
(2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
destV[uv_idx] = CLAMP (
(2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
y_idx += 2;
uv_idx++;
}
/* 3.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = src[4 * y_idx + 1];
u1 = src[4 * y_idx + 2];
v1 = src[4 * y_idx + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[uv_idx] = CLAMP (
(destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
destV[uv_idx] = CLAMP (
(destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
} else if (j == w - 1) {
y1 = src[4 * y_idx + 1];
u1 = src[4 * y_idx + 2];
v1 = src[4 * y_idx + 3];
destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[uv_idx] = CLAMP (
(3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
255);
destV[uv_idx] =
CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
0, 255);
}
}
}
static void
fill_planar_yuv (GstVideoBoxFill fill_type, guint b_alpha,
GstVideoFrame * frame, gboolean sdtv)
{
guint8 empty_pixel[3];
guint8 *destY, *destU, *destV;
gint strideY, strideU, strideV;
gint heightY, heightU, heightV;
gint widthY, widthU, widthV;
if (sdtv) {
empty_pixel[0] = yuv_sdtv_colors_Y[fill_type];
empty_pixel[1] = yuv_sdtv_colors_U[fill_type];
empty_pixel[2] = yuv_sdtv_colors_V[fill_type];
} else {
empty_pixel[0] = yuv_hdtv_colors_Y[fill_type];
empty_pixel[1] = yuv_hdtv_colors_U[fill_type];
empty_pixel[2] = yuv_hdtv_colors_V[fill_type];
}
strideY = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
strideU = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1);
strideV = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2);
destY = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
destU = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
destV = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
widthY = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
widthU = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1);
widthV = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2);
heightY = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0);
heightU = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1);
heightV = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2);
if (strideY == widthY) {
memset (destY, empty_pixel[0], strideY * heightY);
} else if (heightY) {
for (; heightY; --heightY) {
memset (destY, empty_pixel[0], widthY);
destY += strideY;
}
}
if (strideU == widthU) {
memset (destU, empty_pixel[1], strideU * heightU);
} else if (heightU) {
for (; heightU; --heightU) {
memset (destU, empty_pixel[1], widthU);
destU += strideU;
}
}
if (strideV == widthV) {
memset (destV, empty_pixel[2], strideV * heightV);
} else if (heightV) {
for (; heightV; --heightV) {
memset (destV, empty_pixel[2], widthV);
destV += strideV;
}
}
}
static void
copy_y444_y444 (guint i_alpha, GstVideoFrame * dest,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
guint8 *destY, *destU, *destV;
const guint8 *srcY, *srcU, *srcV;
gint dest_strideY, dest_strideU, dest_strideV;
gint src_strideY, src_strideU, src_strideV;
dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest, 1);
dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest, 2);
src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src, 0);
src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src, 1);
src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src, 2);
destY = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
destU = GST_VIDEO_FRAME_COMP_DATA (dest, 1);
destV = GST_VIDEO_FRAME_COMP_DATA (dest, 2);
srcY = GST_VIDEO_FRAME_COMP_DATA (src, 0);
srcU = GST_VIDEO_FRAME_COMP_DATA (src, 1);
srcV = GST_VIDEO_FRAME_COMP_DATA (src, 2);
destY = destY + dest_y * dest_strideY + dest_x;
destU = destU + dest_y * dest_strideU + dest_x;
destV = destV + dest_y * dest_strideV + dest_x;
srcY = srcY + src_y * src_strideY + src_x;
srcU = srcU + src_y * src_strideU + src_x;
srcV = srcV + src_y * src_strideV + src_x;
if (src_sdtv != dest_sdtv) {
gint matrix[12];
gint y, u, v;
memcpy (matrix,
dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
y = APPLY_MATRIX (matrix, 0, srcY[j], srcU[j], srcV[j]);
u = APPLY_MATRIX (matrix, 1, srcY[j], srcU[j], srcV[j]);
v = APPLY_MATRIX (matrix, 2, srcY[j], srcU[j], srcV[j]);
destY[j] = y;
destU[j] = u;
destV[j] = v;
}
destY += dest_strideY;
destU += dest_strideU;
destV += dest_strideV;
srcY += src_strideY;
srcU += src_strideU;
srcV += src_strideV;
}
} else {
for (i = 0; i < h; i++) {
memcpy (destY, srcY, w);
memcpy (destU, srcU, w);
memcpy (destV, srcV, w);
destY += dest_strideY;
destU += dest_strideU;
destV += dest_strideV;
srcY += src_strideY;
srcU += src_strideU;
srcV += src_strideV;
}
}
}
static void
copy_y42b_y42b (guint i_alpha, GstVideoFrame * dest,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
guint8 *destY, *destU, *destV;
const guint8 *srcY, *srcU, *srcV;
gint dest_strideY, dest_strideU, dest_strideV;
gint src_strideY, src_strideU, src_strideV;
gint src_y_idx, src_uv_idx;
gint dest_y_idx, dest_uv_idx;
gint matrix[12];
gint y1, y2;
gint u1, u2;
gint v1, v2;
gint dest_width;
dest_width = GST_VIDEO_FRAME_WIDTH (dest);
dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest, 1);
dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest, 2);
src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src, 0);
src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src, 1);
src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src, 2);
destY = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
destU = GST_VIDEO_FRAME_COMP_DATA (dest, 1);
destV = GST_VIDEO_FRAME_COMP_DATA (dest, 2);
srcY = GST_VIDEO_FRAME_COMP_DATA (src, 0);
srcU = GST_VIDEO_FRAME_COMP_DATA (src, 1);
srcV = GST_VIDEO_FRAME_COMP_DATA (src, 2);
destY = destY + dest_y * dest_strideY + dest_x;
destU = destU + dest_y * dest_strideU + dest_x / 2;
destV = destV + dest_y * dest_strideV + dest_x / 2;
srcY = srcY + src_y * src_strideY + src_x;
srcU = srcU + src_y * src_strideU + src_x / 2;
srcV = srcV + src_y * src_strideV + src_x / 2;
h = dest_y + h;
w = dest_x + w;
if (src_sdtv != dest_sdtv)
memcpy (matrix,
dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
else
memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
/* 1. Copy all macro pixel scanlines, the destination scanline
* now starts at macro pixel boundary. */
for (i = dest_y; i < h; i++) {
/* 1.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 2 == 1) {
y1 = srcY[0];
u1 = srcU[0];
v1 = srcV[0];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[0] = CLAMP (
(destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
destV[0] = CLAMP (
(destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
j = dest_x + 1;
src_y_idx = dest_y_idx = dest_uv_idx = 1;
src_uv_idx = (src_x % 2) + 1;
} else {
j = dest_x;
src_y_idx = dest_y_idx = dest_uv_idx = 0;
src_uv_idx = (src_x % 2);
}
/* 1.2. Copy all macro pixels from the source to the destination.
* All pixels now start at macro pixel boundary, i.e. no
* blending with the background is necessary. */
for (; j < w - 1; j += 2) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
src_uv_idx++;
u2 = srcU[src_uv_idx / 2];
v2 = srcV[src_uv_idx / 2];
src_uv_idx++;
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[dest_uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
u2, v2)) / 2, 0, 255);
destV[dest_uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
u2, v2)) / 2, 0, 255);
dest_y_idx += 2;
src_y_idx += 2;
dest_uv_idx++;
}
/* 1.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
} else if (j == w - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (
(destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 2, 0, 255);
destV[dest_uv_idx] = CLAMP (
(destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 2, 0, 255);
}
destY += dest_strideY;
destU += dest_strideU;
destV += dest_strideV;
srcY += src_strideY;
srcU += src_strideU;
srcV += src_strideV;
}
}
static void
copy_y41b_y41b (guint i_alpha, GstVideoFrame * dest,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
guint8 *destY, *destU, *destV;
const guint8 *srcY, *srcU, *srcV;
gint dest_strideY, dest_strideU, dest_strideV;
gint src_strideY, src_strideU, src_strideV;
gint src_y_idx, src_uv_idx;
gint dest_y_idx, dest_uv_idx;
gint matrix[12];
gint y1, y2, y3, y4;
gint u1, u2, u3, u4;
gint v1, v2, v3, v4;
gint dest_width;
dest_width = GST_VIDEO_FRAME_WIDTH (dest);
dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest, 1);
dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest, 2);
src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src, 0);
src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src, 1);
src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src, 2);
destY = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
destU = GST_VIDEO_FRAME_COMP_DATA (dest, 1);
destV = GST_VIDEO_FRAME_COMP_DATA (dest, 2);
srcY = GST_VIDEO_FRAME_COMP_DATA (src, 0);
srcU = GST_VIDEO_FRAME_COMP_DATA (src, 1);
srcV = GST_VIDEO_FRAME_COMP_DATA (src, 2);
destY = destY + dest_y * dest_strideY + dest_x;
destU = destU + dest_y * dest_strideU + dest_x / 4;
destV = destV + dest_y * dest_strideV + dest_x / 4;
srcY = srcY + src_y * src_strideY + src_x;
srcU = srcU + src_y * src_strideU + src_x / 4;
srcV = srcV + src_y * src_strideV + src_x / 4;
h = dest_y + h;
w = dest_x + w;
if (src_sdtv != dest_sdtv)
memcpy (matrix,
dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
else
memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
/* 1. Copy all macro pixel scanlines, the destination scanline
* now starts at macro pixel boundary. */
for (i = dest_y; i < h; i++) {
/* 1.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 4 == 1) {
y1 = srcY[0];
y2 = srcY[1];
y3 = srcY[2];
u1 = srcU[0];
v1 = srcV[0];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
destY[2] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
destU[0] = CLAMP (
(destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u1,
v1) + APPLY_MATRIX (matrix, 1, y3, u1, v1)) / 4, 0, 255);
destV[0] =
CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u1,
v1) + APPLY_MATRIX (matrix, 2, y3, u1, v1)) / 4, 0, 255);
j = dest_x + 3;
src_y_idx = dest_y_idx = 3;
dest_uv_idx = 1;
src_uv_idx = (src_x % 4) + 3;
} else if (dest_x % 4 == 2) {
y1 = srcY[0];
y2 = srcY[1];
u1 = srcU[0];
v1 = srcV[0];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
destU[0] = CLAMP (
(2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u1, v1)) / 4, 0, 255);
destV[0] =
CLAMP ((2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u1, v1)) / 4, 0, 255);
j = dest_x + 2;
src_y_idx = dest_y_idx = 2;
dest_uv_idx = 1;
src_uv_idx = (src_x % 4) + 2;
} else if (dest_x % 4 == 3) {
y1 = srcY[0];
u1 = srcU[0];
v1 = srcV[0];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[0] = CLAMP (
(3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0, 255);
destV[0] = CLAMP (
(3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0, 255);
j = dest_x + 1;
src_y_idx = dest_y_idx = 1;
dest_uv_idx = 1;
src_uv_idx = (src_x % 4) + 1;
} else {
j = dest_x;
src_y_idx = dest_y_idx = dest_uv_idx = 0;
src_uv_idx = (src_x % 4);
}
/* 1.2. Copy all macro pixels from the source to the destination.
* All pixels now start at macro pixel boundary, i.e. no
* blending with the background is necessary. */
for (; j < w - 3; j += 4) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
y3 = srcY[src_y_idx + 2];
y4 = srcY[src_y_idx + 3];
u1 = srcU[src_uv_idx / 4];
v1 = srcV[src_uv_idx / 4];
src_uv_idx++;
u2 = srcU[src_uv_idx / 4];
v2 = srcV[src_uv_idx / 4];
src_uv_idx++;
u3 = srcU[src_uv_idx / 4];
v3 = srcV[src_uv_idx / 4];
src_uv_idx++;
u4 = srcU[src_uv_idx / 4];
v4 = srcV[src_uv_idx / 4];
src_uv_idx++;
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destY[dest_y_idx + 2] =
CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
destY[dest_y_idx + 3] =
CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
destU[dest_uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
destV[dest_uv_idx] =
CLAMP ((APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix,
2, y2, u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
dest_y_idx += 4;
src_y_idx += 4;
dest_uv_idx++;
}
/* 1.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 4];
v1 = srcV[src_uv_idx / 4];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
} else if (j == w - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 4];
v1 = srcV[src_uv_idx / 4];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (
(destU[dest_uv_idx] + 3 * APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 4, 0, 255);
destV[dest_uv_idx] = CLAMP (
(destV[dest_uv_idx] + 3 * APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 4, 0, 255);
} else if (j == w - 2 && j == dest_width - 2) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
u1 = srcU[src_uv_idx / 4];
v1 = srcV[src_uv_idx / 4];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
} else if (j == w - 2) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
u1 = srcU[src_uv_idx / 4];
v1 = srcV[src_uv_idx / 4];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
destU[dest_uv_idx] =
CLAMP ((destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 2, 0, 255);
destV[dest_uv_idx] =
CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 2, 0, 255);
} else if (j == w - 3 && j == dest_width - 3) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
y3 = srcY[src_y_idx + 2];
u1 = srcU[src_uv_idx / 4];
v1 = srcV[src_uv_idx / 4];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
destY[dest_y_idx + 2] =
CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
} else if (j == w - 3) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
y3 = srcY[src_y_idx + 2];
u1 = srcU[src_uv_idx / 4];
v1 = srcV[src_uv_idx / 4];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
destY[dest_y_idx + 2] =
CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
destU[dest_uv_idx] =
CLAMP ((3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 4, 0, 255);
destV[dest_uv_idx] =
CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 4, 0, 255);
}
destY += dest_strideY;
destU += dest_strideU;
destV += dest_strideV;
srcY += src_strideY;
srcU += src_strideU;
srcV += src_strideV;
}
}
static void
copy_i420_i420 (guint i_alpha, GstVideoFrame * dest,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
guint8 *destY, *destU, *destV;
const guint8 *srcY, *srcU, *srcV;
guint8 *destY2;
const guint8 *srcY2, *srcU2, *srcV2;
gint dest_strideY, dest_strideU, dest_strideV;
gint src_strideY, src_strideU, src_strideV;
gint src_y_idx, src_uv_idx;
gint dest_y_idx, dest_uv_idx;
gint matrix[12];
gint y1, y2, y3, y4;
gint u1, u2, u3, u4;
gint v1, v2, v3, v4;
gint dest_width, dest_height;
dest_width = GST_VIDEO_FRAME_WIDTH (dest);
dest_height = GST_VIDEO_FRAME_HEIGHT (dest);
dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest, 1);
dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest, 2);
src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src, 0);
src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src, 1);
src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src, 2);
destY = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
destU = GST_VIDEO_FRAME_COMP_DATA (dest, 1);
destV = GST_VIDEO_FRAME_COMP_DATA (dest, 2);
srcY = GST_VIDEO_FRAME_COMP_DATA (src, 0);
srcU = GST_VIDEO_FRAME_COMP_DATA (src, 1);
srcV = GST_VIDEO_FRAME_COMP_DATA (src, 2);
destY = destY + dest_y * dest_strideY + dest_x;
destU = destU + (dest_y / 2) * dest_strideU + dest_x / 2;
destV = destV + (dest_y / 2) * dest_strideV + dest_x / 2;
srcY = srcY + src_y * src_strideY + src_x;
srcU = srcU + (src_y / 2) * src_strideU + src_x / 2;
srcV = srcV + (src_y / 2) * src_strideV + src_x / 2;
destY2 = destY + dest_strideY;
srcY2 = srcY + src_strideY;
h = dest_y + h;
w = dest_x + w;
if (src_sdtv != dest_sdtv)
memcpy (matrix,
dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
else
memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
/* 1. Handle the first destination scanline specially if it
* doesn't start at the macro pixel boundary, i.e. blend
* with the background! */
if (dest_y % 2 == 1) {
/* 1.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 2 == 1) {
y1 = srcY[0];
u1 = srcU[0];
v1 = srcV[0];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[0] =
CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
255);
destV[0] =
CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
255);
j = dest_x + 1;
src_y_idx = dest_y_idx = dest_uv_idx = 1;
src_uv_idx = (src_x % 2) + 1;
} else {
j = dest_x;
src_y_idx = dest_y_idx = dest_uv_idx = 0;
src_uv_idx = (src_x % 2);
}
/* 1.2. Copy all macro pixels from the source to the destination
* but blend with the background because we're only filling
* the lower part of the macro pixels. */
for (; j < w - 1; j += 2) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
src_uv_idx++;
u2 = srcU[src_uv_idx / 2];
v2 = srcV[src_uv_idx / 2];
src_uv_idx++;
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[dest_uv_idx] =
CLAMP ((2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
destV[dest_uv_idx] =
CLAMP ((2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
dest_y_idx += 2;
src_y_idx += 2;
dest_uv_idx++;
}
/* 1.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (
(destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
255);
destV[dest_uv_idx] =
CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
v1)) / 2, 0, 255);
} else if (j == w - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (
(3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
0, 255);
destV[dest_uv_idx] =
CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
v1)) / 4, 0, 255);
}
destY += dest_strideY;
destY2 += dest_strideY;
destU += dest_strideU;
destV += dest_strideV;
srcY += src_strideY;
srcY2 += src_strideY;
src_y++;
if (src_y % 2 == 0) {
srcU += src_strideU;
srcV += src_strideV;
}
i = dest_y + 1;
} else {
i = dest_y;
}
/* 2. Copy all macro pixel scanlines, the destination scanline
* now starts at macro pixel boundary. */
for (; i < h - 1; i += 2) {
/* 2.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
srcU2 = srcU;
srcV2 = srcV;
if (src_y % 2 == 1) {
srcU2 += src_strideU;
srcV2 += src_strideV;
}
if (dest_x % 2 == 1) {
y1 = srcY[0];
y2 = srcY2[0];
u1 = srcU[0];
v1 = srcV[0];
u2 = srcU2[0];
v2 = srcV2[0];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY2[0] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[0] = CLAMP (
(2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
destV[0] = CLAMP (
(2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
j = dest_x + 1;
src_y_idx = dest_y_idx = dest_uv_idx = 1;
src_uv_idx = (src_x % 2) + 1;
} else {
j = dest_x;
src_y_idx = dest_y_idx = dest_uv_idx = 0;
src_uv_idx = (src_x % 2);
}
/* 2.2. Copy all macro pixels from the source to the destination.
* All pixels now start at macro pixel boundary, i.e. no
* blending with the background is necessary. */
for (; j < w - 1; j += 2) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
y3 = srcY2[src_y_idx];
y4 = srcY2[src_y_idx + 1];
u1 = srcU[src_uv_idx / 2];
u3 = srcU2[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
v3 = srcV2[src_uv_idx / 2];
src_uv_idx++;
u2 = srcU[src_uv_idx / 2];
u4 = srcU2[src_uv_idx / 2];
v2 = srcV[src_uv_idx / 2];
v4 = srcV2[src_uv_idx / 2];
src_uv_idx++;
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
destY2[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
destU[dest_uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
destV[dest_uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
dest_y_idx += 2;
src_y_idx += 2;
dest_uv_idx++;
}
/* 2.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = srcY[src_y_idx];
y2 = srcY2[src_y_idx];
u1 = srcU[src_uv_idx / 2];
u2 = srcU2[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
v2 = srcV2[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[dest_uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
u2, v2)) / 2, 0, 255);
destV[dest_uv_idx] = CLAMP (
(APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
u2, v2)) / 2, 0, 255);
} else if (j == w - 1) {
y1 = srcY[src_y_idx];
y2 = srcY2[src_y_idx];
u1 = srcU[src_uv_idx / 2];
u2 = srcU2[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
v2 = srcV2[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[dest_uv_idx] = CLAMP (
(2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
destV[dest_uv_idx] = CLAMP (
(2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
}
destY += 2 * dest_strideY;
destY2 += 2 * dest_strideY;
destU += dest_strideU;
destV += dest_strideV;
srcY += 2 * src_strideY;
srcY2 += 2 * src_strideY;
src_y += 2;
srcU += src_strideU;
srcV += src_strideV;
}
/* 3. Handle the last scanline if one exists. This again
* doesn't start at macro pixel boundary but should
* only fill the upper part of the macro pixels. */
if (i == h - 1 && i == dest_height - 1) {
/* 3.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 2 == 1) {
y1 = srcY[0];
u1 = srcU[0];
v1 = srcV[0];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[0] =
CLAMP ((destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
destV[0] =
CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
j = dest_x + 1;
src_y_idx = dest_y_idx = dest_uv_idx = 1;
src_uv_idx = (src_x % 2) + 1;
} else {
j = dest_x;
src_y_idx = dest_y_idx = dest_uv_idx = 0;
src_uv_idx = (src_x % 2);
}
/* 3.2. Copy all macro pixels from the source to the destination
* but blend with the background because we're only filling
* the upper part of the macro pixels. */
for (; j < w - 1; j += 2) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
src_uv_idx++;
u2 = srcU[src_uv_idx / 2];
v2 = srcV[src_uv_idx / 2];
src_uv_idx++;
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[dest_uv_idx] = CLAMP (
(2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
destV[dest_uv_idx] = CLAMP (
(2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
dest_y_idx += 2;
src_y_idx += 2;
dest_uv_idx++;
}
/* 3.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (
(destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
255);
destV[dest_uv_idx] =
CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 2, 0, 255);
} else if (j == w - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (
(3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
0, 255);
destV[dest_uv_idx] =
CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 4, 0, 255);
}
} else if (i == h - 1) {
/* 3.1. Handle the first destination pixel if it doesn't
* start at the macro pixel boundary, i.e. blend with
* the background! */
if (dest_x % 2 == 1) {
y1 = srcY[0];
u1 = srcU[0];
v1 = srcV[0];
destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[0] =
CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
255);
destV[0] =
CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
255);
j = dest_x + 1;
src_y_idx = dest_y_idx = dest_uv_idx = 1;
src_uv_idx = (src_x % 2) + 1;
} else {
j = dest_x;
src_y_idx = dest_y_idx = dest_uv_idx = 0;
src_uv_idx = (src_x % 2);
}
/* 3.2. Copy all macro pixels from the source to the destination
* but blend with the background because we're only filling
* the upper part of the macro pixels. */
for (; j < w - 1; j += 2) {
y1 = srcY[src_y_idx];
y2 = srcY[src_y_idx + 1];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
src_uv_idx++;
u2 = srcU[src_uv_idx / 2];
v2 = srcV[src_uv_idx / 2];
src_uv_idx++;
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destY[dest_y_idx + 1] =
CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
destU[dest_uv_idx] = CLAMP (
(2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
destV[dest_uv_idx] = CLAMP (
(2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
dest_y_idx += 2;
src_y_idx += 2;
dest_uv_idx++;
}
/* 3.3. Now copy the last pixel if one exists and blend it
* with the background because we only fill part of
* the macro pixel. In case this is the last pixel of
* the destination we will a larger part. */
if (j == w - 1 && j == dest_width - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (
(destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
255);
destV[dest_uv_idx] =
CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 2, 0, 255);
} else if (j == w - 1) {
y1 = srcY[src_y_idx];
u1 = srcU[src_uv_idx / 2];
v1 = srcV[src_uv_idx / 2];
destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
destU[dest_uv_idx] = CLAMP (
(3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
0, 255);
destV[dest_uv_idx] =
CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
v1)) / 4, 0, 255);
}
}
}
static void
copy_i420_ayuv (guint i_alpha, GstVideoFrame * dest_frame,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
const guint8 *srcY, *srcU, *srcV;
gint src_strideY, src_strideU, src_strideV;
gint dest_stride;
guint8 *dest;
src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src_frame, 0);
src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src_frame, 1);
src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src_frame, 2);
srcY = GST_VIDEO_FRAME_COMP_DATA (src_frame, 0);
srcU = GST_VIDEO_FRAME_COMP_DATA (src_frame, 1);
srcV = GST_VIDEO_FRAME_COMP_DATA (src_frame, 2);
dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
dest = dest + dest_y * dest_stride + dest_x * 4;
srcY = srcY + src_y * src_strideY + src_x;
srcU = srcU + (src_y / 2) * src_strideU + src_x / 2;
srcV = srcV + (src_y / 2) * src_strideV + src_x / 2;
i_alpha = MIN (i_alpha, 255);
if (src_sdtv != dest_sdtv) {
gint i, j, uv_idx;
gint y, u, v;
gint y1, u1, v1;
gint matrix[12];
memcpy (matrix,
dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
for (i = 0; i < h; i++) {
for (j = 0, uv_idx = src_x % 2; j < w; j++, uv_idx++) {
y = srcY[j];
u = srcU[uv_idx / 2];
v = srcV[uv_idx / 2];
y1 = APPLY_MATRIX (matrix, 0, y, u, v);
u1 = APPLY_MATRIX (matrix, 1, y, u, v);
v1 = APPLY_MATRIX (matrix, 2, y, u, v);
dest[4 * j + 0] = i_alpha;
dest[4 * j + 1] = y1;
dest[4 * j + 2] = u1;
dest[4 * j + 3] = v1;
}
dest += dest_stride;
src_y++;
srcY += src_strideY;
if (src_y % 2 == 0) {
srcU += src_strideU;
srcV += src_strideV;
}
}
} else {
gint i, j, uv_idx;
gint y, u, v;
for (i = 0; i < h; i++) {
for (j = 0, uv_idx = src_x % 2; j < w; j++, uv_idx++) {
y = srcY[j];
u = srcU[uv_idx / 2];
v = srcV[uv_idx / 2];
dest[4 * j + 0] = i_alpha;
dest[4 * j + 1] = y;
dest[4 * j + 2] = u;
dest[4 * j + 3] = v;
}
dest += dest_stride;
src_y++;
srcY += src_strideY;
if (src_y % 2 == 0) {
srcU += src_strideU;
srcV += src_strideV;
}
}
}
}
static void
fill_rgb32 (GstVideoBoxFill fill_type, guint b_alpha,
GstVideoFrame * frame, gboolean sdtv)
{
guint32 empty_pixel;
gint p[4];
guint8 *dest;
guint stride;
gint width, height;
width = GST_VIDEO_FRAME_WIDTH (frame);
height = GST_VIDEO_FRAME_HEIGHT (frame);
dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
p[0] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 3);
p[1] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 0);
p[2] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 1);
p[3] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 2);
b_alpha = MIN (b_alpha, 255);
if (GST_VIDEO_FRAME_N_COMPONENTS (frame) == 4) {
empty_pixel = GUINT32_FROM_LE ((b_alpha << (p[0] * 8)) |
(rgb_colors_R[fill_type] << (p[1] * 8)) |
(rgb_colors_G[fill_type] << (p[2] * 8)) |
(rgb_colors_B[fill_type] << (p[3] * 8)));
} else {
empty_pixel = GUINT32_FROM_LE (
(rgb_colors_R[fill_type] << (p[1] * 8)) |
(rgb_colors_G[fill_type] << (p[2] * 8)) |
(rgb_colors_B[fill_type] << (p[3] * 8)));
}
if (stride == width * 4) {
video_box_orc_splat_u32 ((guint32 *) dest, empty_pixel, width * height);
} else if (height) {
for (; height; --height) {
video_box_orc_splat_u32 ((guint32 *) dest, empty_pixel, width);
dest += stride;
}
}
}
static void
fill_rgb24 (GstVideoBoxFill fill_type, guint b_alpha,
GstVideoFrame * frame, gboolean sdtv)
{
gint dest_stride;
gint p[4];
gint i, j;
guint8 *dest;
gint width, height;
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_PLANE_STRIDE (frame, 0);
p[1] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 0);
p[2] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 1);
p[3] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 2);
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
dest[3 * j + p[1]] = rgb_colors_R[fill_type];
dest[3 * j + p[2]] = rgb_colors_G[fill_type];
dest[3 * j + p[3]] = rgb_colors_B[fill_type];
}
dest += dest_stride;
}
}
static void
copy_rgb32 (guint i_alpha, GstVideoFrame * dest_frame,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
gint src_stride, dest_stride;
gboolean in_alpha, out_alpha;
gint in_bpp, out_bpp;
gint p_out[4];
gint p_in[4];
gboolean packed_out;
gboolean packed_in;
guint8 *src, *dest;
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
in_bpp = GST_VIDEO_FRAME_COMP_PSTRIDE (src_frame, 0);
out_bpp = GST_VIDEO_FRAME_COMP_PSTRIDE (dest_frame, 0);
packed_in = (in_bpp < 4);
packed_out = (out_bpp < 4);
out_alpha = GST_VIDEO_INFO_HAS_ALPHA (&dest_frame->info);
p_out[0] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 3);
p_out[1] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 0);
p_out[2] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 1);
p_out[3] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 2);
in_alpha = GST_VIDEO_INFO_HAS_ALPHA (&src_frame->info);
p_in[0] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 3);
p_in[1] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 0);
p_in[2] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 1);
p_in[3] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 2);
dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
dest = dest + dest_y * dest_stride + dest_x * out_bpp;
src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
src = src + src_y * src_stride + src_x * in_bpp;
if (in_alpha && out_alpha) {
w *= 4;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
dest[j + p_out[0]] = (src[j + p_in[0]] * i_alpha) >> 8;
dest[j + p_out[1]] = src[j + p_in[1]];
dest[j + p_out[2]] = src[j + p_in[2]];
dest[j + p_out[3]] = src[j + p_in[3]];
}
dest += dest_stride;
src += src_stride;
}
} else if (out_alpha && !packed_in) {
w *= 4;
i_alpha = MIN (i_alpha, 255);
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
dest[j + p_out[0]] = i_alpha;
dest[j + p_out[1]] = src[j + p_in[1]];
dest[j + p_out[2]] = src[j + p_in[2]];
dest[j + p_out[3]] = src[j + p_in[3]];
}
dest += dest_stride;
src += src_stride;
}
} else if (out_alpha && packed_in) {
i_alpha = MIN (i_alpha, 255);
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
dest[4 * j + p_out[0]] = i_alpha;
dest[4 * j + p_out[1]] = src[in_bpp * j + p_in[1]];
dest[4 * j + p_out[2]] = src[in_bpp * j + p_in[2]];
dest[4 * j + p_out[3]] = src[in_bpp * j + p_in[3]];
}
dest += dest_stride;
src += src_stride;
}
} else if (!packed_out && !packed_in) {
w *= 4;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
dest[j + p_out[1]] = src[j + p_in[1]];
dest[j + p_out[2]] = src[j + p_in[2]];
dest[j + p_out[3]] = src[j + p_in[3]];
}
dest += dest_stride;
src += src_stride;
}
} else {
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
dest[out_bpp * j + p_out[1]] = src[in_bpp * j + p_in[1]];
dest[out_bpp * j + p_out[2]] = src[in_bpp * j + p_in[2]];
dest[out_bpp * j + p_out[3]] = src[in_bpp * j + p_in[3]];
}
dest += dest_stride;
src += src_stride;
}
}
}
static void
copy_rgb32_ayuv (guint i_alpha, GstVideoFrame * dest_frame,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
gint src_stride, dest_stride;
gboolean in_alpha;
gint in_bpp;
gint p_in[4];
gboolean packed_in;
gint matrix[12];
gint a;
gint y, u, v;
gint r, g, b;
guint8 *dest, *src;
dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
in_bpp = GST_VIDEO_FRAME_COMP_PSTRIDE (src_frame, 0);
packed_in = (in_bpp < 4);
in_alpha = GST_VIDEO_INFO_HAS_ALPHA (&src_frame->info);
p_in[0] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 3);
p_in[1] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 0);
p_in[2] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 1);
p_in[3] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 2);
memcpy (matrix,
(dest_sdtv) ? cog_rgb_to_ycbcr_matrix_8bit_sdtv :
cog_rgb_to_ycbcr_matrix_8bit_hdtv, 12 * sizeof (gint));
dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
dest = dest + dest_y * dest_stride + dest_x * 4;
src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
src = src + src_y * src_stride + src_x * in_bpp;
if (in_alpha) {
w *= 4;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
a = (src[j + p_in[0]] * i_alpha) >> 8;
r = src[j + p_in[1]];
g = src[j + p_in[2]];
b = src[j + p_in[3]];
y = APPLY_MATRIX (matrix, 0, r, g, b);
u = APPLY_MATRIX (matrix, 1, r, g, b);
v = APPLY_MATRIX (matrix, 2, r, g, b);
dest[j + 0] = a;
dest[j + 1] = CLAMP (y, 0, 255);
dest[j + 2] = CLAMP (u, 0, 255);
dest[j + 3] = CLAMP (v, 0, 255);
}
dest += dest_stride;
src += src_stride;
}
} else if (!packed_in) {
w *= 4;
i_alpha = MIN (i_alpha, 255);
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
a = i_alpha;
r = src[j + p_in[1]];
g = src[j + p_in[2]];
b = src[j + p_in[3]];
y = APPLY_MATRIX (matrix, 0, r, g, b);
u = APPLY_MATRIX (matrix, 1, r, g, b);
v = APPLY_MATRIX (matrix, 2, r, g, b);
dest[j + 0] = a;
dest[j + 1] = CLAMP (y, 0, 255);
dest[j + 2] = CLAMP (u, 0, 255);
dest[j + 3] = CLAMP (v, 0, 255);
}
dest += dest_stride;
src += src_stride;
}
} else {
i_alpha = MIN (i_alpha, 255);
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
a = i_alpha;
r = src[in_bpp * j + p_in[1]];
g = src[in_bpp * j + p_in[2]];
b = src[in_bpp * j + p_in[3]];
y = APPLY_MATRIX (matrix, 0, r, g, b);
u = APPLY_MATRIX (matrix, 1, r, g, b);
v = APPLY_MATRIX (matrix, 2, r, g, b);
dest[4 * j + 0] = a;
dest[4 * j + 1] = CLAMP (y, 0, 255);
dest[4 * j + 2] = CLAMP (u, 0, 255);
dest[4 * j + 3] = CLAMP (v, 0, 255);
}
dest += dest_stride;
src += src_stride;
}
}
}
static void
copy_ayuv_rgb32 (guint i_alpha, GstVideoFrame * dest_frame,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
gint src_stride, dest_stride;
gboolean out_alpha;
gint out_bpp;
gint p_out[4];
gboolean packed_out;
gint matrix[12];
gint a;
gint y, u, v;
gint r, g, b;
guint8 *src, *dest;
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
out_bpp = GST_VIDEO_FRAME_COMP_PSTRIDE (dest_frame, 0);
packed_out = (out_bpp < 4);
out_alpha = GST_VIDEO_INFO_HAS_ALPHA (&dest_frame->info);
p_out[0] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 3);
p_out[1] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 0);
p_out[2] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 1);
p_out[3] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 2);
memcpy (matrix,
(src_sdtv) ? cog_ycbcr_to_rgb_matrix_8bit_sdtv :
cog_ycbcr_to_rgb_matrix_8bit_hdtv, 12 * sizeof (gint));
dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
dest = dest + dest_y * dest_stride + dest_x * out_bpp;
src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
src = src + src_y * src_stride + src_x * 4;
if (out_alpha) {
w *= 4;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
a = (src[j + 0] * i_alpha) >> 8;
y = src[j + 1];
u = src[j + 2];
v = src[j + 3];
r = APPLY_MATRIX (matrix, 0, y, u, v);
g = APPLY_MATRIX (matrix, 1, y, u, v);
b = APPLY_MATRIX (matrix, 2, y, u, v);
dest[j + p_out[0]] = a;
dest[j + p_out[1]] = CLAMP (r, 0, 255);
dest[j + p_out[2]] = CLAMP (g, 0, 255);
dest[j + p_out[3]] = CLAMP (b, 0, 255);
}
dest += dest_stride;
src += src_stride;
}
} else if (!packed_out) {
w *= 4;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 4) {
y = src[j + 1];
u = src[j + 2];
v = src[j + 3];
r = APPLY_MATRIX (matrix, 0, y, u, v);
g = APPLY_MATRIX (matrix, 1, y, u, v);
b = APPLY_MATRIX (matrix, 2, y, u, v);
dest[j + p_out[1]] = CLAMP (r, 0, 255);
dest[j + p_out[2]] = CLAMP (g, 0, 255);
dest[j + p_out[3]] = CLAMP (b, 0, 255);
}
dest += dest_stride;
src += src_stride;
}
} else {
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
y = src[4 * j + 1];
u = src[4 * j + 2];
v = src[4 * j + 3];
r = APPLY_MATRIX (matrix, 0, y, u, v);
g = APPLY_MATRIX (matrix, 1, y, u, v);
b = APPLY_MATRIX (matrix, 2, y, u, v);
dest[out_bpp * j + p_out[1]] = CLAMP (r, 0, 255);
dest[out_bpp * j + p_out[2]] = CLAMP (g, 0, 255);
dest[out_bpp * j + p_out[3]] = CLAMP (b, 0, 255);
}
dest += dest_stride;
src += src_stride;
}
}
}
static void
fill_gray (GstVideoBoxFill fill_type, guint b_alpha,
GstVideoFrame * frame, gboolean sdtv)
{
gint i, j;
gint dest_stride;
guint8 *dest;
gint width, height;
GstVideoFormat format;
format = GST_VIDEO_FRAME_FORMAT (frame);
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_PLANE_STRIDE (frame, 0);
if (format == GST_VIDEO_FORMAT_GRAY8) {
guint8 val = yuv_sdtv_colors_Y[fill_type];
for (i = 0; i < height; i++) {
memset (dest, val, width);
dest += dest_stride;
}
} else {
guint16 val = yuv_sdtv_colors_Y[fill_type] << 8;
if (format == GST_VIDEO_FORMAT_GRAY16_BE) {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
GST_WRITE_UINT16_BE (dest + 2 * j, val);
}
dest += dest_stride;
}
} else {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
GST_WRITE_UINT16_LE (dest + 2 * j, val);
}
dest += dest_stride;
}
}
}
}
static void
copy_packed_simple (guint i_alpha, GstVideoFrame * dest_frame,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i;
gint src_stride, dest_stride;
gint pixel_stride, row_size;
guint8 *src, *dest;
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest_frame, 0);
row_size = w * pixel_stride;
dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
dest = dest + dest_y * dest_stride + dest_x * pixel_stride;
src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
src = src + src_y * src_stride + src_x * pixel_stride;
for (i = 0; i < h; i++) {
memcpy (dest, src, row_size);
dest += dest_stride;
src += src_stride;
}
}
static void
fill_yuy2 (GstVideoBoxFill fill_type, guint b_alpha,
GstVideoFrame * frame, gboolean sdtv)
{
guint8 y, u, v;
gint i, j;
gint stride;
gint width, height;
guint8 *dest;
GstVideoFormat format;
format = GST_VIDEO_FRAME_FORMAT (frame);
width = GST_VIDEO_FRAME_WIDTH (frame);
height = GST_VIDEO_FRAME_HEIGHT (frame);
dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
y = (sdtv) ? yuv_sdtv_colors_Y[fill_type] : yuv_hdtv_colors_Y[fill_type];
u = (sdtv) ? yuv_sdtv_colors_U[fill_type] : yuv_hdtv_colors_U[fill_type];
v = (sdtv) ? yuv_sdtv_colors_V[fill_type] : yuv_hdtv_colors_V[fill_type];
width = width + (width % 2);
if (format == GST_VIDEO_FORMAT_YUY2) {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
dest[j * 2 + 0] = y;
dest[j * 2 + 1] = u;
dest[j * 2 + 2] = y;
dest[j * 2 + 3] = v;
}
dest += stride;
}
} else if (format == GST_VIDEO_FORMAT_YVYU) {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
dest[j * 2 + 0] = y;
dest[j * 2 + 1] = v;
dest[j * 2 + 2] = y;
dest[j * 2 + 3] = u;
}
dest += stride;
}
} else {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
dest[j * 2 + 0] = u;
dest[j * 2 + 1] = y;
dest[j * 2 + 2] = v;
dest[j * 2 + 3] = y;
}
dest += stride;
}
}
}
static void
copy_yuy2_yuy2 (guint i_alpha, GstVideoFrame * dest_frame,
gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
gint src_stride, dest_stride;
guint8 *src, *dest;
GstVideoFormat src_format;
src_format = GST_VIDEO_FRAME_FORMAT (src_frame);
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
dest_x = (dest_x & ~1);
src_x = (src_x & ~1);
w = w + (w % 2);
dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
dest = dest + dest_y * dest_stride + dest_x * 2;
src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
src = src + src_y * src_stride + src_x * 2;
if (src_sdtv != dest_sdtv) {
gint y1, u1, v1;
gint y2, u2, v2;
gint matrix[12];
memcpy (matrix,
dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
if (src_format == GST_VIDEO_FORMAT_YUY2) {
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 2) {
y1 = src[j * 2 + 0];
y2 = src[j * 2 + 2];
u1 = u2 = src[j * 2 + 1];
v1 = v2 = src[j * 2 + 3];
dest[j * 2 + 0] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
dest[j * 2 + 1] = APPLY_MATRIX (matrix, 1, y1, u1, v1);
dest[j * 2 + 2] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
dest[j * 2 + 3] = APPLY_MATRIX (matrix, 2, y2, u2, v2);
}
dest += dest_stride;
src += src_stride;
}
} else if (src_format == GST_VIDEO_FORMAT_YVYU) {
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 2) {
y1 = src[j * 2 + 0];
y2 = src[j * 2 + 2];
v1 = v2 = src[j * 2 + 1];
u1 = u2 = src[j * 2 + 3];
dest[j * 2 + 0] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
dest[j * 2 + 1] = APPLY_MATRIX (matrix, 2, y1, u1, v1);
dest[j * 2 + 2] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
dest[j * 2 + 3] = APPLY_MATRIX (matrix, 1, y2, u2, v2);
}
dest += dest_stride;
src += src_stride;
}
} else {
for (i = 0; i < h; i++) {
for (j = 0; j < w; j += 2) {
u1 = u2 = src[j * 2 + 0];
v1 = v2 = src[j * 2 + 2];
y1 = src[j * 2 + 1];
y2 = src[j * 2 + 3];
dest[j * 2 + 1] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
dest[j * 2 + 0] = APPLY_MATRIX (matrix, 1, y1, u1, v1);
dest[j * 2 + 3] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
dest[j * 2 + 2] = APPLY_MATRIX (matrix, 2, y2, u2, v2);
}
dest += dest_stride;
src += src_stride;
}
}
} else {
for (i = 0; i < h; i++) {
memcpy (dest, src, w * 2);
dest += dest_stride;
src += src_stride;
}
}
}
#define DEFAULT_LEFT 0
#define DEFAULT_RIGHT 0
#define DEFAULT_TOP 0
#define DEFAULT_BOTTOM 0
#define DEFAULT_FILL_TYPE VIDEO_BOX_FILL_BLACK
#define DEFAULT_ALPHA 1.0
#define DEFAULT_BORDER_ALPHA 1.0
enum
{
PROP_0,
PROP_LEFT,
PROP_RIGHT,
PROP_TOP,
PROP_BOTTOM,
PROP_FILL_TYPE,
PROP_ALPHA,
PROP_BORDER_ALPHA,
PROP_AUTOCROP
/* FILL ME */
};
static GstStaticPadTemplate gst_video_box_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, "
"ARGB, BGRA, ABGR, RGBA, xRGB, BGRx, xBGR, RGBx, RGB, BGR, "
"Y444, Y42B, YUY2, YVYU, UYVY, I420, YV12, Y41B, "
"GRAY8, GRAY16_BE, GRAY16_LE } "))
);
static GstStaticPadTemplate gst_video_box_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, "
"ARGB, BGRA, ABGR, RGBA, xRGB, BGRx, xBGR, RGBx, RGB, BGR, "
"Y444, Y42B, YUY2, YVYU, UYVY, I420, YV12, Y41B, "
"GRAY8, GRAY16_BE, GRAY16_LE } "))
);
#define gst_video_box_parent_class parent_class
G_DEFINE_TYPE (GstVideoBox, gst_video_box, GST_TYPE_VIDEO_FILTER);
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (videobox, "videobox", GST_RANK_NONE,
GST_TYPE_VIDEO_BOX, GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
"Resizes a video by adding borders or cropping"));
static void gst_video_box_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_video_box_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_video_box_recalc_transform (GstVideoBox * video_box);
static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * from, GstCaps * filter);
static void gst_video_box_before_transform (GstBaseTransform * trans,
GstBuffer * in);
static gboolean gst_video_box_src_event (GstBaseTransform * trans,
GstEvent * event);
static gboolean gst_video_box_set_info (GstVideoFilter * vfilter, GstCaps * in,
GstVideoInfo * in_info, GstCaps * out, GstVideoInfo * out_info);
static GstFlowReturn gst_video_box_transform_frame (GstVideoFilter * vfilter,
GstVideoFrame * in_frame, GstVideoFrame * out_frame);
#define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
static GType
gst_video_box_fill_get_type (void)
{
static GType video_box_fill_type = 0;
static const GEnumValue video_box_fill[] = {
{VIDEO_BOX_FILL_BLACK, "Black", "black"},
{VIDEO_BOX_FILL_GREEN, "Green", "green"},
{VIDEO_BOX_FILL_BLUE, "Blue", "blue"},
{VIDEO_BOX_FILL_RED, "Red", "red"},
{VIDEO_BOX_FILL_YELLOW, "Yellow", "yellow"},
{VIDEO_BOX_FILL_WHITE, "White", "white"},
{0, NULL, NULL},
};
if (!video_box_fill_type) {
video_box_fill_type =
g_enum_register_static ("GstVideoBoxFill", video_box_fill);
}
return video_box_fill_type;
}
static void
gst_video_box_finalize (GObject * object)
{
GstVideoBox *video_box = GST_VIDEO_BOX (object);
g_mutex_clear (&video_box->mutex);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_video_box_class_init (GstVideoBoxClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *element_class = (GstElementClass *) (klass);
GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
gobject_class->set_property = gst_video_box_set_property;
gobject_class->get_property = gst_video_box_get_property;
gobject_class->finalize = gst_video_box_finalize;
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILL_TYPE,
g_param_spec_enum ("fill", "Fill", "How to fill the borders",
GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEFT,
g_param_spec_int ("left", "Left",
"Pixels to box at left (<0 = add a border)", G_MININT, G_MAXINT,
DEFAULT_LEFT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RIGHT,
g_param_spec_int ("right", "Right",
"Pixels to box at right (<0 = add a border)", G_MININT, G_MAXINT,
DEFAULT_RIGHT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOP,
g_param_spec_int ("top", "Top",
"Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
DEFAULT_TOP,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BOTTOM,
g_param_spec_int ("bottom", "Bottom",
"Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
DEFAULT_BOTTOM,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
DEFAULT_ALPHA,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
g_param_spec_double ("border-alpha", "Border Alpha",
"Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
/**
* GstVideoBox:autocrop:
*
* If set to %TRUE videobox will automatically crop/pad the input
* video to be centered in the output.
*/
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOCROP,
g_param_spec_boolean ("autocrop", "Auto crop",
"Auto crop", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
trans_class->before_transform =
GST_DEBUG_FUNCPTR (gst_video_box_before_transform);
trans_class->transform_caps =
GST_DEBUG_FUNCPTR (gst_video_box_transform_caps);
trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_box_src_event);
vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_video_box_set_info);
vfilter_class->transform_frame =
GST_DEBUG_FUNCPTR (gst_video_box_transform_frame);
gst_element_class_set_static_metadata (element_class, "Video box filter",
"Filter/Effect/Video",
"Resizes a video by adding borders or cropping",
"Wim Taymans <wim@fluendo.com>");
gst_element_class_add_static_pad_template (element_class,
&gst_video_box_sink_template);
gst_element_class_add_static_pad_template (element_class,
&gst_video_box_src_template);
gst_type_mark_as_plugin_api (GST_TYPE_VIDEO_BOX_FILL, 0);
}
static void
gst_video_box_init (GstVideoBox * video_box)
{
video_box->box_right = DEFAULT_RIGHT;
video_box->box_left = DEFAULT_LEFT;
video_box->box_top = DEFAULT_TOP;
video_box->box_bottom = DEFAULT_BOTTOM;
video_box->crop_right = 0;
video_box->crop_left = 0;
video_box->crop_top = 0;
video_box->crop_bottom = 0;
video_box->fill_type = DEFAULT_FILL_TYPE;
video_box->alpha = DEFAULT_ALPHA;
video_box->border_alpha = DEFAULT_BORDER_ALPHA;
video_box->autocrop = FALSE;
g_mutex_init (&video_box->mutex);
}
static void
gst_video_box_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstVideoBox *video_box = GST_VIDEO_BOX (object);
g_mutex_lock (&video_box->mutex);
switch (prop_id) {
case PROP_LEFT:
video_box->box_left = g_value_get_int (value);
if (video_box->box_left < 0) {
video_box->border_left = -video_box->box_left;
video_box->crop_left = 0;
} else {
video_box->border_left = 0;
video_box->crop_left = video_box->box_left;
}
break;
case PROP_RIGHT:
video_box->box_right = g_value_get_int (value);
if (video_box->box_right < 0) {
video_box->border_right = -video_box->box_right;
video_box->crop_right = 0;
} else {
video_box->border_right = 0;
video_box->crop_right = video_box->box_right;
}
break;
case PROP_TOP:
video_box->box_top = g_value_get_int (value);
if (video_box->box_top < 0) {
video_box->border_top = -video_box->box_top;
video_box->crop_top = 0;
} else {
video_box->border_top = 0;
video_box->crop_top = video_box->box_top;
}
break;
case PROP_BOTTOM:
video_box->box_bottom = g_value_get_int (value);
if (video_box->box_bottom < 0) {
video_box->border_bottom = -video_box->box_bottom;
video_box->crop_bottom = 0;
} else {
video_box->border_bottom = 0;
video_box->crop_bottom = video_box->box_bottom;
}
break;
case PROP_FILL_TYPE:
video_box->fill_type = g_value_get_enum (value);
break;
case PROP_ALPHA:
video_box->alpha = g_value_get_double (value);
break;
case PROP_BORDER_ALPHA:
video_box->border_alpha = g_value_get_double (value);
break;
case PROP_AUTOCROP:
video_box->autocrop = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
gst_video_box_recalc_transform (video_box);
GST_DEBUG_OBJECT (video_box, "Calling reconfigure");
gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM_CAST (video_box));
g_mutex_unlock (&video_box->mutex);
}
static void
gst_video_box_autocrop (GstVideoBox * video_box)
{
gint crop_w = video_box->in_width - video_box->out_width;
gint crop_h = video_box->in_height - video_box->out_height;
video_box->box_left = crop_w / 2;
if (video_box->box_left < 0) {
video_box->border_left = -video_box->box_left;
video_box->crop_left = 0;
} else {
video_box->border_left = 0;
video_box->crop_left = video_box->box_left;
}
/* Round down/up for odd width differences */
if (crop_w < 0)
crop_w -= 1;
else
crop_w += 1;
video_box->box_right = crop_w / 2;
if (video_box->box_right < 0) {
video_box->border_right = -video_box->box_right;
video_box->crop_right = 0;
} else {
video_box->border_right = 0;
video_box->crop_right = video_box->box_right;
}
video_box->box_top = crop_h / 2;
if (video_box->box_top < 0) {
video_box->border_top = -video_box->box_top;
video_box->crop_top = 0;
} else {
video_box->border_top = 0;
video_box->crop_top = video_box->box_top;
}
/* Round down/up for odd height differences */
if (crop_h < 0)
crop_h -= 1;
else
crop_h += 1;
video_box->box_bottom = crop_h / 2;
if (video_box->box_bottom < 0) {
video_box->border_bottom = -video_box->box_bottom;
video_box->crop_bottom = 0;
} else {
video_box->border_bottom = 0;
video_box->crop_bottom = video_box->box_bottom;
}
}
static void
gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstVideoBox *video_box = GST_VIDEO_BOX (object);
switch (prop_id) {
case PROP_LEFT:
g_value_set_int (value, video_box->box_left);
break;
case PROP_RIGHT:
g_value_set_int (value, video_box->box_right);
break;
case PROP_TOP:
g_value_set_int (value, video_box->box_top);
break;
case PROP_BOTTOM:
g_value_set_int (value, video_box->box_bottom);
break;
case PROP_FILL_TYPE:
g_value_set_enum (value, video_box->fill_type);
break;
case PROP_ALPHA:
g_value_set_double (value, video_box->alpha);
break;
case PROP_BORDER_ALPHA:
g_value_set_double (value, video_box->border_alpha);
break;
case PROP_AUTOCROP:
g_value_set_boolean (value, video_box->autocrop);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static inline gint
gst_video_box_transform_dimension (gint val, gint delta)
{
gint64 new_val = (gint64) val + (gint64) delta;
new_val = CLAMP (new_val, 1, G_MAXINT);
return (gint) new_val;
}
static gboolean
gst_video_box_transform_dimension_value (const GValue * src_val,
gint delta, GValue * dest_val)
{
gboolean ret = TRUE;
g_value_init (dest_val, G_VALUE_TYPE (src_val));
if (G_VALUE_HOLDS_INT (src_val)) {
gint ival = g_value_get_int (src_val);
ival = gst_video_box_transform_dimension (ival, delta);
g_value_set_int (dest_val, ival);
} else if (GST_VALUE_HOLDS_INT_RANGE (src_val)) {
gint min = gst_value_get_int_range_min (src_val);
gint max = gst_value_get_int_range_max (src_val);
min = gst_video_box_transform_dimension (min, delta);
max = gst_video_box_transform_dimension (max, delta);
if (min >= max) {
ret = FALSE;
g_value_unset (dest_val);
} else {
gst_value_set_int_range (dest_val, min, max);
}
} else if (GST_VALUE_HOLDS_LIST (src_val)) {
gint i;
for (i = 0; i < gst_value_list_get_size (src_val); ++i) {
const GValue *list_val;
GValue newval = { 0, };
list_val = gst_value_list_get_value (src_val, i);
if (gst_video_box_transform_dimension_value (list_val, delta, &newval))
gst_value_list_append_value (dest_val, &newval);
g_value_unset (&newval);
}
if (gst_value_list_get_size (dest_val) == 0) {
g_value_unset (dest_val);
ret = FALSE;
}
} else {
g_value_unset (dest_val);
ret = FALSE;
}
return ret;
}
static GstCaps *
gst_video_box_transform_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * from, GstCaps * filter)
{
GstVideoBox *video_box = GST_VIDEO_BOX (trans);
GstCaps *to, *ret;
GstCaps *templ;
GstStructure *structure;
GstPad *other;
gint i, j;
to = gst_caps_new_empty ();
for (i = 0; i < gst_caps_get_size (from); i++) {
const GValue *fval, *lval;
GValue list = { 0, };
GValue val = { 0, };
gboolean seen_yuv = FALSE, seen_rgb = FALSE;
const gchar *str;
structure = gst_structure_copy (gst_caps_get_structure (from, i));
/* Transform width/height */
if (video_box->autocrop) {
gst_structure_remove_field (structure, "width");
gst_structure_remove_field (structure, "height");
} else {
gint dw = 0, dh = 0;
const GValue *v;
GValue w_val = { 0, };
GValue h_val = { 0, };
/* calculate width and height */
if (direction == GST_PAD_SINK) {
dw -= video_box->box_left;
dw -= video_box->box_right;
} else {
dw += video_box->box_left;
dw += video_box->box_right;
}
if (direction == GST_PAD_SINK) {
dh -= video_box->box_top;
dh -= video_box->box_bottom;
} else {
dh += video_box->box_top;
dh += video_box->box_bottom;
}
v = gst_structure_get_value (structure, "width");
if (!gst_video_box_transform_dimension_value (v, dw, &w_val)) {
GST_WARNING_OBJECT (video_box,
"could not transform width value with dw=%d" ", caps structure=%"
GST_PTR_FORMAT, dw, structure);
goto bail;
}
gst_structure_set_value (structure, "width", &w_val);
v = gst_structure_get_value (structure, "height");
if (!gst_video_box_transform_dimension_value (v, dh, &h_val)) {
g_value_unset (&w_val);
GST_WARNING_OBJECT (video_box,
"could not transform height value with dh=%d" ", caps structure=%"
GST_PTR_FORMAT, dh, structure);
goto bail;
}
gst_structure_set_value (structure, "height", &h_val);
g_value_unset (&w_val);
g_value_unset (&h_val);
}
/* Supported conversions:
* I420->AYUV
* I420->YV12
* YV12->AYUV
* YV12->I420
* AYUV->I420
* AYUV->YV12
* AYUV->xRGB (24bpp, 32bpp, incl. alpha)
* xRGB->xRGB (24bpp, 32bpp, from/to all variants, incl. alpha)
* xRGB->AYUV (24bpp, 32bpp, incl. alpha)
*
* Passthrough only for everything else.
*/
fval = gst_structure_get_value (structure, "format");
if (fval && GST_VALUE_HOLDS_LIST (fval)) {
for (j = 0; j < gst_value_list_get_size (fval); j++) {
lval = gst_value_list_get_value (fval, j);
if ((str = g_value_get_string (lval))) {
if (strcmp (str, "AYUV") == 0) {
seen_yuv = TRUE;
seen_rgb = TRUE;
break;
} else if (strstr (str, "RGB") || strstr (str, "BGR")) {
seen_rgb = TRUE;
} else if (strcmp (str, "I420") == 0 || strcmp (str, "YV12") == 0) {
seen_yuv = TRUE;
}
}
}
} else if (fval && G_VALUE_HOLDS_STRING (fval)) {
if ((str = g_value_get_string (fval))) {
if (strcmp (str, "AYUV") == 0) {
seen_yuv = TRUE;
seen_rgb = TRUE;
} else if (strstr (str, "RGB") || strstr (str, "BGR")) {
seen_rgb = TRUE;
} else if (strcmp (str, "I420") == 0 || strcmp (str, "YV12") == 0) {
seen_yuv = TRUE;
}
}
}
if (seen_yuv || seen_rgb) {
g_value_init (&list, GST_TYPE_LIST);
g_value_init (&val, G_TYPE_STRING);
g_value_set_string (&val, "AYUV");
gst_value_list_append_value (&list, &val);
g_value_unset (&val);
if (seen_yuv) {
g_value_init (&val, G_TYPE_STRING);
g_value_set_string (&val, "I420");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "YV12");
gst_value_list_append_value (&list, &val);
g_value_unset (&val);
}
if (seen_rgb) {
g_value_init (&val, G_TYPE_STRING);
g_value_set_string (&val, "RGBx");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "BGRx");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "xRGB");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "xBGR");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "RGBA");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "BGRA");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "ARGB");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "ABGR");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "RGB");
gst_value_list_append_value (&list, &val);
g_value_reset (&val);
g_value_set_string (&val, "BGR");
gst_value_list_append_value (&list, &val);
g_value_unset (&val);
}
gst_value_list_merge (&val, fval, &list);
gst_structure_set_value (structure, "format", &val);
g_value_unset (&val);
g_value_unset (&list);
}
gst_structure_remove_field (structure, "colorimetry");
gst_structure_remove_field (structure, "chroma-site");
gst_caps_append_structure (to, structure);
}
/* filter against set allowed caps on the pad */
other = (direction == GST_PAD_SINK) ? trans->srcpad : trans->sinkpad;
templ = gst_pad_get_pad_template_caps (other);
ret = gst_caps_intersect (to, templ);
gst_caps_unref (to);
gst_caps_unref (templ);
GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
" to %" GST_PTR_FORMAT, direction, from, ret);
if (ret && filter) {
GstCaps *intersection;
GST_DEBUG_OBJECT (video_box, "Using filter caps %" GST_PTR_FORMAT, filter);
intersection =
gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (ret);
ret = intersection;
GST_DEBUG_OBJECT (video_box, "Intersection %" GST_PTR_FORMAT, ret);
}
return ret;
/* ERRORS */
bail:
{
gst_structure_free (structure);
gst_caps_unref (to);
to = gst_caps_new_empty ();
return to;
}
}
static gboolean
gst_video_box_recalc_transform (GstVideoBox * video_box)
{
gboolean res = TRUE;
/* if we have the same format in and out and we don't need to perform any
* cropping at all, we can just operate in passthrough mode */
if (video_box->in_format == video_box->out_format &&
video_box->box_left == 0 && video_box->box_right == 0 &&
video_box->box_top == 0 && video_box->box_bottom == 0 &&
video_box->in_sdtv == video_box->out_sdtv) {
GST_LOG_OBJECT (video_box, "we are using passthrough");
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
TRUE);
} else {
GST_LOG_OBJECT (video_box, "we are not using passthrough");
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
FALSE);
}
return res;
}
static gboolean
gst_video_box_select_processing_functions (GstVideoBox * video_box)
{
switch (video_box->out_format) {
case GST_VIDEO_FORMAT_AYUV:
video_box->fill = fill_ayuv;
switch (video_box->in_format) {
case GST_VIDEO_FORMAT_AYUV:
video_box->copy = copy_ayuv_ayuv;
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
video_box->copy = copy_i420_ayuv;
break;
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
video_box->copy = copy_rgb32_ayuv;
break;
default:
break;
}
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
video_box->fill = fill_planar_yuv;
switch (video_box->in_format) {
case GST_VIDEO_FORMAT_AYUV:
video_box->copy = copy_ayuv_i420;
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
video_box->copy = copy_i420_i420;
break;
default:
break;
}
break;
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
video_box->fill = (video_box->out_format == GST_VIDEO_FORMAT_BGR
|| video_box->out_format ==
GST_VIDEO_FORMAT_RGB) ? fill_rgb24 : fill_rgb32;
switch (video_box->in_format) {
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
video_box->copy = copy_rgb32;
break;
case GST_VIDEO_FORMAT_AYUV:
video_box->copy = copy_ayuv_rgb32;
default:
break;
}
break;
case GST_VIDEO_FORMAT_GRAY8:
case GST_VIDEO_FORMAT_GRAY16_BE:
case GST_VIDEO_FORMAT_GRAY16_LE:
video_box->fill = fill_gray;
switch (video_box->in_format) {
case GST_VIDEO_FORMAT_GRAY8:
case GST_VIDEO_FORMAT_GRAY16_BE:
case GST_VIDEO_FORMAT_GRAY16_LE:
video_box->copy = copy_packed_simple;
break;
default:
break;
}
break;
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_YVYU:
case GST_VIDEO_FORMAT_UYVY:
video_box->fill = fill_yuy2;
switch (video_box->in_format) {
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_YVYU:
case GST_VIDEO_FORMAT_UYVY:
video_box->copy = copy_yuy2_yuy2;
break;
default:
break;
}
break;
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_Y41B:
video_box->fill = fill_planar_yuv;
switch (video_box->in_format) {
case GST_VIDEO_FORMAT_Y444:
video_box->copy = copy_y444_y444;
break;
case GST_VIDEO_FORMAT_Y42B:
video_box->copy = copy_y42b_y42b;
break;
case GST_VIDEO_FORMAT_Y41B:
video_box->copy = copy_y41b_y41b;
break;
default:
break;
}
break;
default:
break;
}
return video_box->fill != NULL && video_box->copy != NULL;
}
static gboolean
gst_video_box_set_info (GstVideoFilter * vfilter, GstCaps * in,
GstVideoInfo * in_info, GstCaps * out, GstVideoInfo * out_info)
{
GstVideoBox *video_box = GST_VIDEO_BOX (vfilter);
gboolean ret;
g_mutex_lock (&video_box->mutex);
video_box->in_format = GST_VIDEO_INFO_FORMAT (in_info);
video_box->in_width = GST_VIDEO_INFO_WIDTH (in_info);
video_box->in_height = GST_VIDEO_INFO_HEIGHT (in_info);
video_box->out_format = GST_VIDEO_INFO_FORMAT (out_info);
video_box->out_width = GST_VIDEO_INFO_WIDTH (out_info);
video_box->out_height = GST_VIDEO_INFO_HEIGHT (out_info);
video_box->in_sdtv =
in_info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_BT601;
video_box->out_sdtv =
out_info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_BT601;
GST_DEBUG_OBJECT (video_box, "Input w: %d h: %d", video_box->in_width,
video_box->in_height);
GST_DEBUG_OBJECT (video_box, "Output w: %d h: %d", video_box->out_width,
video_box->out_height);
if (video_box->autocrop)
gst_video_box_autocrop (video_box);
/* recalc the transformation strategy */
ret = gst_video_box_recalc_transform (video_box);
if (ret)
ret = gst_video_box_select_processing_functions (video_box);
g_mutex_unlock (&video_box->mutex);
return ret;
}
static gboolean
gst_video_box_src_event (GstBaseTransform * trans, GstEvent * event)
{
GstVideoBox *video_box = GST_VIDEO_BOX (trans);
GstNavigationEventType type;
gdouble pointer_x;
gdouble pointer_y;
GST_OBJECT_LOCK (video_box);
type = gst_navigation_event_get_type (event);
if (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION &&
(video_box->box_left != 0 || video_box->box_top != 0) &&
(type == GST_NAVIGATION_EVENT_MOUSE_MOVE
|| type == GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS
|| type == GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE)) {
if (gst_navigation_event_get_coordinates (event, &pointer_x, &pointer_y)) {
gdouble new_pointer_x, new_pointer_y;
event = gst_event_make_writable (event);
new_pointer_x = pointer_x + video_box->box_left;
new_pointer_y = pointer_y + video_box->box_top;
gst_navigation_event_set_coordinates (event, new_pointer_x,
new_pointer_y);
} else {
GST_WARNING_OBJECT (video_box, "Failed to read navigation event");
}
}
GST_OBJECT_UNLOCK (video_box);
return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
}
static void
gst_video_box_process (GstVideoBox * video_box, GstVideoFrame * in,
GstVideoFrame * out)
{
guint b_alpha = CLAMP (video_box->border_alpha * 256, 0, 255);
guint i_alpha = CLAMP (video_box->alpha * 256, 0, 255);
GstVideoBoxFill fill_type = video_box->fill_type;
gint br, bl, bt, bb, crop_w, crop_h;
crop_h = 0;
crop_w = 0;
br = video_box->box_right;
bl = video_box->box_left;
bt = video_box->box_top;
bb = video_box->box_bottom;
if (br >= 0 && bl >= 0) {
crop_w = video_box->in_width - (br + bl);
} else if (br >= 0 && bl < 0) {
crop_w = video_box->in_width - (br);
} else if (br < 0 && bl >= 0) {
crop_w = video_box->in_width - (bl);
} else if (br < 0 && bl < 0) {
crop_w = video_box->in_width;
}
if (bb >= 0 && bt >= 0) {
crop_h = video_box->in_height - (bb + bt);
} else if (bb >= 0 && bt < 0) {
crop_h = video_box->in_height - (bb);
} else if (bb < 0 && bt >= 0) {
crop_h = video_box->in_height - (bt);
} else if (bb < 0 && bt < 0) {
crop_h = video_box->in_height;
}
GST_DEBUG_OBJECT (video_box, "Borders are: L:%d, R:%d, T:%d, B:%d", bl, br,
bt, bb);
GST_DEBUG_OBJECT (video_box, "Alpha value is: %u (frame) %u (border)",
i_alpha, b_alpha);
if (crop_h < 0 || crop_w < 0) {
video_box->fill (fill_type, b_alpha, out, video_box->out_sdtv);
} else if (bb == 0 && bt == 0 && br == 0 && bl == 0) {
video_box->copy (i_alpha, out, video_box->out_sdtv, 0, 0, in,
video_box->in_sdtv, 0, 0, crop_w, crop_h);
} else {
gint src_x = 0, src_y = 0;
gint dest_x = 0, dest_y = 0;
/* Fill everything if a border should be added somewhere */
if (bt < 0 || bb < 0 || br < 0 || bl < 0)
video_box->fill (fill_type, b_alpha, out, video_box->out_sdtv);
/* Top border */
if (bt < 0) {
dest_y += -bt;
} else {
src_y += bt;
}
/* Left border */
if (bl < 0) {
dest_x += -bl;
} else {
src_x += bl;
}
/* Frame */
video_box->copy (i_alpha, out, video_box->out_sdtv, dest_x, dest_y,
in, video_box->in_sdtv, src_x, src_y, crop_w, crop_h);
}
GST_LOG_OBJECT (video_box, "image created");
}
static void
gst_video_box_before_transform (GstBaseTransform * trans, GstBuffer * in)
{
GstVideoBox *video_box = GST_VIDEO_BOX (trans);
GstClockTime timestamp, stream_time;
timestamp = GST_BUFFER_TIMESTAMP (in);
stream_time =
gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
GST_DEBUG_OBJECT (video_box, "sync to %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
if (GST_CLOCK_TIME_IS_VALID (stream_time))
gst_object_sync_values (GST_OBJECT (video_box), stream_time);
}
static GstFlowReturn
gst_video_box_transform_frame (GstVideoFilter * vfilter,
GstVideoFrame * in_frame, GstVideoFrame * out_frame)
{
GstVideoBox *video_box = GST_VIDEO_BOX (vfilter);
g_mutex_lock (&video_box->mutex);
gst_video_box_process (video_box, in_frame, out_frame);
g_mutex_unlock (&video_box->mutex);
return GST_FLOW_OK;
}
/* FIXME: 0.11 merge with videocrop plugin */
static gboolean
plugin_init (GstPlugin * plugin)
{
return GST_ELEMENT_REGISTER (videobox, plugin);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
videobox,
"resizes a video by adding borders or cropping",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)