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