Fix row strides for I420 (fixes #163159)
[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/video/video.h>
25
26 #include <string.h>
27
28 #define GST_TYPE_VIDEO_BOX \
29   (gst_video_box_get_type())
30 #define GST_VIDEO_BOX(obj) \
31   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_BOX,GstVideoBox))
32 #define GST_VIDEO_BOX_CLASS(klass) \
33   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_BOX,GstVideoBoxClass))
34 #define GST_IS_VIDEO_BOX(obj) \
35   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_BOX))
36 #define GST_IS_VIDEO_BOX_CLASS(obj) \
37   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_BOX))
38
39 typedef struct _GstVideoBox GstVideoBox;
40 typedef struct _GstVideoBoxClass GstVideoBoxClass;
41
42 typedef enum
43 {
44   VIDEO_BOX_FILL_BLACK,
45   VIDEO_BOX_FILL_GREEN,
46   VIDEO_BOX_FILL_BLUE,
47 }
48 GstVideoBoxFill;
49
50 struct _GstVideoBox
51 {
52   GstElement element;
53
54   /* pads */
55   GstPad *sinkpad;
56   GstPad *srcpad;
57
58   /* caps */
59   gint in_width, in_height;
60   gint out_width, out_height;
61
62   gint box_left, box_right, box_top, box_bottom;
63
64   gint border_left, border_right, border_top, border_bottom;
65   gint crop_left, crop_right, crop_top, crop_bottom;
66
67   gboolean use_alpha;
68   gdouble alpha;
69   gdouble border_alpha;
70
71   GstVideoBoxFill fill_type;
72 };
73
74 struct _GstVideoBoxClass
75 {
76   GstElementClass parent_class;
77 };
78
79 /* elementfactory information */
80 static GstElementDetails gst_video_box_details =
81 GST_ELEMENT_DETAILS ("video box filter",
82     "Filter/Effect/Video",
83     "Resizes a video by adding borders or cropping",
84     "Wim Taymans <wim@fluendo.com>");
85
86
87 #define DEFAULT_LEFT      0
88 #define DEFAULT_RIGHT     0
89 #define DEFAULT_TOP       0
90 #define DEFAULT_BOTTOM    0
91 #define DEFAULT_FILL_TYPE VIDEO_BOX_FILL_BLACK
92 #define DEFAULT_ALPHA     1.0
93 #define DEFAULT_BORDER_ALPHA 1.0
94
95 enum
96 {
97   ARG_0,
98   ARG_LEFT,
99   ARG_RIGHT,
100   ARG_TOP,
101   ARG_BOTTOM,
102   ARG_FILL_TYPE,
103   ARG_ALPHA,
104   ARG_BORDER_ALPHA,
105   /* FILL ME */
106 };
107
108 static GstStaticPadTemplate gst_video_box_src_template =
109 GST_STATIC_PAD_TEMPLATE ("src",
110     GST_PAD_SRC,
111     GST_PAD_ALWAYS,
112     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, AYUV }"))
113     );
114
115 static GstStaticPadTemplate gst_video_box_sink_template =
116 GST_STATIC_PAD_TEMPLATE ("sink",
117     GST_PAD_SINK,
118     GST_PAD_ALWAYS,
119     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
120     );
121
122
123 static void gst_video_box_base_init (gpointer g_class);
124 static void gst_video_box_class_init (GstVideoBoxClass * klass);
125 static void gst_video_box_init (GstVideoBox * video_box);
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 GstPadLinkReturn
133 gst_video_box_sink_link (GstPad * pad, const GstCaps * caps);
134 static void gst_video_box_chain (GstPad * pad, GstData * _data);
135
136 static GstElementStateReturn gst_video_box_change_state (GstElement * element);
137
138
139 static GstElementClass *parent_class = NULL;
140
141 #define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
142 static GType
143 gst_video_box_fill_get_type (void)
144 {
145   static GType video_box_fill_type = 0;
146   static GEnumValue video_box_fill[] = {
147     {VIDEO_BOX_FILL_BLACK, "0", "Black"},
148     {VIDEO_BOX_FILL_GREEN, "1", "Colorkey green"},
149     {VIDEO_BOX_FILL_BLUE, "2", "Colorkey blue"},
150     {0, NULL, NULL},
151   };
152
153   if (!video_box_fill_type) {
154     video_box_fill_type =
155         g_enum_register_static ("GstVideoBoxFill", video_box_fill);
156   }
157   return video_box_fill_type;
158 }
159
160 /* static guint gst_video_box_signals[LAST_SIGNAL] = { 0 }; */
161
162 GType
163 gst_video_box_get_type (void)
164 {
165   static GType video_box_type = 0;
166
167   if (!video_box_type) {
168     static const GTypeInfo video_box_info = {
169       sizeof (GstVideoBoxClass),
170       gst_video_box_base_init,
171       NULL,
172       (GClassInitFunc) gst_video_box_class_init,
173       NULL,
174       NULL,
175       sizeof (GstVideoBox),
176       0,
177       (GInstanceInitFunc) gst_video_box_init,
178     };
179
180     video_box_type =
181         g_type_register_static (GST_TYPE_ELEMENT, "GstVideoBox",
182         &video_box_info, 0);
183   }
184   return video_box_type;
185 }
186
187 static void
188 gst_video_box_base_init (gpointer g_class)
189 {
190   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
191
192   gst_element_class_set_details (element_class, &gst_video_box_details);
193
194   gst_element_class_add_pad_template (element_class,
195       gst_static_pad_template_get (&gst_video_box_sink_template));
196   gst_element_class_add_pad_template (element_class,
197       gst_static_pad_template_get (&gst_video_box_src_template));
198 }
199 static void
200 gst_video_box_class_init (GstVideoBoxClass * klass)
201 {
202   GObjectClass *gobject_class;
203   GstElementClass *gstelement_class;
204
205   gobject_class = (GObjectClass *) klass;
206   gstelement_class = (GstElementClass *) klass;
207
208   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
209
210   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FILL_TYPE,
211       g_param_spec_enum ("fill", "Fill", "How to fill the borders",
212           GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
213           (GParamFlags) G_PARAM_READWRITE));
214   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LEFT,
215       g_param_spec_int ("left", "Left",
216           "Pixels to box at left (<0  = add a border)", G_MININT, G_MAXINT,
217           DEFAULT_LEFT, G_PARAM_READWRITE));
218   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RIGHT,
219       g_param_spec_int ("right", "Right",
220           "Pixels to box at right (<0 = add a border)", G_MININT, G_MAXINT,
221           DEFAULT_RIGHT, G_PARAM_READWRITE));
222   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TOP,
223       g_param_spec_int ("top", "Top",
224           "Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
225           DEFAULT_TOP, G_PARAM_READWRITE));
226   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BOTTOM,
227       g_param_spec_int ("bottom", "Bottom",
228           "Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
229           DEFAULT_BOTTOM, G_PARAM_READWRITE));
230   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALPHA,
231       g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
232           DEFAULT_ALPHA, G_PARAM_READWRITE));
233   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BORDER_ALPHA,
234       g_param_spec_double ("border_alpha", "Border Alpha",
235           "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
236           G_PARAM_READWRITE));
237
238   gobject_class->set_property = gst_video_box_set_property;
239   gobject_class->get_property = gst_video_box_get_property;
240
241   gstelement_class->change_state = gst_video_box_change_state;
242 }
243
244 static void
245 gst_video_box_init (GstVideoBox * video_box)
246 {
247   /* create the sink and src pads */
248   video_box->sinkpad =
249       gst_pad_new_from_template (gst_static_pad_template_get
250       (&gst_video_box_sink_template), "sink");
251   gst_element_add_pad (GST_ELEMENT (video_box), video_box->sinkpad);
252   gst_pad_set_chain_function (video_box->sinkpad, gst_video_box_chain);
253   gst_pad_set_link_function (video_box->sinkpad, gst_video_box_sink_link);
254
255   video_box->srcpad =
256       gst_pad_new_from_template (gst_static_pad_template_get
257       (&gst_video_box_src_template), "src");
258   gst_element_add_pad (GST_ELEMENT (video_box), video_box->srcpad);
259
260   video_box->box_right = DEFAULT_RIGHT;
261   video_box->box_left = DEFAULT_LEFT;
262   video_box->box_top = DEFAULT_TOP;
263   video_box->box_bottom = DEFAULT_BOTTOM;
264   video_box->fill_type = DEFAULT_FILL_TYPE;
265   video_box->alpha = DEFAULT_ALPHA;
266   video_box->border_alpha = DEFAULT_BORDER_ALPHA;
267
268   GST_FLAG_SET (video_box, GST_ELEMENT_EVENT_AWARE);
269 }
270
271 /* do we need this function? */
272 static void
273 gst_video_box_set_property (GObject * object, guint prop_id,
274     const GValue * value, GParamSpec * pspec)
275 {
276   GstVideoBox *video_box;
277
278   /* it's not null if we got it, but it might not be ours */
279   g_return_if_fail (GST_IS_VIDEO_BOX (object));
280
281   video_box = GST_VIDEO_BOX (object);
282
283   switch (prop_id) {
284     case ARG_LEFT:
285       video_box->box_left = g_value_get_int (value);
286       if (video_box->box_left < 0) {
287         video_box->border_left = -video_box->box_left;
288         video_box->crop_left = 0;
289       } else {
290         video_box->border_left = 0;
291         video_box->crop_left = video_box->box_left;
292       }
293       break;
294     case ARG_RIGHT:
295       video_box->box_right = g_value_get_int (value);
296       if (video_box->box_right < 0) {
297         video_box->border_right = -video_box->box_right;
298         video_box->crop_right = 0;
299       } else {
300         video_box->border_right = 0;
301         video_box->crop_right = video_box->box_right;
302       }
303       break;
304     case ARG_TOP:
305       video_box->box_top = g_value_get_int (value);
306       if (video_box->box_top < 0) {
307         video_box->border_top = -video_box->box_top;
308         video_box->crop_top = 0;
309       } else {
310         video_box->border_top = 0;
311         video_box->crop_top = video_box->box_top;
312       }
313       break;
314     case ARG_BOTTOM:
315       video_box->box_bottom = g_value_get_int (value);
316       if (video_box->box_bottom < 0) {
317         video_box->border_bottom = -video_box->box_bottom;
318         video_box->crop_bottom = 0;
319       } else {
320         video_box->border_bottom = 0;
321         video_box->crop_bottom = video_box->box_bottom;
322       }
323       break;
324     case ARG_FILL_TYPE:
325       video_box->fill_type = g_value_get_enum (value);
326       break;
327     case ARG_ALPHA:
328       video_box->alpha = g_value_get_double (value);
329       break;
330     case ARG_BORDER_ALPHA:
331       video_box->border_alpha = g_value_get_double (value);
332       break;
333     default:
334       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
335       break;
336   }
337 }
338 static void
339 gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
340     GParamSpec * pspec)
341 {
342   GstVideoBox *video_box;
343
344   /* it's not null if we got it, but it might not be ours */
345   g_return_if_fail (GST_IS_VIDEO_BOX (object));
346
347   video_box = GST_VIDEO_BOX (object);
348
349   switch (prop_id) {
350     case ARG_LEFT:
351       g_value_set_int (value, video_box->box_left);
352       break;
353     case ARG_RIGHT:
354       g_value_set_int (value, video_box->box_right);
355       break;
356     case ARG_TOP:
357       g_value_set_int (value, video_box->box_top);
358       break;
359     case ARG_BOTTOM:
360       g_value_set_int (value, video_box->box_bottom);
361       break;
362     case ARG_FILL_TYPE:
363       g_value_set_enum (value, video_box->fill_type);
364       break;
365     case ARG_ALPHA:
366       g_value_set_double (value, video_box->alpha);
367       break;
368     case ARG_BORDER_ALPHA:
369       g_value_set_double (value, video_box->border_alpha);
370       break;
371     default:
372       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
373       break;
374   }
375 }
376
377 static GstPadLinkReturn
378 gst_video_box_sink_link (GstPad * pad, const GstCaps * caps)
379 {
380   GstVideoBox *video_box;
381   GstStructure *structure;
382   gboolean ret;
383
384   video_box = GST_VIDEO_BOX (gst_pad_get_parent (pad));
385   structure = gst_caps_get_structure (caps, 0);
386
387   ret = gst_structure_get_int (structure, "width", &video_box->in_width);
388   ret &= gst_structure_get_int (structure, "height", &video_box->in_height);
389
390   return GST_PAD_LINK_OK;
391 }
392
393 #define ROUND_UP_2(x)  (((x)+1)&~1)
394 #define ROUND_UP_4(x)  (((x)+3)&~3)
395 #define ROUND_UP_8(x)  (((x)+7)&~7)
396
397 /* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
398 #define GST_VIDEO_I420_Y_ROWSTRIDE(width) (ROUND_UP_4(width))
399 #define GST_VIDEO_I420_U_ROWSTRIDE(width) (ROUND_UP_8(width)/2)
400 #define GST_VIDEO_I420_V_ROWSTRIDE(width) ((ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
401
402 #define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
403 #define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*ROUND_UP_2(h)))
404 #define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*ROUND_UP_2(h)/2))
405
406 #define GST_VIDEO_I420_SIZE(w,h)     (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*ROUND_UP_2(h)/2))
407
408 static int yuv_colors_Y[] = { 16, 150, 29 };
409 static int yuv_colors_U[] = { 128, 46, 255 };
410 static int yuv_colors_V[] = { 128, 21, 107 };
411
412 static void
413 gst_video_box_copy_plane_i420 (GstVideoBox * video_box, guint8 * src,
414     guint8 * dest, gint br, gint bl, gint bt, gint bb, gint src_crop_width,
415     gint src_crop_height, gint src_stride, gint dest_width, gint dest_stride,
416     guint8 fill_color)
417 {
418   gint j;
419
420   /* top border */
421   for (j = 0; j < bt; j++) {
422     memset (dest, fill_color, dest_width);
423     dest += dest_stride;
424   }
425
426   /* copy and add left and right border */
427   for (j = 0; j < src_crop_height; j++) {
428     memset (dest, fill_color, bl);
429     memcpy (dest + bl, src, src_crop_width);
430     memset (dest + bl + src_crop_width, fill_color, br);
431     dest += dest_stride;
432     src += src_stride;
433   }
434
435   /* bottom border */
436   for (j = 0; j < bb; j++) {
437     memset (dest, fill_color, dest_width);
438     dest += dest_stride;
439   }
440 }
441
442 static void
443 gst_video_box_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
444 {
445   guint8 *srcY, *srcU, *srcV;
446   guint8 *destY, *destU, *destV;
447   gint crop_width, crop_height;
448   gint out_width, out_height;
449   gint src_width, src_height;
450   gint src_stride, dest_stride;
451   gint br, bl, bt, bb;
452
453   br = video_box->border_right;
454   bl = video_box->border_left;
455   bt = video_box->border_top;
456   bb = video_box->border_bottom;
457
458   out_width = video_box->out_width;
459   out_height = video_box->out_height;
460
461   src_width = video_box->in_width;
462   src_height = video_box->in_height;
463
464   crop_width = src_width - (video_box->crop_left + video_box->crop_right);
465   crop_height = src_height - (video_box->crop_top + video_box->crop_bottom);
466
467   /* Y plane */
468   src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
469   dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
470
471   destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
472
473   srcY = src + GST_VIDEO_I420_Y_OFFSET (src_width, src_height);
474   srcY += src_stride * video_box->crop_top + video_box->crop_left;
475
476   gst_video_box_copy_plane_i420 (video_box, srcY, destY, br, bl, bt, bb,
477       crop_width, crop_height, src_stride, out_width, dest_stride,
478       yuv_colors_Y[video_box->fill_type]);
479
480   /* U plane */
481   src_stride = GST_VIDEO_I420_U_ROWSTRIDE (src_width);
482   dest_stride = GST_VIDEO_I420_U_ROWSTRIDE (out_width);
483
484   destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);
485
486   srcU = src + GST_VIDEO_I420_U_OFFSET (src_width, src_height);
487   srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
488
489   gst_video_box_copy_plane_i420 (video_box, srcU, destU, br / 2, bl / 2, bt / 2,
490       bb / 2, crop_width / 2, crop_height / 2, src_stride, out_width / 2,
491       dest_stride, yuv_colors_U[video_box->fill_type]);
492
493   /* V plane */
494   src_stride = GST_VIDEO_I420_V_ROWSTRIDE (src_width);
495   dest_stride = GST_VIDEO_I420_V_ROWSTRIDE (out_width);
496
497   destV = dest + GST_VIDEO_I420_V_OFFSET (out_width, out_height);
498
499   srcV = src + GST_VIDEO_I420_V_OFFSET (src_width, src_height);
500   srcV += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
501
502   gst_video_box_copy_plane_i420 (video_box, srcV, destV, br / 2, bl / 2, bt / 2,
503       bb / 2, crop_width / 2, crop_height / 2, src_stride, out_width / 2,
504       dest_stride, yuv_colors_V[video_box->fill_type]);
505 }
506
507 /* Note the source image is always I420, we
508  * are converting to AYUV on the fly here */
509
510 static void
511 gst_video_box_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
512 {
513   guint8 *srcY, *srcU, *srcV;
514   gint crop_width, crop_width2, crop_height;
515   gint out_width, out_height;
516   gint src_stride, src_stride2;
517   gint br, bl, bt, bb;
518   gint colorY, colorU, colorV;
519   gint i, j;
520   guint8 b_alpha = (guint8) (video_box->border_alpha * 255);
521   guint8 i_alpha = (guint8) (video_box->alpha * 255);
522   guint32 *destp = (guint32 *) dest;
523   guint32 ayuv;
524
525   br = video_box->border_right;
526   bl = video_box->border_left;
527   bt = video_box->border_top;
528   bb = video_box->border_bottom;
529
530   out_width = video_box->out_width;
531   out_height = video_box->out_height;
532
533   src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
534   src_stride2 = src_stride / 2;
535
536   crop_width =
537       video_box->in_width - (video_box->crop_left + video_box->crop_right);
538   crop_width2 = crop_width / 2;
539   crop_height =
540       video_box->in_height - (video_box->crop_top + video_box->crop_bottom);
541
542   srcY =
543       src + GST_VIDEO_I420_Y_OFFSET (video_box->in_width, video_box->in_height);
544   srcY += src_stride * video_box->crop_top + video_box->crop_left;
545   srcU =
546       src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
547   srcU += src_stride2 * (video_box->crop_top / 2) + (video_box->crop_left / 2);
548   srcV =
549       src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
550   srcV += src_stride2 * (video_box->crop_top / 2) + (video_box->crop_left / 2);
551
552   colorY = yuv_colors_Y[video_box->fill_type];
553   colorU = yuv_colors_U[video_box->fill_type];
554   colorV = yuv_colors_V[video_box->fill_type];
555
556   ayuv =
557       GUINT32_FROM_BE ((b_alpha << 24) | (colorY << 16) | (colorU << 8) |
558       colorV);
559
560   /* top border */
561   for (i = 0; i < bt; i++) {
562     for (j = 0; j < out_width; j++) {
563       *destp++ = ayuv;
564     }
565   }
566   for (i = 0; i < crop_height; i++) {
567     /* left border */
568     for (j = 0; j < bl; j++) {
569       *destp++ = ayuv;
570     }
571     dest = (guint8 *) destp;
572     /* center */
573     for (j = 0; j < crop_width2; j++) {
574       *dest++ = i_alpha;
575       *dest++ = *srcY++;
576       *dest++ = *srcU;
577       *dest++ = *srcV;
578       *dest++ = i_alpha;
579       *dest++ = *srcY++;
580       *dest++ = *srcU++;
581       *dest++ = *srcV++;
582     }
583     if (i % 2 == 0) {
584       srcU -= crop_width2;
585       srcV -= crop_width2;
586     } else {
587       srcU += src_stride2 - crop_width2;
588       srcV += src_stride2 - crop_width2;
589     }
590     srcY += src_stride - crop_width;
591
592     destp = (guint32 *) dest;
593     /* right border */
594     for (j = 0; j < br; j++) {
595       *destp++ = ayuv;
596     }
597   }
598   /* bottom border */
599   for (i = 0; i < bb; i++) {
600     for (j = 0; j < out_width; j++) {
601       *destp++ = ayuv;
602     }
603   }
604 }
605
606 static void
607 gst_video_box_chain (GstPad * pad, GstData * _data)
608 {
609   GstBuffer *buffer;
610   GstVideoBox *video_box;
611   GstBuffer *outbuf;
612   gint new_width, new_height;
613
614   video_box = GST_VIDEO_BOX (gst_pad_get_parent (pad));
615
616   if (GST_IS_EVENT (_data)) {
617     GstEvent *event = GST_EVENT (_data);
618
619     switch (GST_EVENT_TYPE (event)) {
620       default:
621         gst_pad_event_default (pad, event);
622         break;
623     }
624     return;
625   }
626
627   buffer = GST_BUFFER (_data);
628
629   new_width =
630       video_box->in_width - (video_box->box_left + video_box->box_right);
631   new_height =
632       video_box->in_height - (video_box->box_top + video_box->box_bottom);
633
634   if (new_width != video_box->out_width ||
635       new_height != video_box->out_height ||
636       !GST_PAD_CAPS (video_box->srcpad)) {
637     GstCaps *newcaps;
638
639     newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (video_box->sinkpad));
640
641     video_box->use_alpha = TRUE;
642
643     /* try AYUV first */
644     gst_caps_set_simple (newcaps,
645         "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
646         "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
647
648     if (GST_PAD_LINK_FAILED (gst_pad_try_set_caps (video_box->srcpad, newcaps))) {
649       video_box->use_alpha = FALSE;
650       newcaps =
651           gst_caps_copy (gst_pad_get_negotiated_caps (video_box->sinkpad));
652       gst_caps_set_simple (newcaps, "format", GST_TYPE_FOURCC,
653           GST_STR_FOURCC ("I420"), "width", G_TYPE_INT, new_width, "height",
654           G_TYPE_INT, new_height, NULL);
655
656       if (GST_PAD_LINK_FAILED (gst_pad_try_set_caps (video_box->srcpad,
657                   newcaps))) {
658         GST_ELEMENT_ERROR (video_box, CORE, NEGOTIATION, (NULL), (NULL));
659         return;
660       }
661     }
662
663     video_box->out_width = new_width;
664     video_box->out_height = new_height;
665   }
666
667   if (video_box->use_alpha) {
668     outbuf = gst_pad_alloc_buffer (video_box->srcpad,
669         GST_BUFFER_OFFSET_NONE, new_width * new_height * 4);
670
671     gst_video_box_ayuv (video_box,
672         GST_BUFFER_DATA (buffer), GST_BUFFER_DATA (outbuf));
673   } else {
674     outbuf = gst_pad_alloc_buffer (video_box->srcpad,
675         GST_BUFFER_OFFSET_NONE, GST_VIDEO_I420_SIZE (new_width, new_height));
676
677     gst_video_box_i420 (video_box,
678         GST_BUFFER_DATA (buffer), GST_BUFFER_DATA (outbuf));
679   }
680   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
681   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
682
683
684   gst_buffer_unref (buffer);
685
686   gst_pad_push (video_box->srcpad, GST_DATA (outbuf));
687 }
688
689 static GstElementStateReturn
690 gst_video_box_change_state (GstElement * element)
691 {
692   GstVideoBox *video_box;
693
694   video_box = GST_VIDEO_BOX (element);
695
696   switch (GST_STATE_TRANSITION (element)) {
697     case GST_STATE_NULL_TO_READY:
698       break;
699     case GST_STATE_READY_TO_PAUSED:
700       break;
701     case GST_STATE_PAUSED_TO_PLAYING:
702       break;
703     case GST_STATE_PLAYING_TO_PAUSED:
704       break;
705     case GST_STATE_PAUSED_TO_READY:
706       break;
707     case GST_STATE_READY_TO_NULL:
708       break;
709   }
710
711   parent_class->change_state (element);
712
713   return GST_STATE_SUCCESS;
714 }
715
716 static gboolean
717 plugin_init (GstPlugin * plugin)
718 {
719   return gst_element_register (plugin, "videobox", GST_RANK_NONE,
720       GST_TYPE_VIDEO_BOX);
721 }
722
723 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
724     GST_VERSION_MINOR,
725     "videobox",
726     "resizes a video by adding borders or cropping",
727     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)