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