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