2 * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
4 * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
22 * SECTION:element-videobox
23 * @see_also: #GstVideoCrop
25 * This plugin crops or enlarges the image. It takes 4 values as input, a
26 * top, bottom, left and right offset. Positive values will crop that much
27 * pixels from the respective border of the image, negative values will add
28 * that much pixels. When pixels are added, you can specify their color.
29 * Some predefined colors are usable with an enum property.
31 * The plugin is alpha channel aware and will try to negotiate with a format
32 * that supports alpha channels first. When alpha channel is active two
33 * other properties, alpha and border_alpha can be used to set the alpha
34 * values of the inner picture and the border respectively. an alpha value of
35 * 0.0 means total transparency, 1.0 is opaque.
37 * The videobox plugin has many uses such as doing a mosaic of pictures,
38 * letterboxing video, cutting out pieces of video, picture in picture, etc..
40 * Setting autocrop to true changes the behavior of the plugin so that
41 * caps determine crop properties rather than the other way around: given
42 * input and output dimensions, the crop values are selected so that the
43 * smaller frame is effectively centered in the larger frame. This
44 * involves either cropping or padding.
46 * If you use autocrop there is little point in setting the other
47 * properties manually because they will be overriden if the caps change,
48 * but nothing stops you from doing so.
52 * gst-launch videotestsrc ! videobox autocrop=true ! \
53 * "video/x-raw-yuv, width=600, height=400" ! ffmpegcolorspace ! ximagesink
61 #include "gstvideobox.h"
64 #include <liboil/liboil.h>
67 #include <gst/controller/gstcontroller.h>
69 GST_DEBUG_CATEGORY_STATIC (videobox_debug);
70 #define GST_CAT_DEFAULT videobox_debug
72 /* From videotestsrc.c */
73 static const guint8 yuv_sdtv_colors_Y[VIDEO_BOX_FILL_LAST] =
74 { 16, 145, 41, 81, 210, 235 };
75 static const guint8 yuv_sdtv_colors_U[VIDEO_BOX_FILL_LAST] =
76 { 128, 54, 240, 90, 16, 128 };
77 static const guint8 yuv_sdtv_colors_V[VIDEO_BOX_FILL_LAST] =
78 { 128, 34, 110, 240, 146, 128 };
80 static const guint8 yuv_hdtv_colors_Y[VIDEO_BOX_FILL_LAST] =
81 { 16, 173, 32, 63, 219, 235 };
82 static const guint8 yuv_hdtv_colors_U[VIDEO_BOX_FILL_LAST] =
83 { 128, 42, 240, 102, 16, 128 };
84 static const guint8 yuv_hdtv_colors_V[VIDEO_BOX_FILL_LAST] =
85 { 128, 26, 118, 240, 138, 128 };
87 static const guint8 rgb_colors_R[VIDEO_BOX_FILL_LAST] =
88 { 0, 0, 0, 255, 255, 255 };
89 static const guint8 rgb_colors_G[VIDEO_BOX_FILL_LAST] =
90 { 0, 255, 0, 0, 255, 255 };
91 static const guint8 rgb_colors_B[VIDEO_BOX_FILL_LAST] =
92 { 0, 0, 255, 0, 0, 255 };
94 /* Generated by -bad/ext/cog/generate_tables */
95 static const int cog_ycbcr_to_rgb_matrix_8bit_hdtv[] = {
97 298, -55, -136, 19681,
101 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
103 298, -100, -208, 34707,
107 static const gint cog_rgb_to_ycbcr_matrix_8bit_hdtv[] = {
109 -26, -87, 112, 32768,
110 112, -102, -10, 32768,
113 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
115 -38, -74, 112, 32768,
116 112, -94, -18, 32768,
119 static const gint cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit[] = {
120 256, -30, -53, 10600,
125 static const gint cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit[] = {
131 static const gint cog_identity_matrix_8bit[] = {
137 #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)
140 fill_ayuv (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
141 guint8 * dest, gboolean sdtv, gint width, gint height)
145 b_alpha = CLAMP (b_alpha, 0, 255);
148 empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
149 (yuv_sdtv_colors_Y[fill_type] << 16) |
150 (yuv_sdtv_colors_U[fill_type] << 8) | yuv_sdtv_colors_V[fill_type]);
152 empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
153 (yuv_hdtv_colors_Y[fill_type] << 16) |
154 (yuv_hdtv_colors_U[fill_type] << 8) | yuv_hdtv_colors_V[fill_type]);
156 oil_splat_u32_ns ((guint32 *) dest, &empty_pixel, width * height);
160 copy_ayuv_ayuv (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
161 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
162 gint dest_y, GstVideoFormat src_format, const guint8 * src,
163 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
167 gint src_stride = 4 * src_width;
168 gint dest_stride = 4 * dest_width;
170 dest = dest + dest_y * dest_width * 4 + dest_x * 4;
171 src = src + src_y * src_width * 4 + src_x * 4;
175 if (dest_sdtv != src_sdtv) {
180 dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
181 cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
183 for (i = 0; i < h; i++) {
184 for (j = 0; j < w; j += 4) {
185 dest[j] = (src[j] * i_alpha) >> 8;
189 dest[j + 1] = APPLY_MATRIX (matrix, 0, y, u, v);
190 dest[j + 2] = APPLY_MATRIX (matrix, 1, y, u, v);
191 dest[j + 3] = APPLY_MATRIX (matrix, 2, y, u, v);
197 for (i = 0; i < h; i++) {
198 for (j = 0; j < w; j += 4) {
199 dest[j] = (src[j] * i_alpha) >> 8;
200 dest[j + 1] = src[j + 1];
201 dest[j + 2] = src[j + 2];
202 dest[j + 3] = src[j + 3];
211 copy_ayuv_i420 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
212 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
213 gint dest_y, GstVideoFormat src_format, const guint8 * src,
214 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
218 guint8 *destY, *destY2, *destU, *destV;
219 gint dest_strideY, dest_strideUV;
228 dest_strideY = gst_video_format_get_row_stride (dest_format, 0, dest_width);
229 dest_strideUV = gst_video_format_get_row_stride (dest_format, 1, dest_width);
231 src_stride = 4 * src_width;
234 dest + gst_video_format_get_component_offset (dest_format, 0,
235 dest_width, dest_height);
237 dest + gst_video_format_get_component_offset (dest_format, 1,
238 dest_width, dest_height);
240 dest + gst_video_format_get_component_offset (dest_format, 2,
241 dest_width, dest_height);
243 destY = destY + dest_y * dest_strideY + dest_x;
244 destY2 = (dest_y < dest_height) ? destY + dest_strideY : destY;
245 destU = destU + (dest_y / 2) * dest_strideUV + dest_x / 2;
246 destV = destV + (dest_y / 2) * dest_strideUV + dest_x / 2;
248 src = src + src_y * src_stride + src_x * 4;
249 src2 = (src_y < src_height) ? src + src_stride : src;
254 if (src_sdtv != dest_sdtv)
256 dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
257 cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
259 memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
261 /* 1. Handle the first destination scanline specially if it
262 * doesn't start at the macro pixel boundary, i.e. blend
263 * with the background! */
264 if (dest_y % 2 == 1) {
265 /* 1.1. Handle the first destination pixel if it doesn't
266 * start at the macro pixel boundary, i.e. blend with
268 if (dest_x % 2 == 1) {
273 destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
275 CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
278 CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
288 /* 1.2. Copy all macro pixels from the source to the destination
289 * but blend with the background because we're only filling
290 * the lower part of the macro pixels. */
291 for (; j < w - 1; j += 2) {
292 y1 = src[4 * y_idx + 1];
293 y2 = src[4 * y_idx + 4 + 1];
295 u1 = src[4 * y_idx + 2];
296 u2 = src[4 * y_idx + 4 + 2];
298 v1 = src[4 * y_idx + 3];
299 v2 = src[4 * y_idx + 4 + 3];
301 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
302 destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
303 destU[uv_idx] = CLAMP (
304 (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
305 v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
306 destV[uv_idx] = CLAMP (
307 (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
308 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
314 /* 1.3. Now copy the last pixel if one exists and blend it
315 * with the background because we only fill part of
316 * the macro pixel. In case this is the last pixel of
317 * the destination we will a larger part. */
318 if (j == w - 1 && j == dest_width - 1) {
319 y1 = src[4 * y_idx + 1];
320 u1 = src[4 * y_idx + 2];
321 v1 = src[4 * y_idx + 3];
323 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
324 destU[uv_idx] = CLAMP (
325 (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
326 destV[uv_idx] = CLAMP (
327 (destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
328 } else if (j == w - 1) {
329 y1 = src[4 * y_idx + 1];
330 u1 = src[4 * y_idx + 2];
331 v1 = src[4 * y_idx + 3];
333 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
334 destU[uv_idx] = CLAMP (
335 (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
338 CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4,
342 destY += dest_strideY;
343 destY2 += dest_strideY;
344 destU += dest_strideUV;
345 destV += dest_strideUV;
353 /* 2. Copy all macro pixel scanlines, the destination scanline
354 * now starts at macro pixel boundary. */
355 for (; i < h - 1; i += 2) {
356 /* 2.1. Handle the first destination pixel if it doesn't
357 * start at the macro pixel boundary, i.e. blend with
359 if (dest_x % 2 == 1) {
361 y2 = src2[4 * 0 + 1];
363 u2 = src2[4 * 0 + 2];
365 v2 = src2[4 * 0 + 3];
367 destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
368 destY2[0] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
370 (2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
371 v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
373 (2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
374 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
382 /* 2.2. Copy all macro pixels from the source to the destination.
383 * All pixels now start at macro pixel boundary, i.e. no
384 * blending with the background is necessary. */
385 for (; j < w - 1; j += 2) {
386 y1 = src[4 * y_idx + 1];
387 y2 = src[4 * y_idx + 4 + 1];
388 y3 = src2[4 * y_idx + 1];
389 y4 = src2[4 * y_idx + 4 + 1];
391 u1 = src[4 * y_idx + 2];
392 u2 = src[4 * y_idx + 4 + 2];
393 u3 = src2[4 * y_idx + 2];
394 u4 = src2[4 * y_idx + 4 + 2];
396 v1 = src[4 * y_idx + 3];
397 v2 = src[4 * y_idx + 4 + 3];
398 v3 = src2[4 * y_idx + 3];
399 v4 = src2[4 * y_idx + 4 + 3];
401 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
402 destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
403 destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
404 destY2[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
406 destU[uv_idx] = CLAMP (
407 (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
408 u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
409 v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
410 destV[uv_idx] = CLAMP (
411 (APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
412 u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
413 v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
419 /* 2.3. Now copy the last pixel if one exists and blend it
420 * with the background because we only fill part of
421 * the macro pixel. In case this is the last pixel of
422 * the destination we will a larger part. */
423 if (j == w - 1 && j == dest_width - 1) {
424 y1 = src[4 * y_idx + 1];
425 y2 = src2[4 * y_idx + 1];
427 u1 = src[4 * y_idx + 2];
428 u2 = src2[4 * y_idx + 2];
430 v1 = src[4 * y_idx + 3];
431 v2 = src2[4 * y_idx + 3];
433 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
434 destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
435 destU[uv_idx] = CLAMP (
436 (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
437 u2, v2)) / 2, 0, 255);
438 destV[uv_idx] = CLAMP (
439 (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
440 u2, v2)) / 2, 0, 255);
441 } else if (j == w - 1) {
442 y1 = src[4 * y_idx + 1];
443 y2 = src2[4 * y_idx + 1];
445 u1 = src[4 * y_idx + 2];
446 u2 = src2[4 * y_idx + 2];
448 v1 = src[4 * y_idx + 3];
449 v2 = src2[4 * y_idx + 3];
451 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
452 destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
453 destU[uv_idx] = CLAMP (
454 (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
455 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
456 destV[uv_idx] = CLAMP (
457 (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
458 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
461 destY += 2 * dest_strideY;
462 destY2 += 2 * dest_strideY;
463 destU += dest_strideUV;
464 destV += dest_strideUV;
465 src += 2 * src_stride;
466 src2 += 2 * src_stride;
469 /* 3. Handle the last scanline if one exists. This again
470 * doesn't start at macro pixel boundary but should
471 * only fill the upper part of the macro pixels. */
472 if (i == h - 1 && i == dest_height - 1) {
473 /* 3.1. Handle the first destination pixel if it doesn't
474 * start at the macro pixel boundary, i.e. blend with
476 if (dest_x % 2 == 1) {
481 destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
483 CLAMP ((destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
485 CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
494 /* 3.2. Copy all macro pixels from the source to the destination
495 * but blend with the background because we're only filling
496 * the upper part of the macro pixels. */
497 for (; j < w - 1; j += 2) {
498 y1 = src[4 * y_idx + 1];
499 y2 = src[4 * y_idx + 4 + 1];
501 u1 = src[4 * y_idx + 2];
502 u2 = src[4 * y_idx + 4 + 2];
504 v1 = src[4 * y_idx + 3];
505 v2 = src[4 * y_idx + 4 + 3];
507 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
508 destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
510 destU[uv_idx] = CLAMP (
511 (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
512 v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
513 destV[uv_idx] = CLAMP (
514 (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
515 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
521 /* 3.3. Now copy the last pixel if one exists and blend it
522 * with the background because we only fill part of
523 * the macro pixel. In case this is the last pixel of
524 * the destination we will a larger part. */
525 if (j == w - 1 && j == dest_width - 1) {
526 y1 = src[4 * y_idx + 1];
527 u1 = src[4 * y_idx + 2];
528 v1 = src[4 * y_idx + 3];
530 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
531 destU[uv_idx] = CLAMP (
532 (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
533 destV[uv_idx] = CLAMP (
534 (destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
535 } else if (j == w - 1) {
536 y1 = src[4 * y_idx + 1];
537 u1 = src[4 * y_idx + 2];
538 v1 = src[4 * y_idx + 3];
540 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
541 destU[uv_idx] = CLAMP (
542 (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
545 CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
548 } else if (i == h - 1) {
549 /* 3.1. Handle the first destination pixel if it doesn't
550 * start at the macro pixel boundary, i.e. blend with
552 if (dest_x % 2 == 1) {
557 destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
559 CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
562 CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
572 /* 3.2. Copy all macro pixels from the source to the destination
573 * but blend with the background because we're only filling
574 * the upper part of the macro pixels. */
575 for (; j < w - 1; j += 2) {
576 y1 = src[4 * y_idx + 1];
577 y2 = src[4 * y_idx + 4 + 1];
579 u1 = src[4 * y_idx + 2];
580 u2 = src[4 * y_idx + 4 + 2];
582 v1 = src[4 * y_idx + 3];
583 v2 = src[4 * y_idx + 4 + 3];
585 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
586 destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
588 destU[uv_idx] = CLAMP (
589 (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
590 v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
591 destV[uv_idx] = CLAMP (
592 (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
593 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
599 /* 3.3. Now copy the last pixel if one exists and blend it
600 * with the background because we only fill part of
601 * the macro pixel. In case this is the last pixel of
602 * the destination we will a larger part. */
603 if (j == w - 1 && j == dest_width - 1) {
604 y1 = src[4 * y_idx + 1];
605 u1 = src[4 * y_idx + 2];
606 v1 = src[4 * y_idx + 3];
608 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
609 destU[uv_idx] = CLAMP (
610 (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
611 destV[uv_idx] = CLAMP (
612 (destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
613 } else if (j == w - 1) {
614 y1 = src[4 * y_idx + 1];
615 u1 = src[4 * y_idx + 2];
616 v1 = src[4 * y_idx + 3];
618 destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
619 destU[uv_idx] = CLAMP (
620 (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
623 CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
630 fill_i420 (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
631 guint8 * dest, gboolean sdtv, gint width, gint height)
633 guint8 empty_pixel[3];
634 guint8 *destY, *destU, *destV;
635 gint strideY, strideUV;
636 gint heightY, heightUV;
639 empty_pixel[0] = yuv_sdtv_colors_Y[fill_type];
640 empty_pixel[1] = yuv_sdtv_colors_U[fill_type];
641 empty_pixel[2] = yuv_sdtv_colors_V[fill_type];
643 empty_pixel[0] = yuv_hdtv_colors_Y[fill_type];
644 empty_pixel[1] = yuv_hdtv_colors_U[fill_type];
645 empty_pixel[2] = yuv_hdtv_colors_V[fill_type];
648 strideY = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, width);
649 strideUV = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, width);
652 dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
655 dest + gst_video_format_get_component_offset (format, 1, width, height);
657 dest + gst_video_format_get_component_offset (format, 2, width, height);
660 gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 0, height);
662 gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 1, height);
664 oil_splat_u8_ns (destY, &empty_pixel[0], strideY * heightY);
665 oil_splat_u8_ns (destU, &empty_pixel[1], strideUV * heightUV);
666 oil_splat_u8_ns (destV, &empty_pixel[2], strideUV * heightUV);
670 copy_i420_i420 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
671 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
672 gint dest_y, GstVideoFormat src_format, const guint8 * src,
673 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
677 guint8 *destY, *destU, *destV;
678 const guint8 *srcY, *srcU, *srcV;
680 const guint8 *srcY2, *srcU2, *srcV2;
681 gint dest_strideY, dest_strideUV;
682 gint src_strideY, src_strideUV;
683 gint src_y_idx, src_uv_idx;
684 gint dest_y_idx, dest_uv_idx;
691 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, dest_width);
693 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, dest_width);
695 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, src_width);
697 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, src_width);
700 dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
701 dest_width, dest_height);
703 dest + gst_video_format_get_component_offset (dest_format, 1,
704 dest_width, dest_height);
706 dest + gst_video_format_get_component_offset (dest_format, 2,
707 dest_width, dest_height);
710 src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
711 src_width, src_height);
713 src + gst_video_format_get_component_offset (src_format, 1,
714 src_width, src_height);
716 src + gst_video_format_get_component_offset (src_format, 2,
717 src_width, src_height);
720 destY = destY + dest_y * dest_strideY + dest_x;
721 destU = destU + (dest_y / 2) * dest_strideUV + dest_x / 2;
722 destV = destV + (dest_y / 2) * dest_strideUV + dest_x / 2;
724 srcY = srcY + src_y * src_strideY + src_x;
725 srcU = srcU + (src_y / 2) * src_strideUV + src_x / 2;
726 srcV = srcV + (src_y / 2) * src_strideUV + src_x / 2;
728 destY2 = destY + dest_strideY;
729 srcY2 = srcY + src_strideY;
734 if (src_sdtv != dest_sdtv)
736 dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
737 cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
739 memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
741 /* 1. Handle the first destination scanline specially if it
742 * doesn't start at the macro pixel boundary, i.e. blend
743 * with the background! */
744 if (dest_y % 2 == 1) {
745 /* 1.1. Handle the first destination pixel if it doesn't
746 * start at the macro pixel boundary, i.e. blend with
748 if (dest_x % 2 == 1) {
753 destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
755 CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
758 CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
762 src_y_idx = dest_y_idx = dest_uv_idx = 1;
763 src_uv_idx = (src_x % 2) + 1;
766 src_y_idx = dest_y_idx = dest_uv_idx = 0;
767 src_uv_idx = (src_x % 2);
770 /* 1.2. Copy all macro pixels from the source to the destination
771 * but blend with the background because we're only filling
772 * the lower part of the macro pixels. */
773 for (; j < w - 1; j += 2) {
774 y1 = srcY[src_y_idx];
775 y2 = srcY[src_y_idx + 1];
777 u1 = srcU[src_uv_idx / 2];
778 v1 = srcV[src_uv_idx / 2];
780 u2 = srcU[src_uv_idx / 2];
781 v2 = srcV[src_uv_idx / 2];
784 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
785 destY[dest_y_idx + 1] =
786 CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
788 CLAMP ((2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
789 v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
791 CLAMP ((2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
792 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
799 /* 1.3. Now copy the last pixel if one exists and blend it
800 * with the background because we only fill part of
801 * the macro pixel. In case this is the last pixel of
802 * the destination we will a larger part. */
803 if (j == w - 1 && j == dest_width - 1) {
804 y1 = srcY[src_y_idx];
805 u1 = srcU[src_uv_idx / 2];
806 v1 = srcV[src_uv_idx / 2];
808 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
809 destU[dest_uv_idx] = CLAMP (
810 (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
813 CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
815 } else if (j == w - 1) {
816 y1 = srcY[src_y_idx];
817 u1 = srcU[src_uv_idx / 2];
818 v1 = srcV[src_uv_idx / 2];
820 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
821 destU[dest_uv_idx] = CLAMP (
822 (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
825 CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
829 destY += dest_strideY;
830 destY2 += dest_strideY;
831 destU += dest_strideUV;
832 destV += dest_strideUV;
834 srcY2 += src_strideY;
836 if (src_y % 2 == 0) {
837 srcU += src_strideUV;
838 srcV += src_strideUV;
845 /* 2. Copy all macro pixel scanlines, the destination scanline
846 * now starts at macro pixel boundary. */
847 for (; i < h - 1; i += 2) {
848 /* 2.1. Handle the first destination pixel if it doesn't
849 * start at the macro pixel boundary, i.e. blend with
854 if (src_y % 2 == 1) {
855 srcU2 += src_strideUV;
856 srcV2 += src_strideUV;
859 if (dest_x % 2 == 1) {
867 destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
868 destY2[0] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
870 (2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
871 v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
873 (2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
874 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
876 src_y_idx = dest_y_idx = dest_uv_idx = 1;
877 src_uv_idx = (src_x % 2) + 1;
880 src_y_idx = dest_y_idx = dest_uv_idx = 0;
881 src_uv_idx = (src_x % 2);
884 /* 2.2. Copy all macro pixels from the source to the destination.
885 * All pixels now start at macro pixel boundary, i.e. no
886 * blending with the background is necessary. */
887 for (; j < w - 1; j += 2) {
888 y1 = srcY[src_y_idx];
889 y2 = srcY[src_y_idx + 1];
890 y3 = srcY2[src_y_idx];
891 y4 = srcY2[src_y_idx + 1];
893 u1 = srcU[src_uv_idx / 2];
894 u3 = srcU2[src_uv_idx / 2];
895 v1 = srcV[src_uv_idx / 2];
896 v3 = srcV2[src_uv_idx / 2];
898 u2 = srcU[src_uv_idx / 2];
899 u4 = srcU2[src_uv_idx / 2];
900 v2 = srcV[src_uv_idx / 2];
901 v4 = srcV2[src_uv_idx / 2];
904 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
905 destY[dest_y_idx + 1] =
906 CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
907 destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
908 destY2[dest_y_idx + 1] =
909 CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
911 destU[dest_uv_idx] = CLAMP (
912 (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
913 u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
914 v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
915 destV[dest_uv_idx] = CLAMP (
916 (APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
917 u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
918 v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
925 /* 2.3. Now copy the last pixel if one exists and blend it
926 * with the background because we only fill part of
927 * the macro pixel. In case this is the last pixel of
928 * the destination we will a larger part. */
929 if (j == w - 1 && j == dest_width - 1) {
930 y1 = srcY[src_y_idx];
931 y2 = srcY2[src_y_idx];
933 u1 = srcU[src_uv_idx / 2];
934 u2 = srcU2[src_uv_idx / 2];
936 v1 = srcV[src_uv_idx / 2];
937 v2 = srcV2[src_uv_idx / 2];
939 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
940 destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
941 destU[dest_uv_idx] = CLAMP (
942 (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
943 u2, v2)) / 2, 0, 255);
944 destV[dest_uv_idx] = CLAMP (
945 (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
946 u2, v2)) / 2, 0, 255);
947 } else if (j == w - 1) {
948 y1 = srcY[src_y_idx];
949 y2 = srcY2[src_y_idx];
951 u1 = srcU[src_uv_idx / 2];
952 u2 = srcU2[src_uv_idx / 2];
954 v1 = srcV[src_uv_idx / 2];
955 v2 = srcV2[src_uv_idx / 2];
957 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
958 destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
959 destU[dest_uv_idx] = CLAMP (
960 (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
961 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
962 destV[dest_uv_idx] = CLAMP (
963 (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
964 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
967 destY += 2 * dest_strideY;
968 destY2 += 2 * dest_strideY;
969 destU += dest_strideUV;
970 destV += dest_strideUV;
971 srcY += 2 * src_strideY;
972 srcY2 += 2 * src_strideY;
975 srcU += src_strideUV;
976 srcV += src_strideUV;
979 /* 3. Handle the last scanline if one exists. This again
980 * doesn't start at macro pixel boundary but should
981 * only fill the upper part of the macro pixels. */
982 if (i == h - 1 && i == dest_height - 1) {
983 /* 3.1. Handle the first destination pixel if it doesn't
984 * start at the macro pixel boundary, i.e. blend with
986 if (dest_x % 2 == 1) {
991 destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
993 CLAMP ((destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
995 CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
998 src_y_idx = dest_y_idx = dest_uv_idx = 1;
999 src_uv_idx = (src_x % 2) + 1;
1002 src_y_idx = dest_y_idx = dest_uv_idx = 0;
1003 src_uv_idx = (src_x % 2);
1006 /* 3.2. Copy all macro pixels from the source to the destination
1007 * but blend with the background because we're only filling
1008 * the upper part of the macro pixels. */
1009 for (; j < w - 1; j += 2) {
1010 y1 = srcY[src_y_idx];
1011 y2 = srcY[src_y_idx + 1];
1013 u1 = srcU[src_uv_idx / 2];
1014 v1 = srcV[src_uv_idx / 2];
1016 u2 = srcU[src_uv_idx / 2];
1017 v2 = srcV[src_uv_idx / 2];
1020 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1021 destY[dest_y_idx + 1] =
1022 CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1024 destU[dest_uv_idx] = CLAMP (
1025 (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1026 v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
1027 destV[dest_uv_idx] = CLAMP (
1028 (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
1029 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
1036 /* 3.3. Now copy the last pixel if one exists and blend it
1037 * with the background because we only fill part of
1038 * the macro pixel. In case this is the last pixel of
1039 * the destination we will a larger part. */
1040 if (j == w - 1 && j == dest_width - 1) {
1041 y1 = srcY[src_y_idx];
1042 u1 = srcU[src_uv_idx / 2];
1043 v1 = srcV[src_uv_idx / 2];
1045 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1046 destU[dest_uv_idx] = CLAMP (
1047 (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
1049 destV[dest_uv_idx] =
1050 CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1052 } else if (j == w - 1) {
1053 y1 = srcY[src_y_idx];
1054 u1 = srcU[src_uv_idx / 2];
1055 v1 = srcV[src_uv_idx / 2];
1057 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1058 destU[dest_uv_idx] = CLAMP (
1059 (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
1061 destV[dest_uv_idx] =
1062 CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1065 } else if (i == h - 1) {
1066 /* 3.1. Handle the first destination pixel if it doesn't
1067 * start at the macro pixel boundary, i.e. blend with
1068 * the background! */
1069 if (dest_x % 2 == 1) {
1074 destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1076 CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
1079 CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
1083 src_y_idx = dest_y_idx = dest_uv_idx = 1;
1084 src_uv_idx = (src_x % 2) + 1;
1087 src_y_idx = dest_y_idx = dest_uv_idx = 0;
1088 src_uv_idx = (src_x % 2);
1091 /* 3.2. Copy all macro pixels from the source to the destination
1092 * but blend with the background because we're only filling
1093 * the upper part of the macro pixels. */
1094 for (; j < w - 1; j += 2) {
1095 y1 = srcY[src_y_idx];
1096 y2 = srcY[src_y_idx + 1];
1098 u1 = srcU[src_uv_idx / 2];
1099 v1 = srcV[src_uv_idx / 2];
1101 u2 = srcU[src_uv_idx / 2];
1102 v2 = srcV[src_uv_idx / 2];
1105 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1106 destY[dest_y_idx + 1] =
1107 CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1109 destU[dest_uv_idx] = CLAMP (
1110 (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1111 v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
1112 destV[dest_uv_idx] = CLAMP (
1113 (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
1114 v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
1121 /* 3.3. Now copy the last pixel if one exists and blend it
1122 * with the background because we only fill part of
1123 * the macro pixel. In case this is the last pixel of
1124 * the destination we will a larger part. */
1125 if (j == w - 1 && j == dest_width - 1) {
1126 y1 = srcY[src_y_idx];
1127 u1 = srcU[src_uv_idx / 2];
1128 v1 = srcV[src_uv_idx / 2];
1130 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1131 destU[dest_uv_idx] = CLAMP (
1132 (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
1134 destV[dest_uv_idx] =
1135 CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1137 } else if (j == w - 1) {
1138 y1 = srcY[src_y_idx];
1139 u1 = srcU[src_uv_idx / 2];
1140 v1 = srcV[src_uv_idx / 2];
1142 destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1143 destU[dest_uv_idx] = CLAMP (
1144 (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
1146 destV[dest_uv_idx] =
1147 CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1154 copy_i420_ayuv (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1155 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1156 gint dest_y, GstVideoFormat src_format, const guint8 * src,
1157 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1160 const guint8 *srcY, *srcU, *srcV;
1161 gint src_strideY, src_strideUV;
1165 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, src_width);
1167 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, src_width);
1170 src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
1171 src_width, src_height);
1173 src + gst_video_format_get_component_offset (src_format, 1,
1174 src_width, src_height);
1176 src + gst_video_format_get_component_offset (src_format, 2,
1177 src_width, src_height);
1179 dest_stride = dest_width * 4;
1181 dest = dest + dest_y * dest_stride + dest_x * 4;
1183 srcY = srcY + src_y * src_strideY + src_x;
1184 srcU = srcU + (src_y / 2) * src_strideUV + src_x / 2;
1185 srcV = srcV + (src_y / 2) * src_strideUV + src_x / 2;
1187 i_alpha = CLAMP (i_alpha, 0, 255);
1189 if (src_sdtv != dest_sdtv) {
1196 dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
1197 cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
1199 for (i = 0; i < h; i++) {
1200 for (j = 0, uv_idx = src_x % 2; j < w; j++, uv_idx++) {
1202 u = srcU[uv_idx / 2];
1203 v = srcV[uv_idx / 2];
1205 y1 = APPLY_MATRIX (matrix, 0, y, u, v);
1206 u1 = APPLY_MATRIX (matrix, 1, y, u, v);
1207 v1 = APPLY_MATRIX (matrix, 2, y, u, v);
1209 dest[4 * j + 0] = i_alpha;
1210 dest[4 * j + 1] = y1;
1211 dest[4 * j + 2] = u1;
1212 dest[4 * j + 3] = v1;
1214 dest += dest_stride;
1217 srcY += src_strideY;
1218 if (src_y % 2 == 0) {
1219 srcU += src_strideUV;
1220 srcV += src_strideUV;
1227 for (i = 0; i < h; i++) {
1228 for (j = 0, uv_idx = src_x % 2; j < w; j++, uv_idx++) {
1230 u = srcU[uv_idx / 2];
1231 v = srcV[uv_idx / 2];
1233 dest[4 * j + 0] = i_alpha;
1234 dest[4 * j + 1] = y;
1235 dest[4 * j + 2] = u;
1236 dest[4 * j + 3] = v;
1238 dest += dest_stride;
1241 srcY += src_strideY;
1242 if (src_y % 2 == 0) {
1243 srcU += src_strideUV;
1244 srcV += src_strideUV;
1251 fill_rgb32 (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
1252 guint8 * dest, gboolean sdtv, gint width, gint height)
1254 guint32 empty_pixel;
1257 p[0] = gst_video_format_get_component_offset (format, 3, width, height);
1258 p[1] = gst_video_format_get_component_offset (format, 0, width, height);
1259 p[2] = gst_video_format_get_component_offset (format, 1, width, height);
1260 p[3] = gst_video_format_get_component_offset (format, 2, width, height);
1262 b_alpha = CLAMP (b_alpha, 0, 255);
1264 empty_pixel = GUINT32_FROM_LE ((b_alpha << (p[0] * 8)) |
1265 (rgb_colors_R[fill_type] << (p[1] * 8)) |
1266 (rgb_colors_G[fill_type] << (p[2] * 8)) |
1267 (rgb_colors_B[fill_type] << (p[3] * 8)));
1269 oil_splat_u32_ns ((guint32 *) dest, &empty_pixel, width * height);
1273 fill_rgb24 (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
1274 guint8 * dest, gboolean sdtv, gint width, gint height)
1276 gint dest_stride = GST_ROUND_UP_4 (width * 3);
1280 p[0] = gst_video_format_get_component_offset (format, 3, width, height);
1281 p[1] = gst_video_format_get_component_offset (format, 0, width, height);
1282 p[2] = gst_video_format_get_component_offset (format, 1, width, height);
1283 p[3] = gst_video_format_get_component_offset (format, 2, width, height);
1285 for (i = 0; i < height; i++) {
1286 for (j = 0; j < width; j++) {
1287 dest[3 * j + p[1]] = rgb_colors_R[fill_type];
1288 dest[3 * j + p[2]] = rgb_colors_G[fill_type];
1289 dest[3 * j + p[3]] = rgb_colors_B[fill_type];
1291 dest += dest_stride;
1296 copy_rgb32 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1297 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1298 gint dest_y, GstVideoFormat src_format, const guint8 * src,
1299 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1303 gint src_stride, dest_stride;
1304 gboolean in_alpha, out_alpha;
1305 gint in_bpp, out_bpp;
1308 gboolean packed_out = (dest_format == GST_VIDEO_FORMAT_RGB
1309 || dest_format == GST_VIDEO_FORMAT_BGR);
1310 gboolean packed_in = (src_format == GST_VIDEO_FORMAT_RGB
1311 || src_format == GST_VIDEO_FORMAT_BGR);
1313 src_stride = (packed_in) ? GST_ROUND_UP_4 (3 * src_width) : 4 * src_width;
1314 dest_stride = (packed_out) ? GST_ROUND_UP_4 (3 * dest_width) : 4 * dest_width;
1315 in_bpp = (packed_in) ? 3 : 4;
1316 out_bpp = (packed_out) ? 3 : 4;
1318 out_alpha = gst_video_format_has_alpha (dest_format);
1320 gst_video_format_get_component_offset (dest_format, 3, dest_width,
1323 gst_video_format_get_component_offset (dest_format, 0, dest_width,
1326 gst_video_format_get_component_offset (dest_format, 1, dest_width,
1329 gst_video_format_get_component_offset (dest_format, 2, dest_width,
1332 in_alpha = gst_video_format_has_alpha (src_format);
1334 gst_video_format_get_component_offset (src_format, 3, src_width,
1337 gst_video_format_get_component_offset (src_format, 0, src_width,
1340 gst_video_format_get_component_offset (src_format, 1, src_width,
1343 gst_video_format_get_component_offset (src_format, 2, src_width,
1346 dest = dest + dest_y * dest_stride + dest_x * out_bpp;
1347 src = src + src_y * src_stride + src_x * in_bpp;
1349 if (in_alpha && out_alpha) {
1351 for (i = 0; i < h; i++) {
1352 for (j = 0; j < w; j += 4) {
1353 dest[j + p_out[0]] = (src[j + p_in[0]] * i_alpha) >> 8;
1354 dest[j + p_out[1]] = src[j + p_in[1]];
1355 dest[j + p_out[2]] = src[j + p_in[2]];
1356 dest[j + p_out[3]] = src[j + p_in[3]];
1358 dest += dest_stride;
1361 } else if (out_alpha && !packed_in) {
1363 i_alpha = CLAMP (i_alpha, 0, 255);
1365 for (i = 0; i < h; i++) {
1366 for (j = 0; j < w; j += 4) {
1367 dest[j + p_out[0]] = i_alpha;
1368 dest[j + p_out[1]] = src[j + p_in[1]];
1369 dest[j + p_out[2]] = src[j + p_in[2]];
1370 dest[j + p_out[3]] = src[j + p_in[3]];
1372 dest += dest_stride;
1375 } else if (out_alpha && packed_in) {
1376 i_alpha = CLAMP (i_alpha, 0, 255);
1378 for (i = 0; i < h; i++) {
1379 for (j = 0; j < w; j++) {
1380 dest[4 * j + p_out[0]] = i_alpha;
1381 dest[4 * j + p_out[1]] = src[in_bpp * j + p_in[1]];
1382 dest[4 * j + p_out[2]] = src[in_bpp * j + p_in[2]];
1383 dest[4 * j + p_out[3]] = src[in_bpp * j + p_in[3]];
1385 dest += dest_stride;
1388 } else if (!packed_out && !packed_in) {
1390 for (i = 0; i < h; i++) {
1391 for (j = 0; j < w; j += 4) {
1392 dest[j + p_out[1]] = src[j + p_in[1]];
1393 dest[j + p_out[2]] = src[j + p_in[2]];
1394 dest[j + p_out[3]] = src[j + p_in[3]];
1396 dest += dest_stride;
1400 for (i = 0; i < h; i++) {
1401 for (j = 0; j < w; j++) {
1402 dest[out_bpp * j + p_out[1]] = src[in_bpp * j + p_in[1]];
1403 dest[out_bpp * j + p_out[2]] = src[in_bpp * j + p_in[2]];
1404 dest[out_bpp * j + p_out[3]] = src[in_bpp * j + p_in[3]];
1406 dest += dest_stride;
1413 copy_rgb32_ayuv (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1414 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1415 gint dest_y, GstVideoFormat src_format, const guint8 * src,
1416 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1420 gint src_stride, dest_stride;
1424 gboolean packed_in = (src_format == GST_VIDEO_FORMAT_RGB
1425 || src_format == GST_VIDEO_FORMAT_BGR);
1431 src_stride = (packed_in) ? GST_ROUND_UP_4 (3 * src_width) : 4 * src_width;
1432 dest_stride = 4 * dest_width;
1433 in_bpp = (packed_in) ? 3 : 4;
1435 in_alpha = gst_video_format_has_alpha (src_format);
1437 gst_video_format_get_component_offset (src_format, 3, src_width,
1440 gst_video_format_get_component_offset (src_format, 0, src_width,
1443 gst_video_format_get_component_offset (src_format, 1, src_width,
1446 gst_video_format_get_component_offset (src_format, 2, src_width,
1450 (dest_sdtv) ? cog_rgb_to_ycbcr_matrix_8bit_sdtv :
1451 cog_rgb_to_ycbcr_matrix_8bit_hdtv, 12 * sizeof (gint));
1453 dest = dest + dest_y * dest_stride + dest_x * 4;
1454 src = src + src_y * src_stride + src_x * in_bpp;
1458 for (i = 0; i < h; i++) {
1459 for (j = 0; j < w; j += 4) {
1460 a = (src[j + p_in[0]] * i_alpha) >> 8;
1461 r = src[j + p_in[1]];
1462 g = src[j + p_in[2]];
1463 b = src[j + p_in[3]];
1465 y = APPLY_MATRIX (matrix, 0, r, g, b);
1466 u = APPLY_MATRIX (matrix, 1, r, g, b);
1467 v = APPLY_MATRIX (matrix, 2, r, g, b);
1470 dest[j + 1] = CLAMP (y, 0, 255);
1471 dest[j + 2] = CLAMP (u, 0, 255);
1472 dest[j + 3] = CLAMP (v, 0, 255);
1474 dest += dest_stride;
1477 } else if (!packed_in) {
1479 i_alpha = CLAMP (i_alpha, 0, 255);
1481 for (i = 0; i < h; i++) {
1482 for (j = 0; j < w; j += 4) {
1484 r = src[j + p_in[1]];
1485 g = src[j + p_in[2]];
1486 b = src[j + p_in[3]];
1488 y = APPLY_MATRIX (matrix, 0, r, g, b);
1489 u = APPLY_MATRIX (matrix, 1, r, g, b);
1490 v = APPLY_MATRIX (matrix, 2, r, g, b);
1493 dest[j + 1] = CLAMP (y, 0, 255);
1494 dest[j + 2] = CLAMP (u, 0, 255);
1495 dest[j + 3] = CLAMP (v, 0, 255);
1497 dest += dest_stride;
1501 i_alpha = CLAMP (i_alpha, 0, 255);
1503 for (i = 0; i < h; i++) {
1504 for (j = 0; j < w; j++) {
1506 r = src[in_bpp * j + p_in[1]];
1507 g = src[in_bpp * j + p_in[2]];
1508 b = src[in_bpp * j + p_in[3]];
1510 y = APPLY_MATRIX (matrix, 0, r, g, b);
1511 u = APPLY_MATRIX (matrix, 1, r, g, b);
1512 v = APPLY_MATRIX (matrix, 2, r, g, b);
1514 dest[4 * j + 0] = a;
1515 dest[4 * j + 1] = CLAMP (y, 0, 255);
1516 dest[4 * j + 2] = CLAMP (u, 0, 255);
1517 dest[4 * j + 3] = CLAMP (v, 0, 255);
1519 dest += dest_stride;
1526 copy_ayuv_rgb32 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1527 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1528 gint dest_y, GstVideoFormat src_format, const guint8 * src,
1529 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1533 gint src_stride, dest_stride;
1537 gboolean packed_out = (dest_format == GST_VIDEO_FORMAT_RGB
1538 || dest_format == GST_VIDEO_FORMAT_BGR);
1544 dest_stride = (packed_out) ? GST_ROUND_UP_4 (3 * dest_width) : 4 * dest_width;
1545 src_stride = 4 * src_width;
1546 out_bpp = (packed_out) ? 3 : 4;
1548 out_alpha = gst_video_format_has_alpha (dest_format);
1550 gst_video_format_get_component_offset (dest_format, 3, dest_width,
1553 gst_video_format_get_component_offset (dest_format, 0, dest_width,
1556 gst_video_format_get_component_offset (dest_format, 1, dest_width,
1559 gst_video_format_get_component_offset (dest_format, 2, dest_width,
1563 (src_sdtv) ? cog_ycbcr_to_rgb_matrix_8bit_sdtv :
1564 cog_ycbcr_to_rgb_matrix_8bit_hdtv, 12 * sizeof (gint));
1566 dest = dest + dest_y * dest_stride + dest_x * out_bpp;
1567 src = src + src_y * src_stride + src_x * 4;
1571 for (i = 0; i < h; i++) {
1572 for (j = 0; j < w; j += 4) {
1573 a = (src[j + 0] * i_alpha) >> 8;
1578 r = APPLY_MATRIX (matrix, 0, y, u, v);
1579 g = APPLY_MATRIX (matrix, 1, y, u, v);
1580 b = APPLY_MATRIX (matrix, 2, y, u, v);
1582 dest[j + p_out[0]] = a;
1583 dest[j + p_out[1]] = CLAMP (r, 0, 255);
1584 dest[j + p_out[2]] = CLAMP (g, 0, 255);
1585 dest[j + p_out[3]] = CLAMP (b, 0, 255);
1587 dest += dest_stride;
1590 } else if (!packed_out) {
1592 for (i = 0; i < h; i++) {
1593 for (j = 0; j < w; j += 4) {
1598 r = APPLY_MATRIX (matrix, 0, y, u, v);
1599 g = APPLY_MATRIX (matrix, 1, y, u, v);
1600 b = APPLY_MATRIX (matrix, 2, y, u, v);
1602 dest[j + p_out[1]] = CLAMP (r, 0, 255);
1603 dest[j + p_out[2]] = CLAMP (g, 0, 255);
1604 dest[j + p_out[3]] = CLAMP (b, 0, 255);
1606 dest += dest_stride;
1610 for (i = 0; i < h; i++) {
1611 for (j = 0; j < w; j++) {
1616 r = APPLY_MATRIX (matrix, 0, y, u, v);
1617 g = APPLY_MATRIX (matrix, 1, y, u, v);
1618 b = APPLY_MATRIX (matrix, 2, y, u, v);
1620 dest[out_bpp * j + p_out[1]] = CLAMP (r, 0, 255);
1621 dest[out_bpp * j + p_out[2]] = CLAMP (g, 0, 255);
1622 dest[out_bpp * j + p_out[3]] = CLAMP (b, 0, 255);
1624 dest += dest_stride;
1631 fill_gray (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
1632 guint8 * dest, gboolean sdtv, gint width, gint height)
1637 if (format == GST_VIDEO_FORMAT_GRAY8) {
1638 guint8 val = yuv_sdtv_colors_Y[fill_type];
1640 dest_stride = GST_ROUND_UP_4 (width);
1641 for (i = 0; i < height; i++) {
1642 oil_splat_u8_ns (dest, &val, width);
1643 dest += dest_stride;
1646 guint16 val = yuv_sdtv_colors_Y[fill_type] << 8;
1648 dest_stride = GST_ROUND_UP_4 (width * 2);
1649 if (format == GST_VIDEO_FORMAT_GRAY16_BE) {
1650 for (i = 0; i < height; i++) {
1651 for (j = 0; j < width; j++) {
1652 GST_WRITE_UINT16_BE (dest + 2 * j, val);
1654 dest += dest_stride;
1657 for (i = 0; i < height; i++) {
1658 for (j = 0; j < width; j++) {
1659 GST_WRITE_UINT16_LE (dest + 2 * j, val);
1661 dest += dest_stride;
1668 copy_packed_simple (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1669 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1670 gint dest_y, GstVideoFormat src_format, const guint8 * src,
1671 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1675 gint src_stride, dest_stride;
1676 gint pixel_stride, row_size;
1678 src_stride = gst_video_format_get_row_stride (src_format, 0, src_width);
1679 dest_stride = gst_video_format_get_row_stride (dest_format, 0, dest_width);
1680 pixel_stride = gst_video_format_get_pixel_stride (dest_format, 0);
1681 row_size = w * pixel_stride;
1683 dest = dest + dest_y * dest_stride + dest_x * pixel_stride;
1684 src = src + src_y * src_stride + src_x * pixel_stride;
1686 for (i = 0; i < h; i++) {
1687 oil_copy_u8 (dest, src, row_size);
1688 dest += dest_stride;
1694 fill_yuy2 (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
1695 guint8 * dest, gboolean sdtv, gint width, gint height)
1699 gint stride = gst_video_format_get_row_stride (format, 0, width);
1701 y = (sdtv) ? yuv_sdtv_colors_Y[fill_type] : yuv_hdtv_colors_Y[fill_type];
1702 u = (sdtv) ? yuv_sdtv_colors_U[fill_type] : yuv_hdtv_colors_U[fill_type];
1703 v = (sdtv) ? yuv_sdtv_colors_V[fill_type] : yuv_hdtv_colors_V[fill_type];
1705 width = width + (width % 2);
1707 if (format == GST_VIDEO_FORMAT_YUY2) {
1708 for (i = 0; i < height; i++) {
1709 for (j = 0; j < width; j += 2) {
1710 dest[j * 2 + 0] = y;
1711 dest[j * 2 + 1] = u;
1712 dest[j * 2 + 2] = y;
1713 dest[j * 2 + 3] = v;
1718 } else if (format == GST_VIDEO_FORMAT_YVYU) {
1719 for (i = 0; i < height; i++) {
1720 for (j = 0; j < width; j += 2) {
1721 dest[j * 2 + 0] = y;
1722 dest[j * 2 + 1] = v;
1723 dest[j * 2 + 2] = y;
1724 dest[j * 2 + 3] = u;
1730 for (i = 0; i < height; i++) {
1731 for (j = 0; j < width; j += 2) {
1732 dest[j * 2 + 0] = u;
1733 dest[j * 2 + 1] = y;
1734 dest[j * 2 + 2] = v;
1735 dest[j * 2 + 3] = y;
1744 copy_yuy2_yuy2 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1745 gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1746 gint dest_y, GstVideoFormat src_format, const guint8 * src,
1747 gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1751 gint src_stride, dest_stride;
1753 src_stride = gst_video_format_get_row_stride (src_format, 0, src_width);
1754 dest_stride = gst_video_format_get_row_stride (dest_format, 0, dest_width);
1756 dest_x = (dest_x & ~1);
1757 src_x = (src_x & ~1);
1761 dest = dest + dest_y * dest_stride + dest_x * 2;
1762 src = src + src_y * src_stride + src_x * 2;
1764 if (src_sdtv != dest_sdtv) {
1770 dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
1771 cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
1773 if (src_format == GST_VIDEO_FORMAT_YUY2) {
1774 for (i = 0; i < h; i++) {
1775 for (j = 0; j < w; j += 2) {
1776 y1 = src[j * 2 + 0];
1777 y2 = src[j * 2 + 2];
1778 u1 = u2 = src[j * 2 + 1];
1779 v1 = v2 = src[j * 2 + 3];
1781 dest[j * 2 + 0] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
1782 dest[j * 2 + 1] = APPLY_MATRIX (matrix, 1, y1, u1, v1);
1783 dest[j * 2 + 2] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
1784 dest[j * 2 + 3] = APPLY_MATRIX (matrix, 2, y2, u2, v2);
1786 dest += dest_stride;
1789 } else if (src_format == GST_VIDEO_FORMAT_YVYU) {
1790 for (i = 0; i < h; i++) {
1791 for (j = 0; j < w; j += 2) {
1792 y1 = src[j * 2 + 0];
1793 y2 = src[j * 2 + 2];
1794 v1 = v2 = src[j * 2 + 1];
1795 u1 = u2 = src[j * 2 + 3];
1797 dest[j * 2 + 0] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
1798 dest[j * 2 + 1] = APPLY_MATRIX (matrix, 2, y1, u1, v1);
1799 dest[j * 2 + 2] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
1800 dest[j * 2 + 3] = APPLY_MATRIX (matrix, 1, y2, u2, v2);
1802 dest += dest_stride;
1806 for (i = 0; i < h; i++) {
1807 for (j = 0; j < w; j += 2) {
1808 u1 = u2 = src[j * 2 + 0];
1809 v1 = v2 = src[j * 2 + 2];
1810 y1 = src[j * 2 + 1];
1811 y2 = src[j * 2 + 3];
1813 dest[j * 2 + 1] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
1814 dest[j * 2 + 0] = APPLY_MATRIX (matrix, 1, y1, u1, v1);
1815 dest[j * 2 + 3] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
1816 dest[j * 2 + 2] = APPLY_MATRIX (matrix, 2, y2, u2, v2);
1818 dest += dest_stride;
1823 for (i = 0; i < h; i++) {
1824 oil_copy_u8 (dest, src, w * 2);
1825 dest += dest_stride;
1831 #define DEFAULT_LEFT 0
1832 #define DEFAULT_RIGHT 0
1833 #define DEFAULT_TOP 0
1834 #define DEFAULT_BOTTOM 0
1835 #define DEFAULT_FILL_TYPE VIDEO_BOX_FILL_BLACK
1836 #define DEFAULT_ALPHA 1.0
1837 #define DEFAULT_BORDER_ALPHA 1.0
1853 static GstStaticPadTemplate gst_video_box_src_template =
1854 GST_STATIC_PAD_TEMPLATE ("src",
1857 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
1858 GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
1859 GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
1860 GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_BGRx ";"
1861 GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_RGBx ";"
1862 GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
1863 GST_VIDEO_CAPS_YUV ("YUY2") ";"
1864 GST_VIDEO_CAPS_YUV ("YVYU") ";"
1865 GST_VIDEO_CAPS_YUV ("UYVY") ";"
1866 GST_VIDEO_CAPS_YUV ("I420") ";"
1867 GST_VIDEO_CAPS_YUV ("YV12") ";"
1868 GST_VIDEO_CAPS_GRAY8 ";"
1869 GST_VIDEO_CAPS_GRAY16 ("BIG_ENDIAN") ";"
1870 GST_VIDEO_CAPS_GRAY16 ("LITTLE_ENDIAN"))
1873 static GstStaticPadTemplate gst_video_box_sink_template =
1874 GST_STATIC_PAD_TEMPLATE ("sink",
1877 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
1878 GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
1879 GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
1880 GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_BGRx ";"
1881 GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_RGBx ";"
1882 GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
1883 GST_VIDEO_CAPS_YUV ("YUY2") ";"
1884 GST_VIDEO_CAPS_YUV ("YVYU") ";"
1885 GST_VIDEO_CAPS_YUV ("UYVY") ";"
1886 GST_VIDEO_CAPS_YUV ("I420") ";"
1887 GST_VIDEO_CAPS_YUV ("YV12") ";"
1888 GST_VIDEO_CAPS_GRAY8 ";"
1889 GST_VIDEO_CAPS_GRAY16 ("BIG_ENDIAN") ";"
1890 GST_VIDEO_CAPS_GRAY16 ("LITTLE_ENDIAN"))
1893 GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
1894 GST_TYPE_BASE_TRANSFORM);
1896 static void gst_video_box_set_property (GObject * object, guint prop_id,
1897 const GValue * value, GParamSpec * pspec);
1898 static void gst_video_box_get_property (GObject * object, guint prop_id,
1899 GValue * value, GParamSpec * pspec);
1901 static gboolean gst_video_box_recalc_transform (GstVideoBox * video_box);
1902 static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
1903 GstPadDirection direction, GstCaps * from);
1904 static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
1905 GstCaps * in, GstCaps * out);
1906 static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
1907 GstCaps * caps, guint * size);
1908 static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
1909 GstBuffer * in, GstBuffer * out);
1910 static void gst_video_box_before_transform (GstBaseTransform * trans,
1912 static void gst_video_box_fixate_caps (GstBaseTransform * trans,
1913 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
1914 static gboolean gst_video_box_src_event (GstBaseTransform * trans,
1917 #define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
1919 gst_video_box_fill_get_type (void)
1921 static GType video_box_fill_type = 0;
1922 static const GEnumValue video_box_fill[] = {
1923 {VIDEO_BOX_FILL_BLACK, "Black", "black"},
1924 {VIDEO_BOX_FILL_GREEN, "Green", "green"},
1925 {VIDEO_BOX_FILL_BLUE, "Blue", "blue"},
1926 {VIDEO_BOX_FILL_RED, "Red", "red"},
1927 {VIDEO_BOX_FILL_YELLOW, "Yellow", "yellow"},
1928 {VIDEO_BOX_FILL_WHITE, "White", "white"},
1932 if (!video_box_fill_type) {
1933 video_box_fill_type =
1934 g_enum_register_static ("GstVideoBoxFill", video_box_fill);
1936 return video_box_fill_type;
1941 gst_video_box_base_init (gpointer g_class)
1943 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1945 gst_element_class_set_details_simple (element_class, "Video box filter",
1946 "Filter/Effect/Video",
1947 "Resizes a video by adding borders or cropping",
1948 "Wim Taymans <wim@fluendo.com>");
1950 gst_element_class_add_pad_template (element_class,
1951 gst_static_pad_template_get (&gst_video_box_sink_template));
1952 gst_element_class_add_pad_template (element_class,
1953 gst_static_pad_template_get (&gst_video_box_src_template));
1957 gst_video_box_finalize (GObject * object)
1959 GstVideoBox *video_box = GST_VIDEO_BOX (object);
1961 if (video_box->mutex) {
1962 g_mutex_free (video_box->mutex);
1963 video_box->mutex = NULL;
1966 G_OBJECT_CLASS (parent_class)->finalize (object);
1970 gst_video_box_class_init (GstVideoBoxClass * klass)
1972 GObjectClass *gobject_class = (GObjectClass *) klass;
1973 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
1975 gobject_class->set_property = gst_video_box_set_property;
1976 gobject_class->get_property = gst_video_box_get_property;
1977 gobject_class->finalize = gst_video_box_finalize;
1979 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILL_TYPE,
1980 g_param_spec_enum ("fill", "Fill", "How to fill the borders",
1981 GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
1982 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
1983 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEFT,
1984 g_param_spec_int ("left", "Left",
1985 "Pixels to box at left (<0 = add a border)", G_MININT, G_MAXINT,
1987 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
1988 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RIGHT,
1989 g_param_spec_int ("right", "Right",
1990 "Pixels to box at right (<0 = add a border)", G_MININT, G_MAXINT,
1992 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
1993 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOP,
1994 g_param_spec_int ("top", "Top",
1995 "Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
1997 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
1998 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BOTTOM,
1999 g_param_spec_int ("bottom", "Bottom",
2000 "Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
2002 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2003 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
2004 g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
2006 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2007 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
2008 g_param_spec_double ("border-alpha", "Border Alpha",
2009 "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
2010 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2012 * GstVideoBox:autocrop
2014 * If set to %TRUE videobox will automatically crop/pad the input
2015 * video to be centered in the output.
2019 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOCROP,
2020 g_param_spec_boolean ("autocrop", "Auto crop",
2021 "Auto crop", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2023 trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_box_transform);
2024 trans_class->before_transform =
2025 GST_DEBUG_FUNCPTR (gst_video_box_before_transform);
2026 trans_class->transform_caps =
2027 GST_DEBUG_FUNCPTR (gst_video_box_transform_caps);
2028 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_box_set_caps);
2029 trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_video_box_get_unit_size);
2030 trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_box_fixate_caps);
2031 trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_box_src_event);
2035 gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
2037 video_box->box_right = DEFAULT_RIGHT;
2038 video_box->box_left = DEFAULT_LEFT;
2039 video_box->box_top = DEFAULT_TOP;
2040 video_box->box_bottom = DEFAULT_BOTTOM;
2041 video_box->crop_right = 0;
2042 video_box->crop_left = 0;
2043 video_box->crop_top = 0;
2044 video_box->crop_bottom = 0;
2045 video_box->fill_type = DEFAULT_FILL_TYPE;
2046 video_box->alpha = DEFAULT_ALPHA;
2047 video_box->border_alpha = DEFAULT_BORDER_ALPHA;
2048 video_box->autocrop = FALSE;
2050 video_box->mutex = g_mutex_new ();
2054 gst_video_box_set_property (GObject * object, guint prop_id,
2055 const GValue * value, GParamSpec * pspec)
2057 GstVideoBox *video_box = GST_VIDEO_BOX (object);
2059 g_mutex_lock (video_box->mutex);
2062 video_box->box_left = g_value_get_int (value);
2063 if (video_box->box_left < 0) {
2064 video_box->border_left = -video_box->box_left;
2065 video_box->crop_left = 0;
2067 video_box->border_left = 0;
2068 video_box->crop_left = video_box->box_left;
2072 video_box->box_right = g_value_get_int (value);
2073 if (video_box->box_right < 0) {
2074 video_box->border_right = -video_box->box_right;
2075 video_box->crop_right = 0;
2077 video_box->border_right = 0;
2078 video_box->crop_right = video_box->box_right;
2082 video_box->box_top = g_value_get_int (value);
2083 if (video_box->box_top < 0) {
2084 video_box->border_top = -video_box->box_top;
2085 video_box->crop_top = 0;
2087 video_box->border_top = 0;
2088 video_box->crop_top = video_box->box_top;
2092 video_box->box_bottom = g_value_get_int (value);
2093 if (video_box->box_bottom < 0) {
2094 video_box->border_bottom = -video_box->box_bottom;
2095 video_box->crop_bottom = 0;
2097 video_box->border_bottom = 0;
2098 video_box->crop_bottom = video_box->box_bottom;
2101 case PROP_FILL_TYPE:
2102 video_box->fill_type = g_value_get_enum (value);
2105 video_box->alpha = g_value_get_double (value);
2107 case PROP_BORDER_ALPHA:
2108 video_box->border_alpha = g_value_get_double (value);
2111 video_box->autocrop = g_value_get_boolean (value);
2114 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2117 gst_video_box_recalc_transform (video_box);
2119 GST_DEBUG_OBJECT (video_box, "Calling reconfigure");
2120 gst_base_transform_reconfigure (GST_BASE_TRANSFORM_CAST (video_box));
2122 g_mutex_unlock (video_box->mutex);
2126 gst_video_box_autocrop (GstVideoBox * video_box)
2128 gint crop_w = video_box->in_width - video_box->out_width;
2129 gint crop_h = video_box->in_height - video_box->out_height;
2131 video_box->box_left = crop_w / 2;
2132 if (video_box->box_left < 0) {
2133 video_box->border_left = -video_box->box_left;
2134 video_box->crop_left = 0;
2136 video_box->border_left = 0;
2137 video_box->crop_left = video_box->box_left;
2140 /* Round down/up for odd width differences */
2146 video_box->box_right = crop_w / 2;
2147 if (video_box->box_right < 0) {
2148 video_box->border_right = -video_box->box_right;
2149 video_box->crop_right = 0;
2151 video_box->border_right = 0;
2152 video_box->crop_right = video_box->box_right;
2155 video_box->box_top = crop_h / 2;
2156 if (video_box->box_top < 0) {
2157 video_box->border_top = -video_box->box_top;
2158 video_box->crop_top = 0;
2160 video_box->border_top = 0;
2161 video_box->crop_top = video_box->box_top;
2164 /* Round down/up for odd height differences */
2169 video_box->box_bottom = crop_h / 2;
2171 if (video_box->box_bottom < 0) {
2172 video_box->border_bottom = -video_box->box_bottom;
2173 video_box->crop_bottom = 0;
2175 video_box->border_bottom = 0;
2176 video_box->crop_bottom = video_box->box_bottom;
2181 gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
2184 GstVideoBox *video_box = GST_VIDEO_BOX (object);
2188 g_value_set_int (value, video_box->box_left);
2191 g_value_set_int (value, video_box->box_right);
2194 g_value_set_int (value, video_box->box_top);
2197 g_value_set_int (value, video_box->box_bottom);
2199 case PROP_FILL_TYPE:
2200 g_value_set_enum (value, video_box->fill_type);
2203 g_value_set_double (value, video_box->alpha);
2205 case PROP_BORDER_ALPHA:
2206 g_value_set_double (value, video_box->border_alpha);
2209 g_value_set_boolean (value, video_box->autocrop);
2212 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2218 gst_video_box_transform_dimension (gint val, gint delta)
2220 gint64 new_val = (gint64) val + (gint64) delta;
2222 new_val = CLAMP (new_val, 1, G_MAXINT);
2224 return (gint) new_val;
2228 gst_video_box_transform_dimension_value (const GValue * src_val,
2229 gint delta, GValue * dest_val)
2231 gboolean ret = TRUE;
2233 g_value_init (dest_val, G_VALUE_TYPE (src_val));
2235 if (G_VALUE_HOLDS_INT (src_val)) {
2236 gint ival = g_value_get_int (src_val);
2238 ival = gst_video_box_transform_dimension (ival, delta);
2239 g_value_set_int (dest_val, ival);
2240 } else if (GST_VALUE_HOLDS_INT_RANGE (src_val)) {
2241 gint min = gst_value_get_int_range_min (src_val);
2242 gint max = gst_value_get_int_range_max (src_val);
2244 min = gst_video_box_transform_dimension (min, delta);
2245 max = gst_video_box_transform_dimension (max, delta);
2248 g_value_unset (dest_val);
2250 gst_value_set_int_range (dest_val, min, max);
2252 } else if (GST_VALUE_HOLDS_LIST (src_val)) {
2255 for (i = 0; i < gst_value_list_get_size (src_val); ++i) {
2256 const GValue *list_val;
2257 GValue newval = { 0, };
2259 list_val = gst_value_list_get_value (src_val, i);
2260 if (gst_video_box_transform_dimension_value (list_val, delta, &newval))
2261 gst_value_list_append_value (dest_val, &newval);
2262 g_value_unset (&newval);
2265 if (gst_value_list_get_size (dest_val) == 0) {
2266 g_value_unset (dest_val);
2270 g_value_unset (dest_val);
2278 gst_video_box_transform_caps (GstBaseTransform * trans,
2279 GstPadDirection direction, GstCaps * from)
2281 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
2283 const GstCaps *templ;
2285 GstStructure *structure;
2288 to = gst_caps_copy (from);
2289 /* Just to be sure... */
2290 gst_caps_truncate (to);
2291 structure = gst_caps_get_structure (to, 0);
2293 /* Transform width/height */
2294 if (video_box->autocrop) {
2295 gst_structure_remove_field (structure, "width");
2296 gst_structure_remove_field (structure, "height");
2298 gint dw = 0, dh = 0;
2300 GValue w_val = { 0, };
2301 GValue h_val = { 0, };
2303 /* calculate width and height */
2304 if (direction == GST_PAD_SINK) {
2305 dw -= video_box->box_left;
2306 dw -= video_box->box_right;
2308 dw += video_box->box_left;
2309 dw += video_box->box_right;
2312 if (direction == GST_PAD_SINK) {
2313 dh -= video_box->box_top;
2314 dh -= video_box->box_bottom;
2316 dh += video_box->box_top;
2317 dh += video_box->box_bottom;
2320 v = gst_structure_get_value (structure, "width");
2321 if (!gst_video_box_transform_dimension_value (v, dw, &w_val)) {
2322 GST_WARNING_OBJECT (video_box, "could not tranform width value with dw=%d"
2323 ", caps structure=%" GST_PTR_FORMAT, dw, structure);
2324 gst_caps_unref (to);
2325 to = gst_caps_new_empty ();
2328 gst_structure_set_value (structure, "width", &w_val);
2330 v = gst_structure_get_value (structure, "height");
2331 if (!gst_video_box_transform_dimension_value (v, dh, &h_val)) {
2332 g_value_unset (&w_val);
2333 GST_WARNING_OBJECT (video_box,
2334 "could not tranform height value with dh=%d" ", caps structure=%"
2335 GST_PTR_FORMAT, dh, structure);
2336 gst_caps_unref (to);
2337 to = gst_caps_new_empty ();
2340 gst_structure_set_value (structure, "height", &h_val);
2341 g_value_unset (&w_val);
2342 g_value_unset (&h_val);
2345 /* Supported conversions:
2352 * AYUV->xRGB (24bpp, 32bpp, incl. alpha)
2353 * xRGB->xRGB (24bpp, 32bpp, from/to all variants, incl. alpha)
2354 * xRGB->AYUV (24bpp, 32bpp, incl. alpha)
2356 * Passthrough only for everything else.
2358 name = gst_structure_get_name (structure);
2359 if (g_str_equal (name, "video/x-raw-yuv")) {
2362 if (gst_structure_get_fourcc (structure, "format", &fourcc) &&
2363 (fourcc == GST_STR_FOURCC ("AYUV") ||
2364 fourcc == GST_STR_FOURCC ("I420") ||
2365 fourcc == GST_STR_FOURCC ("YV12"))) {
2366 GValue list = { 0, };
2367 GValue val = { 0, };
2370 /* get rid of format */
2371 gst_structure_remove_field (structure, "format");
2372 gst_structure_remove_field (structure, "color-matrix");
2373 gst_structure_remove_field (structure, "chroma-site");
2375 s2 = gst_structure_copy (structure);
2377 g_value_init (&list, GST_TYPE_LIST);
2378 g_value_init (&val, GST_TYPE_FOURCC);
2379 gst_value_set_fourcc (&val, GST_STR_FOURCC ("AYUV"));
2380 gst_value_list_append_value (&list, &val);
2381 g_value_reset (&val);
2382 gst_value_set_fourcc (&val, GST_STR_FOURCC ("I420"));
2383 gst_value_list_append_value (&list, &val);
2384 g_value_reset (&val);
2385 gst_value_set_fourcc (&val, GST_STR_FOURCC ("YV12"));
2386 gst_value_list_append_value (&list, &val);
2387 g_value_unset (&val);
2388 gst_structure_set_value (structure, "format", &list);
2389 g_value_unset (&list);
2391 /* We can only convert to RGB if input is AYUV */
2392 if (fourcc == GST_STR_FOURCC ("AYUV")) {
2393 gst_structure_set_name (s2, "video/x-raw-rgb");
2394 g_value_init (&list, GST_TYPE_LIST);
2395 g_value_init (&val, G_TYPE_INT);
2396 g_value_set_int (&val, 32);
2397 gst_value_list_append_value (&list, &val);
2398 g_value_reset (&val);
2399 g_value_set_int (&val, 24);
2400 gst_value_list_append_value (&list, &val);
2401 g_value_unset (&val);
2402 gst_structure_set_value (s2, "depth", &list);
2403 gst_structure_set_value (s2, "bpp", &list);
2404 g_value_unset (&list);
2406 gst_caps_append_structure (to, s2);
2408 } else if (g_str_equal (name, "video/x-raw-rgb")) {
2411 if (gst_structure_get_int (structure, "bpp", &bpp) &&
2412 (bpp == 32 || bpp == 24)) {
2413 GValue list = { 0, };
2414 GValue val = { 0, };
2417 /* get rid of format */
2418 gst_structure_remove_field (structure, "depth");
2419 gst_structure_remove_field (structure, "bpp");
2420 gst_structure_remove_field (structure, "red_mask");
2421 gst_structure_remove_field (structure, "green_mask");
2422 gst_structure_remove_field (structure, "blue_mask");
2423 gst_structure_remove_field (structure, "alpha_mask");
2425 s2 = gst_structure_copy (structure);
2427 g_value_init (&list, GST_TYPE_LIST);
2428 g_value_init (&val, G_TYPE_INT);
2429 g_value_set_int (&val, 32);
2430 gst_value_list_append_value (&list, &val);
2431 g_value_reset (&val);
2432 g_value_set_int (&val, 24);
2433 gst_value_list_append_value (&list, &val);
2434 g_value_unset (&val);
2435 gst_structure_set_value (structure, "depth", &list);
2436 gst_structure_set_value (structure, "bpp", &list);
2437 g_value_unset (&list);
2439 gst_structure_set_name (s2, "video/x-raw-yuv");
2440 gst_structure_set (s2, "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
2442 gst_caps_append_structure (to, s2);
2446 /* filter against set allowed caps on the pad */
2447 other = (direction == GST_PAD_SINK) ? trans->srcpad : trans->sinkpad;
2449 templ = gst_pad_get_pad_template_caps (other);
2450 ret = gst_caps_intersect (to, templ);
2451 gst_caps_unref (to);
2453 GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
2454 " to %" GST_PTR_FORMAT, direction, from, ret);
2460 gst_video_box_recalc_transform (GstVideoBox * video_box)
2462 gboolean res = TRUE;
2464 /* if we have the same format in and out and we don't need to perform any
2465 * cropping at all, we can just operate in passthrough mode */
2466 if (video_box->in_format == video_box->out_format &&
2467 video_box->box_left == 0 && video_box->box_right == 0 &&
2468 video_box->box_top == 0 && video_box->box_bottom == 0 &&
2469 video_box->in_sdtv == video_box->out_sdtv) {
2471 GST_LOG_OBJECT (video_box, "we are using passthrough");
2472 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
2475 GST_LOG_OBJECT (video_box, "we are not using passthrough");
2476 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
2483 gst_video_box_select_processing_functions (GstVideoBox * video_box)
2485 switch (video_box->out_format) {
2486 case GST_VIDEO_FORMAT_AYUV:
2487 video_box->fill = fill_ayuv;
2488 switch (video_box->in_format) {
2489 case GST_VIDEO_FORMAT_AYUV:
2490 video_box->copy = copy_ayuv_ayuv;
2492 case GST_VIDEO_FORMAT_I420:
2493 case GST_VIDEO_FORMAT_YV12:
2494 video_box->copy = copy_i420_ayuv;
2496 case GST_VIDEO_FORMAT_ARGB:
2497 case GST_VIDEO_FORMAT_ABGR:
2498 case GST_VIDEO_FORMAT_RGBA:
2499 case GST_VIDEO_FORMAT_BGRA:
2500 case GST_VIDEO_FORMAT_xRGB:
2501 case GST_VIDEO_FORMAT_xBGR:
2502 case GST_VIDEO_FORMAT_RGBx:
2503 case GST_VIDEO_FORMAT_BGRx:
2504 case GST_VIDEO_FORMAT_RGB:
2505 case GST_VIDEO_FORMAT_BGR:
2506 video_box->copy = copy_rgb32_ayuv;
2512 case GST_VIDEO_FORMAT_I420:
2513 case GST_VIDEO_FORMAT_YV12:
2514 video_box->fill = fill_i420;
2515 switch (video_box->in_format) {
2516 case GST_VIDEO_FORMAT_AYUV:
2517 video_box->copy = copy_ayuv_i420;
2519 case GST_VIDEO_FORMAT_I420:
2520 case GST_VIDEO_FORMAT_YV12:
2521 video_box->copy = copy_i420_i420;
2527 case GST_VIDEO_FORMAT_ARGB:
2528 case GST_VIDEO_FORMAT_ABGR:
2529 case GST_VIDEO_FORMAT_RGBA:
2530 case GST_VIDEO_FORMAT_BGRA:
2531 case GST_VIDEO_FORMAT_xRGB:
2532 case GST_VIDEO_FORMAT_xBGR:
2533 case GST_VIDEO_FORMAT_RGBx:
2534 case GST_VIDEO_FORMAT_BGRx:
2535 case GST_VIDEO_FORMAT_RGB:
2536 case GST_VIDEO_FORMAT_BGR:
2537 video_box->fill = (video_box->out_format == GST_VIDEO_FORMAT_BGR
2538 || video_box->out_format ==
2539 GST_VIDEO_FORMAT_RGB) ? fill_rgb24 : fill_rgb32;
2540 switch (video_box->in_format) {
2541 case GST_VIDEO_FORMAT_ARGB:
2542 case GST_VIDEO_FORMAT_ABGR:
2543 case GST_VIDEO_FORMAT_RGBA:
2544 case GST_VIDEO_FORMAT_BGRA:
2545 case GST_VIDEO_FORMAT_xRGB:
2546 case GST_VIDEO_FORMAT_xBGR:
2547 case GST_VIDEO_FORMAT_RGBx:
2548 case GST_VIDEO_FORMAT_BGRx:
2549 case GST_VIDEO_FORMAT_RGB:
2550 case GST_VIDEO_FORMAT_BGR:
2551 video_box->copy = copy_rgb32;
2553 case GST_VIDEO_FORMAT_AYUV:
2554 video_box->copy = copy_ayuv_rgb32;
2559 case GST_VIDEO_FORMAT_GRAY8:
2560 case GST_VIDEO_FORMAT_GRAY16_BE:
2561 case GST_VIDEO_FORMAT_GRAY16_LE:
2562 video_box->fill = fill_gray;
2563 switch (video_box->in_format) {
2564 case GST_VIDEO_FORMAT_GRAY8:
2565 case GST_VIDEO_FORMAT_GRAY16_BE:
2566 case GST_VIDEO_FORMAT_GRAY16_LE:
2567 video_box->copy = copy_packed_simple;
2573 case GST_VIDEO_FORMAT_YUY2:
2574 case GST_VIDEO_FORMAT_YVYU:
2575 case GST_VIDEO_FORMAT_UYVY:
2576 video_box->fill = fill_yuy2;
2577 switch (video_box->in_format) {
2578 case GST_VIDEO_FORMAT_YUY2:
2579 case GST_VIDEO_FORMAT_YVYU:
2580 case GST_VIDEO_FORMAT_UYVY:
2581 video_box->copy = copy_yuy2_yuy2;
2591 return video_box->fill != NULL && video_box->copy != NULL;
2595 gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
2597 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
2599 const gchar *matrix;
2601 g_mutex_lock (video_box->mutex);
2604 gst_video_format_parse_caps (in, &video_box->in_format,
2605 &video_box->in_width, &video_box->in_height);
2607 gst_video_format_parse_caps (out, &video_box->out_format,
2608 &video_box->out_width, &video_box->out_height);
2610 matrix = gst_video_parse_caps_color_matrix (in);
2611 video_box->in_sdtv = matrix ? g_str_equal (matrix, "sdtv") : TRUE;
2612 matrix = gst_video_parse_caps_color_matrix (out);
2613 video_box->out_sdtv = matrix ? g_str_equal (matrix, "sdtv") : TRUE;
2615 /* something wrong getting the caps */
2619 GST_DEBUG_OBJECT (trans, "Input w: %d h: %d", video_box->in_width,
2620 video_box->in_height);
2621 GST_DEBUG_OBJECT (trans, "Output w: %d h: %d", video_box->out_width,
2622 video_box->out_height);
2624 if (video_box->autocrop)
2625 gst_video_box_autocrop (video_box);
2627 /* recalc the transformation strategy */
2628 ret = gst_video_box_recalc_transform (video_box);
2631 ret = gst_video_box_select_processing_functions (video_box);
2632 g_mutex_unlock (video_box->mutex);
2639 GST_DEBUG_OBJECT (video_box,
2640 "Invalid caps: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT, in, out);
2641 g_mutex_unlock (video_box->mutex);
2647 gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
2650 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
2651 GstVideoFormat format;
2657 ret = gst_video_format_parse_caps (caps, &format, &width, &height);
2659 GST_ERROR_OBJECT (video_box, "Invalid caps: %" GST_PTR_FORMAT, caps);
2663 *size = gst_video_format_get_size (format, width, height);
2665 GST_LOG_OBJECT (video_box, "Returning from _unit_size %d", *size);
2671 gst_video_box_fixate_caps (GstBaseTransform * trans,
2672 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
2678 ret = gst_video_format_parse_caps (caps, NULL, &width, &height);
2682 s = gst_caps_get_structure (othercaps, 0);
2683 gst_structure_fixate_field_nearest_int (s, "width", width);
2684 gst_structure_fixate_field_nearest_int (s, "height", height);
2688 gst_video_box_src_event (GstBaseTransform * trans, GstEvent * event)
2690 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
2691 GstStructure *new_structure;
2692 const GstStructure *structure;
2693 const gchar *event_name;
2697 GST_OBJECT_LOCK (video_box);
2698 if (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION &&
2699 (video_box->box_left != 0 || video_box->box_top != 0)) {
2700 structure = gst_event_get_structure (event);
2701 event_name = gst_structure_get_string (structure, "event");
2704 (strcmp (event_name, "mouse-move") == 0 ||
2705 strcmp (event_name, "mouse-button-press") == 0 ||
2706 strcmp (event_name, "mouse-button-release") == 0)) {
2707 if (gst_structure_get_double (structure, "pointer_x", &pointer_x) &&
2708 gst_structure_get_double (structure, "pointer_y", &pointer_y)) {
2709 gdouble new_pointer_x, new_pointer_y;
2710 GstEvent *new_event;
2712 new_pointer_x = pointer_x + video_box->box_left;
2713 new_pointer_y = pointer_y + video_box->box_top;
2715 new_structure = gst_structure_copy (structure);
2716 gst_structure_set (new_structure,
2717 "pointer_x", G_TYPE_DOUBLE, (gdouble) (new_pointer_x),
2718 "pointer_y", G_TYPE_DOUBLE, (gdouble) (new_pointer_y), NULL);
2720 new_event = gst_event_new_navigation (new_structure);
2721 gst_event_unref (event);
2724 GST_WARNING_OBJECT (video_box, "Failed to read navigation event");
2728 GST_OBJECT_UNLOCK (video_box);
2730 return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
2734 gst_video_box_process (GstVideoBox * video_box, const guint8 * src,
2737 guint b_alpha = CLAMP ((guint) video_box->border_alpha * 256, 0, 256);
2738 guint i_alpha = CLAMP ((guint) video_box->alpha * 256, 0, 256);
2739 GstVideoBoxFill fill_type = video_box->fill_type;
2740 gint br, bl, bt, bb, crop_w, crop_h;
2745 br = video_box->box_right;
2746 bl = video_box->box_left;
2747 bt = video_box->box_top;
2748 bb = video_box->box_bottom;
2750 if (br >= 0 && bl >= 0) {
2751 crop_w = video_box->in_width - (br + bl);
2752 } else if (br >= 0 && bl < 0) {
2753 crop_w = video_box->in_width - (br);
2754 } else if (br < 0 && bl >= 0) {
2755 crop_w = video_box->in_width - (bl);
2756 } else if (br < 0 && bl < 0) {
2757 crop_w = video_box->in_width;
2760 if (bb >= 0 && bt >= 0) {
2761 crop_h = video_box->in_height - (bb + bt);
2762 } else if (bb >= 0 && bt < 0) {
2763 crop_h = video_box->in_height - (bb);
2764 } else if (bb < 0 && bt >= 0) {
2765 crop_h = video_box->in_height - (bt);
2766 } else if (bb < 0 && bt < 0) {
2767 crop_h = video_box->in_height;
2770 GST_DEBUG_OBJECT (video_box, "Borders are: L:%d, R:%d, T:%d, B:%d", bl, br,
2772 GST_DEBUG_OBJECT (video_box, "Alpha value is: %u (frame) %u (border)",
2775 if (crop_h < 0 || crop_w < 0) {
2776 video_box->fill (fill_type, b_alpha, video_box->out_format, dest,
2777 video_box->out_sdtv, video_box->out_width, video_box->out_height);
2778 } else if (bb == 0 && bt == 0 && br == 0 && bl == 0) {
2779 video_box->copy (i_alpha, video_box->out_format, dest, video_box->out_sdtv,
2780 video_box->out_width, video_box->out_height, 0, 0, video_box->in_format,
2781 src, video_box->in_sdtv, video_box->in_width, video_box->in_height, 0,
2784 gint src_x = 0, src_y = 0;
2785 gint dest_x = 0, dest_y = 0;
2787 /* Fill everything if a border should be added somewhere */
2788 if (bt < 0 || bb < 0 || br < 0 || bl < 0)
2789 video_box->fill (fill_type, b_alpha, video_box->out_format, dest,
2790 video_box->out_sdtv, video_box->out_width, video_box->out_height);
2807 video_box->copy (i_alpha, video_box->out_format, dest, video_box->out_sdtv,
2808 video_box->out_width, video_box->out_height, dest_x, dest_y,
2809 video_box->in_format, src, video_box->in_sdtv, video_box->in_width,
2810 video_box->in_height, src_x, src_y, crop_w, crop_h);
2813 GST_LOG_OBJECT (video_box, "image created");
2817 gst_video_box_before_transform (GstBaseTransform * trans, GstBuffer * in)
2819 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
2820 GstClockTime timestamp, stream_time;
2822 timestamp = GST_BUFFER_TIMESTAMP (in);
2824 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
2826 GST_DEBUG_OBJECT (video_box, "sync to %" GST_TIME_FORMAT,
2827 GST_TIME_ARGS (timestamp));
2829 if (GST_CLOCK_TIME_IS_VALID (stream_time))
2830 gst_object_sync_values (G_OBJECT (video_box), stream_time);
2833 static GstFlowReturn
2834 gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
2837 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
2838 const guint8 *indata;
2841 indata = GST_BUFFER_DATA (in);
2842 outdata = GST_BUFFER_DATA (out);
2844 g_mutex_lock (video_box->mutex);
2845 gst_video_box_process (video_box, indata, outdata);
2846 g_mutex_unlock (video_box->mutex);
2850 /* FIXME: 0.11 merge with videocrop plugin */
2852 plugin_init (GstPlugin * plugin)
2856 gst_controller_init (NULL, NULL);
2858 GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
2859 "Resizes a video by adding borders or cropping");
2861 return gst_element_register (plugin, "videobox", GST_RANK_NONE,
2862 GST_TYPE_VIDEO_BOX);
2865 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2868 "resizes a video by adding borders or cropping",
2869 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)