videoscale: Enable 4-tap scaling for all supported formats
[platform/upstream/gstreamer.git] / gst / videoscale / gstvideoscale.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2005 David Schleef <ds@schleef.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:element-videoscale
23  * @see_also: videorate, ffmpegcolorspace
24  *
25  * This element resizes video frames. By default the element will try to
26  * negotiate to the same size on the source and sinkpad so that no scaling
27  * is needed. It is therefore safe to insert this element in a pipeline to
28  * get more robust behaviour without any cost if no scaling is needed.
29  *
30  * This element supports a wide range of color spaces including various YUV and
31  * RGB formats and is therefore generally able to operate anywhere in a
32  * pipeline.
33  *
34  * <refsect2>
35  * <title>Example pipelines</title>
36  * |[
37  * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! ffmpegcolorspace ! videoscale ! ximagesink
38  * ]| Decode an Ogg/Theora and display the video using ximagesink. Since
39  * ximagesink cannot perform scaling, the video scaling will be performed by
40  * videoscale when you resize the video window.
41  * To create the test Ogg/Theora file refer to the documentation of theoraenc.
42  * |[
43  * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! videoscale ! video/x-raw-yuv, width=50 ! xvimagesink
44  * ]| Decode an Ogg/Theora and display the video using xvimagesink with a width
45  * of 50.
46  * </refsect2>
47  *
48  * Last reviewed on 2006-03-02 (0.10.4)
49  */
50
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54
55 #include <string.h>
56
57 #include <gst/video/video.h>
58 #include <liboil/liboil.h>
59
60 #include "gstvideoscale.h"
61 #include "vs_image.h"
62 #include "vs_4tap.h"
63
64
65 /* debug variable definition */
66 GST_DEBUG_CATEGORY (video_scale_debug);
67
68 /* elementfactory information */
69 static const GstElementDetails video_scale_details =
70 GST_ELEMENT_DETAILS ("Video scaler",
71     "Filter/Effect/Video",
72     "Resizes video",
73     "Wim Taymans <wim.taymans@chello.be>");
74
75 #define DEFAULT_PROP_METHOD     GST_VIDEO_SCALE_BILINEAR
76
77 enum
78 {
79   PROP_0,
80   PROP_METHOD
81       /* FILL ME */
82 };
83
84 static GstStaticCaps gst_video_scale_format_caps[] = {
85   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBx),
86   GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB),
87   GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx),
88   GST_STATIC_CAPS (GST_VIDEO_CAPS_xBGR),
89   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA),
90   GST_STATIC_CAPS (GST_VIDEO_CAPS_ARGB),
91   GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRA),
92   GST_STATIC_CAPS (GST_VIDEO_CAPS_ABGR),
93   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB),
94   GST_STATIC_CAPS (GST_VIDEO_CAPS_BGR),
95   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")),
96   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YUY2")),
97   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YVYU")),
98   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("UYVY")),
99   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y800")),
100   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")),
101   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YV12")),
102   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB_16),
103   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB_15)
104 };
105
106 enum
107 {
108   GST_VIDEO_SCALE_RGBx = 0,
109   GST_VIDEO_SCALE_xRGB,
110   GST_VIDEO_SCALE_BGRx,
111   GST_VIDEO_SCALE_xBGR,
112   GST_VIDEO_SCALE_RGBA,
113   GST_VIDEO_SCALE_ARGB,
114   GST_VIDEO_SCALE_BGRA,
115   GST_VIDEO_SCALE_ABGR,
116   GST_VIDEO_SCALE_RGB,
117   GST_VIDEO_SCALE_BGR,
118   GST_VIDEO_SCALE_AYUV,
119   GST_VIDEO_SCALE_YUY2,
120   GST_VIDEO_SCALE_YVYU,
121   GST_VIDEO_SCALE_UYVY,
122   GST_VIDEO_SCALE_Y,
123   GST_VIDEO_SCALE_I420,
124   GST_VIDEO_SCALE_YV12,
125   GST_VIDEO_SCALE_RGB565,
126   GST_VIDEO_SCALE_RGB555
127 };
128
129 #define GST_TYPE_VIDEO_SCALE_METHOD (gst_video_scale_method_get_type())
130 static GType
131 gst_video_scale_method_get_type (void)
132 {
133   static GType video_scale_method_type = 0;
134
135   static const GEnumValue video_scale_methods[] = {
136     {GST_VIDEO_SCALE_NEAREST, "Nearest Neighbour", "nearest-neighbour"},
137     {GST_VIDEO_SCALE_BILINEAR, "Bilinear", "bilinear"},
138     {GST_VIDEO_SCALE_4TAP, "4-tap", "4-tap"},
139     {0, NULL, NULL},
140   };
141
142   if (!video_scale_method_type) {
143     video_scale_method_type =
144         g_enum_register_static ("GstVideoScaleMethod", video_scale_methods);
145   }
146   return video_scale_method_type;
147 }
148
149 static GstCaps *
150 gst_video_scale_get_capslist (void)
151 {
152   static GstCaps *caps;
153
154   if (caps == NULL) {
155     int i;
156
157     caps = gst_caps_new_empty ();
158     for (i = 0; i < G_N_ELEMENTS (gst_video_scale_format_caps); i++)
159       gst_caps_append (caps,
160           gst_caps_make_writable
161           (gst_static_caps_get (&gst_video_scale_format_caps[i])));
162   }
163
164   return caps;
165 }
166
167 static GstPadTemplate *
168 gst_video_scale_src_template_factory (void)
169 {
170   return gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
171       gst_caps_ref (gst_video_scale_get_capslist ()));
172 }
173
174 static GstPadTemplate *
175 gst_video_scale_sink_template_factory (void)
176 {
177   return gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
178       gst_caps_ref (gst_video_scale_get_capslist ()));
179 }
180
181
182 static void gst_video_scale_base_init (gpointer g_class);
183
184 static void gst_video_scale_class_init (GstVideoScaleClass * klass);
185
186 static void gst_video_scale_init (GstVideoScale * videoscale);
187
188 static void gst_video_scale_finalize (GstVideoScale * videoscale);
189
190 static gboolean gst_video_scale_src_event (GstBaseTransform * trans,
191     GstEvent * event);
192
193 /* base transform vmethods */
194 static GstCaps *gst_video_scale_transform_caps (GstBaseTransform * trans,
195     GstPadDirection direction, GstCaps * caps);
196 static gboolean gst_video_scale_set_caps (GstBaseTransform * trans,
197     GstCaps * in, GstCaps * out);
198 static gboolean gst_video_scale_get_unit_size (GstBaseTransform * trans,
199     GstCaps * caps, guint * size);
200 static GstFlowReturn gst_video_scale_transform (GstBaseTransform * trans,
201     GstBuffer * in, GstBuffer * out);
202 static void gst_video_scale_fixate_caps (GstBaseTransform * base,
203     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
204
205 static void gst_video_scale_set_property (GObject * object, guint prop_id,
206     const GValue * value, GParamSpec * pspec);
207 static void gst_video_scale_get_property (GObject * object, guint prop_id,
208     GValue * value, GParamSpec * pspec);
209
210 static GstElementClass *parent_class = NULL;
211
212
213 GType
214 gst_video_scale_get_type (void)
215 {
216   static GType video_scale_type = 0;
217
218   if (!video_scale_type) {
219     static const GTypeInfo video_scale_info = {
220       sizeof (GstVideoScaleClass),
221       gst_video_scale_base_init,
222       NULL,
223       (GClassInitFunc) gst_video_scale_class_init,
224       NULL,
225       NULL,
226       sizeof (GstVideoScale),
227       0,
228       (GInstanceInitFunc) gst_video_scale_init,
229     };
230
231     video_scale_type =
232         g_type_register_static (GST_TYPE_BASE_TRANSFORM, "GstVideoScale",
233         &video_scale_info, 0);
234   }
235   return video_scale_type;
236 }
237
238 static void
239 gst_video_scale_base_init (gpointer g_class)
240 {
241   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
242
243   gst_element_class_set_details (element_class, &video_scale_details);
244
245   gst_element_class_add_pad_template (element_class,
246       gst_video_scale_sink_template_factory ());
247   gst_element_class_add_pad_template (element_class,
248       gst_video_scale_src_template_factory ());
249 }
250
251 static void
252 gst_video_scale_class_init (GstVideoScaleClass * klass)
253 {
254   GObjectClass *gobject_class;
255   GstBaseTransformClass *trans_class;
256
257   gobject_class = (GObjectClass *) klass;
258   trans_class = (GstBaseTransformClass *) klass;
259
260   gobject_class->finalize = (GObjectFinalizeFunc) gst_video_scale_finalize;
261   gobject_class->set_property = gst_video_scale_set_property;
262   gobject_class->get_property = gst_video_scale_get_property;
263
264   g_object_class_install_property (gobject_class, PROP_METHOD,
265       g_param_spec_enum ("method", "method", "method",
266           GST_TYPE_VIDEO_SCALE_METHOD, DEFAULT_PROP_METHOD,
267           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
268
269   trans_class->transform_caps =
270       GST_DEBUG_FUNCPTR (gst_video_scale_transform_caps);
271   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_scale_set_caps);
272   trans_class->get_unit_size =
273       GST_DEBUG_FUNCPTR (gst_video_scale_get_unit_size);
274   trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_scale_transform);
275   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_scale_fixate_caps);
276   trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_scale_src_event);
277
278   trans_class->passthrough_on_same_caps = TRUE;
279
280   parent_class = g_type_class_peek_parent (klass);
281 }
282
283 static void
284 gst_video_scale_init (GstVideoScale * videoscale)
285 {
286   gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (videoscale), TRUE);
287   videoscale->tmp_buf = NULL;
288   videoscale->method = DEFAULT_PROP_METHOD;
289 }
290
291 static void
292 gst_video_scale_finalize (GstVideoScale * videoscale)
293 {
294   if (videoscale->tmp_buf)
295     g_free (videoscale->tmp_buf);
296
297   G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (videoscale));
298 }
299
300 static void
301 gst_video_scale_set_property (GObject * object, guint prop_id,
302     const GValue * value, GParamSpec * pspec)
303 {
304   GstVideoScale *vscale = GST_VIDEO_SCALE (object);
305
306   switch (prop_id) {
307     case PROP_METHOD:
308       GST_OBJECT_LOCK (vscale);
309       vscale->method = g_value_get_enum (value);
310       GST_OBJECT_UNLOCK (vscale);
311       break;
312     default:
313       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
314       break;
315   }
316 }
317
318 static void
319 gst_video_scale_get_property (GObject * object, guint prop_id, GValue * value,
320     GParamSpec * pspec)
321 {
322   GstVideoScale *vscale = GST_VIDEO_SCALE (object);
323
324   switch (prop_id) {
325     case PROP_METHOD:
326       GST_OBJECT_LOCK (vscale);
327       g_value_set_enum (value, vscale->method);
328       GST_OBJECT_UNLOCK (vscale);
329       break;
330     default:
331       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332       break;
333   }
334 }
335
336 static GstCaps *
337 gst_video_scale_transform_caps (GstBaseTransform * trans,
338     GstPadDirection direction, GstCaps * caps)
339 {
340   GstVideoScale *videoscale;
341   GstCaps *ret;
342   GstStructure *structure;
343   const GValue *par;
344   gint method;
345
346   /* this function is always called with a simple caps */
347   g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), NULL);
348
349   videoscale = GST_VIDEO_SCALE (trans);
350
351   GST_OBJECT_LOCK (videoscale);
352   method = videoscale->method;
353   GST_OBJECT_UNLOCK (videoscale);
354
355   structure = gst_caps_get_structure (caps, 0);
356
357   ret = gst_caps_copy (caps);
358   structure = gst_structure_copy (gst_caps_get_structure (ret, 0));
359
360   gst_structure_set (structure,
361       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
362       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
363
364   gst_caps_merge_structure (ret, gst_structure_copy (structure));
365
366   /* if pixel aspect ratio, make a range of it */
367   if ((par = gst_structure_get_value (structure, "pixel-aspect-ratio"))) {
368     gst_structure_set (structure,
369         "pixel-aspect-ratio", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
370
371     gst_caps_merge_structure (ret, structure);
372   } else {
373     gst_structure_free (structure);
374   }
375
376   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
377
378   return ret;
379 }
380
381 static int
382 gst_video_scale_get_format (GstCaps * caps)
383 {
384   int i;
385   GstCaps *icaps, *scaps;
386
387   for (i = 0; i < G_N_ELEMENTS (gst_video_scale_format_caps); i++) {
388     scaps = gst_static_caps_get (&gst_video_scale_format_caps[i]);
389     icaps = gst_caps_intersect (caps, scaps);
390     if (!gst_caps_is_empty (icaps)) {
391       gst_caps_unref (icaps);
392       return i;
393     }
394     gst_caps_unref (icaps);
395   }
396
397   return -1;
398 }
399
400 /* calculate the size of a buffer */
401 static gboolean
402 gst_video_scale_prepare_size (GstVideoScale * videoscale, gint format,
403     VSImage * img, gint width, gint height, guint * size)
404 {
405   gboolean res = TRUE;
406
407   img->width = width;
408   img->height = height;
409
410   switch (format) {
411     case GST_VIDEO_SCALE_RGBx:
412     case GST_VIDEO_SCALE_xRGB:
413     case GST_VIDEO_SCALE_BGRx:
414     case GST_VIDEO_SCALE_xBGR:
415     case GST_VIDEO_SCALE_RGBA:
416     case GST_VIDEO_SCALE_ARGB:
417     case GST_VIDEO_SCALE_BGRA:
418     case GST_VIDEO_SCALE_ABGR:
419     case GST_VIDEO_SCALE_AYUV:
420       img->stride = img->width * 4;
421       *size = img->stride * img->height;
422       break;
423     case GST_VIDEO_SCALE_RGB:
424     case GST_VIDEO_SCALE_BGR:
425       img->stride = GST_ROUND_UP_4 (img->width * 3);
426       *size = img->stride * img->height;
427       break;
428     case GST_VIDEO_SCALE_YUY2:
429     case GST_VIDEO_SCALE_YVYU:
430     case GST_VIDEO_SCALE_UYVY:
431       img->stride = GST_ROUND_UP_4 (img->width * 2);
432       *size = img->stride * img->height;
433       break;
434     case GST_VIDEO_SCALE_Y:
435       img->stride = GST_ROUND_UP_4 (img->width);
436       *size = img->stride * img->height;
437       break;
438     case GST_VIDEO_SCALE_I420:
439     case GST_VIDEO_SCALE_YV12:
440     {
441       gulong img_u_stride, img_u_height;
442
443       img->stride = GST_ROUND_UP_4 (img->width);
444
445       img_u_height = GST_ROUND_UP_2 (img->height) / 2;
446       img_u_stride = GST_ROUND_UP_4 (img->stride / 2);
447
448       *size = img->stride * GST_ROUND_UP_2 (img->height) +
449           2 * img_u_stride * img_u_height;
450       break;
451     }
452     case GST_VIDEO_SCALE_RGB565:
453       img->stride = GST_ROUND_UP_4 (img->width * 2);
454       *size = img->stride * img->height;
455       break;
456     case GST_VIDEO_SCALE_RGB555:
457       img->stride = GST_ROUND_UP_4 (img->width * 2);
458       *size = img->stride * img->height;
459       break;
460     default:
461       goto unknown_format;
462   }
463
464   return res;
465
466   /* ERRORS */
467 unknown_format:
468   {
469     GST_ELEMENT_ERROR (videoscale, STREAM, NOT_IMPLEMENTED, (NULL),
470         ("Unsupported format %d", videoscale->format));
471     return FALSE;
472   }
473 }
474
475 static gboolean
476 parse_caps (GstCaps * caps, gint * format, gint * width, gint * height)
477 {
478   gboolean ret;
479   GstStructure *structure;
480
481   structure = gst_caps_get_structure (caps, 0);
482   ret = gst_structure_get_int (structure, "width", width);
483   ret &= gst_structure_get_int (structure, "height", height);
484
485   if (format)
486     *format = gst_video_scale_get_format (caps);
487
488   return ret;
489 }
490
491 static gboolean
492 gst_video_scale_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
493 {
494   GstVideoScale *videoscale;
495   gboolean ret;
496
497   videoscale = GST_VIDEO_SCALE (trans);
498
499   ret = parse_caps (in, &videoscale->format, &videoscale->from_width,
500       &videoscale->from_height);
501   ret &= parse_caps (out, NULL, &videoscale->to_width, &videoscale->to_height);
502   if (!ret)
503     goto done;
504
505   if (!(ret = gst_video_scale_prepare_size (videoscale, videoscale->format,
506               &videoscale->src, videoscale->from_width, videoscale->from_height,
507               &videoscale->src_size)))
508     /* prepare size has posted an error when it returns FALSE */
509     goto done;
510
511   if (!(ret = gst_video_scale_prepare_size (videoscale, videoscale->format,
512               &videoscale->dest, videoscale->to_width, videoscale->to_height,
513               &videoscale->dest_size)))
514     /* prepare size has posted an error when it returns FALSE */
515     goto done;
516
517   if (videoscale->tmp_buf)
518     g_free (videoscale->tmp_buf);
519
520   videoscale->tmp_buf = g_malloc (videoscale->dest.stride * 4);
521
522   /* FIXME: par */
523   GST_DEBUG_OBJECT (videoscale, "from=%dx%d, size %d -> to=%dx%d, size %d",
524       videoscale->from_width, videoscale->from_height, videoscale->src_size,
525       videoscale->to_width, videoscale->to_height, videoscale->dest_size);
526
527 done:
528   return ret;
529 }
530
531 static gboolean
532 gst_video_scale_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
533     guint * size)
534 {
535   GstVideoScale *videoscale;
536   gint format, width, height;
537   VSImage img;
538
539   g_assert (size);
540
541   videoscale = GST_VIDEO_SCALE (trans);
542
543   if (!parse_caps (caps, &format, &width, &height))
544     return FALSE;
545
546   if (!gst_video_scale_prepare_size (videoscale, format, &img, width, height,
547           size))
548     return FALSE;
549
550   return TRUE;
551 }
552
553 static void
554 gst_video_scale_fixate_caps (GstBaseTransform * base, GstPadDirection direction,
555     GstCaps * caps, GstCaps * othercaps)
556 {
557   GstStructure *ins, *outs;
558   const GValue *from_par, *to_par;
559
560   g_return_if_fail (gst_caps_is_fixed (caps));
561
562   GST_DEBUG_OBJECT (base, "trying to fixate othercaps %" GST_PTR_FORMAT
563       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
564
565   ins = gst_caps_get_structure (caps, 0);
566   outs = gst_caps_get_structure (othercaps, 0);
567
568   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
569   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
570
571   /* we have both PAR but they might not be fixated */
572   if (from_par && to_par) {
573     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
574
575     gint count = 0, w = 0, h = 0;
576
577     guint num, den;
578
579     /* from_par should be fixed */
580     g_return_if_fail (gst_value_is_fixed (from_par));
581
582     from_par_n = gst_value_get_fraction_numerator (from_par);
583     from_par_d = gst_value_get_fraction_denominator (from_par);
584
585     /* fixate the out PAR */
586     if (!gst_value_is_fixed (to_par)) {
587       GST_DEBUG_OBJECT (base, "fixating to_par to %dx%d", from_par_n,
588           from_par_d);
589       gst_structure_fixate_field_nearest_fraction (outs, "pixel-aspect-ratio",
590           from_par_n, from_par_d);
591     }
592
593     to_par_n = gst_value_get_fraction_numerator (to_par);
594     to_par_d = gst_value_get_fraction_denominator (to_par);
595
596     /* if both width and height are already fixed, we can't do anything
597      * about it anymore */
598     if (gst_structure_get_int (outs, "width", &w))
599       ++count;
600     if (gst_structure_get_int (outs, "height", &h))
601       ++count;
602     if (count == 2) {
603       GST_DEBUG_OBJECT (base, "dimensions already set to %dx%d, not fixating",
604           w, h);
605       return;
606     }
607
608     gst_structure_get_int (ins, "width", &from_w);
609     gst_structure_get_int (ins, "height", &from_h);
610
611     if (!gst_video_calculate_display_ratio (&num, &den, from_w, from_h,
612             from_par_n, from_par_d, to_par_n, to_par_d)) {
613       GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
614           ("Error calculating the output scaled size - integer overflow"));
615       return;
616     }
617
618     GST_DEBUG_OBJECT (base,
619         "scaling input with %dx%d and PAR %d/%d to output PAR %d/%d",
620         from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d);
621     GST_DEBUG_OBJECT (base, "resulting output should respect ratio of %d/%d",
622         num, den);
623
624     /* now find a width x height that respects this display ratio.
625      * prefer those that have one of w/h the same as the incoming video
626      * using wd / hd = num / den */
627
628     /* if one of the output width or height is fixed, we work from there */
629     if (h) {
630       GST_DEBUG_OBJECT (base, "height is fixed,scaling width");
631       w = (guint) gst_util_uint64_scale_int (h, num, den);
632     } else if (w) {
633       GST_DEBUG_OBJECT (base, "width is fixed, scaling height");
634       h = (guint) gst_util_uint64_scale_int (w, den, num);
635     } else {
636       /* none of width or height is fixed, figure out both of them based only on
637        * the input width and height */
638       /* check hd / den is an integer scale factor, and scale wd with the PAR */
639       if (from_h % den == 0) {
640         GST_DEBUG_OBJECT (base, "keeping video height");
641         h = from_h;
642         w = (guint) gst_util_uint64_scale_int (h, num, den);
643       } else if (from_w % num == 0) {
644         GST_DEBUG_OBJECT (base, "keeping video width");
645         w = from_w;
646         h = (guint) gst_util_uint64_scale_int (w, den, num);
647       } else {
648         GST_DEBUG_OBJECT (base, "approximating but keeping video height");
649         h = from_h;
650         w = (guint) gst_util_uint64_scale_int (h, num, den);
651       }
652     }
653     GST_DEBUG_OBJECT (base, "scaling to %dx%d", w, h);
654
655     /* now fixate */
656     gst_structure_fixate_field_nearest_int (outs, "width", w);
657     gst_structure_fixate_field_nearest_int (outs, "height", h);
658   } else {
659     gint width, height;
660
661     if (gst_structure_get_int (ins, "width", &width)) {
662       if (gst_structure_has_field (outs, "width")) {
663         gst_structure_fixate_field_nearest_int (outs, "width", width);
664       }
665     }
666     if (gst_structure_get_int (ins, "height", &height)) {
667       if (gst_structure_has_field (outs, "height")) {
668         gst_structure_fixate_field_nearest_int (outs, "height", height);
669       }
670     }
671   }
672
673   GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
674 }
675
676 static gboolean
677 gst_video_scale_prepare_image (gint format, GstBuffer * buf,
678     VSImage * img, VSImage * img_u, VSImage * img_v)
679 {
680   gboolean res = TRUE;
681
682   img->pixels = GST_BUFFER_DATA (buf);
683
684   switch (format) {
685     case GST_VIDEO_SCALE_I420:
686     case GST_VIDEO_SCALE_YV12:
687       img_u->pixels = img->pixels + GST_ROUND_UP_2 (img->height) * img->stride;
688       img_u->height = GST_ROUND_UP_2 (img->height) / 2;
689       img_u->width = GST_ROUND_UP_2 (img->width) / 2;
690       img_u->stride = GST_ROUND_UP_4 (img_u->width);
691       memcpy (img_v, img_u, sizeof (*img_v));
692       img_v->pixels = img_u->pixels + img_u->height * img_u->stride;
693       break;
694     default:
695       break;
696   }
697   return res;
698 }
699
700 static GstFlowReturn
701 gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in,
702     GstBuffer * out)
703 {
704   GstVideoScale *videoscale;
705   GstFlowReturn ret = GST_FLOW_OK;
706   VSImage *dest;
707   VSImage *src;
708   VSImage dest_u;
709   VSImage dest_v;
710   VSImage src_u;
711   VSImage src_v;
712   gint method;
713
714   videoscale = GST_VIDEO_SCALE (trans);
715
716   GST_OBJECT_LOCK (videoscale);
717   method = videoscale->method;
718   GST_OBJECT_UNLOCK (videoscale);
719
720   src = &videoscale->src;
721   dest = &videoscale->dest;
722
723   gst_video_scale_prepare_image (videoscale->format, in, src, &src_u, &src_v);
724   gst_video_scale_prepare_image (videoscale->format, out, dest, &dest_u,
725       &dest_v);
726
727   switch (method) {
728     case GST_VIDEO_SCALE_NEAREST:
729       GST_LOG_OBJECT (videoscale, "doing nearest scaling");
730       switch (videoscale->format) {
731         case GST_VIDEO_SCALE_RGBx:
732         case GST_VIDEO_SCALE_xRGB:
733         case GST_VIDEO_SCALE_BGRx:
734         case GST_VIDEO_SCALE_xBGR:
735         case GST_VIDEO_SCALE_RGBA:
736         case GST_VIDEO_SCALE_ARGB:
737         case GST_VIDEO_SCALE_BGRA:
738         case GST_VIDEO_SCALE_ABGR:
739         case GST_VIDEO_SCALE_AYUV:
740           vs_image_scale_nearest_RGBA (dest, src, videoscale->tmp_buf);
741           break;
742         case GST_VIDEO_SCALE_RGB:
743         case GST_VIDEO_SCALE_BGR:
744           vs_image_scale_nearest_RGB (dest, src, videoscale->tmp_buf);
745           break;
746         case GST_VIDEO_SCALE_YUY2:
747         case GST_VIDEO_SCALE_YVYU:
748           vs_image_scale_nearest_YUYV (dest, src, videoscale->tmp_buf);
749           break;
750         case GST_VIDEO_SCALE_UYVY:
751           vs_image_scale_nearest_UYVY (dest, src, videoscale->tmp_buf);
752           break;
753         case GST_VIDEO_SCALE_Y:
754           vs_image_scale_nearest_Y (dest, src, videoscale->tmp_buf);
755           break;
756         case GST_VIDEO_SCALE_I420:
757         case GST_VIDEO_SCALE_YV12:
758           vs_image_scale_nearest_Y (dest, src, videoscale->tmp_buf);
759           vs_image_scale_nearest_Y (&dest_u, &src_u, videoscale->tmp_buf);
760           vs_image_scale_nearest_Y (&dest_v, &src_v, videoscale->tmp_buf);
761           break;
762         case GST_VIDEO_SCALE_RGB565:
763           vs_image_scale_nearest_RGB565 (dest, src, videoscale->tmp_buf);
764           break;
765         case GST_VIDEO_SCALE_RGB555:
766           vs_image_scale_nearest_RGB555 (dest, src, videoscale->tmp_buf);
767           break;
768         default:
769           goto unsupported;
770       }
771       break;
772     case GST_VIDEO_SCALE_BILINEAR:
773       GST_LOG_OBJECT (videoscale, "doing bilinear scaling");
774       switch (videoscale->format) {
775         case GST_VIDEO_SCALE_RGBx:
776         case GST_VIDEO_SCALE_xRGB:
777         case GST_VIDEO_SCALE_BGRx:
778         case GST_VIDEO_SCALE_xBGR:
779         case GST_VIDEO_SCALE_RGBA:
780         case GST_VIDEO_SCALE_ARGB:
781         case GST_VIDEO_SCALE_BGRA:
782         case GST_VIDEO_SCALE_ABGR:
783         case GST_VIDEO_SCALE_AYUV:
784           vs_image_scale_linear_RGBA (dest, src, videoscale->tmp_buf);
785           break;
786         case GST_VIDEO_SCALE_RGB:
787         case GST_VIDEO_SCALE_BGR:
788           vs_image_scale_linear_RGB (dest, src, videoscale->tmp_buf);
789           break;
790         case GST_VIDEO_SCALE_YUY2:
791         case GST_VIDEO_SCALE_YVYU:
792           vs_image_scale_linear_YUYV (dest, src, videoscale->tmp_buf);
793           break;
794         case GST_VIDEO_SCALE_UYVY:
795           vs_image_scale_linear_UYVY (dest, src, videoscale->tmp_buf);
796           break;
797         case GST_VIDEO_SCALE_Y:
798           vs_image_scale_linear_Y (dest, src, videoscale->tmp_buf);
799           break;
800         case GST_VIDEO_SCALE_I420:
801         case GST_VIDEO_SCALE_YV12:
802           vs_image_scale_linear_Y (dest, src, videoscale->tmp_buf);
803           vs_image_scale_linear_Y (&dest_u, &src_u, videoscale->tmp_buf);
804           vs_image_scale_linear_Y (&dest_v, &src_v, videoscale->tmp_buf);
805           break;
806         case GST_VIDEO_SCALE_RGB565:
807           vs_image_scale_linear_RGB565 (dest, src, videoscale->tmp_buf);
808           break;
809         case GST_VIDEO_SCALE_RGB555:
810           vs_image_scale_linear_RGB555 (dest, src, videoscale->tmp_buf);
811           break;
812         default:
813           goto unsupported;
814       }
815       break;
816     case GST_VIDEO_SCALE_4TAP:
817       GST_LOG_OBJECT (videoscale, "doing 4tap scaling");
818       switch (videoscale->format) {
819         case GST_VIDEO_SCALE_RGBx:
820         case GST_VIDEO_SCALE_xRGB:
821         case GST_VIDEO_SCALE_BGRx:
822         case GST_VIDEO_SCALE_xBGR:
823         case GST_VIDEO_SCALE_RGBA:
824         case GST_VIDEO_SCALE_ARGB:
825         case GST_VIDEO_SCALE_BGRA:
826         case GST_VIDEO_SCALE_ABGR:
827         case GST_VIDEO_SCALE_AYUV:
828           vs_image_scale_4tap_RGBA (dest, src, videoscale->tmp_buf);
829           break;
830         case GST_VIDEO_SCALE_RGB:
831         case GST_VIDEO_SCALE_BGR:
832           vs_image_scale_4tap_RGB (dest, src, videoscale->tmp_buf);
833           break;
834         case GST_VIDEO_SCALE_YUY2:
835         case GST_VIDEO_SCALE_YVYU:
836           vs_image_scale_4tap_YUYV (dest, src, videoscale->tmp_buf);
837           break;
838         case GST_VIDEO_SCALE_UYVY:
839           vs_image_scale_4tap_UYVY (dest, src, videoscale->tmp_buf);
840           break;
841         case GST_VIDEO_SCALE_Y:
842           vs_image_scale_4tap_Y (dest, src, videoscale->tmp_buf);
843           break;
844         case GST_VIDEO_SCALE_I420:
845         case GST_VIDEO_SCALE_YV12:
846           vs_image_scale_4tap_Y (dest, src, videoscale->tmp_buf);
847           vs_image_scale_4tap_Y (&dest_u, &src_u, videoscale->tmp_buf);
848           vs_image_scale_4tap_Y (&dest_v, &src_v, videoscale->tmp_buf);
849           break;
850         case GST_VIDEO_SCALE_RGB565:
851           vs_image_scale_4tap_RGB565 (dest, src, videoscale->tmp_buf);
852           break;
853         case GST_VIDEO_SCALE_RGB555:
854           vs_image_scale_4tap_RGB555 (dest, src, videoscale->tmp_buf);
855           break;
856         default:
857           goto unsupported;
858       }
859       break;
860     default:
861       goto unknown_mode;
862   }
863
864   GST_LOG_OBJECT (videoscale, "pushing buffer of %d bytes",
865       GST_BUFFER_SIZE (out));
866
867   return ret;
868
869   /* ERRORS */
870 unsupported:
871   {
872     GST_ELEMENT_ERROR (videoscale, STREAM, NOT_IMPLEMENTED, (NULL),
873         ("Unsupported format %d for scaling method %d",
874             videoscale->format, method));
875     return GST_FLOW_ERROR;
876   }
877 unknown_mode:
878   {
879     GST_ELEMENT_ERROR (videoscale, STREAM, NOT_IMPLEMENTED, (NULL),
880         ("Unknown scaling method %d", videoscale->method));
881     return GST_FLOW_ERROR;
882   }
883 }
884
885 static gboolean
886 gst_video_scale_src_event (GstBaseTransform * trans, GstEvent * event)
887 {
888   GstVideoScale *videoscale;
889   gboolean ret;
890   double a;
891   GstStructure *structure;
892
893   videoscale = GST_VIDEO_SCALE (trans);
894
895   GST_DEBUG_OBJECT (videoscale, "handling %s event",
896       GST_EVENT_TYPE_NAME (event));
897
898   switch (GST_EVENT_TYPE (event)) {
899     case GST_EVENT_NAVIGATION:
900       event =
901           GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
902
903       structure = (GstStructure *) gst_event_get_structure (event);
904       if (gst_structure_get_double (structure, "pointer_x", &a)) {
905         gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
906             a * videoscale->from_width / videoscale->to_width, NULL);
907       }
908       if (gst_structure_get_double (structure, "pointer_y", &a)) {
909         gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
910             a * videoscale->from_height / videoscale->to_height, NULL);
911       }
912       break;
913     case GST_EVENT_QOS:
914       break;
915     default:
916       break;
917   }
918
919   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
920
921   return ret;
922 }
923
924 static gboolean
925 plugin_init (GstPlugin * plugin)
926 {
927   oil_init ();
928
929   if (!gst_element_register (plugin, "videoscale", GST_RANK_NONE,
930           GST_TYPE_VIDEO_SCALE))
931     return FALSE;
932
933   GST_DEBUG_CATEGORY_INIT (video_scale_debug, "videoscale", 0,
934       "videoscale element");
935
936   vs_4tap_init ();
937
938   return TRUE;
939 }
940
941 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
942     GST_VERSION_MINOR,
943     "videoscale",
944     "Resizes video", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
945     GST_PACKAGE_ORIGIN)