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