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