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