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