Merge remote-tracking branch 'origin/master' into 0.11
[platform/upstream/gstreamer.git] / ext / libswscale / gstffmpegscale.c
1 /* GStreamer libswscale wrapper
2  * Copyright (C) 2005 Luca Ognibene <luogni@tin.it>
3  * Copyright (C) 2006 Martin Zlomek <martin.zlomek@itonis.tv>
4  * Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #ifdef HAVE_FFMPEG_UNINSTALLED
27 #include <swscale.h>
28 #else
29 #include <libswscale/swscale.h>
30 #endif
31
32 #include <gst/gst.h>
33 #include <gst/base/gstbasetransform.h>
34 #include <gst/video/video.h>
35
36 #ifdef HAVE_ORC
37 #include <orc/orc.h>
38 #endif
39
40 #include <string.h>
41
42 typedef struct _GstFFMpegScale
43 {
44   GstBaseTransform element;
45
46   /* pads */
47   GstPad *sinkpad, *srcpad;
48
49   /* state */
50   GstVideoInfo in_info, out_info;
51
52   enum PixelFormat in_pixfmt, out_pixfmt;
53   struct SwsContext *ctx;
54
55   /* property */
56   gint method;
57 } GstFFMpegScale;
58
59 typedef struct _GstFFMpegScaleClass
60 {
61   GstBaseTransformClass parent_class;
62 } GstFFMpegScaleClass;
63
64 #define GST_TYPE_FFMPEGSCALE \
65         (gst_ffmpegscale_get_type())
66 #define GST_FFMPEGSCALE(obj) \
67         (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGSCALE,GstFFMpegScale))
68 #define GST_FFMPEGSCALE_CLASS(klass) \
69         (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGSCALE,GstFFMpegScaleClass))
70 #define GST_IS_FFMPEGSCALE(obj) \
71         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGSCALE))
72 #define GST_IS_FFMPEGSCALE_CLASS(klass) \
73         (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGSCALE))
74
75 GType gst_ffmpegscale_get_type (void);
76
77 GST_DEBUG_CATEGORY (ffmpegscale_debug);
78 #define GST_CAT_DEFAULT ffmpegscale_debug
79
80 /* libswscale supported formats depend on endianness */
81 #if G_BYTE_ORDER == G_BIG_ENDIAN
82 #define VIDEO_CAPS \
83         GST_VIDEO_CAPS_MAKE ("{ RGB, BGR, xRGB, xBGR, ARGB, ABGR, I420, YUY2, UYVY, Y41B, Y42B }")
84 #else
85 #define VIDEO_CAPS \
86         GST_VIDEO_CAPS_MAKE ("{ RGB, BGR, RGBx, BGRx, RGBA, BGRA, I420, YUY2, UYVY, Y41B, Y42B }")
87 #endif
88
89 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
90     GST_PAD_SRC,
91     GST_PAD_ALWAYS,
92     GST_STATIC_CAPS (VIDEO_CAPS)
93     );
94
95 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
96     GST_PAD_SINK,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS (VIDEO_CAPS)
99     );
100
101 static gint gst_ffmpegscale_method_flags[] = {
102   SWS_FAST_BILINEAR,
103   SWS_BILINEAR,
104   SWS_BICUBIC,
105   SWS_X,
106   SWS_POINT,
107   SWS_AREA,
108   SWS_BICUBLIN,
109   SWS_GAUSS,
110   SWS_SINC,
111   SWS_LANCZOS,
112   SWS_SPLINE,
113 };
114
115 #define GST_TYPE_FFMPEGSCALE_METHOD (gst_ffmpegscale_method_get_type())
116 static GType
117 gst_ffmpegscale_method_get_type (void)
118 {
119   static GType ffmpegscale_method_type = 0;
120
121   static const GEnumValue ffmpegscale_methods[] = {
122     {0, "Fast Bilinear", "fast-bilinear"},
123     {1, "Bilinear", "bilinear"},
124     {2, "Bicubic", "bicubic"},
125     {3, "Experimental", "experimental"},
126     {4, "Nearest Neighbour", "nearest-neighbour"},
127     {5, "Area", "area"},
128     {6, "Luma Bicubic / Chroma Linear", "bicubic-lin"},
129     {7, "Gauss", "gauss"},
130     {8, "SincR", "sincr"},
131     {9, "Lanczos", "lanczos"},
132     {10, "Natural Bicubic Spline", "bicubic-spline"},
133     {0, NULL, NULL},
134   };
135
136   if (!ffmpegscale_method_type) {
137     ffmpegscale_method_type =
138         g_enum_register_static ("GstFFMpegVideoScaleMethod",
139         ffmpegscale_methods);
140   }
141   return ffmpegscale_method_type;
142 }
143
144 #define DEFAULT_PROP_METHOD    2
145
146 enum
147 {
148   PROP_0,
149   PROP_METHOD
150       /* FILL ME */
151 };
152
153 #define gst_ffmpegscale_parent_class parent_class
154 G_DEFINE_TYPE (GstFFMpegScale, gst_ffmpegscale, GST_TYPE_BASE_TRANSFORM);
155
156 static void gst_ffmpegscale_finalize (GObject * object);
157 static void gst_ffmpegscale_set_property (GObject * object, guint prop_id,
158     const GValue * value, GParamSpec * pspec);
159 static void gst_ffmpegscale_get_property (GObject * object, guint prop_id,
160     GValue * value, GParamSpec * pspec);
161
162 static gboolean gst_ffmpegscale_stop (GstBaseTransform * trans);
163 static GstCaps *gst_ffmpegscale_transform_caps (GstBaseTransform * trans,
164     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
165 static void gst_ffmpegscale_fixate_caps (GstBaseTransform * trans,
166     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
167 static gboolean gst_ffmpegscale_get_unit_size (GstBaseTransform * trans,
168     GstCaps * caps, gsize * size);
169 static gboolean gst_ffmpegscale_set_caps (GstBaseTransform * trans,
170     GstCaps * incaps, GstCaps * outcaps);
171 static GstFlowReturn gst_ffmpegscale_transform (GstBaseTransform * trans,
172     GstBuffer * inbuf, GstBuffer * outbuf);
173
174 static gboolean gst_ffmpegscale_handle_src_event (GstPad * pad,
175     GstEvent * event);
176
177 static void
178 gst_ffmpegscale_class_init (GstFFMpegScaleClass * klass)
179 {
180   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
181   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
182   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
183
184   gobject_class->finalize = gst_ffmpegscale_finalize;
185   gobject_class->set_property = gst_ffmpegscale_set_property;
186   gobject_class->get_property = gst_ffmpegscale_get_property;
187
188   g_object_class_install_property (gobject_class, PROP_METHOD,
189       g_param_spec_enum ("method", "method", "method",
190           GST_TYPE_FFMPEGSCALE_METHOD, DEFAULT_PROP_METHOD,
191           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
192
193   gst_element_class_add_pad_template (gstelement_class,
194       gst_static_pad_template_get (&src_factory));
195   gst_element_class_add_pad_template (gstelement_class,
196       gst_static_pad_template_get (&sink_factory));
197
198   gst_element_class_set_details_simple (gstelement_class,
199       "FFMPEG Scale element", "Filter/Converter/Video",
200       "Converts video from one resolution to another",
201       "Luca Ognibene <luogni@tin.it>, Mark Nauwelaerts <mnauw@users.sf.net>");
202
203   trans_class->stop = GST_DEBUG_FUNCPTR (gst_ffmpegscale_stop);
204   trans_class->transform_caps =
205       GST_DEBUG_FUNCPTR (gst_ffmpegscale_transform_caps);
206   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_ffmpegscale_fixate_caps);
207   trans_class->get_unit_size =
208       GST_DEBUG_FUNCPTR (gst_ffmpegscale_get_unit_size);
209   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_ffmpegscale_set_caps);
210   trans_class->transform = GST_DEBUG_FUNCPTR (gst_ffmpegscale_transform);
211
212   trans_class->passthrough_on_same_caps = TRUE;
213 }
214
215 static void
216 gst_ffmpegscale_init (GstFFMpegScale * scale)
217 {
218   GstBaseTransform *trans = GST_BASE_TRANSFORM (scale);
219
220   gst_pad_set_event_function (trans->srcpad, gst_ffmpegscale_handle_src_event);
221
222   scale->method = DEFAULT_PROP_METHOD;
223   scale->ctx = NULL;
224   scale->in_pixfmt = PIX_FMT_NONE;
225   scale->out_pixfmt = PIX_FMT_NONE;
226 }
227
228 static void
229 gst_ffmpegscale_reset (GstFFMpegScale * scale)
230 {
231   if (scale->ctx != NULL) {
232     sws_freeContext (scale->ctx);
233     scale->ctx = NULL;
234   }
235
236   scale->in_pixfmt = PIX_FMT_NONE;
237   scale->out_pixfmt = PIX_FMT_NONE;
238 }
239
240 static void
241 gst_ffmpegscale_finalize (GObject * object)
242 {
243   GstFFMpegScale *scale = GST_FFMPEGSCALE (object);
244
245   gst_ffmpegscale_reset (scale);
246
247   G_OBJECT_CLASS (parent_class)->finalize (object);
248 }
249
250 /* copies the given caps */
251 static GstCaps *
252 gst_ffmpegscale_caps_remove_format_info (GstCaps * caps)
253 {
254   int i;
255   GstStructure *structure;
256   GstCaps *rgbcaps;
257   GstCaps *graycaps;
258
259   caps = gst_caps_copy (caps);
260
261   for (i = 0; i < gst_caps_get_size (caps); i++) {
262     structure = gst_caps_get_structure (caps, i);
263
264     gst_structure_set_name (structure, "video/x-raw-yuv");
265     gst_structure_remove_field (structure, "format");
266     gst_structure_remove_field (structure, "endianness");
267     gst_structure_remove_field (structure, "depth");
268     gst_structure_remove_field (structure, "bpp");
269     gst_structure_remove_field (structure, "red_mask");
270     gst_structure_remove_field (structure, "green_mask");
271     gst_structure_remove_field (structure, "blue_mask");
272     gst_structure_remove_field (structure, "alpha_mask");
273     gst_structure_remove_field (structure, "palette_data");
274   }
275
276   rgbcaps = gst_caps_copy (caps);
277
278   for (i = 0; i < gst_caps_get_size (rgbcaps); i++) {
279     structure = gst_caps_get_structure (rgbcaps, i);
280
281     gst_structure_set_name (structure, "video/x-raw-rgb");
282   }
283   graycaps = gst_caps_copy (caps);
284
285   for (i = 0; i < gst_caps_get_size (graycaps); i++) {
286     structure = gst_caps_get_structure (graycaps, i);
287
288     gst_structure_set_name (structure, "video/x-raw-gray");
289   }
290
291   gst_caps_append (caps, graycaps);
292   gst_caps_append (caps, rgbcaps);
293
294   return caps;
295 }
296
297 static GstCaps *
298 gst_ffmpegscale_transform_caps (GstBaseTransform * trans,
299     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
300 {
301   GstCaps *ret;
302   GstStructure *structure;
303   const GValue *par;
304
305   /* this function is always called with a simple caps */
306   g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), NULL);
307
308   structure = gst_caps_get_structure (caps, 0);
309
310   ret = gst_caps_copy (caps);
311   structure = gst_structure_copy (gst_caps_get_structure (ret, 0));
312
313   gst_structure_set (structure,
314       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
315       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
316
317   gst_caps_merge_structure (ret, gst_structure_copy (structure));
318
319   /* if pixel aspect ratio, make a range of it */
320   if ((par = gst_structure_get_value (structure, "pixel-aspect-ratio"))) {
321     gst_structure_set (structure,
322         "pixel-aspect-ratio", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
323
324     gst_caps_merge_structure (ret, structure);
325   } else {
326     gst_structure_free (structure);
327   }
328
329   /* now also unfix colour space format */
330   gst_caps_append (ret, gst_ffmpegscale_caps_remove_format_info (ret));
331
332   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
333
334   return ret;
335 }
336
337 static void
338 gst_ffmpegscale_fixate_caps (GstBaseTransform * trans,
339     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
340 {
341   GstStructure *ins, *outs;
342   const GValue *from_par, *to_par;
343
344   g_return_if_fail (gst_caps_is_fixed (caps));
345
346   GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
347       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
348
349   ins = gst_caps_get_structure (caps, 0);
350   outs = gst_caps_get_structure (othercaps, 0);
351
352   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
353   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
354
355   /* we have both PAR but they might not be fixated */
356   if (from_par && to_par) {
357     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
358     gint count = 0, w = 0, h = 0;
359     guint num, den;
360
361     /* from_par should be fixed */
362     g_return_if_fail (gst_value_is_fixed (from_par));
363
364     from_par_n = gst_value_get_fraction_numerator (from_par);
365     from_par_d = gst_value_get_fraction_denominator (from_par);
366
367     /* fixate the out PAR */
368     if (!gst_value_is_fixed (to_par)) {
369       GST_DEBUG_OBJECT (trans, "fixating to_par to %dx%d", from_par_n,
370           from_par_d);
371       gst_structure_fixate_field_nearest_fraction (outs, "pixel-aspect-ratio",
372           from_par_n, from_par_d);
373     }
374
375     to_par_n = gst_value_get_fraction_numerator (to_par);
376     to_par_d = gst_value_get_fraction_denominator (to_par);
377
378     /* if both width and height are already fixed, we can't do anything
379      * about it anymore */
380     if (gst_structure_get_int (outs, "width", &w))
381       ++count;
382     if (gst_structure_get_int (outs, "height", &h))
383       ++count;
384     if (count == 2) {
385       GST_DEBUG_OBJECT (trans, "dimensions already set to %dx%d, not fixating",
386           w, h);
387       return;
388     }
389
390     gst_structure_get_int (ins, "width", &from_w);
391     gst_structure_get_int (ins, "height", &from_h);
392
393     if (!gst_video_calculate_display_ratio (&num, &den, from_w, from_h,
394             from_par_n, from_par_d, to_par_n, to_par_d)) {
395       GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
396           ("Error calculating the output scaled size - integer overflow"));
397       return;
398     }
399
400     GST_DEBUG_OBJECT (trans,
401         "scaling input with %dx%d and PAR %d/%d to output PAR %d/%d",
402         from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d);
403     GST_DEBUG_OBJECT (trans, "resulting output should respect ratio of %d/%d",
404         num, den);
405
406     /* now find a width x height that respects this display ratio.
407      * prefer those that have one of w/h the same as the incoming video
408      * using wd / hd = num / den */
409
410     /* if one of the output width or height is fixed, we work from there */
411     if (h) {
412       GST_DEBUG_OBJECT (trans, "height is fixed,scaling width");
413       w = (guint) gst_util_uint64_scale_int (h, num, den);
414     } else if (w) {
415       GST_DEBUG_OBJECT (trans, "width is fixed, scaling height");
416       h = (guint) gst_util_uint64_scale_int (w, den, num);
417     } else {
418       /* none of width or height is fixed, figure out both of them based only on
419        * the input width and height */
420       /* check hd / den is an integer scale factor, and scale wd with the PAR */
421       if (from_h % den == 0) {
422         GST_DEBUG_OBJECT (trans, "keeping video height");
423         h = from_h;
424         w = (guint) gst_util_uint64_scale_int (h, num, den);
425       } else if (from_w % num == 0) {
426         GST_DEBUG_OBJECT (trans, "keeping video width");
427         w = from_w;
428         h = (guint) gst_util_uint64_scale_int (w, den, num);
429       } else {
430         GST_DEBUG_OBJECT (trans, "approximating but keeping video height");
431         h = from_h;
432         w = (guint) gst_util_uint64_scale_int (h, num, den);
433       }
434     }
435     GST_DEBUG_OBJECT (trans, "scaling to %dx%d", w, h);
436
437     /* now fixate */
438     gst_structure_fixate_field_nearest_int (outs, "width", w);
439     gst_structure_fixate_field_nearest_int (outs, "height", h);
440   } else {
441     gint width, height;
442
443     if (gst_structure_get_int (ins, "width", &width)) {
444       if (gst_structure_has_field (outs, "width")) {
445         gst_structure_fixate_field_nearest_int (outs, "width", width);
446       }
447     }
448     if (gst_structure_get_int (ins, "height", &height)) {
449       if (gst_structure_has_field (outs, "height")) {
450         gst_structure_fixate_field_nearest_int (outs, "height", height);
451       }
452     }
453   }
454
455   GST_DEBUG_OBJECT (trans, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
456 }
457
458 static gboolean
459 gst_ffmpegscale_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
460     gsize * size)
461 {
462   GstVideoInfo info;
463
464   if (!gst_video_info_from_caps (&info, caps))
465     return FALSE;
466
467   *size = info.size;
468
469   GST_DEBUG_OBJECT (trans, "unit size = %d for format %d w %d height %d",
470       *size, GST_VIDEO_INFO_FORMAT (&info), GST_VIDEO_INFO_WIDTH (&info),
471       GST_VIDEO_INFO_HEIGHT (&info));
472
473   return TRUE;
474 }
475
476 /* Convert a GstCaps (video/raw) to a FFMPEG PixFmt
477  */
478 static enum PixelFormat
479 gst_ffmpeg_caps_to_pixfmt (const GstCaps * caps)
480 {
481   GstVideoInfo info;
482   enum PixelFormat pix_fmt;
483
484   GST_DEBUG ("converting caps %" GST_PTR_FORMAT, caps);
485
486   if (gst_video_info_from_caps (&info, caps))
487     goto invalid_caps;
488
489   switch (GST_VIDEO_INFO_FORMAT (&info)) {
490     case GST_VIDEO_FORMAT_YUY2:
491       pix_fmt = PIX_FMT_YUYV422;
492       break;
493     case GST_VIDEO_FORMAT_UYVY:
494       pix_fmt = PIX_FMT_UYVY422;
495       break;
496     case GST_VIDEO_FORMAT_I420:
497       pix_fmt = PIX_FMT_YUV420P;
498       break;
499     case GST_VIDEO_FORMAT_Y41B:
500       pix_fmt = PIX_FMT_YUV411P;
501       break;
502     case GST_VIDEO_FORMAT_Y42B:
503       pix_fmt = PIX_FMT_YUV422P;
504       break;
505     case GST_VIDEO_FORMAT_YUV9:
506       pix_fmt = PIX_FMT_YUV410P;
507       break;
508     case GST_VIDEO_FORMAT_ARGB:
509       pix_fmt = PIX_FMT_ARGB;
510       break;
511     case GST_VIDEO_FORMAT_RGBA:
512       pix_fmt = PIX_FMT_RGBA;
513       break;
514     case GST_VIDEO_FORMAT_BGRA:
515       pix_fmt = PIX_FMT_BGRA;
516       break;
517     case GST_VIDEO_FORMAT_ABGR:
518       pix_fmt = PIX_FMT_ABGR;
519       break;
520     case GST_VIDEO_FORMAT_BGR:
521       pix_fmt = PIX_FMT_BGR24;
522       break;
523     case GST_VIDEO_FORMAT_RGB:
524       pix_fmt = PIX_FMT_RGB24;
525       break;
526     case GST_VIDEO_FORMAT_RGB16:
527       pix_fmt = PIX_FMT_RGB565;
528       break;
529     case GST_VIDEO_FORMAT_RGB15:
530       pix_fmt = PIX_FMT_RGB555;
531       break;
532     case GST_VIDEO_FORMAT_RGB8_PALETTED:
533       pix_fmt = PIX_FMT_PAL8;
534       break;
535     default:
536       pix_fmt = PIX_FMT_NONE;
537       break;
538   }
539   return pix_fmt;
540
541   /* ERROR */
542 invalid_caps:
543   {
544     return PIX_FMT_NONE;
545   }
546 }
547
548 static gboolean
549 gst_ffmpegscale_set_caps (GstBaseTransform * trans, GstCaps * incaps,
550     GstCaps * outcaps)
551 {
552   GstFFMpegScale *scale = GST_FFMPEGSCALE (trans);
553   guint mmx_flags, altivec_flags;
554   gint swsflags;
555   gboolean ok;
556
557   g_return_val_if_fail (scale->method <
558       G_N_ELEMENTS (gst_ffmpegscale_method_flags), FALSE);
559
560   if (scale->ctx) {
561     sws_freeContext (scale->ctx);
562     scale->ctx = NULL;
563   }
564
565   ok = gst_video_info_from_caps (&scale->in_info, incaps);
566   ok &= gst_video_info_from_caps (&scale->out_info, outcaps);
567
568   scale->in_pixfmt = gst_ffmpeg_caps_to_pixfmt (incaps);
569   scale->out_pixfmt = gst_ffmpeg_caps_to_pixfmt (outcaps);
570
571   if (!ok || scale->in_pixfmt == PIX_FMT_NONE ||
572       scale->out_pixfmt == PIX_FMT_NONE ||
573       GST_VIDEO_INFO_FORMAT (&scale->in_info) == GST_VIDEO_FORMAT_UNKNOWN ||
574       GST_VIDEO_INFO_FORMAT (&scale->out_info) == GST_VIDEO_FORMAT_UNKNOWN)
575     goto refuse_caps;
576
577   GST_DEBUG_OBJECT (scale, "format %d => %d, from=%dx%d -> to=%dx%d",
578       GST_VIDEO_INFO_FORMAT (&scale->in_info),
579       GST_VIDEO_INFO_FORMAT (&scale->out_info),
580       GST_VIDEO_INFO_WIDTH (&scale->in_info),
581       GST_VIDEO_INFO_HEIGHT (&scale->in_info),
582       GST_VIDEO_INFO_WIDTH (&scale->out_info),
583       GST_VIDEO_INFO_HEIGHT (&scale->out_info));
584
585 #ifdef HAVE_ORC
586   mmx_flags = orc_target_get_default_flags (orc_target_get_by_name ("mmx"));
587   altivec_flags =
588       orc_target_get_default_flags (orc_target_get_by_name ("altivec"));
589   swsflags = (mmx_flags & ORC_TARGET_MMX_MMX ? SWS_CPU_CAPS_MMX : 0)
590       | (mmx_flags & ORC_TARGET_MMX_MMXEXT ? SWS_CPU_CAPS_MMX2 : 0)
591       | (mmx_flags & ORC_TARGET_MMX_3DNOW ? SWS_CPU_CAPS_3DNOW : 0)
592       | (altivec_flags & ORC_TARGET_ALTIVEC_ALTIVEC ? SWS_CPU_CAPS_ALTIVEC : 0);
593 #else
594   mmx_flags = 0;
595   altivec_flags = 0;
596   swsflags = 0;
597 #endif
598
599   scale->ctx = sws_getContext (scale->in_info.width, scale->in_info.height,
600       scale->in_pixfmt, scale->out_info.width, scale->out_info.height,
601       scale->out_pixfmt, swsflags | gst_ffmpegscale_method_flags[scale->method],
602       NULL, NULL, NULL);
603   if (!scale->ctx)
604     goto setup_failed;
605
606   return TRUE;
607
608   /* ERRORS */
609 setup_failed:
610   {
611     GST_ELEMENT_ERROR (trans, LIBRARY, INIT, (NULL), (NULL));
612     return FALSE;
613   }
614 refuse_caps:
615   {
616     GST_DEBUG_OBJECT (trans, "refused caps %" GST_PTR_FORMAT, incaps);
617     return FALSE;
618   }
619 }
620
621 static GstFlowReturn
622 gst_ffmpegscale_transform (GstBaseTransform * trans, GstBuffer * inbuf,
623     GstBuffer * outbuf)
624 {
625   GstFFMpegScale *scale = GST_FFMPEGSCALE (trans);
626   GstVideoFrame in_frame, out_frame;
627
628   if (!gst_video_frame_map (&in_frame, &scale->in_info, inbuf, GST_MAP_READ))
629     goto invalid_buffer;
630
631   if (!gst_video_frame_map (&out_frame, &scale->out_info, outbuf,
632           GST_MAP_WRITE))
633     goto invalid_buffer;
634
635   sws_scale (scale->ctx, (const guint8 **) in_frame.data, in_frame.info.stride,
636       0, scale->in_info.height, (guint8 **) out_frame.data,
637       out_frame.info.stride);
638
639   gst_video_frame_unmap (&in_frame);
640   gst_video_frame_unmap (&out_frame);
641
642   return GST_FLOW_OK;
643
644   /* ERRORS */
645 invalid_buffer:
646   {
647     return GST_FLOW_OK;
648   }
649 }
650
651 static gboolean
652 gst_ffmpegscale_handle_src_event (GstPad * pad, GstEvent * event)
653 {
654   GstFFMpegScale *scale;
655   GstStructure *structure;
656   gdouble pointer;
657   gboolean res;
658
659   scale = GST_FFMPEGSCALE (gst_pad_get_parent (pad));
660
661   switch (GST_EVENT_TYPE (event)) {
662     case GST_EVENT_NAVIGATION:
663       event =
664           GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
665
666       structure = (GstStructure *) gst_event_get_structure (event);
667       if (gst_structure_get_double (structure, "pointer_x", &pointer)) {
668         gst_structure_set (structure,
669             "pointer_x", G_TYPE_DOUBLE,
670             pointer * scale->in_info.width / scale->out_info.width, NULL);
671       }
672       if (gst_structure_get_double (structure, "pointer_y", &pointer)) {
673         gst_structure_set (structure,
674             "pointer_y", G_TYPE_DOUBLE,
675             pointer * scale->in_info.height / scale->out_info.height, NULL);
676       }
677       break;
678     default:
679       break;
680   }
681
682   res = gst_pad_event_default (pad, event);
683
684   gst_object_unref (scale);
685
686   return res;
687 }
688
689 static gboolean
690 gst_ffmpegscale_stop (GstBaseTransform * trans)
691 {
692   GstFFMpegScale *scale = GST_FFMPEGSCALE (trans);
693
694   gst_ffmpegscale_reset (scale);
695
696   return TRUE;
697 }
698
699 static void
700 gst_ffmpegscale_set_property (GObject * object, guint prop_id,
701     const GValue * value, GParamSpec * pspec)
702 {
703   GstFFMpegScale *scale = GST_FFMPEGSCALE (object);
704
705   switch (prop_id) {
706     case PROP_METHOD:
707       scale->method = g_value_get_enum (value);
708       break;
709     default:
710       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
711       break;
712   }
713 }
714
715 static void
716 gst_ffmpegscale_get_property (GObject * object, guint prop_id, GValue * value,
717     GParamSpec * pspec)
718 {
719   GstFFMpegScale *scale = GST_FFMPEGSCALE (object);
720
721   switch (prop_id) {
722     case PROP_METHOD:
723       g_value_set_enum (value, scale->method);
724       break;
725     default:
726       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
727       break;
728   }
729 }
730
731 #ifndef GST_DISABLE_GST_DEBUG
732 static void
733 gst_ffmpeg_log_callback (void *ptr, int level, const char *fmt, va_list vl)
734 {
735   GstDebugLevel gst_level;
736
737   switch (level) {
738     case AV_LOG_QUIET:
739       gst_level = GST_LEVEL_NONE;
740       break;
741     case AV_LOG_ERROR:
742       gst_level = GST_LEVEL_ERROR;
743       break;
744     case AV_LOG_INFO:
745       gst_level = GST_LEVEL_INFO;
746       break;
747     case AV_LOG_DEBUG:
748       gst_level = GST_LEVEL_DEBUG;
749       break;
750     default:
751       gst_level = GST_LEVEL_INFO;
752       break;
753   }
754
755   gst_debug_log_valist (ffmpegscale_debug, gst_level, "", "", 0, NULL, fmt, vl);
756 }
757 #endif
758
759 static gboolean
760 plugin_init (GstPlugin * plugin)
761 {
762   GST_DEBUG_CATEGORY_INIT (ffmpegscale_debug, "ffvideoscale", 0,
763       "video scaling element");
764
765 #ifdef HAVE_ORC
766   orc_init ();
767 #endif
768
769 #ifndef GST_DISABLE_GST_DEBUG
770   av_log_set_callback (gst_ffmpeg_log_callback);
771 #endif
772
773   return gst_element_register (plugin, "ffvideoscale",
774       GST_RANK_NONE, GST_TYPE_FFMPEGSCALE);
775 }
776
777 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
778     GST_VERSION_MINOR,
779     "ffvideoscale",
780     "videoscaling element (" FFMPEG_SOURCE ")",
781     plugin_init,
782     PACKAGE_VERSION,
783 #ifdef GST_FFMPEG_ENABLE_LGPL
784     "LGPL",
785 #else
786     "GPL",
787 #endif
788     "FFMpeg", "http://ffmpeg.sourceforge.net/")