gstreamer/gst/videobox/gstvideobox.c
2010-03-26 13:45:46 +01:00

1432 lines
42 KiB
C

/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-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 overriden if the caps change,
* but nothing stops you from doing so.
*
* Sample pipeline:
* |[
* gst-launch videotestsrc ! videobox autocrop=true ! \
* "video/x-raw-yuv, width=600, height=400" ! ffmpegcolorspace ! ximagesink
* ]|
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstvideobox.h"
#include <math.h>
#include <liboil/liboil.h>
#include <string.h>
#include <gst/controller/gstcontroller.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 };
static const guint8 yuv_sdtv_colors_U[VIDEO_BOX_FILL_LAST] = { 128, 54, 240 };
static const guint8 yuv_sdtv_colors_V[VIDEO_BOX_FILL_LAST] = { 128, 34, 110 };
static const guint8 yuv_hdtv_colors_Y[VIDEO_BOX_FILL_LAST] = { 16, 173, 32 };
static const guint8 yuv_hdtv_colors_U[VIDEO_BOX_FILL_LAST] = { 128, 42, 240 };
static const guint8 yuv_hdtv_colors_V[VIDEO_BOX_FILL_LAST] = { 128, 26, 118 };
static const guint8 rgb_colors_R[VIDEO_BOX_FILL_LAST] = { 0, 255, 0 };
static const guint8 rgb_colors_G[VIDEO_BOX_FILL_LAST] = { 0, 0, 0 };
static const guint8 rgb_colors_B[VIDEO_BOX_FILL_LAST] = { 0, 255, 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,
};
#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, guint8 * dest,
gboolean sdtv, gint width, gint height)
{
guint32 empty_pixel;
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]);
oil_splat_u32_ns ((guint32 *) dest, &empty_pixel, width * height);
}
static void
copy_ayuv_ayuv (guint i_alpha, guint8 * dest, gboolean dest_sdtv,
gint dest_width, gint dest_height, gint dest_x, gint dest_y,
const guint8 * src, gboolean src_sdtv, gint src_width, gint src_height,
gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
gint src_stride = 4 * src_width;
gint dest_stride = 4 * dest_width;
dest = dest + dest_y * dest_width * 4 + dest_x * 4;
src = src + src_y * src_width * 4 + 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) {
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) {
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, guint8 * dest, gboolean dest_sdtv,
gint dest_width, gint dest_height, gint dest_x, gint dest_y,
const guint8 * src, gboolean src_sdtv, gint src_width, gint src_height,
gint src_x, gint src_y, gint w, gint h)
{
gint i, j;
guint8 *destY, *destU, *destV;
gint dest_strideY, dest_strideUV;
gint widthY, widthUV;
gint hY, hUV;
dest_strideY =
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, dest_width);
dest_strideUV =
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, dest_width);
destY =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
dest_width, dest_height);
destU =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
dest_width, dest_height);
destV =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
dest_width, dest_height);
destY = destY + dest_y * dest_strideY + dest_x;
destU = destU + (dest_y / 2) * dest_strideUV + dest_x / 2;
destV = destV + (dest_y / 2) * dest_strideUV + dest_x / 2;
src = src + src_y * src_width * 4 + src_x * 4;
widthY = w;
widthUV = w / 2;
hY = h;
hUV = h / 2;
if (src_sdtv != dest_sdtv) {
gint matrix[12];
gint y1, y2, y3, y4;
gint u1, u2, u3, u4;
gint v1, v2, v3, v4;
guint8 *destY2 = destY + dest_strideY;
const guint8 *src2 = src + src_width * 4;
dest_strideY *= 2;
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 < hUV; i++) {
if (i * 2 == hY) {
destY2 = destY;
}
for (j = 0; j < widthUV; j++) {
y1 = src[8 * j + 1];
u1 = src[8 * j + 2];
v1 = src[8 * j + 3];
y2 = src[8 * j + 5];
u2 = src[8 * j + 6];
v2 = src[8 * j + 7];
if (j * 2 < widthY) {
y3 = src2[8 * j + 1];
u3 = src2[8 * j + 2];
v3 = src2[8 * j + 3];
y4 = src2[8 * j + 5];
u4 = src2[8 * j + 6];
v4 = src2[8 * j + 7];
} else {
y3 = y1;
u3 = u1;
v3 = v1;
y4 = y2;
u4 = u2;
v4 = v2;
}
y1 = APPLY_MATRIX (matrix, 0, y1, u1, v1);
u1 = APPLY_MATRIX (matrix, 1, y1, u1, v1);
v1 = APPLY_MATRIX (matrix, 2, y1, u1, v1);
y3 = APPLY_MATRIX (matrix, 0, y3, u3, v3);
u3 = APPLY_MATRIX (matrix, 1, y3, u3, v3);
v3 = APPLY_MATRIX (matrix, 2, y3, u3, v3);
if (j * 2 < widthY) {
y2 = APPLY_MATRIX (matrix, 0, y2, u2, v2);
u2 = APPLY_MATRIX (matrix, 1, y2, u2, v2);
v2 = APPLY_MATRIX (matrix, 2, y2, u2, v2);
y4 = APPLY_MATRIX (matrix, 0, y4, u4, v4);
u4 = APPLY_MATRIX (matrix, 1, y4, u4, v4);
v4 = APPLY_MATRIX (matrix, 2, y4, u4, v4);
} else {
u2 = u1;
v2 = v1;
u4 = u3;
v4 = v3;
}
destY[2 * j] = y1;
destY2[2 * j] = y3;
if (j * 2 < widthY) {
destY[2 * j + 1] = y2;
destY2[2 * j + 1] = y4;
}
destU[j] = (u1 + u2 + u3 + u4) / 4;
destV[j] = (v1 + v2 + v3 + v4) / 4;
}
src += src_width * 8;
destY += dest_strideY;
src2 += src_width * 8;
destY2 += dest_strideY;
destU += dest_strideUV;
destV += dest_strideUV;
}
} else {
gint y1, y2, y3, y4;
gint u1, u2, u3, u4;
gint v1, v2, v3, v4;
guint8 *destY2 = destY + dest_strideY;
const guint8 *src2 = src + src_width * 4;
dest_strideY *= 2;
for (i = 0; i < hUV; i++) {
if (i * 2 == hY) {
destY2 = destY;
}
for (j = 0; j < widthUV; j++) {
y1 = src[8 * j + 1];
u1 = src[8 * j + 2];
v1 = src[8 * j + 3];
y2 = src[8 * j + 5];
u2 = src[8 * j + 6];
v2 = src[8 * j + 7];
if (j * 2 < widthY) {
y3 = src2[8 * j + 1];
u3 = src2[8 * j + 2];
v3 = src2[8 * j + 3];
y4 = src2[8 * j + 5];
u4 = src2[8 * j + 6];
v4 = src2[8 * j + 7];
} else {
y3 = y1;
u3 = u1;
v3 = v1;
y4 = y2;
u4 = u2;
v4 = v2;
}
destY[2 * j] = y1;
destY2[2 * j] = y3;
if (j * 2 < widthY) {
destY[2 * j + 1] = y2;
destY2[2 * j + 1] = y4;
}
destU[j] = (u1 + u2 + u3 + u4) / 4;
destV[j] = (v1 + v2 + v3 + v4) / 4;
}
src += src_width * 8;
destY += dest_strideY;
src2 += src_width * 8;
destY2 += dest_strideY;
destU += dest_strideUV;
destV += dest_strideUV;
}
}
}
static void
fill_i420 (GstVideoBoxFill fill_type, guint b_alpha, guint8 * dest,
gboolean sdtv, gint width, gint height)
{
guint8 empty_pixel[3];
guint8 *destY, *destU, *destV;
gint strideY, strideUV;
gint heightY, heightUV;
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_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, width);
strideUV = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, width);
destY =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
width, height);
destU =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
width, height);
destV =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
width, height);
heightY =
gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 0, height);
heightUV =
gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 1, height);
oil_splat_u8_ns (destY, &empty_pixel[0], strideY * heightY);
oil_splat_u8_ns (destU, &empty_pixel[1], strideUV * heightUV);
oil_splat_u8_ns (destV, &empty_pixel[2], strideUV * heightUV);
}
static void
copy_i420_i420 (guint i_alpha, guint8 * dest, gboolean dest_sdtv,
gint dest_width, gint dest_height, gint dest_x, gint dest_y,
const guint8 * src, gboolean src_sdtv, gint src_width, gint src_height,
gint src_x, gint src_y, gint w, gint h)
{
gint i;
guint8 *destY, *destU, *destV;
const guint8 *srcY, *srcU, *srcV;
gint dest_strideY, dest_strideUV;
gint src_strideY, src_strideUV;
gint widthY, widthUV;
gint hY, hUV;
dest_strideY =
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, dest_width);
dest_strideUV =
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, dest_width);
src_strideY =
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, src_width);
src_strideUV =
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, src_width);
destY =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
dest_width, dest_height);
destU =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
dest_width, dest_height);
destV =
dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
dest_width, dest_height);
srcY =
src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
src_width, src_height);
srcU =
src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
src_width, src_height);
srcV =
src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
src_width, src_height);
destY = destY + dest_y * dest_strideY + dest_x;
destU = destU + (dest_y / 2) * dest_strideUV + dest_x / 2;
destV = destV + (dest_y / 2) * dest_strideUV + dest_x / 2;
srcY = srcY + src_y * src_strideY + src_x;
srcU = srcU + (src_y / 2) * src_strideUV + src_x / 2;
srcV = srcV + (src_y / 2) * src_strideUV + src_x / 2;
widthY = w;
widthUV = (w + 1) / 2;
hY = h;
hUV = (h + 1) / 2;
if (src_sdtv != dest_sdtv) {
gint matrix[12];
gint y1, y2, y3, y4;
gint u1, u2, u3, u4;
gint v1, v2, v3, v4;
gint j;
guint8 *destY2 = destY + dest_strideY;
const guint8 *srcY2 = srcY + src_strideY;
dest_strideY *= 2;
src_strideY *= 2;
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 < hUV; i++) {
if (i * 2 == hY) {
destY2 = destY;
srcY2 = srcY;
}
for (j = 0; j < widthUV; j++) {
y1 = srcY[2 * j];
y2 = srcY[2 * j + 1];
y3 = srcY2[2 * j];
y4 = srcY2[2 * j + 1];
u1 = u2 = u3 = u4 = srcU[j];
v1 = v2 = v3 = v4 = srcV[j];
y1 = APPLY_MATRIX (matrix, 0, y1, u1, v1);
u1 = APPLY_MATRIX (matrix, 1, y1, u1, v1);
v1 = APPLY_MATRIX (matrix, 2, y1, u1, v1);
y2 = APPLY_MATRIX (matrix, 0, y2, u2, v2);
u2 = APPLY_MATRIX (matrix, 1, y2, u2, v2);
v2 = APPLY_MATRIX (matrix, 2, y2, u2, v2);
y3 = APPLY_MATRIX (matrix, 0, y3, u3, v3);
u3 = APPLY_MATRIX (matrix, 1, y3, u3, v3);
v3 = APPLY_MATRIX (matrix, 2, y3, u3, v3);
y4 = APPLY_MATRIX (matrix, 0, y4, u4, v4);
u4 = APPLY_MATRIX (matrix, 1, y4, u4, v4);
v4 = APPLY_MATRIX (matrix, 2, y4, u4, v4);
destY[2 * j] = y1;
destY[2 * j + 1] = y2;
destY2[2 * j] = y3;
destY2[2 * j + 1] = y4;
destU[j] = (u1 + u2 + u3 + u4) / 4;
destV[j] = (v1 + v2 + v3 + v4) / 4;
}
destY += dest_strideY;
srcY += src_strideY;
destY2 += dest_strideY;
srcY2 += src_strideY;
destU += dest_strideUV;
destV += dest_strideUV;
srcU += src_strideUV;
srcV += src_strideUV;
}
} else {
for (i = 0; i < hY; i++) {
oil_copy_u8 (destY, srcY, widthY);
destY += dest_strideY;
srcY += src_strideY;
}
for (i = 0; i < hUV; i++) {
oil_copy_u8 (destU, srcU, widthUV);
oil_copy_u8 (destV, srcV, widthUV);
destU += dest_strideUV;
destV += dest_strideUV;
srcU += src_strideUV;
srcV += src_strideUV;
}
}
}
static void
copy_i420_ayuv (guint i_alpha, guint8 * dest, gboolean dest_sdtv,
gint dest_width, gint dest_height, gint dest_x, gint dest_y,
const guint8 * src, gboolean src_sdtv, gint src_width, gint src_height,
gint src_x, gint src_y, gint w, gint h)
{
gint i;
const guint8 *srcY, *srcU, *srcV;
gint src_strideY, src_strideUV;
gint widthY, widthUV;
gint hY, hUV;
src_strideY =
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, src_width);
src_strideUV =
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, src_width);
srcY =
src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
src_width, src_height);
srcU =
src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
src_width, src_height);
srcV =
src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
src_width, src_height);
dest = dest + dest_y * dest_width * 4 + dest_x * 4;
srcY = srcY + src_y * src_strideY + src_x;
srcU = srcU + (src_y / 2) * src_strideUV + src_x / 2;
srcV = srcV + (src_y / 2) * src_strideUV + src_x / 2;
widthY = w;
widthUV = (w + 1) / 2;
hY = h;
hUV = (h + 1) / 2;
if (src_sdtv != dest_sdtv) {
gint matrix[12];
gint y1, y2, y3, y4;
gint u1, u2, u3, u4;
gint v1, v2, v3, v4;
gint j;
guint8 *dest2;
const guint8 *srcY2 = srcY + src_strideY;
dest2 = dest + dest_width * 4;
src_strideY *= 2;
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 < hUV; i++) {
if (i * 2 == hY) {
srcY2 = srcY;
}
for (j = 0; j < widthUV; j++) {
y1 = srcY[2 * j];
y3 = srcY2[2 * j];
y2 = (j * 2 < widthY) ? srcY[2 * j + 1] : y1;
y4 = (j * 2 < widthY) ? srcY2[2 * j + 1] : y3;
u1 = u2 = u3 = u4 = srcU[j];
v1 = v2 = v3 = v4 = srcV[j];
y1 = APPLY_MATRIX (matrix, 0, y1, u1, v1);
u1 = APPLY_MATRIX (matrix, 1, y1, u1, v1);
v1 = APPLY_MATRIX (matrix, 2, y1, u1, v1);
y3 = APPLY_MATRIX (matrix, 0, y3, u3, v3);
u3 = APPLY_MATRIX (matrix, 1, y3, u3, v3);
v3 = APPLY_MATRIX (matrix, 2, y3, u3, v3);
if (j * 2 < widthY) {
y2 = APPLY_MATRIX (matrix, 0, y2, u2, v2);
u2 = APPLY_MATRIX (matrix, 1, y2, u2, v2);
v2 = APPLY_MATRIX (matrix, 2, y2, u2, v2);
y4 = APPLY_MATRIX (matrix, 0, y4, u4, v4);
u4 = APPLY_MATRIX (matrix, 1, y4, u4, v4);
v4 = APPLY_MATRIX (matrix, 2, y4, u4, v4);
}
dest[8 * j] = i_alpha;
dest[8 * j + 1] = y1;
dest[8 * j + 2] = u1;
dest[8 * j + 3] = v1;
dest2[8 * j] = i_alpha;
dest2[8 * j + 1] = y3;
dest2[8 * j + 2] = u3;
dest2[8 * j + 3] = v3;
if (j * 2 < widthY) {
dest[8 * j + 4] = i_alpha;
dest[8 * j + 5] = y2;
dest[8 * j + 6] = u2;
dest[8 * j + 7] = v2;
dest2[8 * j + 4] = i_alpha;
dest2[8 * j + 5] = y4;
dest2[8 * j + 6] = u4;
dest2[8 * j + 7] = v4;
}
}
dest += dest_width * 8;
srcY += src_strideY;
dest2 += dest_width * 8;
srcY2 += src_strideY;
srcU += src_strideUV;
srcV += src_strideUV;
}
} else {
gint y1, y2, y3, y4;
gint u1, u2, u3, u4;
gint v1, v2, v3, v4;
gint j;
guint8 *dest2;
const guint8 *srcY2 = srcY + src_strideY;
dest2 = dest + dest_width * 4;
src_strideY *= 2;
for (i = 0; i < hUV; i++) {
if (i * 2 == hY) {
srcY2 = srcY;
}
for (j = 0; j < widthUV; j++) {
y1 = srcY[2 * j];
y3 = srcY2[2 * j];
y2 = (j * 2 < widthY) ? srcY[2 * j + 1] : y1;
y4 = (j * 2 < widthY) ? srcY2[2 * j + 1] : y3;
u1 = u2 = u3 = u4 = srcU[j];
v1 = v2 = v3 = v4 = srcV[j];
dest[8 * j] = i_alpha;
dest[8 * j + 1] = y1;
dest[8 * j + 2] = u1;
dest[8 * j + 3] = v1;
dest2[8 * j] = i_alpha;
dest2[8 * j + 1] = y3;
dest2[8 * j + 2] = u3;
dest2[8 * j + 3] = v3;
if (j * 2 < widthY) {
dest[8 * j + 4] = i_alpha;
dest[8 * j + 5] = y2;
dest[8 * j + 6] = u2;
dest[8 * j + 7] = v2;
dest2[8 * j + 4] = i_alpha;
dest2[8 * j + 5] = y4;
dest2[8 * j + 6] = u4;
dest2[8 * j + 7] = v4;
}
}
dest += dest_width * 8;
srcY += src_strideY;
dest2 += dest_width * 8;
srcY2 += src_strideY;
srcU += src_strideUV;
srcV += src_strideUV;
}
}
}
#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_YUV ("AYUV") ";"
GST_VIDEO_CAPS_YUV ("I420"))
);
static GstStaticPadTemplate gst_video_box_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
GST_VIDEO_CAPS_YUV ("I420"))
);
GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
GST_TYPE_BASE_TRANSFORM);
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);
static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
GstCaps * in, GstCaps * out);
static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
GstCaps * caps, guint * size);
static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
GstBuffer * in, GstBuffer * out);
static void gst_video_box_fixate_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
#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, "Colorkey green", "green"},
{VIDEO_BOX_FILL_BLUE, "Colorkey blue", "blue"},
{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_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_set_details_simple (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_pad_template (element_class,
gst_static_pad_template_get (&gst_video_box_sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_video_box_src_template));
}
static void
gst_video_box_finalize (GObject * object)
{
GstVideoBox *video_box = GST_VIDEO_BOX (object);
if (video_box->mutex) {
g_mutex_free (video_box->mutex);
video_box->mutex = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_video_box_class_init (GstVideoBoxClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstBaseTransformClass *trans_class = (GstBaseTransformClass *) 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.
*
* Since: 0.10.16
**/
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->transform = GST_DEBUG_FUNCPTR (gst_video_box_transform);
trans_class->transform_caps =
GST_DEBUG_FUNCPTR (gst_video_box_transform_caps);
trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_box_set_caps);
trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_video_box_get_unit_size);
trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_box_fixate_caps);
}
static void
gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
{
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;
video_box->mutex = g_mutex_new ();
}
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 (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 GstCaps *
gst_video_box_transform_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * from)
{
GstVideoBox *video_box = GST_VIDEO_BOX (trans);
GstCaps *to, *ret;
const GstCaps *templ;
GstStructure *structure;
GstPad *other;
gint width, height;
to = gst_caps_copy (from);
structure = gst_caps_get_structure (to, 0);
/* get rid of format */
gst_structure_remove_field (structure, "format");
gst_structure_remove_field (structure, "color-matrix");
gst_structure_remove_field (structure, "chroma-site");
/* otherwise caps nego will fail: */
if (video_box->autocrop) {
gst_structure_remove_field (structure, "width");
gst_structure_remove_field (structure, "height");
} else {
/* calculate width and height */
if (gst_structure_get_int (structure, "width", &width)) {
if (direction == GST_PAD_SINK) {
width -= video_box->box_left;
width -= video_box->box_right;
} else {
width += video_box->box_left;
width += video_box->box_right;
}
if (width <= 0)
width = 1;
GST_DEBUG_OBJECT (trans, "New caps width: %d", width);
gst_structure_set (structure, "width", G_TYPE_INT, width, NULL);
}
if (gst_structure_get_int (structure, "height", &height)) {
if (direction == GST_PAD_SINK) {
height -= video_box->box_top;
height -= video_box->box_bottom;
} else {
height += video_box->box_top;
height += video_box->box_bottom;
}
if (height <= 0)
height = 1;
GST_DEBUG_OBJECT (trans, "New caps height: %d", height);
gst_structure_set (structure, "height", G_TYPE_INT, height, NULL);
}
}
/* 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_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
" to %" GST_PTR_FORMAT, direction, from, ret);
return ret;
}
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:
video_box->copy = copy_i420_ayuv;
break;
default:
break;
}
break;
case GST_VIDEO_FORMAT_I420:
video_box->fill = fill_i420;
switch (video_box->in_format) {
case GST_VIDEO_FORMAT_AYUV:
video_box->copy = copy_ayuv_i420;
break;
case GST_VIDEO_FORMAT_I420:
video_box->copy = copy_i420_i420;
break;
default:
break;
}
break;
default:
break;
}
return video_box->fill != NULL && video_box->copy != NULL;
}
static gboolean
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
{
GstVideoBox *video_box = GST_VIDEO_BOX (trans);
gboolean ret;
const gchar *matrix;
g_mutex_lock (video_box->mutex);
ret =
gst_video_format_parse_caps (in, &video_box->in_format,
&video_box->in_width, &video_box->in_height);
ret &=
gst_video_format_parse_caps (out, &video_box->out_format,
&video_box->out_width, &video_box->out_height);
matrix = gst_video_parse_caps_color_matrix (in);
video_box->in_sdtv = matrix ? g_str_equal (matrix, "sdtv") : TRUE;
matrix = gst_video_parse_caps_color_matrix (out);
video_box->out_sdtv = matrix ? g_str_equal (matrix, "sdtv") : TRUE;
/* something wrong getting the caps */
if (!ret)
goto no_caps;
GST_DEBUG_OBJECT (trans, "Input w: %d h: %d", video_box->in_width,
video_box->in_height);
GST_DEBUG_OBJECT (trans, "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;
/* ERRORS */
no_caps:
{
GST_DEBUG_OBJECT (video_box,
"Invalid caps: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT, in, out);
g_mutex_unlock (video_box->mutex);
return FALSE;
}
}
static gboolean
gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
guint * size)
{
GstVideoBox *video_box = GST_VIDEO_BOX (trans);
GstVideoFormat format;
gint width, height;
gboolean ret;
g_assert (size);
ret = gst_video_format_parse_caps (caps, &format, &width, &height);
if (!ret) {
GST_ERROR_OBJECT (video_box, "Invalid caps: %" GST_PTR_FORMAT, caps);
return FALSE;
}
*size = gst_video_format_get_size (format, width, height);
GST_LOG_OBJECT (video_box, "Returning from _unit_size %d", *size);
return TRUE;
}
static void
gst_video_box_fixate_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
{
gint width, height;
GstStructure *s;
gboolean ret;
ret = gst_video_format_parse_caps (caps, NULL, &width, &height);
if (!ret)
return;
s = gst_caps_get_structure (othercaps, 0);
gst_structure_fixate_field_nearest_int (s, "width", width);
gst_structure_fixate_field_nearest_int (s, "height", height);
}
static void
gst_video_box_process (GstVideoBox * video_box, const guint8 * src,
guint8 * dest)
{
guint b_alpha = CLAMP ((guint) video_box->border_alpha * 256, 0, 256);
guint i_alpha = CLAMP ((guint) video_box->alpha * 256, 0, 256);
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, dest, video_box->out_sdtv,
video_box->out_width, video_box->out_height);
} else if (bb == 0 && bt == 0 && br == 0 && bl == 0) {
video_box->copy (i_alpha, dest, video_box->out_sdtv, video_box->out_width,
video_box->out_height, 0, 0, src, video_box->in_sdtv,
video_box->in_width, video_box->in_height, 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, dest, video_box->out_sdtv,
video_box->out_width, video_box->out_height);
/* 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, dest, video_box->out_sdtv, video_box->out_width,
video_box->out_height, dest_x, dest_y, src, video_box->in_sdtv,
video_box->in_width, video_box->in_height, src_x, src_y, crop_w,
crop_h);
}
GST_LOG_OBJECT (video_box, "image created");
}
static GstFlowReturn
gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
GstBuffer * out)
{
GstVideoBox *video_box = GST_VIDEO_BOX (trans);
const guint8 *indata;
guint8 *outdata;
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 (G_OBJECT (video_box), stream_time);
indata = GST_BUFFER_DATA (in);
outdata = GST_BUFFER_DATA (out);
g_mutex_lock (video_box->mutex);
gst_video_box_process (video_box, indata, outdata);
g_mutex_unlock (video_box->mutex);
return GST_FLOW_OK;
}
/* FIXME: 0.11 merge with videocrop plugin */
static gboolean
plugin_init (GstPlugin * plugin)
{
oil_init ();
gst_controller_init (NULL, NULL);
GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
"Resizes a video by adding borders or cropping");
return gst_element_register (plugin, "videobox", GST_RANK_NONE,
GST_TYPE_VIDEO_BOX);
}
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)