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