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  * @title: videoscale
24  * @see_also: videorate, videoconvert
25  *
26  * This element resizes video frames. By default the element will try to
27  * negotiate to the same size on the source and sinkpad so that no scaling
28  * is needed. It is therefore safe to insert this element in a pipeline to
29  * get more robust behaviour without any cost if no scaling is needed.
30  *
31  * This element supports a wide range of color spaces including various YUV and
32  * RGB formats and is therefore generally able to operate anywhere in a
33  * pipeline.
34  *
35  * ## Example pipelines
36  * |[
37  * gst-launch-1.0 -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! videoconvert ! videoscale ! autovideosink
38  * ]|
39  *  Decode an Ogg/Theora and display the video. If the video sink chosen
40  * cannot perform scaling, the video scaling will be performed by videoscale
41  * when you resize the video window.
42  * To create the test Ogg/Theora file refer to the documentation of theoraenc.
43  * |[
44  * gst-launch-1.0 -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! videoconvert ! videoscale ! video/x-raw,width=100 ! autovideosink
45  * ]|
46  *  Decode an Ogg/Theora and display the video with a width of 100.
47  *
48  */
49 
50 /*
51  * Formulas for PAR, DAR, width and height relations:
52  *
53  * dar_n   w   par_n
54  * ----- = - * -----
55  * dar_d   h   par_d
56  *
57  * par_n    h   dar_n
58  * ----- =  - * -----
59  * par_d    w   dar_d
60  *
61  *         dar_n   par_d
62  * w = h * ----- * -----
63  *         dar_d   par_n
64  *
65  *         dar_d   par_n
66  * h = w * ----- * -----
67  *         dar_n   par_d
68  */
69 
70 #ifdef HAVE_CONFIG_H
71 #include "config.h"
72 #endif
73 
74 #include <string.h>
75 
76 #include <math.h>
77 
78 #include <gst/video/gstvideometa.h>
79 #include <gst/video/gstvideopool.h>
80 
81 #include "gstvideoscale.h"
82 
83 #define GST_CAT_DEFAULT video_scale_debug
84 GST_DEBUG_CATEGORY_STATIC (video_scale_debug);
85 GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE);
86 
87 #define DEFAULT_PROP_METHOD       GST_VIDEO_SCALE_BILINEAR
88 #define DEFAULT_PROP_ADD_BORDERS  TRUE
89 #define DEFAULT_PROP_SHARPNESS    1.0
90 #define DEFAULT_PROP_SHARPEN      0.0
91 #define DEFAULT_PROP_DITHER       FALSE
92 #define DEFAULT_PROP_SUBMETHOD    1
93 #define DEFAULT_PROP_ENVELOPE     2.0
94 #define DEFAULT_PROP_GAMMA_DECODE FALSE
95 #define DEFAULT_PROP_N_THREADS    1
96 
97 enum
98 {
99   PROP_0,
100   PROP_METHOD,
101   PROP_ADD_BORDERS,
102   PROP_SHARPNESS,
103   PROP_SHARPEN,
104   PROP_DITHER,
105   PROP_SUBMETHOD,
106   PROP_ENVELOPE,
107   PROP_GAMMA_DECODE,
108   PROP_N_THREADS
109 };
110 
111 #undef GST_VIDEO_SIZE_RANGE
112 #define GST_VIDEO_SIZE_RANGE "(int) [ 1, 32767]"
113 
114 /* FIXME: add v210 support
115  * FIXME: add v216 support
116  * FIXME: add UYVP support
117  * FIXME: add A420 support
118  * FIXME: add YUV9 support
119  * FIXME: add YVU9 support
120  * FIXME: add IYU1 support
121  * FIXME: add r210 support
122  */
123 
124 #define GST_VIDEO_FORMATS GST_VIDEO_FORMATS_ALL
125 
126 static GstStaticCaps gst_video_scale_format_caps =
127     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS) ";"
128     GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS));
129 
130 #define GST_TYPE_VIDEO_SCALE_METHOD (gst_video_scale_method_get_type())
131 static GType
gst_video_scale_method_get_type(void)132 gst_video_scale_method_get_type (void)
133 {
134   static GType video_scale_method_type = 0;
135 
136   static const GEnumValue video_scale_methods[] = {
137     {GST_VIDEO_SCALE_NEAREST, "Nearest Neighbour", "nearest-neighbour"},
138     {GST_VIDEO_SCALE_BILINEAR, "Bilinear (2-tap)", "bilinear"},
139     {GST_VIDEO_SCALE_4TAP, "4-tap Sinc", "4-tap"},
140     {GST_VIDEO_SCALE_LANCZOS, "Lanczos", "lanczos"},
141     {GST_VIDEO_SCALE_BILINEAR2, "Bilinear (multi-tap)", "bilinear2"},
142     {GST_VIDEO_SCALE_SINC, "Sinc (multi-tap)", "sinc"},
143     {GST_VIDEO_SCALE_HERMITE, "Hermite (multi-tap)", "hermite"},
144     {GST_VIDEO_SCALE_SPLINE, "Spline (multi-tap)", "spline"},
145     {GST_VIDEO_SCALE_CATROM, "Catmull-Rom (multi-tap)", "catrom"},
146     {GST_VIDEO_SCALE_MITCHELL, "Mitchell (multi-tap)", "mitchell"},
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 *
gst_video_scale_get_capslist(void)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 *
gst_video_scale_src_template_factory(void)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 *
gst_video_scale_sink_template_factory(void)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
gst_video_scale_class_init(GstVideoScaleClass * klass)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.5, 1.5, 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", 1.0, 5.0, DEFAULT_PROP_ENVELOPE,
260           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
261 
262   g_object_class_install_property (gobject_class, PROP_GAMMA_DECODE,
263       g_param_spec_boolean ("gamma-decode", "Gamma Decode",
264           "Decode gamma before scaling", DEFAULT_PROP_GAMMA_DECODE,
265           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
266 
267   g_object_class_install_property (gobject_class, PROP_N_THREADS,
268       g_param_spec_uint ("n-threads", "Threads",
269           "Maximum number of threads to use", 0, G_MAXUINT,
270           DEFAULT_PROP_N_THREADS,
271           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
272 
273   gst_element_class_set_static_metadata (element_class,
274       "Video scaler", "Filter/Converter/Video/Scaler",
275       "Resizes video", "Wim Taymans <wim.taymans@gmail.com>");
276 
277   gst_element_class_add_pad_template (element_class,
278       gst_video_scale_sink_template_factory ());
279   gst_element_class_add_pad_template (element_class,
280       gst_video_scale_src_template_factory ());
281 
282   trans_class->transform_caps =
283       GST_DEBUG_FUNCPTR (gst_video_scale_transform_caps);
284   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_scale_fixate_caps);
285   trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_scale_src_event);
286 
287   filter_class->set_info = GST_DEBUG_FUNCPTR (gst_video_scale_set_info);
288   filter_class->transform_frame =
289       GST_DEBUG_FUNCPTR (gst_video_scale_transform_frame);
290 }
291 
292 static void
gst_video_scale_init(GstVideoScale * videoscale)293 gst_video_scale_init (GstVideoScale * videoscale)
294 {
295   videoscale->method = DEFAULT_PROP_METHOD;
296   videoscale->add_borders = DEFAULT_PROP_ADD_BORDERS;
297   videoscale->submethod = DEFAULT_PROP_SUBMETHOD;
298   videoscale->sharpness = DEFAULT_PROP_SHARPNESS;
299   videoscale->sharpen = DEFAULT_PROP_SHARPEN;
300   videoscale->dither = DEFAULT_PROP_DITHER;
301   videoscale->envelope = DEFAULT_PROP_ENVELOPE;
302   videoscale->gamma_decode = DEFAULT_PROP_GAMMA_DECODE;
303   videoscale->n_threads = DEFAULT_PROP_N_THREADS;
304 }
305 
306 static void
gst_video_scale_finalize(GstVideoScale * videoscale)307 gst_video_scale_finalize (GstVideoScale * videoscale)
308 {
309   if (videoscale->convert)
310     gst_video_converter_free (videoscale->convert);
311 
312   G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (videoscale));
313 }
314 
315 static void
gst_video_scale_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)316 gst_video_scale_set_property (GObject * object, guint prop_id,
317     const GValue * value, GParamSpec * pspec)
318 {
319   GstVideoScale *vscale = GST_VIDEO_SCALE (object);
320 
321   switch (prop_id) {
322     case PROP_METHOD:
323       GST_OBJECT_LOCK (vscale);
324       vscale->method = g_value_get_enum (value);
325       GST_OBJECT_UNLOCK (vscale);
326       break;
327     case PROP_ADD_BORDERS:
328       GST_OBJECT_LOCK (vscale);
329       vscale->add_borders = g_value_get_boolean (value);
330       GST_OBJECT_UNLOCK (vscale);
331       gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM_CAST (vscale));
332       break;
333     case PROP_SHARPNESS:
334       GST_OBJECT_LOCK (vscale);
335       vscale->sharpness = g_value_get_double (value);
336       GST_OBJECT_UNLOCK (vscale);
337       break;
338     case PROP_SHARPEN:
339       GST_OBJECT_LOCK (vscale);
340       vscale->sharpen = g_value_get_double (value);
341       GST_OBJECT_UNLOCK (vscale);
342       break;
343     case PROP_DITHER:
344       GST_OBJECT_LOCK (vscale);
345       vscale->dither = g_value_get_boolean (value);
346       GST_OBJECT_UNLOCK (vscale);
347       break;
348     case PROP_SUBMETHOD:
349       GST_OBJECT_LOCK (vscale);
350       vscale->submethod = g_value_get_int (value);
351       GST_OBJECT_UNLOCK (vscale);
352       break;
353     case PROP_ENVELOPE:
354       GST_OBJECT_LOCK (vscale);
355       vscale->envelope = g_value_get_double (value);
356       GST_OBJECT_UNLOCK (vscale);
357       break;
358     case PROP_GAMMA_DECODE:
359       GST_OBJECT_LOCK (vscale);
360       vscale->gamma_decode = g_value_get_boolean (value);
361       GST_OBJECT_UNLOCK (vscale);
362       break;
363     case PROP_N_THREADS:
364       GST_OBJECT_LOCK (vscale);
365       vscale->n_threads = g_value_get_uint (value);
366       GST_OBJECT_UNLOCK (vscale);
367       break;
368     default:
369       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370       break;
371   }
372 }
373 
374 static void
gst_video_scale_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)375 gst_video_scale_get_property (GObject * object, guint prop_id, GValue * value,
376     GParamSpec * pspec)
377 {
378   GstVideoScale *vscale = GST_VIDEO_SCALE (object);
379 
380   switch (prop_id) {
381     case PROP_METHOD:
382       GST_OBJECT_LOCK (vscale);
383       g_value_set_enum (value, vscale->method);
384       GST_OBJECT_UNLOCK (vscale);
385       break;
386     case PROP_ADD_BORDERS:
387       GST_OBJECT_LOCK (vscale);
388       g_value_set_boolean (value, vscale->add_borders);
389       GST_OBJECT_UNLOCK (vscale);
390       break;
391     case PROP_SHARPNESS:
392       GST_OBJECT_LOCK (vscale);
393       g_value_set_double (value, vscale->sharpness);
394       GST_OBJECT_UNLOCK (vscale);
395       break;
396     case PROP_SHARPEN:
397       GST_OBJECT_LOCK (vscale);
398       g_value_set_double (value, vscale->sharpen);
399       GST_OBJECT_UNLOCK (vscale);
400       break;
401     case PROP_DITHER:
402       GST_OBJECT_LOCK (vscale);
403       g_value_set_boolean (value, vscale->dither);
404       GST_OBJECT_UNLOCK (vscale);
405       break;
406     case PROP_SUBMETHOD:
407       GST_OBJECT_LOCK (vscale);
408       g_value_set_int (value, vscale->submethod);
409       GST_OBJECT_UNLOCK (vscale);
410       break;
411     case PROP_ENVELOPE:
412       GST_OBJECT_LOCK (vscale);
413       g_value_set_double (value, vscale->envelope);
414       GST_OBJECT_UNLOCK (vscale);
415       break;
416     case PROP_GAMMA_DECODE:
417       GST_OBJECT_LOCK (vscale);
418       g_value_set_boolean (value, vscale->gamma_decode);
419       GST_OBJECT_UNLOCK (vscale);
420       break;
421     case PROP_N_THREADS:
422       GST_OBJECT_LOCK (vscale);
423       g_value_set_uint (value, vscale->n_threads);
424       GST_OBJECT_UNLOCK (vscale);
425       break;
426     default:
427       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
428       break;
429   }
430 }
431 
432 static GstCaps *
gst_video_scale_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)433 gst_video_scale_transform_caps (GstBaseTransform * trans,
434     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
435 {
436   GstCaps *ret;
437   GstStructure *structure;
438   GstCapsFeatures *features;
439   gint i, n;
440 
441   GST_DEBUG_OBJECT (trans,
442       "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
443       (direction == GST_PAD_SINK) ? "sink" : "src");
444 
445   ret = gst_caps_new_empty ();
446   n = gst_caps_get_size (caps);
447   for (i = 0; i < n; i++) {
448     structure = gst_caps_get_structure (caps, i);
449     features = gst_caps_get_features (caps, i);
450 
451     /* If this is already expressed by the existing caps
452      * skip this structure */
453     if (i > 0 && gst_caps_is_subset_structure_full (ret, structure, features))
454       continue;
455 
456     /* make copy */
457     structure = gst_structure_copy (structure);
458 
459     /* If the features are non-sysmem we can only do passthrough */
460     if (!gst_caps_features_is_any (features)
461         && gst_caps_features_is_equal (features,
462             GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)) {
463       gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
464           "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
465 
466       /* if pixel aspect ratio, make a range of it */
467       if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
468         gst_structure_set (structure, "pixel-aspect-ratio",
469             GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
470       }
471     }
472     gst_caps_append_structure_full (ret, structure,
473         gst_caps_features_copy (features));
474   }
475 
476   if (filter) {
477     GstCaps *intersection;
478 
479     intersection =
480         gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
481     gst_caps_unref (ret);
482     ret = intersection;
483   }
484 
485   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
486 
487   return ret;
488 }
489 
490 static gboolean
gst_video_scale_set_info(GstVideoFilter * filter,GstCaps * in,GstVideoInfo * in_info,GstCaps * out,GstVideoInfo * out_info)491 gst_video_scale_set_info (GstVideoFilter * filter, GstCaps * in,
492     GstVideoInfo * in_info, GstCaps * out, GstVideoInfo * out_info)
493 {
494   GstVideoScale *videoscale = GST_VIDEO_SCALE (filter);
495   gint from_dar_n, from_dar_d, to_dar_n, to_dar_d;
496 
497   if (!gst_util_fraction_multiply (in_info->width,
498           in_info->height, in_info->par_n, in_info->par_d, &from_dar_n,
499           &from_dar_d)) {
500     from_dar_n = from_dar_d = -1;
501   }
502 
503   if (!gst_util_fraction_multiply (out_info->width,
504           out_info->height, out_info->par_n, out_info->par_d, &to_dar_n,
505           &to_dar_d)) {
506     to_dar_n = to_dar_d = -1;
507   }
508 
509   videoscale->borders_w = videoscale->borders_h = 0;
510   if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
511     if (videoscale->add_borders) {
512       gint n, d, to_h, to_w;
513 
514       if (from_dar_n != -1 && from_dar_d != -1
515           && gst_util_fraction_multiply (from_dar_n, from_dar_d,
516               out_info->par_d, out_info->par_n, &n, &d)) {
517         to_h = gst_util_uint64_scale_int (out_info->width, d, n);
518         if (to_h <= out_info->height) {
519           videoscale->borders_h = out_info->height - to_h;
520           videoscale->borders_w = 0;
521         } else {
522           to_w = gst_util_uint64_scale_int (out_info->height, n, d);
523           g_assert (to_w <= out_info->width);
524           videoscale->borders_h = 0;
525           videoscale->borders_w = out_info->width - to_w;
526         }
527       } else {
528         GST_WARNING_OBJECT (videoscale, "Can't calculate borders");
529       }
530     } else {
531       GST_WARNING_OBJECT (videoscale, "Can't keep DAR!");
532     }
533   }
534 
535   if (in_info->width == out_info->width && in_info->height == out_info->height
536       && videoscale->borders_w == 0 && videoscale->borders_h == 0) {
537     gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter), TRUE);
538   } else {
539     GstStructure *options;
540     GST_CAT_DEBUG_OBJECT (CAT_PERFORMANCE, filter, "setup videoscaling");
541     gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter), FALSE);
542 
543     options = gst_structure_new_empty ("videoscale");
544 
545     switch (videoscale->method) {
546       case GST_VIDEO_SCALE_NEAREST:
547         gst_structure_set (options,
548             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
549             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_NEAREST,
550             NULL);
551         break;
552       case GST_VIDEO_SCALE_BILINEAR:
553         gst_structure_set (options,
554             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
555             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_LINEAR,
556             GST_VIDEO_RESAMPLER_OPT_MAX_TAPS, G_TYPE_INT, 2, NULL);
557         break;
558       case GST_VIDEO_SCALE_4TAP:
559         gst_structure_set (options,
560             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
561             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_SINC,
562             GST_VIDEO_RESAMPLER_OPT_MAX_TAPS, G_TYPE_INT, 4, NULL);
563         break;
564       case GST_VIDEO_SCALE_LANCZOS:
565         gst_structure_set (options,
566             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
567             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_LANCZOS,
568             NULL);
569         break;
570       case GST_VIDEO_SCALE_BILINEAR2:
571         gst_structure_set (options,
572             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
573             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_LINEAR,
574             NULL);
575         break;
576       case GST_VIDEO_SCALE_SINC:
577         gst_structure_set (options,
578             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
579             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_SINC,
580             NULL);
581         break;
582       case GST_VIDEO_SCALE_HERMITE:
583         gst_structure_set (options,
584             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
585             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_CUBIC,
586             GST_VIDEO_RESAMPLER_OPT_CUBIC_B, G_TYPE_DOUBLE, (gdouble) 0.0,
587             GST_VIDEO_RESAMPLER_OPT_CUBIC_C, G_TYPE_DOUBLE, (gdouble) 0.0,
588             NULL);
589         break;
590       case GST_VIDEO_SCALE_SPLINE:
591         gst_structure_set (options,
592             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
593             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_CUBIC,
594             GST_VIDEO_RESAMPLER_OPT_CUBIC_B, G_TYPE_DOUBLE, (gdouble) 1.0,
595             GST_VIDEO_RESAMPLER_OPT_CUBIC_C, G_TYPE_DOUBLE, (gdouble) 0.0,
596             NULL);
597         break;
598       case GST_VIDEO_SCALE_CATROM:
599         gst_structure_set (options,
600             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
601             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_CUBIC,
602             GST_VIDEO_RESAMPLER_OPT_CUBIC_B, G_TYPE_DOUBLE, (gdouble) 0.0,
603             GST_VIDEO_RESAMPLER_OPT_CUBIC_C, G_TYPE_DOUBLE, (gdouble) 0.5,
604             NULL);
605         break;
606       case GST_VIDEO_SCALE_MITCHELL:
607         gst_structure_set (options,
608             GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
609             GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_CUBIC,
610             GST_VIDEO_RESAMPLER_OPT_CUBIC_B, G_TYPE_DOUBLE, (gdouble) 1.0 / 3.0,
611             GST_VIDEO_RESAMPLER_OPT_CUBIC_C, G_TYPE_DOUBLE, (gdouble) 1.0 / 3.0,
612             NULL);
613         break;
614     }
615     gst_structure_set (options,
616         GST_VIDEO_RESAMPLER_OPT_ENVELOPE, G_TYPE_DOUBLE, videoscale->envelope,
617         GST_VIDEO_RESAMPLER_OPT_SHARPNESS, G_TYPE_DOUBLE, videoscale->sharpness,
618         GST_VIDEO_RESAMPLER_OPT_SHARPEN, G_TYPE_DOUBLE, videoscale->sharpen,
619         GST_VIDEO_CONVERTER_OPT_DEST_X, G_TYPE_INT, videoscale->borders_w / 2,
620         GST_VIDEO_CONVERTER_OPT_DEST_Y, G_TYPE_INT, videoscale->borders_h / 2,
621         GST_VIDEO_CONVERTER_OPT_DEST_WIDTH, G_TYPE_INT,
622         out_info->width - videoscale->borders_w,
623         GST_VIDEO_CONVERTER_OPT_DEST_HEIGHT, G_TYPE_INT,
624         out_info->height - videoscale->borders_h,
625         GST_VIDEO_CONVERTER_OPT_MATRIX_MODE, GST_TYPE_VIDEO_MATRIX_MODE,
626         GST_VIDEO_MATRIX_MODE_NONE, GST_VIDEO_CONVERTER_OPT_DITHER_METHOD,
627         GST_TYPE_VIDEO_DITHER_METHOD, GST_VIDEO_DITHER_NONE,
628         GST_VIDEO_CONVERTER_OPT_CHROMA_MODE, GST_TYPE_VIDEO_CHROMA_MODE,
629         GST_VIDEO_CHROMA_MODE_NONE,
630         GST_VIDEO_CONVERTER_OPT_THREADS, G_TYPE_UINT, videoscale->n_threads,
631         NULL);
632 
633     if (videoscale->gamma_decode) {
634       gst_structure_set (options,
635           GST_VIDEO_CONVERTER_OPT_GAMMA_MODE, GST_TYPE_VIDEO_GAMMA_MODE,
636           GST_VIDEO_GAMMA_MODE_REMAP, NULL);
637     }
638 
639     if (videoscale->convert)
640       gst_video_converter_free (videoscale->convert);
641     videoscale->convert = gst_video_converter_new (in_info, out_info, options);
642   }
643 
644   GST_DEBUG_OBJECT (videoscale, "from=%dx%d (par=%d/%d dar=%d/%d), size %"
645       G_GSIZE_FORMAT " -> to=%dx%d (par=%d/%d dar=%d/%d borders=%d:%d), "
646       "size %" G_GSIZE_FORMAT,
647       in_info->width, in_info->height, in_info->par_n, in_info->par_d,
648       from_dar_n, from_dar_d, in_info->size, out_info->width,
649       out_info->height, out_info->par_n, out_info->par_d, to_dar_n, to_dar_d,
650       videoscale->borders_w, videoscale->borders_h, out_info->size);
651 
652   return TRUE;
653 }
654 
655 static GstCaps *
gst_video_scale_fixate_caps(GstBaseTransform * base,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)656 gst_video_scale_fixate_caps (GstBaseTransform * base, GstPadDirection direction,
657     GstCaps * caps, GstCaps * othercaps)
658 {
659   GstStructure *ins, *outs;
660   const GValue *from_par, *to_par;
661   GValue fpar = { 0, }, tpar = {
662   0,};
663 
664   othercaps = gst_caps_truncate (othercaps);
665   othercaps = gst_caps_make_writable (othercaps);
666 
667   GST_DEBUG_OBJECT (base, "trying to fixate othercaps %" GST_PTR_FORMAT
668       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
669 
670   ins = gst_caps_get_structure (caps, 0);
671   outs = gst_caps_get_structure (othercaps, 0);
672 
673   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
674   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
675 
676   /* If we're fixating from the sinkpad we always set the PAR and
677    * assume that missing PAR on the sinkpad means 1/1 and
678    * missing PAR on the srcpad means undefined
679    */
680   if (direction == GST_PAD_SINK) {
681     if (!from_par) {
682       g_value_init (&fpar, GST_TYPE_FRACTION);
683       gst_value_set_fraction (&fpar, 1, 1);
684       from_par = &fpar;
685     }
686     if (!to_par) {
687       g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
688       gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
689       to_par = &tpar;
690     }
691   } else {
692     if (!to_par) {
693       g_value_init (&tpar, GST_TYPE_FRACTION);
694       gst_value_set_fraction (&tpar, 1, 1);
695       to_par = &tpar;
696 
697       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
698           NULL);
699     }
700     if (!from_par) {
701       g_value_init (&fpar, GST_TYPE_FRACTION);
702       gst_value_set_fraction (&fpar, 1, 1);
703       from_par = &fpar;
704     }
705   }
706 
707   /* we have both PAR but they might not be fixated */
708   {
709     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
710     gint w = 0, h = 0;
711     gint from_dar_n, from_dar_d;
712     gint num, den;
713 
714     /* from_par should be fixed */
715     g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
716 
717     from_par_n = gst_value_get_fraction_numerator (from_par);
718     from_par_d = gst_value_get_fraction_denominator (from_par);
719 
720     gst_structure_get_int (ins, "width", &from_w);
721     gst_structure_get_int (ins, "height", &from_h);
722 
723     gst_structure_get_int (outs, "width", &w);
724     gst_structure_get_int (outs, "height", &h);
725 
726     /* if both width and height are already fixed, we can't do anything
727      * about it anymore */
728     if (w && h) {
729       guint n, d;
730 
731       GST_DEBUG_OBJECT (base, "dimensions already set to %dx%d, not fixating",
732           w, h);
733       if (!gst_value_is_fixed (to_par)) {
734         if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
735                 from_par_n, from_par_d, w, h)) {
736           GST_DEBUG_OBJECT (base, "fixating to_par to %dx%d", n, d);
737           if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
738             gst_structure_fixate_field_nearest_fraction (outs,
739                 "pixel-aspect-ratio", n, d);
740           else if (n != d)
741             gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
742                 n, d, NULL);
743         }
744       }
745       goto done;
746     }
747 
748     /* Calculate input DAR */
749     if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
750             &from_dar_n, &from_dar_d)) {
751       GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
752           ("Error calculating the output scaled size - integer overflow"));
753       goto done;
754     }
755 
756     GST_DEBUG_OBJECT (base, "Input DAR is %d/%d", from_dar_n, from_dar_d);
757 
758     /* If either width or height are fixed there's not much we
759      * can do either except choosing a height or width and PAR
760      * that matches the DAR as good as possible
761      */
762     if (h) {
763       GstStructure *tmp;
764       gint set_w, set_par_n, set_par_d;
765 
766       GST_DEBUG_OBJECT (base, "height is fixed (%d)", h);
767 
768       /* If the PAR is fixed too, there's not much to do
769        * except choosing the width that is nearest to the
770        * width with the same DAR */
771       if (gst_value_is_fixed (to_par)) {
772         to_par_n = gst_value_get_fraction_numerator (to_par);
773         to_par_d = gst_value_get_fraction_denominator (to_par);
774 
775         GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
776 
777         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
778                 to_par_n, &num, &den)) {
779           GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
780               ("Error calculating the output scaled size - integer overflow"));
781           goto done;
782         }
783 
784         w = (guint) gst_util_uint64_scale_int_round (h, num, den);
785         gst_structure_fixate_field_nearest_int (outs, "width", w);
786 
787         goto done;
788       }
789 
790       /* The PAR is not fixed and it's quite likely that we can set
791        * an arbitrary PAR. */
792 
793       /* Check if we can keep the input width */
794       tmp = gst_structure_copy (outs);
795       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
796       gst_structure_get_int (tmp, "width", &set_w);
797 
798       /* Might have failed but try to keep the DAR nonetheless by
799        * adjusting the PAR */
800       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
801               &to_par_n, &to_par_d)) {
802         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
803             ("Error calculating the output scaled size - integer overflow"));
804         gst_structure_free (tmp);
805         goto done;
806       }
807 
808       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
809         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
810       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
811           to_par_n, to_par_d);
812       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
813           &set_par_d);
814       gst_structure_free (tmp);
815 
816       /* Check if the adjusted PAR is accepted */
817       if (set_par_n == to_par_n && set_par_d == to_par_d) {
818         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
819             set_par_n != set_par_d)
820           gst_structure_set (outs, "width", G_TYPE_INT, set_w,
821               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
822               NULL);
823         goto done;
824       }
825 
826       /* Otherwise scale the width to the new PAR and check if the
827        * adjusted with is accepted. If all that fails we can't keep
828        * the DAR */
829       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
830               set_par_n, &num, &den)) {
831         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
832             ("Error calculating the output scaled size - integer overflow"));
833         goto done;
834       }
835 
836       w = (guint) gst_util_uint64_scale_int_round (h, num, den);
837       gst_structure_fixate_field_nearest_int (outs, "width", w);
838       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
839           set_par_n != set_par_d)
840         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
841             set_par_n, set_par_d, NULL);
842 
843       goto done;
844     } else if (w) {
845       GstStructure *tmp;
846       gint set_h, set_par_n, set_par_d;
847 
848       GST_DEBUG_OBJECT (base, "width is fixed (%d)", w);
849 
850       /* If the PAR is fixed too, there's not much to do
851        * except choosing the height that is nearest to the
852        * height with the same DAR */
853       if (gst_value_is_fixed (to_par)) {
854         to_par_n = gst_value_get_fraction_numerator (to_par);
855         to_par_d = gst_value_get_fraction_denominator (to_par);
856 
857         GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
858 
859         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
860                 to_par_n, &num, &den)) {
861           GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
862               ("Error calculating the output scaled size - integer overflow"));
863           goto done;
864         }
865 
866         h = (guint) gst_util_uint64_scale_int_round (w, den, num);
867         gst_structure_fixate_field_nearest_int (outs, "height", h);
868 
869         goto done;
870       }
871 
872       /* The PAR is not fixed and it's quite likely that we can set
873        * an arbitrary PAR. */
874 
875       /* Check if we can keep the input height */
876       tmp = gst_structure_copy (outs);
877       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
878       gst_structure_get_int (tmp, "height", &set_h);
879 
880       /* Might have failed but try to keep the DAR nonetheless by
881        * adjusting the PAR */
882       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
883               &to_par_n, &to_par_d)) {
884         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
885             ("Error calculating the output scaled size - integer overflow"));
886         gst_structure_free (tmp);
887         goto done;
888       }
889       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
890         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
891       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
892           to_par_n, to_par_d);
893       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
894           &set_par_d);
895       gst_structure_free (tmp);
896 
897       /* Check if the adjusted PAR is accepted */
898       if (set_par_n == to_par_n && set_par_d == to_par_d) {
899         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
900             set_par_n != set_par_d)
901           gst_structure_set (outs, "height", G_TYPE_INT, set_h,
902               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
903               NULL);
904         goto done;
905       }
906 
907       /* Otherwise scale the height to the new PAR and check if the
908        * adjusted with is accepted. If all that fails we can't keep
909        * the DAR */
910       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
911               set_par_n, &num, &den)) {
912         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
913             ("Error calculating the output scaled size - integer overflow"));
914         goto done;
915       }
916 
917       h = (guint) gst_util_uint64_scale_int_round (w, den, num);
918       gst_structure_fixate_field_nearest_int (outs, "height", h);
919       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
920           set_par_n != set_par_d)
921         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
922             set_par_n, set_par_d, NULL);
923 
924       goto done;
925     } else if (gst_value_is_fixed (to_par)) {
926       GstStructure *tmp;
927       gint set_h, set_w, f_h, f_w;
928 
929       to_par_n = gst_value_get_fraction_numerator (to_par);
930       to_par_d = gst_value_get_fraction_denominator (to_par);
931 
932       /* Calculate scale factor for the PAR change */
933       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
934               to_par_d, &num, &den)) {
935         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
936             ("Error calculating the output scaled size - integer overflow"));
937         goto done;
938       }
939 
940       /* Try to keep the input height (because of interlacing) */
941       tmp = gst_structure_copy (outs);
942       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
943       gst_structure_get_int (tmp, "height", &set_h);
944 
945       /* This might have failed but try to scale the width
946        * to keep the DAR nonetheless */
947       w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
948       gst_structure_fixate_field_nearest_int (tmp, "width", w);
949       gst_structure_get_int (tmp, "width", &set_w);
950       gst_structure_free (tmp);
951 
952       /* We kept the DAR and the height is nearest to the original height */
953       if (set_w == w) {
954         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
955             G_TYPE_INT, set_h, NULL);
956         goto done;
957       }
958 
959       f_h = set_h;
960       f_w = set_w;
961 
962       /* If the former failed, try to keep the input width at least */
963       tmp = gst_structure_copy (outs);
964       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
965       gst_structure_get_int (tmp, "width", &set_w);
966 
967       /* This might have failed but try to scale the width
968        * to keep the DAR nonetheless */
969       h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
970       gst_structure_fixate_field_nearest_int (tmp, "height", h);
971       gst_structure_get_int (tmp, "height", &set_h);
972       gst_structure_free (tmp);
973 
974       /* We kept the DAR and the width is nearest to the original width */
975       if (set_h == h) {
976         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
977             G_TYPE_INT, set_h, NULL);
978         goto done;
979       }
980 
981       /* If all this failed, keep the dimensions with the DAR that was closest
982        * to the correct DAR. This changes the DAR but there's not much else to
983        * do here.
984        */
985       if (set_w * ABS (set_h - h) < ABS (f_w - w) * f_h) {
986         f_h = set_h;
987         f_w = set_w;
988       }
989       gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
990           f_h, NULL);
991       goto done;
992     } else {
993       GstStructure *tmp;
994       gint set_h, set_w, set_par_n, set_par_d, tmp2;
995 
996       /* width, height and PAR are not fixed but passthrough is not possible */
997 
998       /* First try to keep the height and width as good as possible
999        * and scale PAR */
1000       tmp = gst_structure_copy (outs);
1001       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1002       gst_structure_get_int (tmp, "height", &set_h);
1003       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1004       gst_structure_get_int (tmp, "width", &set_w);
1005 
1006       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
1007               &to_par_n, &to_par_d)) {
1008         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
1009             ("Error calculating the output scaled size - integer overflow"));
1010         gst_structure_free (tmp);
1011         goto done;
1012       }
1013 
1014       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1015         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1016       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1017           to_par_n, to_par_d);
1018       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1019           &set_par_d);
1020       gst_structure_free (tmp);
1021 
1022       if (set_par_n == to_par_n && set_par_d == to_par_d) {
1023         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1024             G_TYPE_INT, set_h, NULL);
1025 
1026         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1027             set_par_n != set_par_d)
1028           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1029               set_par_n, set_par_d, NULL);
1030         goto done;
1031       }
1032 
1033       /* Otherwise try to scale width to keep the DAR with the set
1034        * PAR and height */
1035       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1036               set_par_n, &num, &den)) {
1037         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
1038             ("Error calculating the output scaled size - integer overflow"));
1039         goto done;
1040       }
1041 
1042       w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1043       tmp = gst_structure_copy (outs);
1044       gst_structure_fixate_field_nearest_int (tmp, "width", w);
1045       gst_structure_get_int (tmp, "width", &tmp2);
1046       gst_structure_free (tmp);
1047 
1048       if (tmp2 == w) {
1049         gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
1050             G_TYPE_INT, set_h, NULL);
1051         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1052             set_par_n != set_par_d)
1053           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1054               set_par_n, set_par_d, NULL);
1055         goto done;
1056       }
1057 
1058       /* ... or try the same with the height */
1059       h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1060       tmp = gst_structure_copy (outs);
1061       gst_structure_fixate_field_nearest_int (tmp, "height", h);
1062       gst_structure_get_int (tmp, "height", &tmp2);
1063       gst_structure_free (tmp);
1064 
1065       if (tmp2 == h) {
1066         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1067             G_TYPE_INT, tmp2, NULL);
1068         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1069             set_par_n != set_par_d)
1070           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1071               set_par_n, set_par_d, NULL);
1072         goto done;
1073       }
1074 
1075       /* If all fails we can't keep the DAR and take the nearest values
1076        * for everything from the first try */
1077       gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1078           G_TYPE_INT, set_h, NULL);
1079       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1080           set_par_n != set_par_d)
1081         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1082             set_par_n, set_par_d, NULL);
1083     }
1084   }
1085 
1086 done:
1087   GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
1088 
1089   if (from_par == &fpar)
1090     g_value_unset (&fpar);
1091   if (to_par == &tpar)
1092     g_value_unset (&tpar);
1093 
1094   return othercaps;
1095 }
1096 
1097 #define GET_LINE(frame, line) \
1098     (gpointer)(((guint8*)(GST_VIDEO_FRAME_PLANE_DATA (frame, 0))) + \
1099      GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) * (line))
1100 
1101 static GstFlowReturn
gst_video_scale_transform_frame(GstVideoFilter * filter,GstVideoFrame * in_frame,GstVideoFrame * out_frame)1102 gst_video_scale_transform_frame (GstVideoFilter * filter,
1103     GstVideoFrame * in_frame, GstVideoFrame * out_frame)
1104 {
1105   GstVideoScale *videoscale = GST_VIDEO_SCALE_CAST (filter);
1106   GstFlowReturn ret = GST_FLOW_OK;
1107 
1108   GST_CAT_DEBUG_OBJECT (CAT_PERFORMANCE, filter, "doing video scaling");
1109 
1110   gst_video_converter_frame (videoscale->convert, in_frame, out_frame);
1111 
1112   return ret;
1113 }
1114 
1115 static gboolean
gst_video_scale_src_event(GstBaseTransform * trans,GstEvent * event)1116 gst_video_scale_src_event (GstBaseTransform * trans, GstEvent * event)
1117 {
1118   GstVideoScale *videoscale = GST_VIDEO_SCALE_CAST (trans);
1119   GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
1120   gboolean ret;
1121   gdouble a;
1122   GstStructure *structure;
1123 
1124   GST_DEBUG_OBJECT (videoscale, "handling %s event",
1125       GST_EVENT_TYPE_NAME (event));
1126 
1127   switch (GST_EVENT_TYPE (event)) {
1128     case GST_EVENT_NAVIGATION:
1129       if (filter->in_info.width != filter->out_info.width ||
1130           filter->in_info.height != filter->out_info.height) {
1131         event =
1132             GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1133 
1134         structure = (GstStructure *) gst_event_get_structure (event);
1135         if (gst_structure_get_double (structure, "pointer_x", &a)) {
1136           gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1137               a * filter->in_info.width / filter->out_info.width, NULL);
1138         }
1139         if (gst_structure_get_double (structure, "pointer_y", &a)) {
1140           gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1141               a * filter->in_info.height / filter->out_info.height, NULL);
1142         }
1143       }
1144       break;
1145     default:
1146       break;
1147   }
1148 
1149   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
1150 
1151   return ret;
1152 }
1153 
1154 static gboolean
plugin_init(GstPlugin * plugin)1155 plugin_init (GstPlugin * plugin)
1156 {
1157   if (!gst_element_register (plugin, "videoscale", GST_RANK_NONE,
1158           GST_TYPE_VIDEO_SCALE))
1159     return FALSE;
1160 
1161   GST_DEBUG_CATEGORY_INIT (video_scale_debug, "videoscale", 0,
1162       "videoscale element");
1163   GST_DEBUG_CATEGORY_GET (CAT_PERFORMANCE, "GST_PERFORMANCE");
1164 
1165   return TRUE;
1166 }
1167 
1168 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1169     GST_VERSION_MINOR,
1170     videoscale,
1171     "Resizes video", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
1172     GST_PACKAGE_ORIGIN)
1173