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