2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2010> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 * SECTION:element-videobox
22 * @see_also: #GstVideoCrop
24 * This plugin crops or enlarges the image. It takes 4 values as input, a
25 * top, bottom, left and right offset. Positive values will crop that much
26 * pixels from the respective border of the image, negative values will add
27 * that much pixels. When pixels are added, you can specify their color.
28 * Some predefined colors are usable with an enum property.
30 * The plugin is alpha channel aware and will try to negotiate with a format
31 * that supports alpha channels first. When alpha channel is active two
32 * other properties, alpha and border_alpha can be used to set the alpha
33 * values of the inner picture and the border respectively. an alpha value of
34 * 0.0 means total transparency, 1.0 is opaque.
36 * The videobox plugin has many uses such as doing a mosaic of pictures,
37 * letterboxing video, cutting out pieces of video, picture in picture, etc..
39 * Setting autocrop to true changes the behavior of the plugin so that
40 * caps determine crop properties rather than the other way around: given
41 * input and output dimensions, the crop values are selected so that the
42 * smaller frame is effectively centered in the larger frame. This
43 * involves either cropping or padding.
45 * If you use autocrop there is little point in setting the other
46 * properties manually because they will be overriden if the caps change,
47 * but nothing stops you from doing so.
51 * gst-launch videotestsrc ! videobox autocrop=true ! \
52 * "video/x-raw-yuv, width=600, height=400" ! ffmpegcolorspace ! ximagesink
60 #include "gstvideobox.h"
63 #include <liboil/liboil.h>
66 #include <gst/controller/gstcontroller.h>
68 GST_DEBUG_CATEGORY_STATIC (videobox_debug);
69 #define GST_CAT_DEFAULT videobox_debug
71 #define DEFAULT_LEFT 0
72 #define DEFAULT_RIGHT 0
74 #define DEFAULT_BOTTOM 0
75 #define DEFAULT_FILL_TYPE VIDEO_BOX_FILL_BLACK
76 #define DEFAULT_ALPHA 1.0
77 #define DEFAULT_BORDER_ALPHA 1.0
93 static GstStaticPadTemplate gst_video_box_src_template =
94 GST_STATIC_PAD_TEMPLATE ("src",
97 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
98 GST_VIDEO_CAPS_YUV ("I420"))
101 static GstStaticPadTemplate gst_video_box_sink_template =
102 GST_STATIC_PAD_TEMPLATE ("sink",
105 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
106 GST_VIDEO_CAPS_YUV ("I420"))
109 GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
110 GST_TYPE_BASE_TRANSFORM);
112 static void gst_video_box_set_property (GObject * object, guint prop_id,
113 const GValue * value, GParamSpec * pspec);
114 static void gst_video_box_get_property (GObject * object, guint prop_id,
115 GValue * value, GParamSpec * pspec);
117 static gboolean video_box_recalc_transform (GstVideoBox * video_box);
118 static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
119 GstPadDirection direction, GstCaps * from);
120 static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
121 GstCaps * in, GstCaps * out);
122 static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
123 GstCaps * caps, guint * size);
124 static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
125 GstBuffer * in, GstBuffer * out);
127 #define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
129 gst_video_box_fill_get_type (void)
131 static GType video_box_fill_type = 0;
132 static const GEnumValue video_box_fill[] = {
133 {VIDEO_BOX_FILL_BLACK, "Black", "black"},
134 {VIDEO_BOX_FILL_GREEN, "Colorkey green", "green"},
135 {VIDEO_BOX_FILL_BLUE, "Colorkey blue", "blue"},
139 if (!video_box_fill_type) {
140 video_box_fill_type =
141 g_enum_register_static ("GstVideoBoxFill", video_box_fill);
143 return video_box_fill_type;
148 gst_video_box_base_init (gpointer g_class)
150 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
152 gst_element_class_set_details_simple (element_class, "Video box filter",
153 "Filter/Effect/Video",
154 "Resizes a video by adding borders or cropping",
155 "Wim Taymans <wim@fluendo.com>");
157 gst_element_class_add_pad_template (element_class,
158 gst_static_pad_template_get (&gst_video_box_sink_template));
159 gst_element_class_add_pad_template (element_class,
160 gst_static_pad_template_get (&gst_video_box_src_template));
164 gst_video_box_finalize (GObject * object)
166 GstVideoBox *video_box = GST_VIDEO_BOX (object);
168 if (video_box->mutex) {
169 g_mutex_free (video_box->mutex);
170 video_box->mutex = NULL;
173 G_OBJECT_CLASS (parent_class)->finalize (object);
177 gst_video_box_class_init (GstVideoBoxClass * klass)
179 GObjectClass *gobject_class = (GObjectClass *) klass;
180 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
182 gobject_class->set_property = gst_video_box_set_property;
183 gobject_class->get_property = gst_video_box_get_property;
184 gobject_class->finalize = gst_video_box_finalize;
186 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILL_TYPE,
187 g_param_spec_enum ("fill", "Fill", "How to fill the borders",
188 GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
189 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
190 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEFT,
191 g_param_spec_int ("left", "Left",
192 "Pixels to box at left (<0 = add a border)", G_MININT, G_MAXINT,
194 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
195 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RIGHT,
196 g_param_spec_int ("right", "Right",
197 "Pixels to box at right (<0 = add a border)", G_MININT, G_MAXINT,
199 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
200 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOP,
201 g_param_spec_int ("top", "Top",
202 "Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
204 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
205 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BOTTOM,
206 g_param_spec_int ("bottom", "Bottom",
207 "Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
209 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
210 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
211 g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
213 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
214 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
215 g_param_spec_double ("border-alpha", "Border Alpha",
216 "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
217 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
219 * GstVideoBox:autocrop
221 * If set to %TRUE videobox will automatically crop/pad the input
222 * video to be centered in the output.
226 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOCROP,
227 g_param_spec_boolean ("autocrop", "Auto crop",
228 "Auto crop", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
230 trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_box_transform);
231 trans_class->transform_caps =
232 GST_DEBUG_FUNCPTR (gst_video_box_transform_caps);
233 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_box_set_caps);
234 trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_video_box_get_unit_size);
238 gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
240 video_box->box_right = DEFAULT_RIGHT;
241 video_box->box_left = DEFAULT_LEFT;
242 video_box->box_top = DEFAULT_TOP;
243 video_box->box_bottom = DEFAULT_BOTTOM;
244 video_box->crop_right = 0;
245 video_box->crop_left = 0;
246 video_box->crop_top = 0;
247 video_box->crop_bottom = 0;
248 video_box->fill_type = DEFAULT_FILL_TYPE;
249 video_box->alpha = DEFAULT_ALPHA;
250 video_box->border_alpha = DEFAULT_BORDER_ALPHA;
251 video_box->autocrop = FALSE;
253 video_box->mutex = g_mutex_new ();
257 gst_video_box_set_property (GObject * object, guint prop_id,
258 const GValue * value, GParamSpec * pspec)
260 GstVideoBox *video_box = GST_VIDEO_BOX (object);
262 g_mutex_lock (video_box->mutex);
265 video_box->box_left = g_value_get_int (value);
266 if (video_box->box_left < 0) {
267 video_box->border_left = -video_box->box_left;
268 video_box->crop_left = 0;
270 video_box->border_left = 0;
271 video_box->crop_left = video_box->box_left;
275 video_box->box_right = g_value_get_int (value);
276 if (video_box->box_right < 0) {
277 video_box->border_right = -video_box->box_right;
278 video_box->crop_right = 0;
280 video_box->border_right = 0;
281 video_box->crop_right = video_box->box_right;
285 video_box->box_top = g_value_get_int (value);
286 if (video_box->box_top < 0) {
287 video_box->border_top = -video_box->box_top;
288 video_box->crop_top = 0;
290 video_box->border_top = 0;
291 video_box->crop_top = video_box->box_top;
295 video_box->box_bottom = g_value_get_int (value);
296 if (video_box->box_bottom < 0) {
297 video_box->border_bottom = -video_box->box_bottom;
298 video_box->crop_bottom = 0;
300 video_box->border_bottom = 0;
301 video_box->crop_bottom = video_box->box_bottom;
305 video_box->fill_type = g_value_get_enum (value);
308 video_box->alpha = g_value_get_double (value);
310 case PROP_BORDER_ALPHA:
311 video_box->border_alpha = g_value_get_double (value);
314 video_box->autocrop = g_value_get_boolean (value);
317 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
320 video_box_recalc_transform (video_box);
322 GST_DEBUG_OBJECT (video_box, "Calling reconfigure");
323 gst_base_transform_reconfigure (GST_BASE_TRANSFORM_CAST (video_box));
325 g_mutex_unlock (video_box->mutex);
329 gst_video_box_autocrop (GstVideoBox * video_box)
331 gint crop_w = video_box->in_width - video_box->out_width;
332 gint crop_h = video_box->in_height - video_box->out_height;
334 video_box->box_left = crop_w / 2;
335 if (video_box->box_left < 0) {
336 video_box->border_left = -video_box->box_left;
337 video_box->crop_left = 0;
339 video_box->border_left = 0;
340 video_box->crop_left = video_box->box_left;
343 /* Round down/up for odd width differences */
349 video_box->box_right = crop_w / 2;
350 if (video_box->box_right < 0) {
351 video_box->border_right = -video_box->box_right;
352 video_box->crop_right = 0;
354 video_box->border_right = 0;
355 video_box->crop_right = video_box->box_right;
358 video_box->box_top = crop_h / 2;
359 if (video_box->box_top < 0) {
360 video_box->border_top = -video_box->box_top;
361 video_box->crop_top = 0;
363 video_box->border_top = 0;
364 video_box->crop_top = video_box->box_top;
367 /* Round down/up for odd height differences */
372 video_box->box_bottom = crop_h / 2;
374 if (video_box->box_bottom < 0) {
375 video_box->border_bottom = -video_box->box_bottom;
376 video_box->crop_bottom = 0;
378 video_box->border_bottom = 0;
379 video_box->crop_bottom = video_box->box_bottom;
384 gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
387 GstVideoBox *video_box = GST_VIDEO_BOX (object);
391 g_value_set_int (value, video_box->box_left);
394 g_value_set_int (value, video_box->box_right);
397 g_value_set_int (value, video_box->box_top);
400 g_value_set_int (value, video_box->box_bottom);
403 g_value_set_enum (value, video_box->fill_type);
406 g_value_set_double (value, video_box->alpha);
408 case PROP_BORDER_ALPHA:
409 g_value_set_double (value, video_box->border_alpha);
412 g_value_set_boolean (value, video_box->autocrop);
415 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
421 gst_video_box_transform_caps (GstBaseTransform * trans,
422 GstPadDirection direction, GstCaps * from)
424 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
426 const GstCaps *templ;
427 GstStructure *structure;
431 to = gst_caps_copy (from);
432 structure = gst_caps_get_structure (to, 0);
434 /* get rid of format */
435 gst_structure_remove_field (structure, "format");
437 /* otherwise caps nego will fail: */
438 if (video_box->autocrop) {
439 gst_structure_remove_field (structure, "width");
440 gst_structure_remove_field (structure, "height");
442 /* calculate width and height */
443 if (gst_structure_get_int (structure, "width", &width)) {
444 if (direction == GST_PAD_SINK) {
445 width -= video_box->box_left;
446 width -= video_box->box_right;
448 width += video_box->box_left;
449 width += video_box->box_right;
454 GST_DEBUG_OBJECT (trans, "New caps width: %d", width);
455 gst_structure_set (structure, "width", G_TYPE_INT, width, NULL);
458 if (gst_structure_get_int (structure, "height", &height)) {
459 if (direction == GST_PAD_SINK) {
460 height -= video_box->box_top;
461 height -= video_box->box_bottom;
463 height += video_box->box_top;
464 height += video_box->box_bottom;
470 GST_DEBUG_OBJECT (trans, "New caps height: %d", height);
471 gst_structure_set (structure, "height", G_TYPE_INT, height, NULL);
475 /* filter against set allowed caps on the pad */
476 other = (direction == GST_PAD_SINK) ? trans->srcpad : trans->sinkpad;
478 templ = gst_pad_get_pad_template_caps (other);
479 ret = gst_caps_intersect (to, templ);
482 GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
483 " to %" GST_PTR_FORMAT, direction, from, ret);
489 video_box_recalc_transform (GstVideoBox * video_box)
493 /* if we have the same format in and out and we don't need to perform any
494 * cropping at all, we can just operate in passthrough mode */
495 if (video_box->in_format == video_box->out_format &&
496 video_box->box_left == 0 && video_box->box_right == 0 &&
497 video_box->box_top == 0 && video_box->box_bottom == 0) {
499 GST_LOG_OBJECT (video_box, "we are using passthrough");
500 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
503 GST_LOG_OBJECT (video_box, "we are not using passthrough");
504 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
511 gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
513 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
516 g_mutex_lock (video_box->mutex);
519 gst_video_format_parse_caps (in, &video_box->in_format,
520 &video_box->in_width, &video_box->in_height);
522 gst_video_format_parse_caps (out, &video_box->out_format,
523 &video_box->out_width, &video_box->out_height);
525 /* something wrong getting the caps */
529 GST_DEBUG_OBJECT (trans, "Input w: %d h: %d", video_box->in_width,
530 video_box->in_height);
531 GST_DEBUG_OBJECT (trans, "Output w: %d h: %d", video_box->out_width,
532 video_box->out_height);
534 if (video_box->autocrop)
535 gst_video_box_autocrop (video_box);
537 /* recalc the transformation strategy */
538 ret = video_box_recalc_transform (video_box);
540 g_mutex_unlock (video_box->mutex);
547 GST_DEBUG_OBJECT (video_box,
548 "Invalid caps: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT, in, out);
549 g_mutex_unlock (video_box->mutex);
555 gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
558 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
559 GstVideoFormat format;
565 ret = gst_video_format_parse_caps (caps, &format, &width, &height);
567 GST_ERROR_OBJECT (video_box, "Invalid caps: %" GST_PTR_FORMAT, caps);
571 *size = gst_video_format_get_size (format, width, height);
573 GST_LOG_OBJECT (video_box, "Returning from _unit_size %d", *size);
578 static const guint8 yuv_colors_Y[VIDEO_BOX_FILL_LAST] = { 16, 150, 29 };
579 static const guint8 yuv_colors_U[VIDEO_BOX_FILL_LAST] = { 128, 46, 255 };
580 static const guint8 yuv_colors_V[VIDEO_BOX_FILL_LAST] = { 128, 21, 107 };
583 gst_video_box_copy_plane_i420 (GstVideoBox * video_box, const guint8 * src,
584 guint8 * dest, gint br, gint bl, gint bt, gint bb, gint src_crop_width,
585 gint src_crop_height, gint src_stride, gint dest_width, gint dest_stride,
591 for (j = 0; j < bt; j++) {
592 oil_splat_u8_ns (dest, &fill_color, dest_width);
596 /* copy and add left and right border */
597 for (j = 0; j < src_crop_height; j++) {
598 oil_splat_u8_ns (dest, &fill_color, bl);
599 oil_memcpy (dest + bl, src, src_crop_width);
600 oil_splat_u8_ns (dest + bl + src_crop_width, &fill_color, br);
606 for (j = 0; j < bb; j++) {
607 oil_splat_u8_ns (dest, &fill_color, dest_width);
613 gst_video_box_apply_alpha (guint8 * dest, guint8 alpha)
620 gst_video_box_ayuv_ayuv (GstVideoBox * video_box, const guint8 * src,
623 gint dblen = video_box->out_height * video_box->out_width;
624 guint32 *destb = (guint32 *) dest;
625 const guint32 *srcb = (const guint32 *) src;
626 guint8 b_alpha = (guint8) (video_box->border_alpha * 255);
627 guint8 i_alpha = (guint8) (video_box->alpha * 255);
628 gint br, bl, bt, bb, crop_w, crop_h;
630 guint32 *loc = destb;
633 GST_LOG_OBJECT (video_box, "Processing AYUV -> AYUV data");
637 empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
638 (yuv_colors_Y[video_box->fill_type] << 16) |
639 (yuv_colors_U[video_box->fill_type] << 8) |
640 yuv_colors_V[video_box->fill_type]);
642 br = video_box->box_right;
643 bl = video_box->box_left;
644 bt = video_box->box_top;
645 bb = video_box->box_bottom;
647 if (br >= 0 && bl >= 0) {
648 crop_w = video_box->in_width - (br + bl);
649 } else if (br >= 0 && bl < 0) {
650 crop_w = video_box->in_width - (br);
651 } else if (br < 0 && bl >= 0) {
652 crop_w = video_box->in_width - (bl);
653 } else if (br < 0 && bl < 0) {
654 crop_w = video_box->in_width;
657 if (bb >= 0 && bt >= 0) {
658 crop_h = video_box->in_height - (bb + bt);
659 } else if (bb >= 0 && bt < 0) {
660 crop_h = video_box->in_height - (bb);
661 } else if (bb < 0 && bt >= 0) {
662 crop_h = video_box->in_height - (bt);
663 } else if (bb < 0 && bt < 0) {
664 crop_h = video_box->in_height;
667 GST_DEBUG_OBJECT (video_box, "Borders are: L:%d, R:%d, T:%d, B:%d", bl, br,
669 GST_DEBUG_OBJECT (video_box, "Alpha value is: %d", i_alpha);
671 if (crop_h <= 0 || crop_w <= 0) {
672 oil_splat_u32_ns (destb, &empty_pixel, dblen);
674 const guint32 *src_loc = srcb;
678 oil_splat_u32_ns (loc, &empty_pixel, (-bt) * video_box->out_width);
679 loc = loc + ((-bt) * video_box->out_width);
681 src_loc = src_loc + (bt * video_box->in_width);
687 for (i = 0; i < crop_h; i++) {
692 oil_splat_u32_ns (loc, &empty_pixel, -bl);
697 oil_copy_u8 ((guint8 *) loc, (guint8 *) src_loc, crop_w * 4);
699 for (j = 0; j < crop_w; j++)
700 gst_video_box_apply_alpha ((guint8 *) & loc[j], i_alpha);
702 src_loc += video_box->in_width;
707 oil_splat_u32_ns (loc, &empty_pixel, -br);
714 oil_splat_u32_ns (loc, &empty_pixel, (-bb) * video_box->out_width);
718 GST_LOG_OBJECT (video_box, "image created");
722 gst_video_box_clear (gpointer dest, gint size)
726 oil_splat_u8_ns (dest, &nil, size);
734 return floor (((float) j) / 2);
740 return ceil (((float) j) / 2);
744 gst_video_box_ayuv_i420 (GstVideoBox * video_box, const guint8 * src,
747 gint br, bl, bt, bb, crop_w, crop_h, rest;
748 gint Ysize, Usize, Vsize;
749 guint8 *Ydest, *Udest, *Vdest;
750 guint8 *Utemp, *Vtemp;
751 guint32 empty_px_values[3];
753 guint Ywidth, Uwidth, Vwidth;
755 GST_LOG_OBJECT (video_box, "AYUV to I420 conversion");
761 empty_px_values[0] = yuv_colors_Y[video_box->fill_type];
762 empty_px_values[1] = yuv_colors_U[video_box->fill_type];
763 empty_px_values[2] = yuv_colors_V[video_box->fill_type];
766 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0,
767 video_box->out_width);
769 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1,
770 video_box->out_width);
772 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2,
773 video_box->out_width);
776 dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
777 video_box->out_width, video_box->out_height);
779 dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
780 video_box->out_width, video_box->out_height);
782 dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
783 video_box->out_width, video_box->out_height);
786 Ywidth * gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 0,
787 video_box->out_height);
789 Ywidth * gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 1,
790 video_box->out_height);
792 Ywidth * gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 2,
793 video_box->out_height);
795 br = video_box->box_right;
796 bl = video_box->box_left;
797 bt = video_box->box_top;
798 bb = video_box->box_bottom;
800 if (br >= 0 && bl >= 0) {
801 rest = Ywidth - video_box->out_width;
802 crop_w = video_box->in_width - (bl + br);
803 } else if (br >= 0 && bl < 0) {
804 rest = Ywidth - video_box->out_width;
805 crop_w = video_box->in_width - (br);
806 } else if (br < 0 && bl >= 0) {
807 rest = Ywidth - video_box->out_width;
808 crop_w = video_box->in_width - (bl);
809 } else if (br < 0 && bl < 0) {
810 rest = Ywidth - video_box->out_width;
811 crop_w = video_box->in_width;
814 if (bb >= 0 && bt >= 0) {
815 crop_h = video_box->in_height - (bb + bt);
816 } else if (bb >= 0 && bt < 0) {
817 crop_h = video_box->in_height - (bb);
818 } else if (bb < 0 && bt >= 0) {
819 crop_h = video_box->in_height - (bt);
820 } else if (bb < 0 && bt < 0) {
821 crop_h = video_box->in_height;
824 Utemp = g_malloc0 (Uwidth);
825 Vtemp = g_malloc0 (Vwidth);
827 GST_LOG_OBJECT (video_box, "Borders are: L:%d, R:%d, T:%d, B:%d", bl, br, bt,
830 GST_LOG_OBJECT (video_box, "Starting conversion");
832 if (crop_h <= 0 || crop_w <= 0) {
833 oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], Ysize);
834 oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1], Usize);
835 oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2], Vsize);
837 gboolean sumbuff = FALSE;
838 const guint32 *src_loc1;
841 src_loc1 = (const guint32 *) src;
844 oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], (-bt) * Ywidth);
846 oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1],
847 (UVfloor (-bt) * Uwidth) + 7);
848 oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2],
849 UVfloor (-bt) * Vwidth);
852 oil_splat_u8_ns (Utemp, (guint8 *) & empty_px_values[1], Uwidth);
853 oil_splat_u8_ns (Vtemp, (guint8 *) & empty_px_values[2], Vwidth);
857 Ydest += ((-bt) * Ywidth);
858 Udest += (UVfloor (-bt) * Uwidth);
859 Vdest += (UVfloor (-bt) * Vwidth);
861 src_loc1 = src_loc1 + (bt * video_box->in_width);
867 GST_LOG_OBJECT (video_box, "Cropped area");
868 GST_LOG_OBJECT (video_box, "Ydest value: %p Ywidth: %u", Ydest, Ywidth);
869 GST_LOG_OBJECT (video_box, "Udest value: %p Uwidth: %u", Udest, Uwidth);
870 GST_LOG_OBJECT (video_box, "Vdest value: %p Vwidth: %u", Vdest, Vwidth);
871 GST_LOG_OBJECT (video_box, "Rest: %d", rest);
872 for (i = 0; i < crop_h; i++) {
877 oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -bl);
879 for (j = 0; j < -bl; j++) {
880 Utemp[UVfloor (j)] = (Utemp[UVfloor (j)] + empty_px_values[1]) / 2;
881 Vtemp[UVfloor (j)] = (Vtemp[UVfloor (j)] + empty_px_values[2]) / 2;
887 for (j = 0; j < crop_w; j++) {
889 Ydest[j] = ((guint8 *) & src_loc1[j])[1];
890 Utemp[UVfloor (a + j)] =
891 (Utemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[2]) / 2;
892 Vtemp[UVfloor (a + j)] =
893 (Vtemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[3]) / 2;
899 oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -br);
900 for (j = 0; j < -br; j++) {
901 Utemp[UVfloor (a + crop_w + j)] =
902 (Utemp[UVfloor (a + crop_w + j)] + empty_px_values[1]) / 2;
903 Vtemp[UVfloor (a + crop_w + j)] =
904 (Vtemp[UVfloor (a + crop_w + j)] + empty_px_values[2]) / 2;
908 oil_copy_u8 (Udest, Utemp, Uwidth);
909 oil_copy_u8 (Vdest, Vtemp, Vwidth);
913 gst_video_box_clear (Utemp, Uwidth);
914 gst_video_box_clear (Vtemp, Vwidth);
915 src_loc1 += video_box->in_width;
921 oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -bl);
922 oil_splat_u8_ns (Vtemp, (guint8 *) & empty_px_values[1],
924 oil_splat_u8_ns (Utemp, (guint8 *) & empty_px_values[2],
930 for (j = 0; j < crop_w; j++) {
932 Ydest[j] = ((guint8 *) & src_loc1[j])[1];
934 if ((a + j) % 2 > 0) {
935 Utemp[UVfloor (a + j)] =
936 (Utemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[2]) / 2;
937 Vtemp[UVfloor (a + j)] =
938 (Vtemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[3]) / 2;
940 Utemp[UVfloor (a + j)] = ((guint8 *) & src_loc1[j])[2];
941 Vtemp[UVfloor (a + j)] = ((guint8 *) & src_loc1[j])[3];
949 if ((a + crop_w) % 2 > 0) {
950 Utemp[UVfloor (a + crop_w)] =
951 (Utemp[UVfloor (a + crop_w)] + empty_px_values[1]) / 2;
952 Vtemp[UVfloor (a + crop_w)] =
953 (Vtemp[UVfloor (a + crop_w)] + empty_px_values[2]) / 2;
958 oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -br);
959 oil_splat_u8_ns (&Utemp[UVfloor (a + crop_w)],
960 (guint8 *) & empty_px_values[1], UVceil ((-br) + j));
961 oil_splat_u8_ns (&Vtemp[UVfloor (a + crop_w)],
962 (guint8 *) & empty_px_values[2], UVceil ((-br) + j));
966 src_loc1 += video_box->in_width;
973 oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], (-bb) * Ywidth);
975 for (i = 0; i < Uwidth; i++) {
976 Utemp[i] = (Utemp[i] + empty_px_values[1]) / 2;
978 for (i = 0; i < Vwidth; i++) {
979 Vtemp[i] = (Vtemp[i] + empty_px_values[2]) / 2;
982 oil_copy_u8 (Udest, Utemp, Uwidth);
983 oil_copy_u8 (Vdest, Vtemp, Vwidth);
988 oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1],
989 (UVfloor ((-bb))) * Uwidth);
990 oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2],
991 (UVfloor ((-bb))) * Vwidth);
994 oil_copy_u8 (Udest, Utemp, Uwidth);
995 oil_copy_u8 (Vdest, Vtemp, Vwidth);
999 GST_LOG_OBJECT (video_box, "image created");
1005 gst_video_box_i420_ayuv (GstVideoBox * video_box, const guint8 * src,
1008 const guint8 *srcY, *srcU, *srcV;
1009 gint crop_width, crop_width2, crop_height;
1010 gint out_width, out_height;
1011 gint src_stridey, src_strideu, src_stridev;
1012 gint br, bl, bt, bb;
1013 gint colorY, colorU, colorV;
1015 guint8 b_alpha = (guint8) (video_box->border_alpha * 255);
1016 guint8 i_alpha = (guint8) (video_box->alpha * 255);
1018 guint32 *destb = (guint32 *) dest;
1021 br = video_box->border_right;
1022 bl = video_box->border_left;
1023 bt = video_box->border_top;
1024 bb = video_box->border_bottom;
1026 out_width = video_box->out_width;
1027 out_height = video_box->out_height;
1030 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0,
1031 video_box->in_width);
1033 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1,
1034 video_box->in_width);
1036 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2,
1037 video_box->in_width);
1039 crop_width = video_box->in_width;
1040 crop_width -= (video_box->crop_left + video_box->crop_right);
1041 crop_width2 = crop_width / 2;
1042 crop_height = video_box->in_height;
1043 crop_height -= (video_box->crop_top + video_box->crop_bottom);
1046 src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
1047 video_box->in_width, video_box->in_height);
1048 srcY += src_stridey * video_box->crop_top + video_box->crop_left;
1050 src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
1051 video_box->in_width, video_box->in_height);
1052 srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1054 src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
1055 video_box->in_width, video_box->in_height);
1056 srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1058 colorY = yuv_colors_Y[video_box->fill_type];
1059 colorU = yuv_colors_U[video_box->fill_type];
1060 colorV = yuv_colors_V[video_box->fill_type];
1063 GUINT32_FROM_BE ((b_alpha << 24) | (colorY << 16) | (colorU << 8) |
1068 size_t nb_pixels = bt * out_width;
1070 oil_splat_u32_ns (destb, &ayuv, nb_pixels);
1073 for (i = 0; i < crop_height; i++) {
1077 oil_splat_u32_ns (destp, &ayuv, bl);
1080 dest = (guint8 *) destp;
1082 /* We can splat the alpha channel for the whole line */
1083 oil_splat_u8 (dest, 4, &i_alpha, crop_width);
1084 for (j = 0; j < crop_width2; j++) {
1095 srcU -= crop_width2;
1096 srcV -= crop_width2;
1098 srcU += src_strideu - crop_width2;
1099 srcV += src_stridev - crop_width2;
1101 srcY += src_stridey - (crop_width2 * 2);
1103 destp = (guint32 *) dest;
1106 oil_splat_u32_ns (destp, &ayuv, br);
1112 size_t nb_pixels = bb * out_width;
1114 oil_splat_u32_ns (destb, &ayuv, nb_pixels);
1120 gst_video_box_i420_i420 (GstVideoBox * video_box, const guint8 * src,
1123 const guint8 *srcY, *srcU, *srcV;
1124 guint8 *destY, *destU, *destV;
1125 gint crop_width, crop_height;
1126 gint out_width, out_height;
1127 gint src_width, src_height;
1128 gint src_stride, dest_stride;
1129 gint br, bl, bt, bb;
1131 br = video_box->border_right;
1132 bl = video_box->border_left;
1133 bt = video_box->border_top;
1134 bb = video_box->border_bottom;
1136 out_width = video_box->out_width;
1137 out_height = video_box->out_height;
1139 src_width = video_box->in_width;
1140 src_height = video_box->in_height;
1142 crop_width = src_width - (video_box->crop_left + video_box->crop_right);
1143 crop_height = src_height - (video_box->crop_top + video_box->crop_bottom);
1147 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, src_width);
1149 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, out_width);
1152 dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
1153 out_width, out_height);
1156 src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
1157 src_width, src_height);
1158 srcY += src_stride * video_box->crop_top + video_box->crop_left;
1160 gst_video_box_copy_plane_i420 (video_box, srcY, destY, br, bl, bt, bb,
1161 crop_width, crop_height, src_stride, out_width, dest_stride,
1162 yuv_colors_Y[video_box->fill_type]);
1169 /* we need to round up to make sure we draw all the U and V lines */
1170 crop_width = (crop_width + 1) / 2;
1171 crop_height = (crop_height + 1) / 2;
1175 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, src_width);
1177 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, out_width);
1180 dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
1181 out_width, out_height);
1184 src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
1185 src_width, src_height);
1186 srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1188 gst_video_box_copy_plane_i420 (video_box, srcU, destU, br, bl, bt, bb,
1189 crop_width, crop_height, src_stride, out_width / 2, dest_stride,
1190 yuv_colors_U[video_box->fill_type]);
1194 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, src_width);
1196 gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, out_width);
1199 dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
1200 out_width, out_height);
1203 src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
1204 src_width, src_height);
1205 srcV += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1207 gst_video_box_copy_plane_i420 (video_box, srcV, destV, br, bl, bt, bb,
1208 crop_width, crop_height, src_stride, out_width / 2, dest_stride,
1209 yuv_colors_V[video_box->fill_type]);
1212 static GstFlowReturn
1213 gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
1216 GstVideoBox *video_box = GST_VIDEO_BOX (trans);
1217 const guint8 *indata;
1219 GstClockTime timestamp, stream_time;
1221 timestamp = GST_BUFFER_TIMESTAMP (in);
1223 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
1225 GST_DEBUG_OBJECT (video_box, "sync to %" GST_TIME_FORMAT,
1226 GST_TIME_ARGS (timestamp));
1228 if (GST_CLOCK_TIME_IS_VALID (stream_time))
1229 gst_object_sync_values (G_OBJECT (video_box), stream_time);
1231 indata = GST_BUFFER_DATA (in);
1232 outdata = GST_BUFFER_DATA (out);
1234 g_mutex_lock (video_box->mutex);
1235 switch (video_box->in_format) {
1236 case GST_VIDEO_FORMAT_AYUV:
1237 switch (video_box->out_format) {
1238 case GST_VIDEO_FORMAT_AYUV:
1239 gst_video_box_ayuv_ayuv (video_box, indata, outdata);
1241 case GST_VIDEO_FORMAT_I420:
1242 gst_video_box_ayuv_i420 (video_box, indata, outdata);
1245 goto invalid_format;
1248 case GST_VIDEO_FORMAT_I420:
1249 switch (video_box->out_format) {
1250 case GST_VIDEO_FORMAT_AYUV:
1251 gst_video_box_i420_ayuv (video_box, indata, outdata);
1253 case GST_VIDEO_FORMAT_I420:
1254 gst_video_box_i420_i420 (video_box, indata, outdata);
1257 goto invalid_format;
1261 goto invalid_format;
1263 g_mutex_unlock (video_box->mutex);
1269 g_mutex_unlock (video_box->mutex);
1270 return GST_FLOW_ERROR;
1274 /* FIXME: 0.11 merge with videocrop plugin */
1276 plugin_init (GstPlugin * plugin)
1280 gst_controller_init (NULL, NULL);
1282 GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
1283 "Resizes a video by adding borders or cropping");
1285 return gst_element_register (plugin, "videobox", GST_RANK_NONE,
1286 GST_TYPE_VIDEO_BOX);
1289 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1292 "resizes a video by adding borders or cropping",
1293 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)