videoscale: Add support for adding black borders to keep the DAR if necessary
[platform/upstream/gstreamer.git] / gst / videoscale / gstvideoscale.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2005 David Schleef <ds@schleef.org>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:element-videoscale
23  * @see_also: videorate, ffmpegcolorspace
24  *
25  * This element resizes video frames. By default the element will try to
26  * negotiate to the same size on the source and sinkpad so that no scaling
27  * is needed. It is therefore safe to insert this element in a pipeline to
28  * get more robust behaviour without any cost if no scaling is needed.
29  *
30  * This element supports a wide range of color spaces including various YUV and
31  * RGB formats and is therefore generally able to operate anywhere in a
32  * pipeline.
33  *
34  * <refsect2>
35  * <title>Example pipelines</title>
36  * |[
37  * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! ffmpegcolorspace ! videoscale ! ximagesink
38  * ]| Decode an Ogg/Theora and display the video using ximagesink. Since
39  * ximagesink cannot perform scaling, the video scaling will be performed by
40  * videoscale when you resize the video window.
41  * To create the test Ogg/Theora file refer to the documentation of theoraenc.
42  * |[
43  * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! videoscale ! video/x-raw-yuv, width=50 ! xvimagesink
44  * ]| Decode an Ogg/Theora and display the video using xvimagesink with a width
45  * of 50.
46  * </refsect2>
47  *
48  * Last reviewed on 2006-03-02 (0.10.4)
49  */
50
51 /* 
52  * Formulas for PAR, DAR, width and height relations:
53  *
54  * dar_n   w   par_n
55  * ----- = - * -----
56  * dar_d   h   par_d
57  *
58  * par_n    h   dar_n
59  * ----- =  - * -----
60  * par_d    w   dar_d
61  * 
62  *         dar_n   par_d
63  * w = h * ----- * -----
64  *         dar_d   par_n
65  *
66  *         dar_d   par_n
67  * h = w * ----- * -----
68  *         dar_n   par_d
69  */
70
71 #ifdef HAVE_CONFIG_H
72 #include "config.h"
73 #endif
74
75 #include <string.h>
76
77 #include <math.h>
78
79 #include <gst/video/video.h>
80
81 #include "gstvideoscale.h"
82 #include "vs_image.h"
83 #include "vs_4tap.h"
84 #include "vs_fill_borders.h"
85
86 /* debug variable definition */
87 GST_DEBUG_CATEGORY (video_scale_debug);
88
89 #define DEFAULT_PROP_METHOD       GST_VIDEO_SCALE_BILINEAR
90 #define DEFAULT_PROP_ADD_BORDERS  FALSE
91
92 enum
93 {
94   PROP_0,
95   PROP_METHOD,
96   PROP_ADD_BORDERS
97       /* FILL ME */
98 };
99
100 #undef GST_VIDEO_SIZE_RANGE
101 #define GST_VIDEO_SIZE_RANGE "(int) [ 1, 32767]"
102
103 static GstStaticCaps gst_video_scale_format_caps[] = {
104   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA),
105   GST_STATIC_CAPS (GST_VIDEO_CAPS_ARGB),
106   GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRA),
107   GST_STATIC_CAPS (GST_VIDEO_CAPS_ABGR),
108   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")),
109   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBx),
110   GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB),
111   GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx),
112   GST_STATIC_CAPS (GST_VIDEO_CAPS_xBGR),
113   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y444")),
114   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("v308")),
115   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB),
116   GST_STATIC_CAPS (GST_VIDEO_CAPS_BGR),
117   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y42B")),
118   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YUY2")),
119   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YVYU")),
120   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("UYVY")),
121   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")),
122   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YV12")),
123   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y41B")),
124   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB_16),
125   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB_15),
126   GST_STATIC_CAPS (GST_VIDEO_CAPS_GRAY16 ("BYTE_ORDER")),
127   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y16 ")),
128   GST_STATIC_CAPS (GST_VIDEO_CAPS_GRAY8),
129   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y800")),
130   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y8  ")),
131   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("GREY"))
132 };
133
134 #define GST_TYPE_VIDEO_SCALE_METHOD (gst_video_scale_method_get_type())
135 static GType
136 gst_video_scale_method_get_type (void)
137 {
138   static GType video_scale_method_type = 0;
139
140   static const GEnumValue video_scale_methods[] = {
141     {GST_VIDEO_SCALE_NEAREST, "Nearest Neighbour", "nearest-neighbour"},
142     {GST_VIDEO_SCALE_BILINEAR, "Bilinear", "bilinear"},
143     {GST_VIDEO_SCALE_4TAP, "4-tap", "4-tap"},
144     {0, NULL, NULL},
145   };
146
147   if (!video_scale_method_type) {
148     video_scale_method_type =
149         g_enum_register_static ("GstVideoScaleMethod", video_scale_methods);
150   }
151   return video_scale_method_type;
152 }
153
154 static GstCaps *
155 gst_video_scale_get_capslist (void)
156 {
157   static GstCaps *caps = NULL;
158   static volatile gsize inited = 0;
159
160   if (g_once_init_enter (&inited)) {
161     gint i;
162
163     g_assert (caps == NULL);
164
165     caps = gst_caps_new_empty ();
166     for (i = 0; i < G_N_ELEMENTS (gst_video_scale_format_caps); i++)
167       gst_caps_append (caps,
168           gst_caps_make_writable
169           (gst_static_caps_get (&gst_video_scale_format_caps[i])));
170     g_once_init_leave (&inited, 1);
171   }
172
173   return caps;
174 }
175
176 static GstPadTemplate *
177 gst_video_scale_src_template_factory (void)
178 {
179   return gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
180       gst_caps_ref (gst_video_scale_get_capslist ()));
181 }
182
183 static GstPadTemplate *
184 gst_video_scale_sink_template_factory (void)
185 {
186   return gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
187       gst_caps_ref (gst_video_scale_get_capslist ()));
188 }
189
190
191 static void gst_video_scale_finalize (GstVideoScale * videoscale);
192 static gboolean gst_video_scale_src_event (GstBaseTransform * trans,
193     GstEvent * event);
194
195 /* base transform vmethods */
196 static GstCaps *gst_video_scale_transform_caps (GstBaseTransform * trans,
197     GstPadDirection direction, GstCaps * caps);
198 static gboolean gst_video_scale_set_caps (GstBaseTransform * trans,
199     GstCaps * in, GstCaps * out);
200 static gboolean gst_video_scale_get_unit_size (GstBaseTransform * trans,
201     GstCaps * caps, guint * size);
202 static GstFlowReturn gst_video_scale_transform (GstBaseTransform * trans,
203     GstBuffer * in, GstBuffer * out);
204 static void gst_video_scale_fixate_caps (GstBaseTransform * base,
205     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
206
207 static void gst_video_scale_set_property (GObject * object, guint prop_id,
208     const GValue * value, GParamSpec * pspec);
209 static void gst_video_scale_get_property (GObject * object, guint prop_id,
210     GValue * value, GParamSpec * pspec);
211
212 GST_BOILERPLATE (GstVideoScale, gst_video_scale, GstVideoFilter,
213     GST_TYPE_VIDEO_FILTER);
214
215 static void
216 gst_video_scale_base_init (gpointer g_class)
217 {
218   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
219
220   gst_element_class_set_details_simple (element_class,
221       "Video scaler", "Filter/Effect/Video",
222       "Resizes video", "Wim Taymans <wim.taymans@chello.be>");
223
224   gst_element_class_add_pad_template (element_class,
225       gst_video_scale_sink_template_factory ());
226   gst_element_class_add_pad_template (element_class,
227       gst_video_scale_src_template_factory ());
228 }
229
230 static void
231 gst_video_scale_class_init (GstVideoScaleClass * klass)
232 {
233   GObjectClass *gobject_class = (GObjectClass *) klass;
234   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
235
236   gobject_class->finalize = (GObjectFinalizeFunc) gst_video_scale_finalize;
237   gobject_class->set_property = gst_video_scale_set_property;
238   gobject_class->get_property = gst_video_scale_get_property;
239
240   g_object_class_install_property (gobject_class, PROP_METHOD,
241       g_param_spec_enum ("method", "method", "method",
242           GST_TYPE_VIDEO_SCALE_METHOD, DEFAULT_PROP_METHOD,
243           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
244
245   g_object_class_install_property (gobject_class, PROP_ADD_BORDERS,
246       g_param_spec_boolean ("borders", "Add Borders",
247           "Add black borders if necessary to keep the display aspect ratio",
248           DEFAULT_PROP_ADD_BORDERS,
249           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
250
251   trans_class->transform_caps =
252       GST_DEBUG_FUNCPTR (gst_video_scale_transform_caps);
253   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_scale_set_caps);
254   trans_class->get_unit_size =
255       GST_DEBUG_FUNCPTR (gst_video_scale_get_unit_size);
256   trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_scale_transform);
257   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_scale_fixate_caps);
258   trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_scale_src_event);
259 }
260
261 static void
262 gst_video_scale_init (GstVideoScale * videoscale, GstVideoScaleClass * klass)
263 {
264   videoscale->tmp_buf = NULL;
265   videoscale->method = DEFAULT_PROP_METHOD;
266   videoscale->add_borders = DEFAULT_PROP_ADD_BORDERS;
267 }
268
269 static void
270 gst_video_scale_finalize (GstVideoScale * videoscale)
271 {
272   if (videoscale->tmp_buf)
273     g_free (videoscale->tmp_buf);
274
275   G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (videoscale));
276 }
277
278 static void
279 gst_video_scale_set_property (GObject * object, guint prop_id,
280     const GValue * value, GParamSpec * pspec)
281 {
282   GstVideoScale *vscale = GST_VIDEO_SCALE (object);
283
284   switch (prop_id) {
285     case PROP_METHOD:
286       GST_OBJECT_LOCK (vscale);
287       vscale->method = g_value_get_enum (value);
288       GST_OBJECT_UNLOCK (vscale);
289       break;
290     case PROP_ADD_BORDERS:
291       GST_OBJECT_LOCK (vscale);
292       vscale->add_borders = g_value_get_boolean (value);
293       GST_OBJECT_UNLOCK (vscale);
294       gst_base_transform_reconfigure (GST_BASE_TRANSFORM_CAST (vscale));
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_scale_get_property (GObject * object, guint prop_id, GValue * value,
304     GParamSpec * pspec)
305 {
306   GstVideoScale *vscale = GST_VIDEO_SCALE (object);
307
308   switch (prop_id) {
309     case PROP_METHOD:
310       GST_OBJECT_LOCK (vscale);
311       g_value_set_enum (value, vscale->method);
312       GST_OBJECT_UNLOCK (vscale);
313       break;
314     case PROP_ADD_BORDERS:
315       GST_OBJECT_LOCK (vscale);
316       g_value_set_boolean (value, vscale->add_borders);
317       GST_OBJECT_UNLOCK (vscale);
318       break;
319     default:
320       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
321       break;
322   }
323 }
324
325 static GstCaps *
326 gst_video_scale_transform_caps (GstBaseTransform * trans,
327     GstPadDirection direction, GstCaps * caps)
328 {
329   GstCaps *ret;
330   GstStructure *structure;
331
332   /* this function is always called with a simple caps */
333   g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), NULL);
334
335   ret = gst_caps_copy (caps);
336   structure = gst_structure_copy (gst_caps_get_structure (ret, 0));
337
338   gst_structure_set (structure,
339       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
340       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
341
342   /* if pixel aspect ratio, make a range of it */
343   if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
344     gst_structure_set (structure,
345         "pixel-aspect-ratio", GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1,
346         NULL);
347   }
348   gst_caps_merge_structure (ret, gst_structure_copy (structure));
349   gst_structure_free (structure);
350
351   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
352
353   return ret;
354 }
355
356 static gboolean
357 gst_video_scale_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
358 {
359   GstVideoScale *videoscale = GST_VIDEO_SCALE (trans);
360   gboolean ret;
361   gint from_dar_n, from_dar_d, to_dar_n, to_dar_d;
362   gint from_par_n, from_par_d, to_par_n, to_par_d;
363
364   ret =
365       gst_video_format_parse_caps (in, &videoscale->format,
366       &videoscale->from_width, &videoscale->from_height);
367   ret &=
368       gst_video_format_parse_caps (out, NULL, &videoscale->to_width,
369       &videoscale->to_height);
370   if (!ret)
371     goto done;
372
373   videoscale->src_size = gst_video_format_get_size (videoscale->format,
374       videoscale->from_width, videoscale->from_height);
375   videoscale->dest_size = gst_video_format_get_size (videoscale->format,
376       videoscale->to_width, videoscale->to_height);
377
378   if (!gst_video_parse_caps_pixel_aspect_ratio (in, &from_par_n, &from_par_d))
379     from_par_n = from_par_d = 1;
380   if (!gst_video_parse_caps_pixel_aspect_ratio (out, &to_par_n, &to_par_d))
381     to_par_n = to_par_d = 1;
382
383   if (!gst_util_fraction_multiply (videoscale->from_width,
384           videoscale->from_height, from_par_n, from_par_d, &from_dar_n,
385           &from_dar_d)) {
386     from_dar_n = from_dar_d = -1;
387   }
388
389   if (!gst_util_fraction_multiply (videoscale->to_width,
390           videoscale->to_height, to_par_n, to_par_d, &to_dar_n, &to_dar_d)) {
391     to_dar_n = to_dar_d = -1;
392   }
393
394   videoscale->borders_w = videoscale->borders_h = 0;
395   if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
396     if (videoscale->add_borders) {
397       gint n, d, to_h, to_w;
398
399       if (from_dar_n != -1 && from_dar_d != -1
400           && gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
401               to_par_d, &n, &d)) {
402         to_h = gst_util_uint64_scale_int (videoscale->to_width, d, n);
403         if (to_h <= videoscale->to_height) {
404           videoscale->borders_h = videoscale->to_height - to_h;
405           videoscale->borders_w = 0;
406         } else {
407           to_w = gst_util_uint64_scale_int (videoscale->to_height, n, d);
408           g_assert (to_w <= videoscale->to_width);
409           videoscale->borders_h = 0;
410           videoscale->borders_w = videoscale->to_width - to_w;
411         }
412       } else {
413         GST_WARNING_OBJECT (videoscale, "Can't calculate borders");
414       }
415     } else {
416       GST_WARNING_OBJECT (videoscale, "Can't keep DAR!");
417     }
418   }
419
420   if (videoscale->tmp_buf)
421     g_free (videoscale->tmp_buf);
422   videoscale->tmp_buf =
423       g_malloc (gst_video_format_get_row_stride (videoscale->format, 0,
424           videoscale->to_width) * 4);
425
426   gst_base_transform_set_passthrough (trans,
427       (videoscale->from_width == videoscale->to_width
428           && videoscale->from_height == videoscale->to_height));
429
430   GST_DEBUG_OBJECT (videoscale, "from=%dx%d (par=%d/%d dar=%d/%d), size %d "
431       "-> to=%dx%d (par=%d/%d dar=%d/%d borders=%d:%d), size %d",
432       videoscale->from_width, videoscale->from_height, from_par_n, from_par_d,
433       from_dar_n, from_dar_d, videoscale->src_size, videoscale->to_width,
434       videoscale->to_height, to_par_n, to_par_d, to_dar_n, to_dar_d,
435       videoscale->borders_w, videoscale->borders_h, videoscale->dest_size);
436
437 done:
438   return ret;
439 }
440
441 static gboolean
442 gst_video_scale_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
443     guint * size)
444 {
445   GstVideoFormat format;
446   gint width, height;
447
448   if (!gst_video_format_parse_caps (caps, &format, &width, &height))
449     return FALSE;
450
451   *size = gst_video_format_get_size (format, width, height);
452
453   return TRUE;
454 }
455
456 static void
457 gst_video_scale_fixate_caps (GstBaseTransform * base, GstPadDirection direction,
458     GstCaps * caps, GstCaps * othercaps)
459 {
460   GstStructure *ins, *outs;
461   const GValue *from_par, *to_par;
462   GValue fpar = { 0, }, tpar = {
463   0,};
464
465   g_return_if_fail (gst_caps_is_fixed (caps));
466
467   GST_DEBUG_OBJECT (base, "trying to fixate othercaps %" GST_PTR_FORMAT
468       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
469
470   ins = gst_caps_get_structure (caps, 0);
471   outs = gst_caps_get_structure (othercaps, 0);
472
473   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
474   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
475
476   /* If we're fixating from the sinkpad we always set the PAR and
477    * assume that missing PAR on the sinkpad means 1/1 and
478    * missing PAR on the srcpad means undefined
479    */
480   if (direction == GST_PAD_SINK) {
481     if (!from_par) {
482       g_value_init (&fpar, GST_TYPE_FRACTION);
483       gst_value_set_fraction (&fpar, 1, 1);
484       from_par = &fpar;
485     }
486     if (!to_par) {
487       g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
488       gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
489       to_par = &tpar;
490
491       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION_RANGE, 1,
492           G_MAXINT, G_MAXINT, 1, NULL);
493     }
494   } else {
495     if (!to_par) {
496       g_value_init (&tpar, GST_TYPE_FRACTION);
497       gst_value_set_fraction (&tpar, 1, 1);
498       to_par = &tpar;
499
500       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
501           NULL);
502     }
503     if (!from_par) {
504       g_value_init (&fpar, GST_TYPE_FRACTION_RANGE);
505       gst_value_set_fraction_range_full (&fpar, 1, G_MAXINT, G_MAXINT, 1);
506       from_par = &fpar;
507     }
508   }
509
510   /* we have both PAR but they might not be fixated */
511   {
512     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
513     gint w = 0, h = 0;
514     gint from_dar_n, from_dar_d;
515     gint num, den;
516
517     /* from_par should be fixed */
518     g_return_if_fail (gst_value_is_fixed (from_par));
519
520     from_par_n = gst_value_get_fraction_numerator (from_par);
521     from_par_d = gst_value_get_fraction_denominator (from_par);
522
523     gst_structure_get_int (ins, "width", &from_w);
524     gst_structure_get_int (ins, "height", &from_h);
525
526     gst_structure_get_int (outs, "width", &w);
527     gst_structure_get_int (outs, "height", &h);
528
529     /* if both width and height are already fixed, we can't do anything
530      * about it anymore */
531     if (w && h) {
532       guint n, d;
533
534       GST_DEBUG_OBJECT (base, "dimensions already set to %dx%d, not fixating",
535           w, h);
536       if (!gst_value_is_fixed (to_par)) {
537         if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
538                 from_par_n, from_par_d, w, h)) {
539           GST_DEBUG_OBJECT (base, "fixating to_par to %dx%d", n, d);
540           if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
541             gst_structure_fixate_field_nearest_fraction (outs,
542                 "pixel-aspect-ratio", n, d);
543           else
544             gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
545                 n, d, NULL);
546         }
547       }
548       goto done;
549     }
550
551     /* Calculate input DAR */
552     if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
553             &from_dar_n, &from_dar_d)) {
554       GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
555           ("Error calculating the output scaled size - integer overflow"));
556       goto done;
557     }
558
559     GST_DEBUG_OBJECT (base, "Input DAR is %d/%d", from_dar_n, from_dar_d);
560
561     /* If either width or height are fixed there's not much we
562      * can do either except choosing a height or width and PAR
563      * that matches the DAR as good as possible
564      */
565     if (h) {
566       GstStructure *tmp;
567       gint set_w, set_par_n, set_par_d;
568
569       GST_DEBUG_OBJECT (base, "height is fixed (%d)", h);
570
571       /* If the PAR is fixed too, there's not much to do
572        * except choosing the width that is nearest to the
573        * width with the same DAR */
574       if (gst_value_is_fixed (to_par)) {
575         to_par_n = gst_value_get_fraction_numerator (to_par);
576         to_par_d = gst_value_get_fraction_denominator (to_par);
577
578         GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
579
580         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
581                 to_par_n, &num, &den)) {
582           GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
583               ("Error calculating the output scaled size - integer overflow"));
584           goto done;
585         }
586
587         w = (guint) gst_util_uint64_scale_int (h, num, den);
588         gst_structure_fixate_field_nearest_int (outs, "width", w);
589
590         goto done;
591       }
592
593       /* The PAR is not fixed and it's quite likely that we can set
594        * an arbitrary PAR. */
595
596       /* Check if we can keep the input width */
597       tmp = gst_structure_copy (outs);
598       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
599       gst_structure_get_int (tmp, "width", &set_w);
600
601       /* Might have failed but try to keep the DAR nonetheless by
602        * adjusting the PAR */
603       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
604               &to_par_n, &to_par_d)) {
605         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
606             ("Error calculating the output scaled size - integer overflow"));
607         gst_structure_free (tmp);
608         goto done;
609       }
610       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
611           to_par_n, to_par_d);
612       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
613           &set_par_d);
614       gst_structure_free (tmp);
615
616       /* Check if the adjusted PAR is accepted */
617       if (set_par_n == to_par_n && set_par_d == to_par_d) {
618         gst_structure_set (outs, "width", G_TYPE_INT, set_w,
619             "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
620             NULL);
621         goto done;
622       }
623
624       /* Otherwise scale the width to the new PAR and check if the
625        * adjusted with is accepted. If all that fails we can't keep
626        * the DAR */
627       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
628               set_par_n, &num, &den)) {
629         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
630             ("Error calculating the output scaled size - integer overflow"));
631         goto done;
632       }
633
634       w = (guint) gst_util_uint64_scale_int (h, num, den);
635       gst_structure_fixate_field_nearest_int (outs, "width", w);
636       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
637           set_par_n, set_par_d, NULL);
638
639       goto done;
640     } else if (w) {
641       GstStructure *tmp;
642       gint set_h, set_par_n, set_par_d;
643
644       GST_DEBUG_OBJECT (base, "width is fixed (%d)", w);
645
646       /* If the PAR is fixed too, there's not much to do
647        * except choosing the height that is nearest to the
648        * height with the same DAR */
649       if (gst_value_is_fixed (to_par)) {
650         to_par_n = gst_value_get_fraction_numerator (to_par);
651         to_par_d = gst_value_get_fraction_denominator (to_par);
652
653         GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
654
655         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
656                 to_par_n, &num, &den)) {
657           GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
658               ("Error calculating the output scaled size - integer overflow"));
659           goto done;
660         }
661
662         h = (guint) gst_util_uint64_scale_int (w, den, num);
663         gst_structure_fixate_field_nearest_int (outs, "height", h);
664
665         goto done;
666       }
667
668       /* The PAR is not fixed and it's quite likely that we can set
669        * an arbitrary PAR. */
670
671       /* Check if we can keep the input height */
672       tmp = gst_structure_copy (outs);
673       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
674       gst_structure_get_int (tmp, "height", &set_h);
675
676       /* Might have failed but try to keep the DAR nonetheless by
677        * adjusting the PAR */
678       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
679               &to_par_n, &to_par_d)) {
680         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
681             ("Error calculating the output scaled size - integer overflow"));
682         gst_structure_free (tmp);
683         goto done;
684       }
685       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
686           to_par_n, to_par_d);
687       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
688           &set_par_d);
689       gst_structure_free (tmp);
690
691       /* Check if the adjusted PAR is accepted */
692       if (set_par_n == to_par_n && set_par_d == to_par_d) {
693         gst_structure_set (outs, "height", G_TYPE_INT, set_h,
694             "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
695             NULL);
696         goto done;
697       }
698
699       /* Otherwise scale the height to the new PAR and check if the
700        * adjusted with is accepted. If all that fails we can't keep
701        * the DAR */
702       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
703               set_par_n, &num, &den)) {
704         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
705             ("Error calculating the output scaled size - integer overflow"));
706         goto done;
707       }
708
709       h = (guint) gst_util_uint64_scale_int (w, den, num);
710       gst_structure_fixate_field_nearest_int (outs, "height", h);
711       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
712           set_par_n, set_par_d, NULL);
713
714       goto done;
715     } else if (gst_value_is_fixed (to_par)) {
716       GstStructure *tmp;
717       gint set_h, set_w, f_h, f_w;
718
719       to_par_n = gst_value_get_fraction_numerator (to_par);
720       to_par_d = gst_value_get_fraction_denominator (to_par);
721
722       /* Calculate scale factor for the PAR change */
723       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
724               to_par_d, &num, &den)) {
725         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
726             ("Error calculating the output scaled size - integer overflow"));
727         goto done;
728       }
729
730       /* Try to keep the input height (because of interlacing) */
731       tmp = gst_structure_copy (outs);
732       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
733       gst_structure_get_int (tmp, "height", &set_h);
734
735       /* This might have failed but try to scale the width
736        * to keep the DAR nonetheless */
737       w = (guint) gst_util_uint64_scale_int (set_h, num, den);
738       gst_structure_fixate_field_nearest_int (tmp, "width", w);
739       gst_structure_get_int (tmp, "width", &set_w);
740       gst_structure_free (tmp);
741
742       /* We kept the DAR and the height is nearest to the original height */
743       if (set_w == w) {
744         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
745             G_TYPE_INT, set_h, NULL);
746         goto done;
747       }
748
749       f_h = set_h;
750       f_w = set_w;
751
752       /* If the former failed, try to keep the input width at least */
753       tmp = gst_structure_copy (outs);
754       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
755       gst_structure_get_int (tmp, "width", &set_w);
756
757       /* This might have failed but try to scale the width
758        * to keep the DAR nonetheless */
759       h = (guint) gst_util_uint64_scale_int (set_w, den, num);
760       gst_structure_fixate_field_nearest_int (tmp, "height", h);
761       gst_structure_get_int (tmp, "height", &set_h);
762       gst_structure_free (tmp);
763
764       /* We kept the DAR and the width is nearest to the original width */
765       if (set_h == h) {
766         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
767             G_TYPE_INT, set_h, NULL);
768         goto done;
769       }
770
771       /* If all this failed, keep the height that was nearest to the orignal
772        * height and the nearest possible width. This changes the DAR but
773        * there's not much else to do here.
774        */
775       gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
776           f_h, NULL);
777       goto done;
778     } else {
779       GstStructure *tmp;
780       gint set_h, set_w, set_par_n, set_par_d, tmp2;
781
782       /* width, height and PAR are not fixed but passthrough is not possible */
783
784       /* First try to keep the height and width as good as possible
785        * and scale PAR */
786       tmp = gst_structure_copy (outs);
787       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
788       gst_structure_get_int (tmp, "height", &set_h);
789       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
790       gst_structure_get_int (tmp, "width", &set_w);
791
792       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
793               &to_par_n, &to_par_d)) {
794         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
795             ("Error calculating the output scaled size - integer overflow"));
796         goto done;
797       }
798
799       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
800           to_par_n, to_par_d);
801       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
802           &set_par_d);
803       gst_structure_free (tmp);
804
805       if (set_par_n == to_par_n && set_par_d == to_par_d) {
806         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
807             G_TYPE_INT, set_h, "pixel-aspect-ratio", GST_TYPE_FRACTION,
808             set_par_n, set_par_d, NULL);
809         goto done;
810       }
811
812       /* Otherwise try to scale width to keep the DAR with the set
813        * PAR and height */
814       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
815               set_par_n, &num, &den)) {
816         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
817             ("Error calculating the output scaled size - integer overflow"));
818         goto done;
819       }
820
821       w = (guint) gst_util_uint64_scale_int (set_h, num, den);
822       tmp = gst_structure_copy (outs);
823       gst_structure_fixate_field_nearest_int (tmp, "width", w);
824       gst_structure_get_int (tmp, "width", &tmp2);
825       gst_structure_free (tmp);
826
827       if (tmp2 == w) {
828         gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
829             G_TYPE_INT, set_h, "pixel-aspect-ratio", GST_TYPE_FRACTION,
830             set_par_n, set_par_d, NULL);
831         goto done;
832       }
833
834       /* ... or try the same with the height */
835       h = (guint) gst_util_uint64_scale_int (set_w, den, num);
836       tmp = gst_structure_copy (outs);
837       gst_structure_fixate_field_nearest_int (tmp, "height", h);
838       gst_structure_get_int (tmp, "height", &tmp2);
839       gst_structure_free (tmp);
840
841       if (tmp2 == h) {
842         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
843             G_TYPE_INT, tmp2, "pixel-aspect-ratio", GST_TYPE_FRACTION,
844             set_par_n, set_par_d, NULL);
845         goto done;
846       }
847
848       /* If all fails we can't keep the DAR and take the nearest values
849        * for everything from the first try */
850       gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
851           G_TYPE_INT, set_h, "pixel-aspect-ratio", GST_TYPE_FRACTION,
852           set_par_n, set_par_d, NULL);
853     }
854   }
855
856 done:
857   GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
858
859   if (from_par == &fpar)
860     g_value_unset (&fpar);
861   if (to_par == &tpar)
862     g_value_unset (&tpar);
863 }
864
865 static void
866 gst_video_scale_setup_vs_image (VSImage * image, GstVideoFormat format,
867     gint component, gint width, gint height, gint b_w, gint b_h, uint8_t * data)
868 {
869   image->real_width =
870       gst_video_format_get_component_width (format, component, width);
871   image->real_height =
872       gst_video_format_get_component_height (format, component, height);
873   image->width =
874       gst_video_format_get_component_width (format, component, MAX (1,
875           width - b_w));
876   image->height =
877       gst_video_format_get_component_height (format, component, MAX (1,
878           height - b_h));
879   image->stride = gst_video_format_get_row_stride (format, component, width);
880
881   image->border_top = (image->real_height - image->height) / 2;
882   image->border_bottom = image->real_height - image->height - image->border_top;
883
884   if (format == GST_VIDEO_FORMAT_YUY2 || format == GST_VIDEO_FORMAT_YVYU
885       || format == GST_VIDEO_FORMAT_UYVY) {
886     g_assert (component == 0);
887
888     image->border_left = (image->real_width - image->width) / 2;
889
890     if (image->border_left % 2 == 1)
891       image->border_left--;
892     image->border_right = image->real_width - image->width - image->border_left;
893   } else {
894     image->border_left = (image->real_width - image->width) / 2;
895     image->border_right = image->real_width - image->width - image->border_left;
896   }
897
898   if (format == GST_VIDEO_FORMAT_I420
899       || format == GST_VIDEO_FORMAT_YV12
900       || format == GST_VIDEO_FORMAT_Y444
901       || format == GST_VIDEO_FORMAT_Y42B || format == GST_VIDEO_FORMAT_Y41B) {
902     image->real_pixels = data + gst_video_format_get_component_offset (format,
903         component, width, height);
904   } else {
905     g_assert (component == 0);
906     image->real_pixels = data;
907   }
908
909   image->pixels =
910       image->real_pixels + image->border_top * image->stride +
911       image->border_left * gst_video_format_get_pixel_stride (format,
912       component);
913 }
914
915 static const guint8 *
916 _get_black_for_format (GstVideoFormat format)
917 {
918   static const guint8 black[][4] = {
919     {255, 0, 0, 0},             /*  0 = ARGB, ABGR, xRGB, xBGR */
920     {0, 0, 0, 255},             /*  1 = RGBA, BGRA, RGBx, BGRx */
921     {255, 16, 128, 128},        /*  2 = AYUV */
922     {0, 0, 0, 0},               /*  3 = RGB and BGR */
923     {16, 128, 128, 0},          /*  4 = v301 */
924     {16, 128, 16, 128},         /*  5 = YUY2, YUYV */
925     {128, 16, 128, 16},         /*  6 = UYVY */
926     {16, 0, 0, 0},              /*  7 = Y */
927     {0, 0, 0, 0}                /*  8 = RGB565, RGB666 */
928   };
929
930   switch (format) {
931     case GST_VIDEO_FORMAT_ARGB:
932     case GST_VIDEO_FORMAT_ABGR:
933     case GST_VIDEO_FORMAT_xRGB:
934     case GST_VIDEO_FORMAT_xBGR:
935       return black[0];
936     case GST_VIDEO_FORMAT_RGBA:
937     case GST_VIDEO_FORMAT_BGRA:
938     case GST_VIDEO_FORMAT_RGBx:
939     case GST_VIDEO_FORMAT_BGRx:
940       return black[1];
941     case GST_VIDEO_FORMAT_AYUV:
942       return black[2];
943     case GST_VIDEO_FORMAT_RGB:
944     case GST_VIDEO_FORMAT_BGR:
945       return black[3];
946     case GST_VIDEO_FORMAT_v308:
947       return black[4];
948     case GST_VIDEO_FORMAT_YUY2:
949     case GST_VIDEO_FORMAT_YVYU:
950       return black[5];
951     case GST_VIDEO_FORMAT_UYVY:
952       return black[6];
953     case GST_VIDEO_FORMAT_Y800:
954     case GST_VIDEO_FORMAT_GRAY8:
955       return black[7];
956     case GST_VIDEO_FORMAT_GRAY16_LE:
957     case GST_VIDEO_FORMAT_GRAY16_BE:
958     case GST_VIDEO_FORMAT_Y16:
959       return NULL;              /* Handled by the caller */
960     case GST_VIDEO_FORMAT_I420:
961     case GST_VIDEO_FORMAT_YV12:
962     case GST_VIDEO_FORMAT_Y444:
963     case GST_VIDEO_FORMAT_Y42B:
964     case GST_VIDEO_FORMAT_Y41B:
965       return black[4];          /* Y, U, V, 0 */
966     case GST_VIDEO_FORMAT_RGB16:
967     case GST_VIDEO_FORMAT_RGB15:
968       return black[8];
969     default:
970       return NULL;
971   }
972 }
973
974 static GstFlowReturn
975 gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in,
976     GstBuffer * out)
977 {
978   GstVideoScale *videoscale = GST_VIDEO_SCALE (trans);
979   GstFlowReturn ret = GST_FLOW_OK;
980   VSImage dest = { NULL, };
981   VSImage src = { NULL, };
982   VSImage dest_u = { NULL, };
983   VSImage dest_v = { NULL, };
984   VSImage src_u = { NULL, };
985   VSImage src_v = { NULL, };
986   gint method;
987   const guint8 *black = _get_black_for_format (videoscale->format);
988   gboolean add_borders;
989
990   GST_OBJECT_LOCK (videoscale);
991   method = videoscale->method;
992   add_borders = videoscale->add_borders;
993   GST_OBJECT_UNLOCK (videoscale);
994
995   gst_video_scale_setup_vs_image (&src, videoscale->format, 0,
996       videoscale->from_width, videoscale->from_height, 0, 0,
997       GST_BUFFER_DATA (in));
998   gst_video_scale_setup_vs_image (&dest, videoscale->format, 0,
999       videoscale->to_width, videoscale->to_height, videoscale->borders_w,
1000       videoscale->borders_h, GST_BUFFER_DATA (out));
1001
1002   if (videoscale->format == GST_VIDEO_FORMAT_I420
1003       || videoscale->format == GST_VIDEO_FORMAT_YV12
1004       || videoscale->format == GST_VIDEO_FORMAT_Y444
1005       || videoscale->format == GST_VIDEO_FORMAT_Y42B
1006       || videoscale->format == GST_VIDEO_FORMAT_Y41B) {
1007     gst_video_scale_setup_vs_image (&src_u, videoscale->format, 1,
1008         videoscale->from_width, videoscale->from_height, 0, 0,
1009         GST_BUFFER_DATA (in));
1010     gst_video_scale_setup_vs_image (&src_v, videoscale->format, 2,
1011         videoscale->from_width, videoscale->from_height, 0, 0,
1012         GST_BUFFER_DATA (in));
1013     gst_video_scale_setup_vs_image (&dest_u, videoscale->format, 1,
1014         videoscale->to_width, videoscale->to_height, videoscale->borders_w,
1015         videoscale->borders_h, GST_BUFFER_DATA (out));
1016     gst_video_scale_setup_vs_image (&dest_v, videoscale->format, 2,
1017         videoscale->to_width, videoscale->to_height, videoscale->borders_w,
1018         videoscale->borders_h, GST_BUFFER_DATA (out));
1019   }
1020
1021   switch (videoscale->format) {
1022     case GST_VIDEO_FORMAT_RGBx:
1023     case GST_VIDEO_FORMAT_xRGB:
1024     case GST_VIDEO_FORMAT_BGRx:
1025     case GST_VIDEO_FORMAT_xBGR:
1026     case GST_VIDEO_FORMAT_RGBA:
1027     case GST_VIDEO_FORMAT_ARGB:
1028     case GST_VIDEO_FORMAT_BGRA:
1029     case GST_VIDEO_FORMAT_ABGR:
1030     case GST_VIDEO_FORMAT_AYUV:
1031       if (add_borders)
1032         vs_fill_borders_RGBA (&dest, black);
1033       switch (method) {
1034         case GST_VIDEO_SCALE_NEAREST:
1035           vs_image_scale_nearest_RGBA (&dest, &src, videoscale->tmp_buf);
1036           break;
1037         case GST_VIDEO_SCALE_BILINEAR:
1038           vs_image_scale_linear_RGBA (&dest, &src, videoscale->tmp_buf);
1039           break;
1040         case GST_VIDEO_SCALE_4TAP:
1041           vs_image_scale_4tap_RGBA (&dest, &src, videoscale->tmp_buf);
1042           break;
1043         default:
1044           goto unknown_mode;
1045       }
1046       break;
1047     case GST_VIDEO_FORMAT_RGB:
1048     case GST_VIDEO_FORMAT_BGR:
1049     case GST_VIDEO_FORMAT_v308:
1050       if (add_borders)
1051         vs_fill_borders_RGB (&dest, black);
1052       switch (method) {
1053         case GST_VIDEO_SCALE_NEAREST:
1054           vs_image_scale_nearest_RGB (&dest, &src, videoscale->tmp_buf);
1055           break;
1056         case GST_VIDEO_SCALE_BILINEAR:
1057           vs_image_scale_linear_RGB (&dest, &src, videoscale->tmp_buf);
1058           break;
1059         case GST_VIDEO_SCALE_4TAP:
1060           vs_image_scale_4tap_RGB (&dest, &src, videoscale->tmp_buf);
1061           break;
1062         default:
1063           goto unknown_mode;
1064       }
1065       break;
1066     case GST_VIDEO_FORMAT_YUY2:
1067     case GST_VIDEO_FORMAT_YVYU:
1068       if (add_borders)
1069         vs_fill_borders_YUYV (&dest, black);
1070       switch (method) {
1071         case GST_VIDEO_SCALE_NEAREST:
1072           vs_image_scale_nearest_YUYV (&dest, &src, videoscale->tmp_buf);
1073           break;
1074         case GST_VIDEO_SCALE_BILINEAR:
1075           vs_image_scale_linear_YUYV (&dest, &src, videoscale->tmp_buf);
1076           break;
1077         case GST_VIDEO_SCALE_4TAP:
1078           vs_image_scale_4tap_YUYV (&dest, &src, videoscale->tmp_buf);
1079           break;
1080         default:
1081           goto unknown_mode;
1082       }
1083       break;
1084     case GST_VIDEO_FORMAT_UYVY:
1085       if (add_borders)
1086         vs_fill_borders_UYVY (&dest, black);
1087       switch (method) {
1088         case GST_VIDEO_SCALE_NEAREST:
1089           vs_image_scale_nearest_UYVY (&dest, &src, videoscale->tmp_buf);
1090           break;
1091         case GST_VIDEO_SCALE_BILINEAR:
1092           vs_image_scale_linear_UYVY (&dest, &src, videoscale->tmp_buf);
1093           break;
1094         case GST_VIDEO_SCALE_4TAP:
1095           vs_image_scale_4tap_UYVY (&dest, &src, videoscale->tmp_buf);
1096           break;
1097         default:
1098           goto unknown_mode;
1099       }
1100       break;
1101     case GST_VIDEO_FORMAT_Y800:
1102     case GST_VIDEO_FORMAT_GRAY8:
1103       if (add_borders)
1104         vs_fill_borders_Y (&dest, black);
1105       switch (method) {
1106         case GST_VIDEO_SCALE_NEAREST:
1107           vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf);
1108           break;
1109         case GST_VIDEO_SCALE_BILINEAR:
1110           vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf);
1111           break;
1112         case GST_VIDEO_SCALE_4TAP:
1113           vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf);
1114           break;
1115         default:
1116           goto unknown_mode;
1117       }
1118       break;
1119     case GST_VIDEO_FORMAT_GRAY16_LE:
1120     case GST_VIDEO_FORMAT_GRAY16_BE:
1121     case GST_VIDEO_FORMAT_Y16:
1122       if (add_borders)
1123         vs_fill_borders_Y16 (&dest, 0);
1124       switch (method) {
1125         case GST_VIDEO_SCALE_NEAREST:
1126           vs_image_scale_nearest_Y16 (&dest, &src, videoscale->tmp_buf);
1127           break;
1128         case GST_VIDEO_SCALE_BILINEAR:
1129           vs_image_scale_linear_Y16 (&dest, &src, videoscale->tmp_buf);
1130           break;
1131         case GST_VIDEO_SCALE_4TAP:
1132           vs_image_scale_4tap_Y16 (&dest, &src, videoscale->tmp_buf);
1133           break;
1134         default:
1135           goto unknown_mode;
1136       }
1137       break;
1138     case GST_VIDEO_FORMAT_I420:
1139     case GST_VIDEO_FORMAT_YV12:
1140     case GST_VIDEO_FORMAT_Y444:
1141     case GST_VIDEO_FORMAT_Y42B:
1142     case GST_VIDEO_FORMAT_Y41B:
1143       if (add_borders) {
1144         vs_fill_borders_Y (&dest, black);
1145         vs_fill_borders_Y (&dest_u, black + 1);
1146         vs_fill_borders_Y (&dest_v, black + 2);
1147       }
1148       switch (method) {
1149         case GST_VIDEO_SCALE_NEAREST:
1150           vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf);
1151           vs_image_scale_nearest_Y (&dest_u, &src_u, videoscale->tmp_buf);
1152           vs_image_scale_nearest_Y (&dest_v, &src_v, videoscale->tmp_buf);
1153           break;
1154         case GST_VIDEO_SCALE_BILINEAR:
1155           vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf);
1156           vs_image_scale_linear_Y (&dest_u, &src_u, videoscale->tmp_buf);
1157           vs_image_scale_linear_Y (&dest_v, &src_v, videoscale->tmp_buf);
1158           break;
1159         case GST_VIDEO_SCALE_4TAP:
1160           vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf);
1161           vs_image_scale_4tap_Y (&dest_u, &src_u, videoscale->tmp_buf);
1162           vs_image_scale_4tap_Y (&dest_v, &src_v, videoscale->tmp_buf);
1163           break;
1164         default:
1165           goto unknown_mode;
1166       }
1167       break;
1168     case GST_VIDEO_FORMAT_RGB16:
1169       if (add_borders)
1170         vs_fill_borders_RGB565 (&dest, black);
1171       switch (method) {
1172         case GST_VIDEO_SCALE_NEAREST:
1173           vs_image_scale_nearest_RGB565 (&dest, &src, videoscale->tmp_buf);
1174           break;
1175         case GST_VIDEO_SCALE_BILINEAR:
1176           vs_image_scale_linear_RGB565 (&dest, &src, videoscale->tmp_buf);
1177           break;
1178         case GST_VIDEO_SCALE_4TAP:
1179           vs_image_scale_4tap_RGB565 (&dest, &src, videoscale->tmp_buf);
1180           break;
1181         default:
1182           goto unknown_mode;
1183       }
1184       break;
1185     case GST_VIDEO_FORMAT_RGB15:
1186       if (add_borders)
1187         vs_fill_borders_RGB555 (&dest, black);
1188       switch (method) {
1189         case GST_VIDEO_SCALE_NEAREST:
1190           vs_image_scale_nearest_RGB555 (&dest, &src, videoscale->tmp_buf);
1191           break;
1192         case GST_VIDEO_SCALE_BILINEAR:
1193           vs_image_scale_linear_RGB555 (&dest, &src, videoscale->tmp_buf);
1194           break;
1195         case GST_VIDEO_SCALE_4TAP:
1196           vs_image_scale_4tap_RGB555 (&dest, &src, videoscale->tmp_buf);
1197           break;
1198         default:
1199           goto unknown_mode;
1200       }
1201       break;
1202     default:
1203       goto unsupported;
1204   }
1205
1206   GST_LOG_OBJECT (videoscale, "pushing buffer of %d bytes",
1207       GST_BUFFER_SIZE (out));
1208
1209   return ret;
1210
1211   /* ERRORS */
1212 unsupported:
1213   {
1214     GST_ELEMENT_ERROR (videoscale, STREAM, NOT_IMPLEMENTED, (NULL),
1215         ("Unsupported format %d for scaling method %d",
1216             videoscale->format, method));
1217     return GST_FLOW_ERROR;
1218   }
1219 unknown_mode:
1220   {
1221     GST_ELEMENT_ERROR (videoscale, STREAM, NOT_IMPLEMENTED, (NULL),
1222         ("Unknown scaling method %d", videoscale->method));
1223     return GST_FLOW_ERROR;
1224   }
1225 }
1226
1227 static gboolean
1228 gst_video_scale_src_event (GstBaseTransform * trans, GstEvent * event)
1229 {
1230   GstVideoScale *videoscale = GST_VIDEO_SCALE (trans);
1231   gboolean ret;
1232   gdouble a;
1233   GstStructure *structure;
1234
1235   GST_DEBUG_OBJECT (videoscale, "handling %s event",
1236       GST_EVENT_TYPE_NAME (event));
1237
1238   switch (GST_EVENT_TYPE (event)) {
1239     case GST_EVENT_NAVIGATION:
1240       event =
1241           GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1242
1243       structure = (GstStructure *) gst_event_get_structure (event);
1244       if (gst_structure_get_double (structure, "pointer_x", &a)) {
1245         gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1246             a * videoscale->from_width / videoscale->to_width, NULL);
1247       }
1248       if (gst_structure_get_double (structure, "pointer_y", &a)) {
1249         gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1250             a * videoscale->from_height / videoscale->to_height, NULL);
1251       }
1252       break;
1253     default:
1254       break;
1255   }
1256
1257   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
1258
1259   return ret;
1260 }
1261
1262 static gboolean
1263 plugin_init (GstPlugin * plugin)
1264 {
1265   if (!gst_element_register (plugin, "videoscale", GST_RANK_NONE,
1266           GST_TYPE_VIDEO_SCALE))
1267     return FALSE;
1268
1269   GST_DEBUG_CATEGORY_INIT (video_scale_debug, "videoscale", 0,
1270       "videoscale element");
1271
1272   vs_4tap_init ();
1273
1274   return TRUE;
1275 }
1276
1277 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1278     GST_VERSION_MINOR,
1279     "videoscale",
1280     "Resizes video", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
1281     GST_PACKAGE_ORIGIN)