fix up more enums
[platform/upstream/gst-plugins-good.git] / gst / videobox / gstvideobox.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <gst/gst.h>
24 #include <gst/base/gstbasetransform.h>
25 #include <gst/video/video.h>
26
27 #include <liboil/liboil.h>
28 #include <string.h>
29
30 GST_DEBUG_CATEGORY (videobox_debug);
31 #define GST_CAT_DEFAULT videobox_debug
32
33 #define GST_TYPE_VIDEO_BOX \
34   (gst_video_box_get_type())
35 #define GST_VIDEO_BOX(obj) \
36   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_BOX,GstVideoBox))
37 #define GST_VIDEO_BOX_CLASS(klass) \
38   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_BOX,GstVideoBoxClass))
39 #define GST_IS_VIDEO_BOX(obj) \
40   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_BOX))
41 #define GST_IS_VIDEO_BOX_CLASS(obj) \
42   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_BOX))
43
44 typedef struct _GstVideoBox GstVideoBox;
45 typedef struct _GstVideoBoxClass GstVideoBoxClass;
46
47 typedef enum
48 {
49   VIDEO_BOX_FILL_BLACK,
50   VIDEO_BOX_FILL_GREEN,
51   VIDEO_BOX_FILL_BLUE,
52 }
53 GstVideoBoxFill;
54
55 struct _GstVideoBox
56 {
57   GstBaseTransform element;
58
59   /* caps */
60   gint in_width, in_height;
61   gint out_width, out_height;
62
63   gint box_left, box_right, box_top, box_bottom;
64
65   gint border_left, border_right, border_top, border_bottom;
66   gint crop_left, crop_right, crop_top, crop_bottom;
67
68   gboolean use_alpha;
69   gdouble alpha;
70   gdouble border_alpha;
71
72   GstVideoBoxFill fill_type;
73 };
74
75 struct _GstVideoBoxClass
76 {
77   GstBaseTransformClass parent_class;
78 };
79
80 /* elementfactory information */
81 static GstElementDetails gst_video_box_details =
82 GST_ELEMENT_DETAILS ("video box filter",
83     "Filter/Effect/Video",
84     "Resizes a video by adding borders or cropping",
85     "Wim Taymans <wim@fluendo.com>");
86
87
88 #define DEFAULT_LEFT      0
89 #define DEFAULT_RIGHT     0
90 #define DEFAULT_TOP       0
91 #define DEFAULT_BOTTOM    0
92 #define DEFAULT_FILL_TYPE VIDEO_BOX_FILL_BLACK
93 #define DEFAULT_ALPHA     1.0
94 #define DEFAULT_BORDER_ALPHA 1.0
95
96 enum
97 {
98   PROP_0,
99   PROP_LEFT,
100   PROP_RIGHT,
101   PROP_TOP,
102   PROP_BOTTOM,
103   PROP_FILL_TYPE,
104   PROP_ALPHA,
105   PROP_BORDER_ALPHA,
106   /* FILL ME */
107 };
108
109 static GstStaticPadTemplate gst_video_box_src_template =
110 GST_STATIC_PAD_TEMPLATE ("src",
111     GST_PAD_SRC,
112     GST_PAD_ALWAYS,
113     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ AYUV, I420 }"))
114     );
115
116 static GstStaticPadTemplate gst_video_box_sink_template =
117 GST_STATIC_PAD_TEMPLATE ("sink",
118     GST_PAD_SINK,
119     GST_PAD_ALWAYS,
120     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
121     );
122
123
124 GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
125     GST_TYPE_BASE_TRANSFORM);
126
127 static void gst_video_box_set_property (GObject * object, guint prop_id,
128     const GValue * value, GParamSpec * pspec);
129 static void gst_video_box_get_property (GObject * object, guint prop_id,
130     GValue * value, GParamSpec * pspec);
131
132 static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
133     GstPadDirection direction, GstCaps * from);
134 static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
135     GstCaps * in, GstCaps * out);
136 static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
137     GstCaps * caps, guint * size);
138 static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
139     GstBuffer * in, GstBuffer * out);
140
141
142 #define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
143 static GType
144 gst_video_box_fill_get_type (void)
145 {
146   static GType video_box_fill_type = 0;
147   static GEnumValue video_box_fill[] = {
148     {VIDEO_BOX_FILL_BLACK, "Black", "black"},
149     {VIDEO_BOX_FILL_GREEN, "Colorkey green", "green"},
150     {VIDEO_BOX_FILL_BLUE, "Colorkey blue", "blue"},
151     {0, NULL, NULL},
152   };
153
154   if (!video_box_fill_type) {
155     video_box_fill_type =
156         g_enum_register_static ("GstVideoBoxFill", video_box_fill);
157   }
158   return video_box_fill_type;
159 }
160
161
162 static void
163 gst_video_box_base_init (gpointer g_class)
164 {
165   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
166
167   gst_element_class_set_details (element_class, &gst_video_box_details);
168
169   gst_element_class_add_pad_template (element_class,
170       gst_static_pad_template_get (&gst_video_box_sink_template));
171   gst_element_class_add_pad_template (element_class,
172       gst_static_pad_template_get (&gst_video_box_src_template));
173 }
174
175 static void
176 gst_video_box_class_init (GstVideoBoxClass * klass)
177 {
178   GObjectClass *gobject_class;
179   GstBaseTransformClass *trans_class;
180
181   gobject_class = (GObjectClass *) klass;
182   trans_class = (GstBaseTransformClass *) klass;
183
184   gobject_class->set_property = gst_video_box_set_property;
185   gobject_class->get_property = gst_video_box_get_property;
186
187   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILL_TYPE,
188       g_param_spec_enum ("fill", "Fill", "How to fill the borders",
189           GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
190           (GParamFlags) G_PARAM_READWRITE));
191   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEFT,
192       g_param_spec_int ("left", "Left",
193           "Pixels to box at left (<0  = add a border)", G_MININT, G_MAXINT,
194           DEFAULT_LEFT, G_PARAM_READWRITE));
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,
198           DEFAULT_RIGHT, G_PARAM_READWRITE));
199   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOP,
200       g_param_spec_int ("top", "Top",
201           "Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
202           DEFAULT_TOP, G_PARAM_READWRITE));
203   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BOTTOM,
204       g_param_spec_int ("bottom", "Bottom",
205           "Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
206           DEFAULT_BOTTOM, G_PARAM_READWRITE));
207   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
208       g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
209           DEFAULT_ALPHA, G_PARAM_READWRITE));
210   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
211       g_param_spec_double ("border_alpha", "Border Alpha",
212           "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
213           G_PARAM_READWRITE));
214
215   trans_class->transform_caps = gst_video_box_transform_caps;
216   trans_class->set_caps = gst_video_box_set_caps;
217   trans_class->get_unit_size = gst_video_box_get_unit_size;
218   trans_class->transform = gst_video_box_transform;
219
220   GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
221       "Resizes a video by adding borders or cropping");
222 }
223
224 static void
225 gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
226 {
227   video_box->box_right = DEFAULT_RIGHT;
228   video_box->box_left = DEFAULT_LEFT;
229   video_box->box_top = DEFAULT_TOP;
230   video_box->box_bottom = DEFAULT_BOTTOM;
231   video_box->crop_right = 0;
232   video_box->crop_left = 0;
233   video_box->crop_top = 0;
234   video_box->crop_bottom = 0;
235   video_box->fill_type = DEFAULT_FILL_TYPE;
236   video_box->alpha = DEFAULT_ALPHA;
237   video_box->border_alpha = DEFAULT_BORDER_ALPHA;
238 }
239
240 static void
241 gst_video_box_set_property (GObject * object, guint prop_id,
242     const GValue * value, GParamSpec * pspec)
243 {
244   GstVideoBox *video_box = GST_VIDEO_BOX (object);
245
246   switch (prop_id) {
247     case PROP_LEFT:
248       video_box->box_left = g_value_get_int (value);
249       if (video_box->box_left < 0) {
250         video_box->border_left = -video_box->box_left;
251         video_box->crop_left = 0;
252       } else {
253         video_box->border_left = 0;
254         video_box->crop_left = video_box->box_left;
255       }
256       break;
257     case PROP_RIGHT:
258       video_box->box_right = g_value_get_int (value);
259       if (video_box->box_right < 0) {
260         video_box->border_right = -video_box->box_right;
261         video_box->crop_right = 0;
262       } else {
263         video_box->border_right = 0;
264         video_box->crop_right = video_box->box_right;
265       }
266       break;
267     case PROP_TOP:
268       video_box->box_top = g_value_get_int (value);
269       if (video_box->box_top < 0) {
270         video_box->border_top = -video_box->box_top;
271         video_box->crop_top = 0;
272       } else {
273         video_box->border_top = 0;
274         video_box->crop_top = video_box->box_top;
275       }
276       break;
277     case PROP_BOTTOM:
278       video_box->box_bottom = g_value_get_int (value);
279       if (video_box->box_bottom < 0) {
280         video_box->border_bottom = -video_box->box_bottom;
281         video_box->crop_bottom = 0;
282       } else {
283         video_box->border_bottom = 0;
284         video_box->crop_bottom = video_box->box_bottom;
285       }
286       break;
287     case PROP_FILL_TYPE:
288       video_box->fill_type = g_value_get_enum (value);
289       break;
290     case PROP_ALPHA:
291       video_box->alpha = g_value_get_double (value);
292       break;
293     case PROP_BORDER_ALPHA:
294       video_box->border_alpha = g_value_get_double (value);
295       break;
296     default:
297       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
298       break;
299   }
300 }
301
302 static void
303 gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
304     GParamSpec * pspec)
305 {
306   GstVideoBox *video_box = GST_VIDEO_BOX (object);
307
308   switch (prop_id) {
309     case PROP_LEFT:
310       g_value_set_int (value, video_box->box_left);
311       break;
312     case PROP_RIGHT:
313       g_value_set_int (value, video_box->box_right);
314       break;
315     case PROP_TOP:
316       g_value_set_int (value, video_box->box_top);
317       break;
318     case PROP_BOTTOM:
319       g_value_set_int (value, video_box->box_bottom);
320       break;
321     case PROP_FILL_TYPE:
322       g_value_set_enum (value, video_box->fill_type);
323       break;
324     case PROP_ALPHA:
325       g_value_set_double (value, video_box->alpha);
326       break;
327     case PROP_BORDER_ALPHA:
328       g_value_set_double (value, video_box->border_alpha);
329       break;
330     default:
331       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332       break;
333   }
334 }
335
336 static GstCaps *
337 gst_video_box_transform_caps (GstBaseTransform * trans,
338     GstPadDirection direction, GstCaps * from)
339 {
340   GstVideoBox *video_box;
341   GstCaps *to;
342   GstStructure *structure;
343   GValue list_value = { 0 }, value = {
344   0};
345   gint dir, i, tmp;
346
347   video_box = GST_VIDEO_BOX (trans);
348
349   g_value_init (&list_value, GST_TYPE_LIST);
350   g_value_init (&value, GST_TYPE_FOURCC);
351   gst_value_set_fourcc (&value, GST_MAKE_FOURCC ('I', '4', '2', '0'));
352   gst_value_list_append_value (&list_value, &value);
353   g_value_unset (&value);
354
355   to = gst_caps_copy (from);
356   dir = (direction == GST_PAD_SINK) ? -1 : 1;
357
358   for (i = 0; i < gst_caps_get_size (to); i++) {
359     structure = gst_caps_get_structure (to, i);
360     if (direction == GST_PAD_SINK) {    /* I420 to { I420, AYUV } */
361       g_value_init (&value, GST_TYPE_FOURCC);
362       gst_value_set_fourcc (&value, GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'));
363       gst_value_list_append_value (&list_value, &value);
364       g_value_unset (&value);
365       gst_structure_set_value (structure, "format", &list_value);
366     } else if (direction == GST_PAD_SRC) {
367       gst_structure_set_value (structure, "format", &list_value);
368     }
369     if (gst_structure_get_int (structure, "width", &tmp))
370       gst_structure_set (structure, "width", G_TYPE_INT,
371           tmp + dir * (video_box->box_left + video_box->box_right), NULL);
372     if (gst_structure_get_int (structure, "height", &tmp))
373       gst_structure_set (structure, "height", G_TYPE_INT,
374           tmp + dir * (video_box->box_top + video_box->box_bottom), NULL);
375   }
376
377   g_value_unset (&list_value);
378
379   GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
380       " to %" GST_PTR_FORMAT, direction, from, to);
381
382   return to;
383 }
384
385 static gboolean
386 gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
387 {
388   GstVideoBox *video_box;
389   GstStructure *structure;
390   gboolean ret;
391   guint32 fourcc = 0;
392
393   video_box = GST_VIDEO_BOX (trans);
394
395   structure = gst_caps_get_structure (in, 0);
396   ret = gst_structure_get_int (structure, "width", &video_box->in_width);
397   ret &= gst_structure_get_int (structure, "height", &video_box->in_height);
398
399   structure = gst_caps_get_structure (out, 0);
400   ret &= gst_structure_get_int (structure, "width", &video_box->out_width);
401   ret &= gst_structure_get_int (structure, "height", &video_box->out_height);
402   ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
403
404   if (fourcc == GST_STR_FOURCC ("AYUV")) {
405     video_box->use_alpha = TRUE;
406   } else {
407     if (video_box->box_left == 0 && video_box->box_right == 0 &&
408         video_box->box_top == 0 && video_box->box_bottom == 0) {
409       gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box), TRUE);
410       GST_LOG ("we are using passthrough");
411     } else {
412       gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box),
413           FALSE);
414       GST_LOG ("we are not using passthrough");
415     }
416   }
417
418   return ret;
419 }
420
421 /* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
422 #define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
423 #define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
424 #define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
425
426 #define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
427 #define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
428 #define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
429
430 #define GST_VIDEO_I420_SIZE(w,h)     (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
431
432 static gboolean
433 gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
434     guint * size)
435 {
436   GstVideoBox *video_box;
437   GstStructure *structure = NULL;
438   guint32 fourcc;
439   gint width, height;
440
441   g_return_val_if_fail (size, FALSE);
442   video_box = GST_VIDEO_BOX (trans);
443
444   structure = gst_caps_get_structure (caps, 0);
445   gst_structure_get_fourcc (structure, "format", &fourcc);
446   gst_structure_get_int (structure, "width", &width);
447   gst_structure_get_int (structure, "height", &height);
448
449   switch (fourcc) {
450     case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
451       *size = width * height * 4;
452       break;
453     case GST_MAKE_FOURCC ('I', '4', '2', '0'):
454       *size = GST_VIDEO_I420_SIZE (width, height);
455       break;
456     default:
457       return FALSE;
458       break;
459   }
460
461   return TRUE;
462 }
463
464 static int yuv_colors_Y[] = { 16, 150, 29 };
465 static int yuv_colors_U[] = { 128, 46, 255 };
466 static int yuv_colors_V[] = { 128, 21, 107 };
467
468 static void
469 gst_video_box_copy_plane_i420 (GstVideoBox * video_box, guint8 * src,
470     guint8 * dest, gint br, gint bl, gint bt, gint bb, gint src_crop_width,
471     gint src_crop_height, gint src_stride, gint dest_width, gint dest_stride,
472     guint8 fill_color)
473 {
474   gint j;
475
476   /* top border */
477   for (j = 0; j < bt; j++) {
478     oil_splat_u8_ns (dest, &fill_color, dest_width);
479     dest += dest_stride;
480   }
481
482   /* copy and add left and right border */
483   for (j = 0; j < src_crop_height; j++) {
484     oil_splat_u8_ns (dest, &fill_color, bl);
485     oil_memcpy (dest + bl, src, src_crop_width);
486     oil_splat_u8_ns (dest + bl + src_crop_width, &fill_color, br);
487     dest += dest_stride;
488     src += src_stride;
489   }
490
491   /* bottom border */
492   for (j = 0; j < bb; j++) {
493     oil_splat_u8_ns (dest, &fill_color, dest_width);
494     dest += dest_stride;
495   }
496 }
497
498 static void
499 gst_video_box_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
500 {
501   guint8 *srcY, *srcU, *srcV;
502   guint8 *destY, *destU, *destV;
503   gint crop_width, crop_height;
504   gint out_width, out_height;
505   gint src_width, src_height;
506   gint src_stride, dest_stride;
507   gint br, bl, bt, bb;
508
509   br = video_box->border_right;
510   bl = video_box->border_left;
511   bt = video_box->border_top;
512   bb = video_box->border_bottom;
513
514   out_width = video_box->out_width;
515   out_height = video_box->out_height;
516
517   src_width = video_box->in_width;
518   src_height = video_box->in_height;
519
520   crop_width = src_width - (video_box->crop_left + video_box->crop_right);
521   crop_height = src_height - (video_box->crop_top + video_box->crop_bottom);
522
523   /* Y plane */
524   src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
525   dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
526
527   destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
528
529   srcY = src + GST_VIDEO_I420_Y_OFFSET (src_width, src_height);
530   srcY += src_stride * video_box->crop_top + video_box->crop_left;
531
532   gst_video_box_copy_plane_i420 (video_box, srcY, destY, br, bl, bt, bb,
533       crop_width, crop_height, src_stride, out_width, dest_stride,
534       yuv_colors_Y[video_box->fill_type]);
535
536   /* U plane */
537   src_stride = GST_VIDEO_I420_U_ROWSTRIDE (src_width);
538   dest_stride = GST_VIDEO_I420_U_ROWSTRIDE (out_width);
539
540   destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);
541
542   srcU = src + GST_VIDEO_I420_U_OFFSET (src_width, src_height);
543   srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
544
545   gst_video_box_copy_plane_i420 (video_box, srcU, destU, br / 2, bl / 2, bt / 2,
546       bb / 2, crop_width / 2, crop_height / 2, src_stride, out_width / 2,
547       dest_stride, yuv_colors_U[video_box->fill_type]);
548
549   /* V plane */
550   src_stride = GST_VIDEO_I420_V_ROWSTRIDE (src_width);
551   dest_stride = GST_VIDEO_I420_V_ROWSTRIDE (out_width);
552
553   destV = dest + GST_VIDEO_I420_V_OFFSET (out_width, out_height);
554
555   srcV = src + GST_VIDEO_I420_V_OFFSET (src_width, src_height);
556   srcV += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
557
558   gst_video_box_copy_plane_i420 (video_box, srcV, destV, br / 2, bl / 2, bt / 2,
559       bb / 2, crop_width / 2, crop_height / 2, src_stride, out_width / 2,
560       dest_stride, yuv_colors_V[video_box->fill_type]);
561 }
562
563 /* Note the source image is always I420, we
564  * are converting to AYUV on the fly here */
565 static void
566 gst_video_box_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
567 {
568   guint8 *srcY, *srcU, *srcV;
569   gint crop_width, crop_width2, crop_height;
570   gint out_width, out_height;
571   gint src_stridey, src_strideu, src_stridev;
572   gint br, bl, bt, bb;
573   gint colorY, colorU, colorV;
574   gint i, j;
575   guint8 b_alpha = (guint8) (video_box->border_alpha * 255);
576   guint8 i_alpha = (guint8) (video_box->alpha * 255);
577   guint32 *destp = (guint32 *) dest;
578   guint32 ayuv;
579
580   br = video_box->border_right;
581   bl = video_box->border_left;
582   bt = video_box->border_top;
583   bb = video_box->border_bottom;
584
585   out_width = video_box->out_width;
586   out_height = video_box->out_height;
587
588   src_stridey = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
589   src_strideu = GST_VIDEO_I420_U_ROWSTRIDE (video_box->in_width);
590   src_stridev = GST_VIDEO_I420_V_ROWSTRIDE (video_box->in_width);
591
592   crop_width =
593       video_box->in_width - (video_box->crop_left + video_box->crop_right);
594   crop_width2 = crop_width / 2;
595   crop_height =
596       video_box->in_height - (video_box->crop_top + video_box->crop_bottom);
597
598   srcY =
599       src + GST_VIDEO_I420_Y_OFFSET (video_box->in_width, video_box->in_height);
600   srcY += src_stridey * video_box->crop_top + video_box->crop_left;
601   srcU =
602       src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
603   srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
604   srcV =
605       src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
606   srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
607
608   colorY = yuv_colors_Y[video_box->fill_type];
609   colorU = yuv_colors_U[video_box->fill_type];
610   colorV = yuv_colors_V[video_box->fill_type];
611
612   ayuv =
613       GUINT32_FROM_BE ((b_alpha << 24) | (colorY << 16) | (colorU << 8) |
614       colorV);
615
616   /* top border */
617   if (bt) {
618     size_t nb_pixels = bt * out_width;
619
620     oil_splat_u32_ns (destp, &ayuv, nb_pixels);
621     destp += nb_pixels;
622   }
623   for (i = 0; i < crop_height; i++) {
624     /* left border */
625     if (bl) {
626       oil_splat_u32_ns (destp, &ayuv, bl);
627       destp += bl;
628     }
629     dest = (guint8 *) destp;
630     /* center */
631     /* We can splat the alpha channel for the whole line */
632     oil_splat_u8 (dest, 4, &i_alpha, crop_width);
633     for (j = 0; j < crop_width2; j++) {
634       dest++;
635       *dest++ = *srcY++;
636       *dest++ = *srcU;
637       *dest++ = *srcV;
638       dest++;
639       *dest++ = *srcY++;
640       *dest++ = *srcU++;
641       *dest++ = *srcV++;
642     }
643     if (i % 2 == 0) {
644       srcU -= crop_width2;
645       srcV -= crop_width2;
646     } else {
647       srcU += src_strideu - crop_width2;
648       srcV += src_stridev - crop_width2;
649     }
650     srcY += src_stridey - crop_width;
651
652     destp = (guint32 *) dest;
653     /* right border */
654     if (br) {
655       oil_splat_u32_ns (destp, &ayuv, br);
656       destp += br;
657     }
658   }
659   /* bottom border */
660   if (bb) {
661     size_t nb_pixels = bb * out_width;
662
663     oil_splat_u32_ns (destp, &ayuv, nb_pixels);
664     destp += nb_pixels;
665   }
666 }
667
668 static GstFlowReturn
669 gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
670     GstBuffer * out)
671 {
672   GstVideoBox *video_box;
673
674   video_box = GST_VIDEO_BOX (trans);
675
676   if (video_box->use_alpha) {
677     gst_video_box_ayuv (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
678   } else {
679     gst_video_box_i420 (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
680   }
681
682   return GST_FLOW_OK;
683 }
684
685 static gboolean
686 plugin_init (GstPlugin * plugin)
687 {
688   return gst_element_register (plugin, "videobox", GST_RANK_NONE,
689       GST_TYPE_VIDEO_BOX);
690 }
691
692 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
693     GST_VERSION_MINOR,
694     "videobox",
695     "resizes a video by adding borders or cropping",
696     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)