b3aeb210794e2dfb9d6b652fa766a8e3875be94d
[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 ("add-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   GST_DEBUG_OBJECT (trans,
336       "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
337       (direction == GST_PAD_SINK) ? "sink" : "src");
338
339   ret = gst_caps_copy (caps);
340   structure = gst_structure_copy (gst_caps_get_structure (ret, 0));
341
342   gst_structure_set (structure,
343       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
344       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
345
346   /* if pixel aspect ratio, make a range of it */
347   if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
348     gst_structure_set (structure, "pixel-aspect-ratio", GST_TYPE_FRACTION_RANGE,
349         1, G_MAXINT, G_MAXINT, 1, NULL);
350   }
351   gst_caps_append_structure (ret, structure);
352
353   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
354
355   return ret;
356 }
357
358 static gboolean
359 gst_video_scale_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
360 {
361   GstVideoScale *videoscale = GST_VIDEO_SCALE (trans);
362   gboolean ret;
363   gint from_dar_n, from_dar_d, to_dar_n, to_dar_d;
364   gint from_par_n, from_par_d, to_par_n, to_par_d;
365
366   ret =
367       gst_video_format_parse_caps (in, &videoscale->format,
368       &videoscale->from_width, &videoscale->from_height);
369   ret &=
370       gst_video_format_parse_caps (out, NULL, &videoscale->to_width,
371       &videoscale->to_height);
372   if (!ret)
373     goto done;
374
375   videoscale->src_size = gst_video_format_get_size (videoscale->format,
376       videoscale->from_width, videoscale->from_height);
377   videoscale->dest_size = gst_video_format_get_size (videoscale->format,
378       videoscale->to_width, videoscale->to_height);
379
380   if (!gst_video_parse_caps_pixel_aspect_ratio (in, &from_par_n, &from_par_d))
381     from_par_n = from_par_d = 1;
382   if (!gst_video_parse_caps_pixel_aspect_ratio (out, &to_par_n, &to_par_d))
383     to_par_n = to_par_d = 1;
384
385   if (!gst_util_fraction_multiply (videoscale->from_width,
386           videoscale->from_height, from_par_n, from_par_d, &from_dar_n,
387           &from_dar_d)) {
388     from_dar_n = from_dar_d = -1;
389   }
390
391   if (!gst_util_fraction_multiply (videoscale->to_width,
392           videoscale->to_height, to_par_n, to_par_d, &to_dar_n, &to_dar_d)) {
393     to_dar_n = to_dar_d = -1;
394   }
395
396   videoscale->borders_w = videoscale->borders_h = 0;
397   if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
398     if (videoscale->add_borders) {
399       gint n, d, to_h, to_w;
400
401       if (from_dar_n != -1 && from_dar_d != -1
402           && gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
403               to_par_d, &n, &d)) {
404         to_h = gst_util_uint64_scale_int (videoscale->to_width, d, n);
405         if (to_h <= videoscale->to_height) {
406           videoscale->borders_h = videoscale->to_height - to_h;
407           videoscale->borders_w = 0;
408         } else {
409           to_w = gst_util_uint64_scale_int (videoscale->to_height, n, d);
410           g_assert (to_w <= videoscale->to_width);
411           videoscale->borders_h = 0;
412           videoscale->borders_w = videoscale->to_width - to_w;
413         }
414       } else {
415         GST_WARNING_OBJECT (videoscale, "Can't calculate borders");
416       }
417     } else {
418       GST_WARNING_OBJECT (videoscale, "Can't keep DAR!");
419     }
420   }
421
422   if (videoscale->tmp_buf)
423     g_free (videoscale->tmp_buf);
424   videoscale->tmp_buf =
425       g_malloc (gst_video_format_get_row_stride (videoscale->format, 0,
426           videoscale->to_width) * 4);
427
428   gst_base_transform_set_passthrough (trans,
429       (videoscale->from_width == videoscale->to_width
430           && videoscale->from_height == videoscale->to_height));
431
432   GST_DEBUG_OBJECT (videoscale, "from=%dx%d (par=%d/%d dar=%d/%d), size %d "
433       "-> to=%dx%d (par=%d/%d dar=%d/%d borders=%d:%d), size %d",
434       videoscale->from_width, videoscale->from_height, from_par_n, from_par_d,
435       from_dar_n, from_dar_d, videoscale->src_size, videoscale->to_width,
436       videoscale->to_height, to_par_n, to_par_d, to_dar_n, to_dar_d,
437       videoscale->borders_w, videoscale->borders_h, videoscale->dest_size);
438
439 done:
440   return ret;
441 }
442
443 static gboolean
444 gst_video_scale_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
445     guint * size)
446 {
447   GstVideoFormat format;
448   gint width, height;
449
450   if (!gst_video_format_parse_caps (caps, &format, &width, &height))
451     return FALSE;
452
453   *size = gst_video_format_get_size (format, width, height);
454
455   return TRUE;
456 }
457
458 static void
459 gst_video_scale_fixate_caps (GstBaseTransform * base, GstPadDirection direction,
460     GstCaps * caps, GstCaps * othercaps)
461 {
462   GstStructure *ins, *outs;
463   const GValue *from_par, *to_par;
464   GValue fpar = { 0, }, tpar = {
465   0,};
466
467   g_return_if_fail (gst_caps_is_fixed (caps));
468
469   GST_DEBUG_OBJECT (base, "trying to fixate othercaps %" GST_PTR_FORMAT
470       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
471
472   ins = gst_caps_get_structure (caps, 0);
473   outs = gst_caps_get_structure (othercaps, 0);
474
475   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
476   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
477
478   /* If we're fixating from the sinkpad we always set the PAR and
479    * assume that missing PAR on the sinkpad means 1/1 and
480    * missing PAR on the srcpad means undefined
481    */
482   if (direction == GST_PAD_SINK) {
483     if (!from_par) {
484       g_value_init (&fpar, GST_TYPE_FRACTION);
485       gst_value_set_fraction (&fpar, 1, 1);
486       from_par = &fpar;
487     }
488     if (!to_par) {
489       g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
490       gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
491       to_par = &tpar;
492     }
493   } else {
494     if (!to_par) {
495       g_value_init (&tpar, GST_TYPE_FRACTION);
496       gst_value_set_fraction (&tpar, 1, 1);
497       to_par = &tpar;
498
499       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
500           NULL);
501     }
502     if (!from_par) {
503       g_value_init (&fpar, GST_TYPE_FRACTION_RANGE);
504       gst_value_set_fraction_range_full (&fpar, 1, G_MAXINT, G_MAXINT, 1);
505       from_par = &fpar;
506     }
507   }
508
509   /* we have both PAR but they might not be fixated */
510   {
511     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
512     gint w = 0, h = 0;
513     gint from_dar_n, from_dar_d;
514     gint num, den;
515
516     /* from_par should be fixed */
517     g_return_if_fail (gst_value_is_fixed (from_par));
518
519     from_par_n = gst_value_get_fraction_numerator (from_par);
520     from_par_d = gst_value_get_fraction_denominator (from_par);
521
522     gst_structure_get_int (ins, "width", &from_w);
523     gst_structure_get_int (ins, "height", &from_h);
524
525     gst_structure_get_int (outs, "width", &w);
526     gst_structure_get_int (outs, "height", &h);
527
528     /* if both width and height are already fixed, we can't do anything
529      * about it anymore */
530     if (w && h) {
531       guint n, d;
532
533       GST_DEBUG_OBJECT (base, "dimensions already set to %dx%d, not fixating",
534           w, h);
535       if (!gst_value_is_fixed (to_par)) {
536         if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
537                 from_par_n, from_par_d, w, h)) {
538           GST_DEBUG_OBJECT (base, "fixating to_par to %dx%d", n, d);
539           if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
540             gst_structure_fixate_field_nearest_fraction (outs,
541                 "pixel-aspect-ratio", n, d);
542           else if (n != d)
543             gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
544                 n, d, NULL);
545         }
546       }
547       goto done;
548     }
549
550     /* Calculate input DAR */
551     if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
552             &from_dar_n, &from_dar_d)) {
553       GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
554           ("Error calculating the output scaled size - integer overflow"));
555       goto done;
556     }
557
558     GST_DEBUG_OBJECT (base, "Input DAR is %d/%d", from_dar_n, from_dar_d);
559
560     /* If either width or height are fixed there's not much we
561      * can do either except choosing a height or width and PAR
562      * that matches the DAR as good as possible
563      */
564     if (h) {
565       GstStructure *tmp;
566       gint set_w, set_par_n, set_par_d;
567
568       GST_DEBUG_OBJECT (base, "height is fixed (%d)", h);
569
570       /* If the PAR is fixed too, there's not much to do
571        * except choosing the width that is nearest to the
572        * width with the same DAR */
573       if (gst_value_is_fixed (to_par)) {
574         to_par_n = gst_value_get_fraction_numerator (to_par);
575         to_par_d = gst_value_get_fraction_denominator (to_par);
576
577         GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
578
579         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
580                 to_par_n, &num, &den)) {
581           GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
582               ("Error calculating the output scaled size - integer overflow"));
583           goto done;
584         }
585
586         w = (guint) gst_util_uint64_scale_int (h, num, den);
587         gst_structure_fixate_field_nearest_int (outs, "width", w);
588
589         goto done;
590       }
591
592       /* The PAR is not fixed and it's quite likely that we can set
593        * an arbitrary PAR. */
594
595       /* Check if we can keep the input width */
596       tmp = gst_structure_copy (outs);
597       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
598       gst_structure_get_int (tmp, "width", &set_w);
599
600       /* Might have failed but try to keep the DAR nonetheless by
601        * adjusting the PAR */
602       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
603               &to_par_n, &to_par_d)) {
604         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
605             ("Error calculating the output scaled size - integer overflow"));
606         gst_structure_free (tmp);
607         goto done;
608       }
609
610       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
611         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
612       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
613           to_par_n, to_par_d);
614       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
615           &set_par_d);
616       gst_structure_free (tmp);
617
618       /* Check if the adjusted PAR is accepted */
619       if (set_par_n == to_par_n && set_par_d == to_par_d) {
620         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
621             set_par_n != set_par_d)
622           gst_structure_set (outs, "width", G_TYPE_INT, set_w,
623               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
624               NULL);
625         goto done;
626       }
627
628       /* Otherwise scale the width to the new PAR and check if the
629        * adjusted with is accepted. If all that fails we can't keep
630        * the DAR */
631       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
632               set_par_n, &num, &den)) {
633         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
634             ("Error calculating the output scaled size - integer overflow"));
635         goto done;
636       }
637
638       w = (guint) gst_util_uint64_scale_int (h, num, den);
639       gst_structure_fixate_field_nearest_int (outs, "width", w);
640       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
641           set_par_n != set_par_d)
642         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
643             set_par_n, set_par_d, NULL);
644
645       goto done;
646     } else if (w) {
647       GstStructure *tmp;
648       gint set_h, set_par_n, set_par_d;
649
650       GST_DEBUG_OBJECT (base, "width is fixed (%d)", w);
651
652       /* If the PAR is fixed too, there's not much to do
653        * except choosing the height that is nearest to the
654        * height with the same DAR */
655       if (gst_value_is_fixed (to_par)) {
656         to_par_n = gst_value_get_fraction_numerator (to_par);
657         to_par_d = gst_value_get_fraction_denominator (to_par);
658
659         GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
660
661         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
662                 to_par_n, &num, &den)) {
663           GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
664               ("Error calculating the output scaled size - integer overflow"));
665           goto done;
666         }
667
668         h = (guint) gst_util_uint64_scale_int (w, den, num);
669         gst_structure_fixate_field_nearest_int (outs, "height", h);
670
671         goto done;
672       }
673
674       /* The PAR is not fixed and it's quite likely that we can set
675        * an arbitrary PAR. */
676
677       /* Check if we can keep the input height */
678       tmp = gst_structure_copy (outs);
679       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
680       gst_structure_get_int (tmp, "height", &set_h);
681
682       /* Might have failed but try to keep the DAR nonetheless by
683        * adjusting the PAR */
684       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
685               &to_par_n, &to_par_d)) {
686         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
687             ("Error calculating the output scaled size - integer overflow"));
688         gst_structure_free (tmp);
689         goto done;
690       }
691       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
692         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
693       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
694           to_par_n, to_par_d);
695       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
696           &set_par_d);
697       gst_structure_free (tmp);
698
699       /* Check if the adjusted PAR is accepted */
700       if (set_par_n == to_par_n && set_par_d == to_par_d) {
701         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
702             set_par_n != set_par_d)
703           gst_structure_set (outs, "height", G_TYPE_INT, set_h,
704               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
705               NULL);
706         goto done;
707       }
708
709       /* Otherwise scale the height to the new PAR and check if the
710        * adjusted with is accepted. If all that fails we can't keep
711        * the DAR */
712       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
713               set_par_n, &num, &den)) {
714         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
715             ("Error calculating the output scaled size - integer overflow"));
716         goto done;
717       }
718
719       h = (guint) gst_util_uint64_scale_int (w, den, num);
720       gst_structure_fixate_field_nearest_int (outs, "height", h);
721       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
722           set_par_n != set_par_d)
723         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
724             set_par_n, set_par_d, NULL);
725
726       goto done;
727     } else if (gst_value_is_fixed (to_par)) {
728       GstStructure *tmp;
729       gint set_h, set_w, f_h, f_w;
730
731       to_par_n = gst_value_get_fraction_numerator (to_par);
732       to_par_d = gst_value_get_fraction_denominator (to_par);
733
734       /* Calculate scale factor for the PAR change */
735       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
736               to_par_d, &num, &den)) {
737         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
738             ("Error calculating the output scaled size - integer overflow"));
739         goto done;
740       }
741
742       /* Try to keep the input height (because of interlacing) */
743       tmp = gst_structure_copy (outs);
744       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
745       gst_structure_get_int (tmp, "height", &set_h);
746
747       /* This might have failed but try to scale the width
748        * to keep the DAR nonetheless */
749       w = (guint) gst_util_uint64_scale_int (set_h, num, den);
750       gst_structure_fixate_field_nearest_int (tmp, "width", w);
751       gst_structure_get_int (tmp, "width", &set_w);
752       gst_structure_free (tmp);
753
754       /* We kept the DAR and the height is nearest to the original height */
755       if (set_w == w) {
756         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
757             G_TYPE_INT, set_h, NULL);
758         goto done;
759       }
760
761       f_h = set_h;
762       f_w = set_w;
763
764       /* If the former failed, try to keep the input width at least */
765       tmp = gst_structure_copy (outs);
766       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
767       gst_structure_get_int (tmp, "width", &set_w);
768
769       /* This might have failed but try to scale the width
770        * to keep the DAR nonetheless */
771       h = (guint) gst_util_uint64_scale_int (set_w, den, num);
772       gst_structure_fixate_field_nearest_int (tmp, "height", h);
773       gst_structure_get_int (tmp, "height", &set_h);
774       gst_structure_free (tmp);
775
776       /* We kept the DAR and the width is nearest to the original width */
777       if (set_h == h) {
778         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
779             G_TYPE_INT, set_h, NULL);
780         goto done;
781       }
782
783       /* If all this failed, keep the height that was nearest to the orignal
784        * height and the nearest possible width. This changes the DAR but
785        * there's not much else to do here.
786        */
787       gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
788           f_h, NULL);
789       goto done;
790     } else {
791       GstStructure *tmp;
792       gint set_h, set_w, set_par_n, set_par_d, tmp2;
793
794       /* width, height and PAR are not fixed but passthrough is not possible */
795
796       /* First try to keep the height and width as good as possible
797        * and scale PAR */
798       tmp = gst_structure_copy (outs);
799       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
800       gst_structure_get_int (tmp, "height", &set_h);
801       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
802       gst_structure_get_int (tmp, "width", &set_w);
803
804       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
805               &to_par_n, &to_par_d)) {
806         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
807             ("Error calculating the output scaled size - integer overflow"));
808         goto done;
809       }
810
811       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
812         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
813       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
814           to_par_n, to_par_d);
815       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
816           &set_par_d);
817       gst_structure_free (tmp);
818
819       if (set_par_n == to_par_n && set_par_d == to_par_d) {
820         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
821             G_TYPE_INT, set_h, NULL);
822
823         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
824             set_par_n != set_par_d)
825           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
826               set_par_n, set_par_d, NULL);
827         goto done;
828       }
829
830       /* Otherwise try to scale width to keep the DAR with the set
831        * PAR and height */
832       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
833               set_par_n, &num, &den)) {
834         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
835             ("Error calculating the output scaled size - integer overflow"));
836         goto done;
837       }
838
839       w = (guint) gst_util_uint64_scale_int (set_h, num, den);
840       tmp = gst_structure_copy (outs);
841       gst_structure_fixate_field_nearest_int (tmp, "width", w);
842       gst_structure_get_int (tmp, "width", &tmp2);
843       gst_structure_free (tmp);
844
845       if (tmp2 == w) {
846         gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
847             G_TYPE_INT, set_h, NULL);
848         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
849             set_par_n != set_par_d)
850           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
851               set_par_n, set_par_d, NULL);
852         goto done;
853       }
854
855       /* ... or try the same with the height */
856       h = (guint) gst_util_uint64_scale_int (set_w, den, num);
857       tmp = gst_structure_copy (outs);
858       gst_structure_fixate_field_nearest_int (tmp, "height", h);
859       gst_structure_get_int (tmp, "height", &tmp2);
860       gst_structure_free (tmp);
861
862       if (tmp2 == h) {
863         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
864             G_TYPE_INT, tmp2, NULL);
865         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
866             set_par_n != set_par_d)
867           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
868               set_par_n, set_par_d, NULL);
869         goto done;
870       }
871
872       /* If all fails we can't keep the DAR and take the nearest values
873        * for everything from the first try */
874       gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
875           G_TYPE_INT, set_h, NULL);
876       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
877           set_par_n != set_par_d)
878         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
879             set_par_n, set_par_d, NULL);
880     }
881   }
882
883 done:
884   GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
885
886   if (from_par == &fpar)
887     g_value_unset (&fpar);
888   if (to_par == &tpar)
889     g_value_unset (&tpar);
890 }
891
892 static void
893 gst_video_scale_setup_vs_image (VSImage * image, GstVideoFormat format,
894     gint component, gint width, gint height, gint b_w, gint b_h, uint8_t * data)
895 {
896   image->real_width =
897       gst_video_format_get_component_width (format, component, width);
898   image->real_height =
899       gst_video_format_get_component_height (format, component, height);
900   image->width =
901       gst_video_format_get_component_width (format, component, MAX (1,
902           width - b_w));
903   image->height =
904       gst_video_format_get_component_height (format, component, MAX (1,
905           height - b_h));
906   image->stride = gst_video_format_get_row_stride (format, component, width);
907
908   image->border_top = (image->real_height - image->height) / 2;
909   image->border_bottom = image->real_height - image->height - image->border_top;
910
911   if (format == GST_VIDEO_FORMAT_YUY2 || format == GST_VIDEO_FORMAT_YVYU
912       || format == GST_VIDEO_FORMAT_UYVY) {
913     g_assert (component == 0);
914
915     image->border_left = (image->real_width - image->width) / 2;
916
917     if (image->border_left % 2 == 1)
918       image->border_left--;
919     image->border_right = image->real_width - image->width - image->border_left;
920   } else {
921     image->border_left = (image->real_width - image->width) / 2;
922     image->border_right = image->real_width - image->width - image->border_left;
923   }
924
925   if (format == GST_VIDEO_FORMAT_I420
926       || format == GST_VIDEO_FORMAT_YV12
927       || format == GST_VIDEO_FORMAT_Y444
928       || format == GST_VIDEO_FORMAT_Y42B || format == GST_VIDEO_FORMAT_Y41B) {
929     image->real_pixels = data + gst_video_format_get_component_offset (format,
930         component, width, height);
931   } else {
932     g_assert (component == 0);
933     image->real_pixels = data;
934   }
935
936   image->pixels =
937       image->real_pixels + image->border_top * image->stride +
938       image->border_left * gst_video_format_get_pixel_stride (format,
939       component);
940 }
941
942 static const guint8 *
943 _get_black_for_format (GstVideoFormat format)
944 {
945   static const guint8 black[][4] = {
946     {255, 0, 0, 0},             /*  0 = ARGB, ABGR, xRGB, xBGR */
947     {0, 0, 0, 255},             /*  1 = RGBA, BGRA, RGBx, BGRx */
948     {255, 16, 128, 128},        /*  2 = AYUV */
949     {0, 0, 0, 0},               /*  3 = RGB and BGR */
950     {16, 128, 128, 0},          /*  4 = v301 */
951     {16, 128, 16, 128},         /*  5 = YUY2, YUYV */
952     {128, 16, 128, 16},         /*  6 = UYVY */
953     {16, 0, 0, 0},              /*  7 = Y */
954     {0, 0, 0, 0}                /*  8 = RGB565, RGB666 */
955   };
956
957   switch (format) {
958     case GST_VIDEO_FORMAT_ARGB:
959     case GST_VIDEO_FORMAT_ABGR:
960     case GST_VIDEO_FORMAT_xRGB:
961     case GST_VIDEO_FORMAT_xBGR:
962       return black[0];
963     case GST_VIDEO_FORMAT_RGBA:
964     case GST_VIDEO_FORMAT_BGRA:
965     case GST_VIDEO_FORMAT_RGBx:
966     case GST_VIDEO_FORMAT_BGRx:
967       return black[1];
968     case GST_VIDEO_FORMAT_AYUV:
969       return black[2];
970     case GST_VIDEO_FORMAT_RGB:
971     case GST_VIDEO_FORMAT_BGR:
972       return black[3];
973     case GST_VIDEO_FORMAT_v308:
974       return black[4];
975     case GST_VIDEO_FORMAT_YUY2:
976     case GST_VIDEO_FORMAT_YVYU:
977       return black[5];
978     case GST_VIDEO_FORMAT_UYVY:
979       return black[6];
980     case GST_VIDEO_FORMAT_Y800:
981     case GST_VIDEO_FORMAT_GRAY8:
982       return black[7];
983     case GST_VIDEO_FORMAT_GRAY16_LE:
984     case GST_VIDEO_FORMAT_GRAY16_BE:
985     case GST_VIDEO_FORMAT_Y16:
986       return NULL;              /* Handled by the caller */
987     case GST_VIDEO_FORMAT_I420:
988     case GST_VIDEO_FORMAT_YV12:
989     case GST_VIDEO_FORMAT_Y444:
990     case GST_VIDEO_FORMAT_Y42B:
991     case GST_VIDEO_FORMAT_Y41B:
992       return black[4];          /* Y, U, V, 0 */
993     case GST_VIDEO_FORMAT_RGB16:
994     case GST_VIDEO_FORMAT_RGB15:
995       return black[8];
996     default:
997       return NULL;
998   }
999 }
1000
1001 static GstFlowReturn
1002 gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in,
1003     GstBuffer * out)
1004 {
1005   GstVideoScale *videoscale = GST_VIDEO_SCALE (trans);
1006   GstFlowReturn ret = GST_FLOW_OK;
1007   VSImage dest = { NULL, };
1008   VSImage src = { NULL, };
1009   VSImage dest_u = { NULL, };
1010   VSImage dest_v = { NULL, };
1011   VSImage src_u = { NULL, };
1012   VSImage src_v = { NULL, };
1013   gint method;
1014   const guint8 *black = _get_black_for_format (videoscale->format);
1015   gboolean add_borders;
1016
1017   GST_OBJECT_LOCK (videoscale);
1018   method = videoscale->method;
1019   add_borders = videoscale->add_borders;
1020   GST_OBJECT_UNLOCK (videoscale);
1021
1022   gst_video_scale_setup_vs_image (&src, videoscale->format, 0,
1023       videoscale->from_width, videoscale->from_height, 0, 0,
1024       GST_BUFFER_DATA (in));
1025   gst_video_scale_setup_vs_image (&dest, videoscale->format, 0,
1026       videoscale->to_width, videoscale->to_height, videoscale->borders_w,
1027       videoscale->borders_h, GST_BUFFER_DATA (out));
1028
1029   if (videoscale->format == GST_VIDEO_FORMAT_I420
1030       || videoscale->format == GST_VIDEO_FORMAT_YV12
1031       || videoscale->format == GST_VIDEO_FORMAT_Y444
1032       || videoscale->format == GST_VIDEO_FORMAT_Y42B
1033       || videoscale->format == GST_VIDEO_FORMAT_Y41B) {
1034     gst_video_scale_setup_vs_image (&src_u, videoscale->format, 1,
1035         videoscale->from_width, videoscale->from_height, 0, 0,
1036         GST_BUFFER_DATA (in));
1037     gst_video_scale_setup_vs_image (&src_v, videoscale->format, 2,
1038         videoscale->from_width, videoscale->from_height, 0, 0,
1039         GST_BUFFER_DATA (in));
1040     gst_video_scale_setup_vs_image (&dest_u, videoscale->format, 1,
1041         videoscale->to_width, videoscale->to_height, videoscale->borders_w,
1042         videoscale->borders_h, GST_BUFFER_DATA (out));
1043     gst_video_scale_setup_vs_image (&dest_v, videoscale->format, 2,
1044         videoscale->to_width, videoscale->to_height, videoscale->borders_w,
1045         videoscale->borders_h, GST_BUFFER_DATA (out));
1046   }
1047
1048   switch (videoscale->format) {
1049     case GST_VIDEO_FORMAT_RGBx:
1050     case GST_VIDEO_FORMAT_xRGB:
1051     case GST_VIDEO_FORMAT_BGRx:
1052     case GST_VIDEO_FORMAT_xBGR:
1053     case GST_VIDEO_FORMAT_RGBA:
1054     case GST_VIDEO_FORMAT_ARGB:
1055     case GST_VIDEO_FORMAT_BGRA:
1056     case GST_VIDEO_FORMAT_ABGR:
1057     case GST_VIDEO_FORMAT_AYUV:
1058       if (add_borders)
1059         vs_fill_borders_RGBA (&dest, black);
1060       switch (method) {
1061         case GST_VIDEO_SCALE_NEAREST:
1062           vs_image_scale_nearest_RGBA (&dest, &src, videoscale->tmp_buf);
1063           break;
1064         case GST_VIDEO_SCALE_BILINEAR:
1065           vs_image_scale_linear_RGBA (&dest, &src, videoscale->tmp_buf);
1066           break;
1067         case GST_VIDEO_SCALE_4TAP:
1068           vs_image_scale_4tap_RGBA (&dest, &src, videoscale->tmp_buf);
1069           break;
1070         default:
1071           goto unknown_mode;
1072       }
1073       break;
1074     case GST_VIDEO_FORMAT_RGB:
1075     case GST_VIDEO_FORMAT_BGR:
1076     case GST_VIDEO_FORMAT_v308:
1077       if (add_borders)
1078         vs_fill_borders_RGB (&dest, black);
1079       switch (method) {
1080         case GST_VIDEO_SCALE_NEAREST:
1081           vs_image_scale_nearest_RGB (&dest, &src, videoscale->tmp_buf);
1082           break;
1083         case GST_VIDEO_SCALE_BILINEAR:
1084           vs_image_scale_linear_RGB (&dest, &src, videoscale->tmp_buf);
1085           break;
1086         case GST_VIDEO_SCALE_4TAP:
1087           vs_image_scale_4tap_RGB (&dest, &src, videoscale->tmp_buf);
1088           break;
1089         default:
1090           goto unknown_mode;
1091       }
1092       break;
1093     case GST_VIDEO_FORMAT_YUY2:
1094     case GST_VIDEO_FORMAT_YVYU:
1095       if (add_borders)
1096         vs_fill_borders_YUYV (&dest, black);
1097       switch (method) {
1098         case GST_VIDEO_SCALE_NEAREST:
1099           vs_image_scale_nearest_YUYV (&dest, &src, videoscale->tmp_buf);
1100           break;
1101         case GST_VIDEO_SCALE_BILINEAR:
1102           vs_image_scale_linear_YUYV (&dest, &src, videoscale->tmp_buf);
1103           break;
1104         case GST_VIDEO_SCALE_4TAP:
1105           vs_image_scale_4tap_YUYV (&dest, &src, videoscale->tmp_buf);
1106           break;
1107         default:
1108           goto unknown_mode;
1109       }
1110       break;
1111     case GST_VIDEO_FORMAT_UYVY:
1112       if (add_borders)
1113         vs_fill_borders_UYVY (&dest, black);
1114       switch (method) {
1115         case GST_VIDEO_SCALE_NEAREST:
1116           vs_image_scale_nearest_UYVY (&dest, &src, videoscale->tmp_buf);
1117           break;
1118         case GST_VIDEO_SCALE_BILINEAR:
1119           vs_image_scale_linear_UYVY (&dest, &src, videoscale->tmp_buf);
1120           break;
1121         case GST_VIDEO_SCALE_4TAP:
1122           vs_image_scale_4tap_UYVY (&dest, &src, videoscale->tmp_buf);
1123           break;
1124         default:
1125           goto unknown_mode;
1126       }
1127       break;
1128     case GST_VIDEO_FORMAT_Y800:
1129     case GST_VIDEO_FORMAT_GRAY8:
1130       if (add_borders)
1131         vs_fill_borders_Y (&dest, black);
1132       switch (method) {
1133         case GST_VIDEO_SCALE_NEAREST:
1134           vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf);
1135           break;
1136         case GST_VIDEO_SCALE_BILINEAR:
1137           vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf);
1138           break;
1139         case GST_VIDEO_SCALE_4TAP:
1140           vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf);
1141           break;
1142         default:
1143           goto unknown_mode;
1144       }
1145       break;
1146     case GST_VIDEO_FORMAT_GRAY16_LE:
1147     case GST_VIDEO_FORMAT_GRAY16_BE:
1148     case GST_VIDEO_FORMAT_Y16:
1149       if (add_borders)
1150         vs_fill_borders_Y16 (&dest, 0);
1151       switch (method) {
1152         case GST_VIDEO_SCALE_NEAREST:
1153           vs_image_scale_nearest_Y16 (&dest, &src, videoscale->tmp_buf);
1154           break;
1155         case GST_VIDEO_SCALE_BILINEAR:
1156           vs_image_scale_linear_Y16 (&dest, &src, videoscale->tmp_buf);
1157           break;
1158         case GST_VIDEO_SCALE_4TAP:
1159           vs_image_scale_4tap_Y16 (&dest, &src, videoscale->tmp_buf);
1160           break;
1161         default:
1162           goto unknown_mode;
1163       }
1164       break;
1165     case GST_VIDEO_FORMAT_I420:
1166     case GST_VIDEO_FORMAT_YV12:
1167     case GST_VIDEO_FORMAT_Y444:
1168     case GST_VIDEO_FORMAT_Y42B:
1169     case GST_VIDEO_FORMAT_Y41B:
1170       if (add_borders) {
1171         vs_fill_borders_Y (&dest, black);
1172         vs_fill_borders_Y (&dest_u, black + 1);
1173         vs_fill_borders_Y (&dest_v, black + 2);
1174       }
1175       switch (method) {
1176         case GST_VIDEO_SCALE_NEAREST:
1177           vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf);
1178           vs_image_scale_nearest_Y (&dest_u, &src_u, videoscale->tmp_buf);
1179           vs_image_scale_nearest_Y (&dest_v, &src_v, videoscale->tmp_buf);
1180           break;
1181         case GST_VIDEO_SCALE_BILINEAR:
1182           vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf);
1183           vs_image_scale_linear_Y (&dest_u, &src_u, videoscale->tmp_buf);
1184           vs_image_scale_linear_Y (&dest_v, &src_v, videoscale->tmp_buf);
1185           break;
1186         case GST_VIDEO_SCALE_4TAP:
1187           vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf);
1188           vs_image_scale_4tap_Y (&dest_u, &src_u, videoscale->tmp_buf);
1189           vs_image_scale_4tap_Y (&dest_v, &src_v, videoscale->tmp_buf);
1190           break;
1191         default:
1192           goto unknown_mode;
1193       }
1194       break;
1195     case GST_VIDEO_FORMAT_RGB16:
1196       if (add_borders)
1197         vs_fill_borders_RGB565 (&dest, black);
1198       switch (method) {
1199         case GST_VIDEO_SCALE_NEAREST:
1200           vs_image_scale_nearest_RGB565 (&dest, &src, videoscale->tmp_buf);
1201           break;
1202         case GST_VIDEO_SCALE_BILINEAR:
1203           vs_image_scale_linear_RGB565 (&dest, &src, videoscale->tmp_buf);
1204           break;
1205         case GST_VIDEO_SCALE_4TAP:
1206           vs_image_scale_4tap_RGB565 (&dest, &src, videoscale->tmp_buf);
1207           break;
1208         default:
1209           goto unknown_mode;
1210       }
1211       break;
1212     case GST_VIDEO_FORMAT_RGB15:
1213       if (add_borders)
1214         vs_fill_borders_RGB555 (&dest, black);
1215       switch (method) {
1216         case GST_VIDEO_SCALE_NEAREST:
1217           vs_image_scale_nearest_RGB555 (&dest, &src, videoscale->tmp_buf);
1218           break;
1219         case GST_VIDEO_SCALE_BILINEAR:
1220           vs_image_scale_linear_RGB555 (&dest, &src, videoscale->tmp_buf);
1221           break;
1222         case GST_VIDEO_SCALE_4TAP:
1223           vs_image_scale_4tap_RGB555 (&dest, &src, videoscale->tmp_buf);
1224           break;
1225         default:
1226           goto unknown_mode;
1227       }
1228       break;
1229     default:
1230       goto unsupported;
1231   }
1232
1233   GST_LOG_OBJECT (videoscale, "pushing buffer of %d bytes",
1234       GST_BUFFER_SIZE (out));
1235
1236   return ret;
1237
1238   /* ERRORS */
1239 unsupported:
1240   {
1241     GST_ELEMENT_ERROR (videoscale, STREAM, NOT_IMPLEMENTED, (NULL),
1242         ("Unsupported format %d for scaling method %d",
1243             videoscale->format, method));
1244     return GST_FLOW_ERROR;
1245   }
1246 unknown_mode:
1247   {
1248     GST_ELEMENT_ERROR (videoscale, STREAM, NOT_IMPLEMENTED, (NULL),
1249         ("Unknown scaling method %d", videoscale->method));
1250     return GST_FLOW_ERROR;
1251   }
1252 }
1253
1254 static gboolean
1255 gst_video_scale_src_event (GstBaseTransform * trans, GstEvent * event)
1256 {
1257   GstVideoScale *videoscale = GST_VIDEO_SCALE (trans);
1258   gboolean ret;
1259   gdouble a;
1260   GstStructure *structure;
1261
1262   GST_DEBUG_OBJECT (videoscale, "handling %s event",
1263       GST_EVENT_TYPE_NAME (event));
1264
1265   switch (GST_EVENT_TYPE (event)) {
1266     case GST_EVENT_NAVIGATION:
1267       event =
1268           GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1269
1270       structure = (GstStructure *) gst_event_get_structure (event);
1271       if (gst_structure_get_double (structure, "pointer_x", &a)) {
1272         gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1273             a * videoscale->from_width / videoscale->to_width, NULL);
1274       }
1275       if (gst_structure_get_double (structure, "pointer_y", &a)) {
1276         gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1277             a * videoscale->from_height / videoscale->to_height, NULL);
1278       }
1279       break;
1280     default:
1281       break;
1282   }
1283
1284   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
1285
1286   return ret;
1287 }
1288
1289 static gboolean
1290 plugin_init (GstPlugin * plugin)
1291 {
1292   if (!gst_element_register (plugin, "videoscale", GST_RANK_NONE,
1293           GST_TYPE_VIDEO_SCALE))
1294     return FALSE;
1295
1296   GST_DEBUG_CATEGORY_INIT (video_scale_debug, "videoscale", 0,
1297       "videoscale element");
1298
1299   vs_4tap_init ();
1300
1301   return TRUE;
1302 }
1303
1304 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1305     GST_VERSION_MINOR,
1306     "videoscale",
1307     "Resizes video", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
1308     GST_PACKAGE_ORIGIN)