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 static GQuark _size_quark;
131 static GQuark _scale_quark;
132
133 #define GST_TYPE_VIDEO_SCALE_METHOD (gst_video_scale_method_get_type())
134 static GType
gst_video_scale_method_get_type(void)135 gst_video_scale_method_get_type (void)
136 {
137 static GType video_scale_method_type = 0;
138
139 static const GEnumValue video_scale_methods[] = {
140 {GST_VIDEO_SCALE_NEAREST, "Nearest Neighbour", "nearest-neighbour"},
141 {GST_VIDEO_SCALE_BILINEAR, "Bilinear (2-tap)", "bilinear"},
142 {GST_VIDEO_SCALE_4TAP, "4-tap Sinc", "4-tap"},
143 {GST_VIDEO_SCALE_LANCZOS, "Lanczos", "lanczos"},
144 {GST_VIDEO_SCALE_BILINEAR2, "Bilinear (multi-tap)", "bilinear2"},
145 {GST_VIDEO_SCALE_SINC, "Sinc (multi-tap)", "sinc"},
146 {GST_VIDEO_SCALE_HERMITE, "Hermite (multi-tap)", "hermite"},
147 {GST_VIDEO_SCALE_SPLINE, "Spline (multi-tap)", "spline"},
148 {GST_VIDEO_SCALE_CATROM, "Catmull-Rom (multi-tap)", "catrom"},
149 {GST_VIDEO_SCALE_MITCHELL, "Mitchell (multi-tap)", "mitchell"},
150 {0, NULL, NULL},
151 };
152
153 if (!video_scale_method_type) {
154 video_scale_method_type =
155 g_enum_register_static ("GstVideoScaleMethod", video_scale_methods);
156 }
157 return video_scale_method_type;
158 }
159
160 static GstCaps *
gst_video_scale_get_capslist(void)161 gst_video_scale_get_capslist (void)
162 {
163 static GstCaps *caps = NULL;
164 static gsize inited = 0;
165
166 if (g_once_init_enter (&inited)) {
167 caps = gst_static_caps_get (&gst_video_scale_format_caps);
168 g_once_init_leave (&inited, 1);
169 }
170 return caps;
171 }
172
173 static GstPadTemplate *
gst_video_scale_src_template_factory(void)174 gst_video_scale_src_template_factory (void)
175 {
176 return gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
177 gst_video_scale_get_capslist ());
178 }
179
180 static GstPadTemplate *
gst_video_scale_sink_template_factory(void)181 gst_video_scale_sink_template_factory (void)
182 {
183 return gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
184 gst_video_scale_get_capslist ());
185 }
186
187
188 static void gst_video_scale_finalize (GstVideoScale * videoscale);
189 static gboolean gst_video_scale_src_event (GstBaseTransform * trans,
190 GstEvent * event);
191
192 /* base transform vmethods */
193 static GstCaps *gst_video_scale_transform_caps (GstBaseTransform * trans,
194 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
195 static GstCaps *gst_video_scale_fixate_caps (GstBaseTransform * base,
196 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
197 static gboolean gst_video_scale_transform_meta (GstBaseTransform * trans,
198 GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf);
199
200 static gboolean gst_video_scale_set_info (GstVideoFilter * filter,
201 GstCaps * in, GstVideoInfo * in_info, GstCaps * out,
202 GstVideoInfo * out_info);
203 static GstFlowReturn gst_video_scale_transform_frame (GstVideoFilter * filter,
204 GstVideoFrame * in, GstVideoFrame * out);
205
206 static void gst_video_scale_set_property (GObject * object, guint prop_id,
207 const GValue * value, GParamSpec * pspec);
208 static void gst_video_scale_get_property (GObject * object, guint prop_id,
209 GValue * value, GParamSpec * pspec);
210
211 #define gst_video_scale_parent_class parent_class
212 G_DEFINE_TYPE (GstVideoScale, gst_video_scale, GST_TYPE_VIDEO_FILTER);
213 GST_ELEMENT_REGISTER_DEFINE (videoscale, "videoscale",
214 GST_RANK_NONE, GST_TYPE_VIDEO_SCALE);
215
216 static GstCapsFeatures *features_format_interlaced,
217 *features_format_interlaced_sysmem;
218
219 static void
gst_video_scale_class_init(GstVideoScaleClass * klass)220 gst_video_scale_class_init (GstVideoScaleClass * klass)
221 {
222 GObjectClass *gobject_class = (GObjectClass *) klass;
223 GstElementClass *element_class = (GstElementClass *) klass;
224 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
225 GstVideoFilterClass *filter_class = (GstVideoFilterClass *) klass;
226
227 gobject_class->finalize = (GObjectFinalizeFunc) gst_video_scale_finalize;
228 gobject_class->set_property = gst_video_scale_set_property;
229 gobject_class->get_property = gst_video_scale_get_property;
230
231 g_object_class_install_property (gobject_class, PROP_METHOD,
232 g_param_spec_enum ("method", "method", "method",
233 GST_TYPE_VIDEO_SCALE_METHOD, DEFAULT_PROP_METHOD,
234 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
235
236 g_object_class_install_property (gobject_class, PROP_ADD_BORDERS,
237 g_param_spec_boolean ("add-borders", "Add Borders",
238 "Add black borders if necessary to keep the display aspect ratio",
239 DEFAULT_PROP_ADD_BORDERS,
240 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
241
242 g_object_class_install_property (gobject_class, PROP_SHARPNESS,
243 g_param_spec_double ("sharpness", "Sharpness",
244 "Sharpness of filter", 0.5, 1.5, DEFAULT_PROP_SHARPNESS,
245 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
246
247 g_object_class_install_property (gobject_class, PROP_SHARPEN,
248 g_param_spec_double ("sharpen", "Sharpen",
249 "Sharpening", 0.0, 1.0, DEFAULT_PROP_SHARPEN,
250 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
251
252 g_object_class_install_property (gobject_class, PROP_DITHER,
253 g_param_spec_boolean ("dither", "Dither",
254 "Add dither (only used for Lanczos method)",
255 DEFAULT_PROP_DITHER,
256 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
257
258 #if 0
259 /* I am hiding submethod for now, since it's poorly named, poorly
260 * documented, and will probably just get people into trouble. */
261 g_object_class_install_property (gobject_class, PROP_SUBMETHOD,
262 g_param_spec_int ("submethod", "submethod",
263 "submethod", 0, 3, DEFAULT_PROP_SUBMETHOD,
264 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
265 #endif
266
267 g_object_class_install_property (gobject_class, PROP_ENVELOPE,
268 g_param_spec_double ("envelope", "Envelope",
269 "Size of filter envelope", 1.0, 5.0, DEFAULT_PROP_ENVELOPE,
270 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
271
272 g_object_class_install_property (gobject_class, PROP_GAMMA_DECODE,
273 g_param_spec_boolean ("gamma-decode", "Gamma Decode",
274 "Decode gamma before scaling", DEFAULT_PROP_GAMMA_DECODE,
275 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
276
277 g_object_class_install_property (gobject_class, PROP_N_THREADS,
278 g_param_spec_uint ("n-threads", "Threads",
279 "Maximum number of threads to use", 0, G_MAXUINT,
280 DEFAULT_PROP_N_THREADS,
281 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
282
283 gst_element_class_set_static_metadata (element_class,
284 "Video scaler", "Filter/Converter/Video/Scaler",
285 "Resizes video", "Wim Taymans <wim.taymans@gmail.com>");
286
287 gst_element_class_add_pad_template (element_class,
288 gst_video_scale_sink_template_factory ());
289 gst_element_class_add_pad_template (element_class,
290 gst_video_scale_src_template_factory ());
291
292 trans_class->transform_caps =
293 GST_DEBUG_FUNCPTR (gst_video_scale_transform_caps);
294 trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_scale_fixate_caps);
295 trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_scale_src_event);
296 trans_class->transform_meta =
297 GST_DEBUG_FUNCPTR (gst_video_scale_transform_meta);
298
299 filter_class->set_info = GST_DEBUG_FUNCPTR (gst_video_scale_set_info);
300 filter_class->transform_frame =
301 GST_DEBUG_FUNCPTR (gst_video_scale_transform_frame);
302
303 _size_quark = g_quark_from_static_string (GST_META_TAG_VIDEO_SIZE_STR);
304 _scale_quark = gst_video_meta_transform_scale_get_quark ();
305
306 gst_type_mark_as_plugin_api (GST_TYPE_VIDEO_SCALE_METHOD, 0);
307 }
308
309 static void
gst_video_scale_init(GstVideoScale * videoscale)310 gst_video_scale_init (GstVideoScale * videoscale)
311 {
312 videoscale->method = DEFAULT_PROP_METHOD;
313 videoscale->add_borders = DEFAULT_PROP_ADD_BORDERS;
314 videoscale->submethod = DEFAULT_PROP_SUBMETHOD;
315 videoscale->sharpness = DEFAULT_PROP_SHARPNESS;
316 videoscale->sharpen = DEFAULT_PROP_SHARPEN;
317 videoscale->dither = DEFAULT_PROP_DITHER;
318 videoscale->envelope = DEFAULT_PROP_ENVELOPE;
319 videoscale->gamma_decode = DEFAULT_PROP_GAMMA_DECODE;
320 videoscale->n_threads = DEFAULT_PROP_N_THREADS;
321 }
322
323 static void
gst_video_scale_finalize(GstVideoScale * videoscale)324 gst_video_scale_finalize (GstVideoScale * videoscale)
325 {
326 if (videoscale->convert)
327 gst_video_converter_free (videoscale->convert);
328
329 G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (videoscale));
330 }
331
332 static void
gst_video_scale_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)333 gst_video_scale_set_property (GObject * object, guint prop_id,
334 const GValue * value, GParamSpec * pspec)
335 {
336 GstVideoScale *vscale = GST_VIDEO_SCALE (object);
337
338 switch (prop_id) {
339 case PROP_METHOD:
340 GST_OBJECT_LOCK (vscale);
341 vscale->method = g_value_get_enum (value);
342 GST_OBJECT_UNLOCK (vscale);
343 break;
344 case PROP_ADD_BORDERS:
345 GST_OBJECT_LOCK (vscale);
346 vscale->add_borders = g_value_get_boolean (value);
347 GST_OBJECT_UNLOCK (vscale);
348 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM_CAST (vscale));
349 break;
350 case PROP_SHARPNESS:
351 GST_OBJECT_LOCK (vscale);
352 vscale->sharpness = g_value_get_double (value);
353 GST_OBJECT_UNLOCK (vscale);
354 break;
355 case PROP_SHARPEN:
356 GST_OBJECT_LOCK (vscale);
357 vscale->sharpen = g_value_get_double (value);
358 GST_OBJECT_UNLOCK (vscale);
359 break;
360 case PROP_DITHER:
361 GST_OBJECT_LOCK (vscale);
362 vscale->dither = g_value_get_boolean (value);
363 GST_OBJECT_UNLOCK (vscale);
364 break;
365 case PROP_SUBMETHOD:
366 GST_OBJECT_LOCK (vscale);
367 vscale->submethod = g_value_get_int (value);
368 GST_OBJECT_UNLOCK (vscale);
369 break;
370 case PROP_ENVELOPE:
371 GST_OBJECT_LOCK (vscale);
372 vscale->envelope = g_value_get_double (value);
373 GST_OBJECT_UNLOCK (vscale);
374 break;
375 case PROP_GAMMA_DECODE:
376 GST_OBJECT_LOCK (vscale);
377 vscale->gamma_decode = g_value_get_boolean (value);
378 GST_OBJECT_UNLOCK (vscale);
379 break;
380 case PROP_N_THREADS:
381 GST_OBJECT_LOCK (vscale);
382 vscale->n_threads = g_value_get_uint (value);
383 GST_OBJECT_UNLOCK (vscale);
384 break;
385 default:
386 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
387 break;
388 }
389 }
390
391 static void
gst_video_scale_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)392 gst_video_scale_get_property (GObject * object, guint prop_id, GValue * value,
393 GParamSpec * pspec)
394 {
395 GstVideoScale *vscale = GST_VIDEO_SCALE (object);
396
397 switch (prop_id) {
398 case PROP_METHOD:
399 GST_OBJECT_LOCK (vscale);
400 g_value_set_enum (value, vscale->method);
401 GST_OBJECT_UNLOCK (vscale);
402 break;
403 case PROP_ADD_BORDERS:
404 GST_OBJECT_LOCK (vscale);
405 g_value_set_boolean (value, vscale->add_borders);
406 GST_OBJECT_UNLOCK (vscale);
407 break;
408 case PROP_SHARPNESS:
409 GST_OBJECT_LOCK (vscale);
410 g_value_set_double (value, vscale->sharpness);
411 GST_OBJECT_UNLOCK (vscale);
412 break;
413 case PROP_SHARPEN:
414 GST_OBJECT_LOCK (vscale);
415 g_value_set_double (value, vscale->sharpen);
416 GST_OBJECT_UNLOCK (vscale);
417 break;
418 case PROP_DITHER:
419 GST_OBJECT_LOCK (vscale);
420 g_value_set_boolean (value, vscale->dither);
421 GST_OBJECT_UNLOCK (vscale);
422 break;
423 case PROP_SUBMETHOD:
424 GST_OBJECT_LOCK (vscale);
425 g_value_set_int (value, vscale->submethod);
426 GST_OBJECT_UNLOCK (vscale);
427 break;
428 case PROP_ENVELOPE:
429 GST_OBJECT_LOCK (vscale);
430 g_value_set_double (value, vscale->envelope);
431 GST_OBJECT_UNLOCK (vscale);
432 break;
433 case PROP_GAMMA_DECODE:
434 GST_OBJECT_LOCK (vscale);
435 g_value_set_boolean (value, vscale->gamma_decode);
436 GST_OBJECT_UNLOCK (vscale);
437 break;
438 case PROP_N_THREADS:
439 GST_OBJECT_LOCK (vscale);
440 g_value_set_uint (value, vscale->n_threads);
441 GST_OBJECT_UNLOCK (vscale);
442 break;
443 default:
444 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
445 break;
446 }
447 }
448
449 static GstCaps *
gst_video_scale_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)450 gst_video_scale_transform_caps (GstBaseTransform * trans,
451 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
452 {
453 GstCaps *ret;
454 GstStructure *structure;
455 GstCapsFeatures *features;
456 gint i, n;
457
458 GST_DEBUG_OBJECT (trans,
459 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
460 (direction == GST_PAD_SINK) ? "sink" : "src");
461
462 ret = gst_caps_new_empty ();
463 n = gst_caps_get_size (caps);
464 for (i = 0; i < n; i++) {
465 structure = gst_caps_get_structure (caps, i);
466 features = gst_caps_get_features (caps, i);
467
468 /* If this is already expressed by the existing caps
469 * skip this structure */
470 if (i > 0 && gst_caps_is_subset_structure_full (ret, structure, features))
471 continue;
472
473 /* make copy */
474 structure = gst_structure_copy (structure);
475
476 /* If the features are non-sysmem we can only do passthrough */
477 if (!gst_caps_features_is_any (features)
478 && (gst_caps_features_is_equal (features,
479 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)
480 || gst_caps_features_is_equal (features, features_format_interlaced)
481 || gst_caps_features_is_equal (features,
482 features_format_interlaced_sysmem))) {
483 gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
484 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
485
486 /* if pixel aspect ratio, make a range of it */
487 if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
488 gst_structure_set (structure, "pixel-aspect-ratio",
489 GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
490 }
491 }
492 gst_caps_append_structure_full (ret, structure,
493 gst_caps_features_copy (features));
494 }
495
496 if (filter) {
497 GstCaps *intersection;
498
499 intersection =
500 gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
501 gst_caps_unref (ret);
502 ret = intersection;
503 }
504
505 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
506
507 return ret;
508 }
509
510 static gboolean
gst_video_scale_transform_meta(GstBaseTransform * trans,GstBuffer * outbuf,GstMeta * meta,GstBuffer * inbuf)511 gst_video_scale_transform_meta (GstBaseTransform * trans, GstBuffer * outbuf,
512 GstMeta * meta, GstBuffer * inbuf)
513 {
514 GstVideoFilter *videofilter = GST_VIDEO_FILTER (trans);
515 const GstMetaInfo *info = meta->info;
516 const gchar *const *tags;
517 const gchar *const *curr = NULL;
518 gboolean should_copy = TRUE;
519 const gchar *const valid_tags[] = { GST_META_TAG_VIDEO_STR,
520 GST_META_TAG_VIDEO_COLORSPACE_STR,
521 GST_META_TAG_VIDEO_ORIENTATION_STR,
522 GST_META_TAG_VIDEO_SIZE_STR
523 };
524
525 tags = gst_meta_api_type_get_tags (info->api);
526
527 /* No specific tags, we are good to copy */
528 if (!tags) {
529 return TRUE;
530 }
531
532 /* We are only changing size, we can preserve other metas tagged as
533 orientation and colorspace */
534 for (curr = tags; *curr; ++curr) {
535
536 /* We dont handle any other tag */
537 if (!g_strv_contains (valid_tags, *curr)) {
538 should_copy = FALSE;
539 break;
540 }
541 }
542
543 /* Cant handle the tags in this meta, let the parent class handle it */
544 if (!should_copy) {
545 return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (trans,
546 outbuf, meta, inbuf);
547 }
548
549 /* This meta is size sensitive, try to transform it accordingly */
550 if (gst_meta_api_type_has_tag (info->api, _size_quark)) {
551 GstVideoMetaTransform trans =
552 { &videofilter->in_info, &videofilter->out_info };
553
554 if (info->transform_func)
555 return info->transform_func (outbuf, meta, inbuf, _scale_quark, &trans);
556 return FALSE;
557 }
558
559 /* No need to transform, we can safely copy this meta */
560 return TRUE;
561 }
562
563 static gboolean
gst_video_scale_set_info(GstVideoFilter * filter,GstCaps * in,GstVideoInfo * in_info,GstCaps * out,GstVideoInfo * out_info)564 gst_video_scale_set_info (GstVideoFilter * filter, GstCaps * in,
565 GstVideoInfo * in_info, GstCaps * out, GstVideoInfo * out_info)
566 {
567 GstVideoScale *videoscale = GST_VIDEO_SCALE (filter);
568 gint from_dar_n, from_dar_d, to_dar_n, to_dar_d;
569
570 if (!gst_util_fraction_multiply (in_info->width,
571 in_info->height, in_info->par_n, in_info->par_d, &from_dar_n,
572 &from_dar_d)) {
573 from_dar_n = from_dar_d = -1;
574 }
575
576 if (!gst_util_fraction_multiply (out_info->width,
577 out_info->height, out_info->par_n, out_info->par_d, &to_dar_n,
578 &to_dar_d)) {
579 to_dar_n = to_dar_d = -1;
580 }
581
582 videoscale->borders_w = videoscale->borders_h = 0;
583 if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
584 if (videoscale->add_borders) {
585 gint n, d, to_h, to_w;
586
587 if (from_dar_n != -1 && from_dar_d != -1
588 && gst_util_fraction_multiply (from_dar_n, from_dar_d,
589 out_info->par_d, out_info->par_n, &n, &d)) {
590 to_h = gst_util_uint64_scale_int (out_info->width, d, n);
591 if (to_h <= out_info->height) {
592 videoscale->borders_h = out_info->height - to_h;
593 videoscale->borders_w = 0;
594 } else {
595 to_w = gst_util_uint64_scale_int (out_info->height, n, d);
596 g_assert (to_w <= out_info->width);
597 videoscale->borders_h = 0;
598 videoscale->borders_w = out_info->width - to_w;
599 }
600 } else {
601 GST_WARNING_OBJECT (videoscale, "Can't calculate borders");
602 }
603 } else {
604 GST_WARNING_OBJECT (videoscale, "Can't keep DAR!");
605 }
606 }
607
608 if (in_info->width == out_info->width && in_info->height == out_info->height
609 && videoscale->borders_w == 0 && videoscale->borders_h == 0) {
610 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter), TRUE);
611 } else {
612 GstStructure *options;
613 GST_CAT_DEBUG_OBJECT (CAT_PERFORMANCE, filter, "setup videoscaling");
614 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter), FALSE);
615
616 options = gst_structure_new_empty ("videoscale");
617
618 switch (videoscale->method) {
619 case GST_VIDEO_SCALE_NEAREST:
620 gst_structure_set (options,
621 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
622 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_NEAREST,
623 NULL);
624 break;
625 case GST_VIDEO_SCALE_BILINEAR:
626 gst_structure_set (options,
627 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
628 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_LINEAR,
629 GST_VIDEO_RESAMPLER_OPT_MAX_TAPS, G_TYPE_INT, 2, NULL);
630 break;
631 case GST_VIDEO_SCALE_4TAP:
632 gst_structure_set (options,
633 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
634 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_SINC,
635 GST_VIDEO_RESAMPLER_OPT_MAX_TAPS, G_TYPE_INT, 4, NULL);
636 break;
637 case GST_VIDEO_SCALE_LANCZOS:
638 gst_structure_set (options,
639 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
640 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_LANCZOS,
641 NULL);
642 break;
643 case GST_VIDEO_SCALE_BILINEAR2:
644 gst_structure_set (options,
645 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
646 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_LINEAR,
647 NULL);
648 break;
649 case GST_VIDEO_SCALE_SINC:
650 gst_structure_set (options,
651 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
652 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_SINC,
653 NULL);
654 break;
655 case GST_VIDEO_SCALE_HERMITE:
656 gst_structure_set (options,
657 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
658 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_CUBIC,
659 GST_VIDEO_RESAMPLER_OPT_CUBIC_B, G_TYPE_DOUBLE, (gdouble) 0.0,
660 GST_VIDEO_RESAMPLER_OPT_CUBIC_C, G_TYPE_DOUBLE, (gdouble) 0.0,
661 NULL);
662 break;
663 case GST_VIDEO_SCALE_SPLINE:
664 gst_structure_set (options,
665 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
666 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_CUBIC,
667 GST_VIDEO_RESAMPLER_OPT_CUBIC_B, G_TYPE_DOUBLE, (gdouble) 1.0,
668 GST_VIDEO_RESAMPLER_OPT_CUBIC_C, G_TYPE_DOUBLE, (gdouble) 0.0,
669 NULL);
670 break;
671 case GST_VIDEO_SCALE_CATROM:
672 gst_structure_set (options,
673 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
674 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_CUBIC,
675 GST_VIDEO_RESAMPLER_OPT_CUBIC_B, G_TYPE_DOUBLE, (gdouble) 0.0,
676 GST_VIDEO_RESAMPLER_OPT_CUBIC_C, G_TYPE_DOUBLE, (gdouble) 0.5,
677 NULL);
678 break;
679 case GST_VIDEO_SCALE_MITCHELL:
680 gst_structure_set (options,
681 GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD,
682 GST_TYPE_VIDEO_RESAMPLER_METHOD, GST_VIDEO_RESAMPLER_METHOD_CUBIC,
683 GST_VIDEO_RESAMPLER_OPT_CUBIC_B, G_TYPE_DOUBLE, (gdouble) 1.0 / 3.0,
684 GST_VIDEO_RESAMPLER_OPT_CUBIC_C, G_TYPE_DOUBLE, (gdouble) 1.0 / 3.0,
685 NULL);
686 break;
687 }
688 gst_structure_set (options,
689 GST_VIDEO_RESAMPLER_OPT_ENVELOPE, G_TYPE_DOUBLE, videoscale->envelope,
690 GST_VIDEO_RESAMPLER_OPT_SHARPNESS, G_TYPE_DOUBLE, videoscale->sharpness,
691 GST_VIDEO_RESAMPLER_OPT_SHARPEN, G_TYPE_DOUBLE, videoscale->sharpen,
692 GST_VIDEO_CONVERTER_OPT_DEST_X, G_TYPE_INT, videoscale->borders_w / 2,
693 GST_VIDEO_CONVERTER_OPT_DEST_Y, G_TYPE_INT, videoscale->borders_h / 2,
694 GST_VIDEO_CONVERTER_OPT_DEST_WIDTH, G_TYPE_INT,
695 out_info->width - videoscale->borders_w,
696 GST_VIDEO_CONVERTER_OPT_DEST_HEIGHT, G_TYPE_INT,
697 out_info->height - videoscale->borders_h,
698 GST_VIDEO_CONVERTER_OPT_MATRIX_MODE, GST_TYPE_VIDEO_MATRIX_MODE,
699 GST_VIDEO_MATRIX_MODE_NONE, GST_VIDEO_CONVERTER_OPT_DITHER_METHOD,
700 GST_TYPE_VIDEO_DITHER_METHOD, GST_VIDEO_DITHER_NONE,
701 GST_VIDEO_CONVERTER_OPT_CHROMA_MODE, GST_TYPE_VIDEO_CHROMA_MODE,
702 GST_VIDEO_CHROMA_MODE_NONE,
703 GST_VIDEO_CONVERTER_OPT_THREADS, G_TYPE_UINT, videoscale->n_threads,
704 NULL);
705
706 if (videoscale->gamma_decode) {
707 gst_structure_set (options,
708 GST_VIDEO_CONVERTER_OPT_GAMMA_MODE, GST_TYPE_VIDEO_GAMMA_MODE,
709 GST_VIDEO_GAMMA_MODE_REMAP, NULL);
710 }
711
712 if (videoscale->convert)
713 gst_video_converter_free (videoscale->convert);
714 videoscale->convert = gst_video_converter_new (in_info, out_info, options);
715 }
716
717 GST_DEBUG_OBJECT (videoscale, "from=%dx%d (par=%d/%d dar=%d/%d), size %"
718 G_GSIZE_FORMAT " -> to=%dx%d (par=%d/%d dar=%d/%d borders=%d:%d), "
719 "size %" G_GSIZE_FORMAT,
720 in_info->width, in_info->height, in_info->par_n, in_info->par_d,
721 from_dar_n, from_dar_d, in_info->size, out_info->width,
722 out_info->height, out_info->par_n, out_info->par_d, to_dar_n, to_dar_d,
723 videoscale->borders_w, videoscale->borders_h, out_info->size);
724
725 return TRUE;
726 }
727
728 static GstCaps *
gst_video_scale_fixate_caps(GstBaseTransform * base,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)729 gst_video_scale_fixate_caps (GstBaseTransform * base, GstPadDirection direction,
730 GstCaps * caps, GstCaps * othercaps)
731 {
732 GstStructure *ins, *outs;
733 const GValue *from_par, *to_par;
734 GValue fpar = { 0, };
735 GValue tpar = { 0, };
736
737 othercaps = gst_caps_truncate (othercaps);
738 othercaps = gst_caps_make_writable (othercaps);
739
740 GST_DEBUG_OBJECT (base, "trying to fixate othercaps %" GST_PTR_FORMAT
741 " based on caps %" GST_PTR_FORMAT, othercaps, caps);
742
743 ins = gst_caps_get_structure (caps, 0);
744 outs = gst_caps_get_structure (othercaps, 0);
745
746 from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
747 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
748
749 /* If we're fixating from the sinkpad we always set the PAR and
750 * assume that missing PAR on the sinkpad means 1/1 and
751 * missing PAR on the srcpad means undefined
752 */
753 if (direction == GST_PAD_SINK) {
754 if (!from_par) {
755 g_value_init (&fpar, GST_TYPE_FRACTION);
756 gst_value_set_fraction (&fpar, 1, 1);
757 from_par = &fpar;
758 }
759 if (!to_par) {
760 g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
761 gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
762 to_par = &tpar;
763 }
764 } else {
765 if (!to_par) {
766 g_value_init (&tpar, GST_TYPE_FRACTION);
767 gst_value_set_fraction (&tpar, 1, 1);
768 to_par = &tpar;
769
770 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
771 NULL);
772 }
773 if (!from_par) {
774 g_value_init (&fpar, GST_TYPE_FRACTION);
775 gst_value_set_fraction (&fpar, 1, 1);
776 from_par = &fpar;
777 }
778 }
779
780 /* we have both PAR but they might not be fixated */
781 {
782 gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
783 gint w = 0, h = 0;
784 gint from_dar_n, from_dar_d;
785 gint num, den;
786
787 /* from_par should be fixed */
788 g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
789
790 from_par_n = gst_value_get_fraction_numerator (from_par);
791 from_par_d = gst_value_get_fraction_denominator (from_par);
792
793 gst_structure_get_int (ins, "width", &from_w);
794 gst_structure_get_int (ins, "height", &from_h);
795
796 gst_structure_get_int (outs, "width", &w);
797 gst_structure_get_int (outs, "height", &h);
798
799 /* if both width and height are already fixed, we can't do anything
800 * about it anymore */
801 if (w && h) {
802 guint n, d;
803
804 GST_DEBUG_OBJECT (base, "dimensions already set to %dx%d, not fixating",
805 w, h);
806 if (!gst_value_is_fixed (to_par)) {
807 if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
808 from_par_n, from_par_d, w, h)) {
809 GST_DEBUG_OBJECT (base, "fixating to_par to %dx%d", n, d);
810 if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
811 gst_structure_fixate_field_nearest_fraction (outs,
812 "pixel-aspect-ratio", n, d);
813 else if (n != d)
814 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
815 n, d, NULL);
816 }
817 }
818 goto done;
819 }
820
821 /* Calculate input DAR */
822 if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
823 &from_dar_n, &from_dar_d)) {
824 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
825 ("Error calculating the output scaled size - integer overflow"));
826 goto done;
827 }
828
829 GST_DEBUG_OBJECT (base, "Input DAR is %d/%d", from_dar_n, from_dar_d);
830
831 /* If either width or height are fixed there's not much we
832 * can do either except choosing a height or width and PAR
833 * that matches the DAR as good as possible
834 */
835 if (h) {
836 GstStructure *tmp;
837 gint set_w, set_par_n, set_par_d;
838
839 GST_DEBUG_OBJECT (base, "height is fixed (%d)", h);
840
841 /* If the PAR is fixed too, there's not much to do
842 * except choosing the width that is nearest to the
843 * width with the same DAR */
844 if (gst_value_is_fixed (to_par)) {
845 to_par_n = gst_value_get_fraction_numerator (to_par);
846 to_par_d = gst_value_get_fraction_denominator (to_par);
847
848 GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
849
850 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
851 to_par_n, &num, &den)) {
852 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
853 ("Error calculating the output scaled size - integer overflow"));
854 goto done;
855 }
856
857 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
858 gst_structure_fixate_field_nearest_int (outs, "width", w);
859
860 goto done;
861 }
862
863 /* The PAR is not fixed and it's quite likely that we can set
864 * an arbitrary PAR. */
865
866 /* Check if we can keep the input width */
867 tmp = gst_structure_copy (outs);
868 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
869 gst_structure_get_int (tmp, "width", &set_w);
870
871 /* Might have failed but try to keep the DAR nonetheless by
872 * adjusting the PAR */
873 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
874 &to_par_n, &to_par_d)) {
875 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
876 ("Error calculating the output scaled size - integer overflow"));
877 gst_structure_free (tmp);
878 goto done;
879 }
880
881 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
882 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
883 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
884 to_par_n, to_par_d);
885 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
886 &set_par_d);
887 gst_structure_free (tmp);
888
889 /* Check if the adjusted PAR is accepted */
890 if (set_par_n == to_par_n && set_par_d == to_par_d) {
891 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
892 set_par_n != set_par_d)
893 gst_structure_set (outs, "width", G_TYPE_INT, set_w,
894 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
895 NULL);
896 goto done;
897 }
898
899 /* Otherwise scale the width to the new PAR and check if the
900 * adjusted with is accepted. If all that fails we can't keep
901 * the DAR */
902 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
903 set_par_n, &num, &den)) {
904 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
905 ("Error calculating the output scaled size - integer overflow"));
906 goto done;
907 }
908
909 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
910 gst_structure_fixate_field_nearest_int (outs, "width", w);
911 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
912 set_par_n != set_par_d)
913 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
914 set_par_n, set_par_d, NULL);
915
916 goto done;
917 } else if (w) {
918 GstStructure *tmp;
919 gint set_h, set_par_n, set_par_d;
920
921 GST_DEBUG_OBJECT (base, "width is fixed (%d)", w);
922
923 /* If the PAR is fixed too, there's not much to do
924 * except choosing the height that is nearest to the
925 * height with the same DAR */
926 if (gst_value_is_fixed (to_par)) {
927 to_par_n = gst_value_get_fraction_numerator (to_par);
928 to_par_d = gst_value_get_fraction_denominator (to_par);
929
930 GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
931
932 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
933 to_par_n, &num, &den)) {
934 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
935 ("Error calculating the output scaled size - integer overflow"));
936 goto done;
937 }
938
939 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
940 gst_structure_fixate_field_nearest_int (outs, "height", h);
941
942 goto done;
943 }
944
945 /* The PAR is not fixed and it's quite likely that we can set
946 * an arbitrary PAR. */
947
948 /* Check if we can keep the input height */
949 tmp = gst_structure_copy (outs);
950 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
951 gst_structure_get_int (tmp, "height", &set_h);
952
953 /* Might have failed but try to keep the DAR nonetheless by
954 * adjusting the PAR */
955 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
956 &to_par_n, &to_par_d)) {
957 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
958 ("Error calculating the output scaled size - integer overflow"));
959 gst_structure_free (tmp);
960 goto done;
961 }
962 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
963 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
964 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
965 to_par_n, to_par_d);
966 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
967 &set_par_d);
968 gst_structure_free (tmp);
969
970 /* Check if the adjusted PAR is accepted */
971 if (set_par_n == to_par_n && set_par_d == to_par_d) {
972 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
973 set_par_n != set_par_d)
974 gst_structure_set (outs, "height", G_TYPE_INT, set_h,
975 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
976 NULL);
977 goto done;
978 }
979
980 /* Otherwise scale the height to the new PAR and check if the
981 * adjusted with is accepted. If all that fails we can't keep
982 * the DAR */
983 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
984 set_par_n, &num, &den)) {
985 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
986 ("Error calculating the output scaled size - integer overflow"));
987 goto done;
988 }
989
990 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
991 gst_structure_fixate_field_nearest_int (outs, "height", h);
992 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
993 set_par_n != set_par_d)
994 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
995 set_par_n, set_par_d, NULL);
996
997 goto done;
998 } else if (gst_value_is_fixed (to_par)) {
999 GstStructure *tmp;
1000 gint set_h, set_w, f_h, f_w;
1001
1002 to_par_n = gst_value_get_fraction_numerator (to_par);
1003 to_par_d = gst_value_get_fraction_denominator (to_par);
1004
1005 /* Calculate scale factor for the PAR change */
1006 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
1007 to_par_d, &num, &den)) {
1008 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
1009 ("Error calculating the output scaled size - integer overflow"));
1010 goto done;
1011 }
1012
1013 /* Try to keep the input height (because of interlacing) */
1014 tmp = gst_structure_copy (outs);
1015 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1016 gst_structure_get_int (tmp, "height", &set_h);
1017
1018 /* This might have failed but try to scale the width
1019 * to keep the DAR nonetheless */
1020 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1021 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1022 gst_structure_get_int (tmp, "width", &set_w);
1023 gst_structure_free (tmp);
1024
1025 /* We kept the DAR and the height is nearest to the original height */
1026 if (set_w == w) {
1027 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1028 G_TYPE_INT, set_h, NULL);
1029 goto done;
1030 }
1031
1032 f_h = set_h;
1033 f_w = set_w;
1034
1035 /* If the former failed, try to keep the input width at least */
1036 tmp = gst_structure_copy (outs);
1037 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1038 gst_structure_get_int (tmp, "width", &set_w);
1039
1040 /* This might have failed but try to scale the width
1041 * to keep the DAR nonetheless */
1042 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1043 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1044 gst_structure_get_int (tmp, "height", &set_h);
1045 gst_structure_free (tmp);
1046
1047 /* We kept the DAR and the width is nearest to the original width */
1048 if (set_h == h) {
1049 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1050 G_TYPE_INT, set_h, NULL);
1051 goto done;
1052 }
1053
1054 /* If all this failed, keep the dimensions with the DAR that was closest
1055 * to the correct DAR. This changes the DAR but there's not much else to
1056 * do here.
1057 */
1058 if (set_w * ABS (set_h - h) < ABS (f_w - w) * f_h) {
1059 f_h = set_h;
1060 f_w = set_w;
1061 }
1062 gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
1063 f_h, NULL);
1064 goto done;
1065 } else {
1066 GstStructure *tmp;
1067 gint set_h, set_w, set_par_n, set_par_d, tmp2;
1068
1069 /* width, height and PAR are not fixed but passthrough is not possible */
1070
1071 /* First try to keep the height and width as good as possible
1072 * and scale PAR */
1073 tmp = gst_structure_copy (outs);
1074 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1075 gst_structure_get_int (tmp, "height", &set_h);
1076 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1077 gst_structure_get_int (tmp, "width", &set_w);
1078
1079 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
1080 &to_par_n, &to_par_d)) {
1081 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
1082 ("Error calculating the output scaled size - integer overflow"));
1083 gst_structure_free (tmp);
1084 goto done;
1085 }
1086
1087 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1088 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1089 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1090 to_par_n, to_par_d);
1091 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1092 &set_par_d);
1093 gst_structure_free (tmp);
1094
1095 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1096 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1097 G_TYPE_INT, set_h, NULL);
1098
1099 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1100 set_par_n != set_par_d)
1101 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1102 set_par_n, set_par_d, NULL);
1103 goto done;
1104 }
1105
1106 /* Otherwise try to scale width to keep the DAR with the set
1107 * PAR and height */
1108 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1109 set_par_n, &num, &den)) {
1110 GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
1111 ("Error calculating the output scaled size - integer overflow"));
1112 goto done;
1113 }
1114
1115 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1116 tmp = gst_structure_copy (outs);
1117 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1118 gst_structure_get_int (tmp, "width", &tmp2);
1119 gst_structure_free (tmp);
1120
1121 if (tmp2 == w) {
1122 gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
1123 G_TYPE_INT, set_h, NULL);
1124 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1125 set_par_n != set_par_d)
1126 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1127 set_par_n, set_par_d, NULL);
1128 goto done;
1129 }
1130
1131 /* ... or try the same with the height */
1132 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1133 tmp = gst_structure_copy (outs);
1134 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1135 gst_structure_get_int (tmp, "height", &tmp2);
1136 gst_structure_free (tmp);
1137
1138 if (tmp2 == h) {
1139 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1140 G_TYPE_INT, tmp2, NULL);
1141 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1142 set_par_n != set_par_d)
1143 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1144 set_par_n, set_par_d, NULL);
1145 goto done;
1146 }
1147
1148 /* If all fails we can't keep the DAR and take the nearest values
1149 * for everything from the first try */
1150 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1151 G_TYPE_INT, set_h, NULL);
1152 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1153 set_par_n != set_par_d)
1154 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1155 set_par_n, set_par_d, NULL);
1156 }
1157 }
1158
1159 done:
1160 othercaps = gst_caps_fixate (othercaps);
1161
1162 GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
1163
1164 if (from_par == &fpar)
1165 g_value_unset (&fpar);
1166 if (to_par == &tpar)
1167 g_value_unset (&tpar);
1168
1169 return othercaps;
1170 }
1171
1172 #define GET_LINE(frame, line) \
1173 (gpointer)(((guint8*)(GST_VIDEO_FRAME_PLANE_DATA (frame, 0))) + \
1174 GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) * (line))
1175
1176 static GstFlowReturn
gst_video_scale_transform_frame(GstVideoFilter * filter,GstVideoFrame * in_frame,GstVideoFrame * out_frame)1177 gst_video_scale_transform_frame (GstVideoFilter * filter,
1178 GstVideoFrame * in_frame, GstVideoFrame * out_frame)
1179 {
1180 GstVideoScale *videoscale = GST_VIDEO_SCALE_CAST (filter);
1181 GstFlowReturn ret = GST_FLOW_OK;
1182
1183 GST_CAT_DEBUG_OBJECT (CAT_PERFORMANCE, filter, "doing video scaling");
1184
1185 gst_video_converter_frame (videoscale->convert, in_frame, out_frame);
1186
1187 return ret;
1188 }
1189
1190 static gboolean
gst_video_scale_src_event(GstBaseTransform * trans,GstEvent * event)1191 gst_video_scale_src_event (GstBaseTransform * trans, GstEvent * event)
1192 {
1193 GstVideoScale *videoscale = GST_VIDEO_SCALE_CAST (trans);
1194 GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
1195 gboolean ret;
1196 gdouble a;
1197 GstStructure *structure;
1198
1199 GST_DEBUG_OBJECT (videoscale, "handling %s event",
1200 GST_EVENT_TYPE_NAME (event));
1201
1202 switch (GST_EVENT_TYPE (event)) {
1203 case GST_EVENT_NAVIGATION:
1204 if (filter->in_info.width != filter->out_info.width ||
1205 filter->in_info.height != filter->out_info.height) {
1206 event =
1207 GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1208
1209 structure = (GstStructure *) gst_event_get_structure (event);
1210 if (gst_structure_get_double (structure, "pointer_x", &a)) {
1211 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1212 a * filter->in_info.width / filter->out_info.width, NULL);
1213 }
1214 if (gst_structure_get_double (structure, "pointer_y", &a)) {
1215 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1216 a * filter->in_info.height / filter->out_info.height, NULL);
1217 }
1218 }
1219 break;
1220 default:
1221 break;
1222 }
1223
1224 ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
1225
1226 return ret;
1227 }
1228
1229 static gboolean
plugin_init(GstPlugin * plugin)1230 plugin_init (GstPlugin * plugin)
1231 {
1232 features_format_interlaced =
1233 gst_caps_features_new (GST_CAPS_FEATURE_FORMAT_INTERLACED, NULL);
1234 features_format_interlaced_sysmem =
1235 gst_caps_features_copy (features_format_interlaced);
1236 gst_caps_features_add (features_format_interlaced_sysmem,
1237 GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
1238
1239 GST_DEBUG_CATEGORY_INIT (video_scale_debug, "videoscale", 0,
1240 "videoscale element");
1241 GST_DEBUG_CATEGORY_GET (CAT_PERFORMANCE, "GST_PERFORMANCE");
1242
1243 return GST_ELEMENT_REGISTER (videoscale, plugin);
1244 }
1245
1246 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1247 GST_VERSION_MINOR,
1248 videoscale,
1249 "Resizes video", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
1250 GST_PACKAGE_ORIGIN)
1251