v4l2: rely on gst_v4l2_dup() to set no_initial_format and keep_aspect
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / gstv4l2transform.c
1 /*
2  * Copyright (C) 2014 Collabora Ltd.
3  *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <string.h>
31
32 #include "gstv4l2object.h"
33 #include "gstv4l2transform.h"
34
35 #include <string.h>
36 #include <gst/gst-i18n-plugin.h>
37
38 #define DEFAULT_PROP_DEVICE "/dev/video10"
39
40 #define V4L2_TRANSFORM_QUARK \
41         g_quark_from_static_string("gst-v4l2-transform-info")
42
43 GST_DEBUG_CATEGORY_STATIC (gst_v4l2_transform_debug);
44 #define GST_CAT_DEFAULT gst_v4l2_transform_debug
45
46
47 enum
48 {
49   PROP_0,
50   V4L2_STD_OBJECT_PROPS
51 };
52
53 typedef struct
54 {
55   gchar *device;
56   GstCaps *sink_caps;
57   GstCaps *src_caps;
58 } GstV4l2TransformCData;
59
60 #define gst_v4l2_transform_parent_class parent_class
61 G_DEFINE_ABSTRACT_TYPE (GstV4l2Transform, gst_v4l2_transform,
62     GST_TYPE_BASE_TRANSFORM);
63
64 static void
65 gst_v4l2_transform_set_property (GObject * object,
66     guint prop_id, const GValue * value, GParamSpec * pspec)
67 {
68   GstV4l2Transform *self = GST_V4L2_TRANSFORM (object);
69
70   switch (prop_id) {
71     case PROP_OUTPUT_IO_MODE:
72       gst_v4l2_object_set_property_helper (self->v4l2output, prop_id, value,
73           pspec);
74       break;
75     case PROP_CAPTURE_IO_MODE:
76       gst_v4l2_object_set_property_helper (self->v4l2capture, prop_id, value,
77           pspec);
78       break;
79
80       /* By default, only set on output */
81     default:
82       if (!gst_v4l2_object_set_property_helper (self->v4l2output,
83               prop_id, value, pspec)) {
84         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
85       }
86       break;
87   }
88 }
89
90 static void
91 gst_v4l2_transform_get_property (GObject * object,
92     guint prop_id, GValue * value, GParamSpec * pspec)
93 {
94   GstV4l2Transform *self = GST_V4L2_TRANSFORM (object);
95
96   switch (prop_id) {
97     case PROP_OUTPUT_IO_MODE:
98       gst_v4l2_object_get_property_helper (self->v4l2output, prop_id, value,
99           pspec);
100       break;
101     case PROP_CAPTURE_IO_MODE:
102       gst_v4l2_object_get_property_helper (self->v4l2capture, prop_id, value,
103           pspec);
104       break;
105
106       /* By default read from output */
107     default:
108       if (!gst_v4l2_object_get_property_helper (self->v4l2output,
109               prop_id, value, pspec)) {
110         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111       }
112       break;
113   }
114 }
115
116 static gboolean
117 gst_v4l2_transform_open (GstV4l2Transform * self)
118 {
119   GST_DEBUG_OBJECT (self, "Opening");
120
121   if (!gst_v4l2_object_open (self->v4l2output))
122     goto failure;
123
124   if (!gst_v4l2_object_open_shared (self->v4l2capture, self->v4l2output))
125     goto failure;
126
127   self->probed_sinkcaps = gst_v4l2_object_get_caps (self->v4l2output,
128       gst_v4l2_object_get_raw_caps ());
129
130   if (gst_caps_is_empty (self->probed_sinkcaps))
131     goto no_input_format;
132
133   self->probed_srccaps = gst_v4l2_object_get_caps (self->v4l2capture,
134       gst_v4l2_object_get_raw_caps ());
135
136   if (gst_caps_is_empty (self->probed_srccaps))
137     goto no_output_format;
138
139   return TRUE;
140
141 no_input_format:
142   GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
143       (_("Converter on device %s has no supported input format"),
144           self->v4l2output->videodev), (NULL));
145   goto failure;
146
147
148 no_output_format:
149   GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
150       (_("Converter on device %s has no supported output format"),
151           self->v4l2output->videodev), (NULL));
152   goto failure;
153
154 failure:
155   if (GST_V4L2_IS_OPEN (self->v4l2output))
156     gst_v4l2_object_close (self->v4l2output);
157
158   if (GST_V4L2_IS_OPEN (self->v4l2capture))
159     gst_v4l2_object_close (self->v4l2capture);
160
161   gst_caps_replace (&self->probed_srccaps, NULL);
162   gst_caps_replace (&self->probed_sinkcaps, NULL);
163
164   return FALSE;
165 }
166
167 static void
168 gst_v4l2_transform_close (GstV4l2Transform * self)
169 {
170   GST_DEBUG_OBJECT (self, "Closing");
171
172   gst_v4l2_object_close (self->v4l2output);
173   gst_v4l2_object_close (self->v4l2capture);
174
175   gst_caps_replace (&self->probed_srccaps, NULL);
176   gst_caps_replace (&self->probed_sinkcaps, NULL);
177 }
178
179 static gboolean
180 gst_v4l2_transform_stop (GstBaseTransform * trans)
181 {
182   GstV4l2Transform *self = GST_V4L2_TRANSFORM (trans);
183
184   GST_DEBUG_OBJECT (self, "Stop");
185
186   gst_v4l2_object_stop (self->v4l2output);
187   gst_v4l2_object_stop (self->v4l2capture);
188   gst_caps_replace (&self->incaps, NULL);
189   gst_caps_replace (&self->outcaps, NULL);
190
191   return TRUE;
192 }
193
194 static gboolean
195 gst_v4l2_transform_set_caps (GstBaseTransform * trans, GstCaps * incaps,
196     GstCaps * outcaps)
197 {
198   GstV4l2Error error = GST_V4L2_ERROR_INIT;
199   GstV4l2Transform *self = GST_V4L2_TRANSFORM (trans);
200
201   if (self->incaps && self->outcaps) {
202     if (gst_caps_is_equal (incaps, self->incaps) &&
203         gst_caps_is_equal (outcaps, self->outcaps)) {
204       GST_DEBUG_OBJECT (trans, "Caps did not changed");
205       return TRUE;
206     }
207   }
208
209   /* TODO Add renegotiation support */
210   g_return_val_if_fail (!GST_V4L2_IS_ACTIVE (self->v4l2output), FALSE);
211   g_return_val_if_fail (!GST_V4L2_IS_ACTIVE (self->v4l2capture), FALSE);
212
213   gst_caps_replace (&self->incaps, incaps);
214   gst_caps_replace (&self->outcaps, outcaps);
215
216   if (!gst_v4l2_object_set_format (self->v4l2output, incaps, &error))
217     goto incaps_failed;
218
219   if (!gst_v4l2_object_set_format (self->v4l2capture, outcaps, &error))
220     goto outcaps_failed;
221
222   /* FIXME implement fallback if crop not supported */
223   if (!gst_v4l2_object_set_crop (self->v4l2output))
224     goto failed;
225
226   if (!gst_v4l2_object_set_crop (self->v4l2capture))
227     goto failed;
228
229   return TRUE;
230
231 incaps_failed:
232   {
233     GST_ERROR_OBJECT (self, "failed to set input caps: %" GST_PTR_FORMAT,
234         incaps);
235     gst_v4l2_error (self, &error);
236     goto failed;
237   }
238 outcaps_failed:
239   {
240     gst_v4l2_object_stop (self->v4l2output);
241     GST_ERROR_OBJECT (self, "failed to set output caps: %" GST_PTR_FORMAT,
242         outcaps);
243     gst_v4l2_error (self, &error);
244     goto failed;
245   }
246 failed:
247   return FALSE;
248 }
249
250 static gboolean
251 gst_v4l2_transform_query (GstBaseTransform * trans, GstPadDirection direction,
252     GstQuery * query)
253 {
254   GstV4l2Transform *self = GST_V4L2_TRANSFORM (trans);
255   gboolean ret = TRUE;
256
257   switch (GST_QUERY_TYPE (query)) {
258     case GST_QUERY_CAPS:{
259       GstCaps *filter, *caps = NULL, *result = NULL;
260       GstPad *pad, *otherpad;
261
262       gst_query_parse_caps (query, &filter);
263
264       if (direction == GST_PAD_SRC) {
265         pad = GST_BASE_TRANSFORM_SRC_PAD (trans);
266         otherpad = GST_BASE_TRANSFORM_SINK_PAD (trans);
267         if (self->probed_srccaps)
268           caps = gst_caps_ref (self->probed_srccaps);
269       } else {
270         pad = GST_BASE_TRANSFORM_SINK_PAD (trans);
271         otherpad = GST_BASE_TRANSFORM_SRC_PAD (trans);
272         if (self->probed_sinkcaps)
273           caps = gst_caps_ref (self->probed_sinkcaps);
274       }
275
276       if (!caps)
277         caps = gst_pad_get_pad_template_caps (pad);
278
279       if (filter) {
280         GstCaps *tmp = caps;
281         caps = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
282         gst_caps_unref (tmp);
283       }
284
285       result = gst_pad_peer_query_caps (otherpad, caps);
286       result = gst_caps_make_writable (result);
287       gst_caps_append (result, caps);
288
289       GST_DEBUG_OBJECT (self, "Returning %s caps %" GST_PTR_FORMAT,
290           GST_PAD_NAME (pad), result);
291
292       gst_query_set_caps_result (query, result);
293       gst_caps_unref (result);
294       break;
295     }
296
297     default:
298       ret = GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
299           query);
300       break;
301   }
302
303   return ret;
304 }
305
306 static gboolean
307 gst_v4l2_transform_decide_allocation (GstBaseTransform * trans,
308     GstQuery * query)
309 {
310   GstV4l2Transform *self = GST_V4L2_TRANSFORM (trans);
311   gboolean ret = FALSE;
312
313   GST_DEBUG_OBJECT (self, "called");
314
315   if (gst_v4l2_object_decide_allocation (self->v4l2capture, query)) {
316     GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2capture->pool);
317
318     ret = GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
319         query);
320
321     if (!gst_buffer_pool_set_active (pool, TRUE))
322       goto activate_failed;
323   }
324
325   return ret;
326
327 activate_failed:
328   GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
329       ("failed to activate bufferpool"), ("failed to activate bufferpool"));
330   return TRUE;
331 }
332
333 static gboolean
334 gst_v4l2_transform_propose_allocation (GstBaseTransform * trans,
335     GstQuery * decide_query, GstQuery * query)
336 {
337   GstV4l2Transform *self = GST_V4L2_TRANSFORM (trans);
338   gboolean ret = FALSE;
339
340   GST_DEBUG_OBJECT (self, "called");
341
342   if (decide_query == NULL)
343     ret = TRUE;
344   else
345     ret = gst_v4l2_object_propose_allocation (self->v4l2output, query);
346
347   if (ret)
348     ret = GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
349         decide_query, query);
350
351   return ret;
352 }
353
354 /* copies the given caps */
355 static GstCaps *
356 gst_v4l2_transform_caps_remove_format_info (GstCaps * caps)
357 {
358   GstStructure *st;
359   GstCapsFeatures *f;
360   gint i, n;
361   GstCaps *res;
362
363   res = gst_caps_new_empty ();
364
365   n = gst_caps_get_size (caps);
366   for (i = 0; i < n; i++) {
367     st = gst_caps_get_structure (caps, i);
368     f = gst_caps_get_features (caps, i);
369
370     /* If this is already expressed by the existing caps
371      * skip this structure */
372     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
373       continue;
374
375     st = gst_structure_copy (st);
376     /* Only remove format info for the cases when we can actually convert */
377     if (!gst_caps_features_is_any (f)
378         && gst_caps_features_is_equal (f,
379             GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))
380       gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
381           "width", "height", "pixel-aspect-ratio", NULL);
382
383     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
384   }
385
386   return res;
387 }
388
389 /* The caps can be transformed into any other caps with format info removed.
390  * However, we should prefer passthrough, so if passthrough is possible,
391  * put it first in the list. */
392 static GstCaps *
393 gst_v4l2_transform_transform_caps (GstBaseTransform * btrans,
394     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
395 {
396   GstCaps *tmp, *tmp2;
397   GstCaps *result;
398
399   /* Get all possible caps that we can transform to */
400   tmp = gst_v4l2_transform_caps_remove_format_info (caps);
401
402   if (filter) {
403     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
404     gst_caps_unref (tmp);
405     tmp = tmp2;
406   }
407
408   result = tmp;
409
410   GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
411       GST_PTR_FORMAT, caps, result);
412
413   return result;
414 }
415
416 static GstCaps *
417 gst_v4l2_transform_fixate_caps (GstBaseTransform * trans,
418     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
419 {
420   GstStructure *ins, *outs;
421   const GValue *from_par, *to_par;
422   GValue fpar = { 0, }, tpar = {
423   0,};
424
425   othercaps = gst_caps_truncate (othercaps);
426   othercaps = gst_caps_make_writable (othercaps);
427
428   GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
429       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
430
431   ins = gst_caps_get_structure (caps, 0);
432   outs = gst_caps_get_structure (othercaps, 0);
433
434   {
435     const gchar *in_format;
436
437     in_format = gst_structure_get_string (ins, "format");
438     if (in_format) {
439       /* Try to set output format for pass through */
440       gst_structure_fixate_field_string (outs, "format", in_format);
441     }
442
443   }
444
445   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
446   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
447
448   /* If we're fixating from the sinkpad we always set the PAR and
449    * assume that missing PAR on the sinkpad means 1/1 and
450    * missing PAR on the srcpad means undefined
451    */
452   if (direction == GST_PAD_SINK) {
453     if (!from_par) {
454       g_value_init (&fpar, GST_TYPE_FRACTION);
455       gst_value_set_fraction (&fpar, 1, 1);
456       from_par = &fpar;
457     }
458     if (!to_par) {
459       g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
460       gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
461       to_par = &tpar;
462     }
463   } else {
464     if (!to_par) {
465       g_value_init (&tpar, GST_TYPE_FRACTION);
466       gst_value_set_fraction (&tpar, 1, 1);
467       to_par = &tpar;
468
469       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
470           NULL);
471     }
472     if (!from_par) {
473       g_value_init (&fpar, GST_TYPE_FRACTION);
474       gst_value_set_fraction (&fpar, 1, 1);
475       from_par = &fpar;
476     }
477   }
478
479   /* we have both PAR but they might not be fixated */
480   {
481     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
482     gint w = 0, h = 0;
483     gint from_dar_n, from_dar_d;
484     gint num, den;
485
486     /* from_par should be fixed */
487     g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
488
489     from_par_n = gst_value_get_fraction_numerator (from_par);
490     from_par_d = gst_value_get_fraction_denominator (from_par);
491
492     gst_structure_get_int (ins, "width", &from_w);
493     gst_structure_get_int (ins, "height", &from_h);
494
495     gst_structure_get_int (outs, "width", &w);
496     gst_structure_get_int (outs, "height", &h);
497
498     /* if both width and height are already fixed, we can't do anything
499      * about it anymore */
500     if (w && h) {
501       guint n, d;
502
503       GST_DEBUG_OBJECT (trans, "dimensions already set to %dx%d, not fixating",
504           w, h);
505       if (!gst_value_is_fixed (to_par)) {
506         if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
507                 from_par_n, from_par_d, w, h)) {
508           GST_DEBUG_OBJECT (trans, "fixating to_par to %dx%d", n, d);
509           if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
510             gst_structure_fixate_field_nearest_fraction (outs,
511                 "pixel-aspect-ratio", n, d);
512           else if (n != d)
513             gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
514                 n, d, NULL);
515         }
516       }
517       goto done;
518     }
519
520     /* Calculate input DAR */
521     if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
522             &from_dar_n, &from_dar_d)) {
523       GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
524           ("Error calculating the output scaled size - integer overflow"));
525       goto done;
526     }
527
528     GST_DEBUG_OBJECT (trans, "Input DAR is %d/%d", from_dar_n, from_dar_d);
529
530     /* If either width or height are fixed there's not much we
531      * can do either except choosing a height or width and PAR
532      * that matches the DAR as good as possible
533      */
534     if (h) {
535       GstStructure *tmp;
536       gint set_w, set_par_n, set_par_d;
537
538       GST_DEBUG_OBJECT (trans, "height is fixed (%d)", h);
539
540       /* If the PAR is fixed too, there's not much to do
541        * except choosing the width that is nearest to the
542        * width with the same DAR */
543       if (gst_value_is_fixed (to_par)) {
544         to_par_n = gst_value_get_fraction_numerator (to_par);
545         to_par_d = gst_value_get_fraction_denominator (to_par);
546
547         GST_DEBUG_OBJECT (trans, "PAR is fixed %d/%d", to_par_n, to_par_d);
548
549         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
550                 to_par_n, &num, &den)) {
551           GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
552               ("Error calculating the output scaled size - integer overflow"));
553           goto done;
554         }
555
556         w = (guint) gst_util_uint64_scale_int (h, num, den);
557         gst_structure_fixate_field_nearest_int (outs, "width", w);
558
559         goto done;
560       }
561
562       /* The PAR is not fixed and it's quite likely that we can set
563        * an arbitrary PAR. */
564
565       /* Check if we can keep the input width */
566       tmp = gst_structure_copy (outs);
567       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
568       gst_structure_get_int (tmp, "width", &set_w);
569
570       /* Might have failed but try to keep the DAR nonetheless by
571        * adjusting the PAR */
572       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
573               &to_par_n, &to_par_d)) {
574         GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
575             ("Error calculating the output scaled size - integer overflow"));
576         gst_structure_free (tmp);
577         goto done;
578       }
579
580       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
581         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
582       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
583           to_par_n, to_par_d);
584       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
585           &set_par_d);
586       gst_structure_free (tmp);
587
588       /* Check if the adjusted PAR is accepted */
589       if (set_par_n == to_par_n && set_par_d == to_par_d) {
590         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
591             set_par_n != set_par_d)
592           gst_structure_set (outs, "width", G_TYPE_INT, set_w,
593               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
594               NULL);
595         goto done;
596       }
597
598       /* Otherwise scale the width to the new PAR and check if the
599        * adjusted with is accepted. If all that fails we can't keep
600        * the DAR */
601       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
602               set_par_n, &num, &den)) {
603         GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
604             ("Error calculating the output scaled size - integer overflow"));
605         goto done;
606       }
607
608       w = (guint) gst_util_uint64_scale_int (h, num, den);
609       gst_structure_fixate_field_nearest_int (outs, "width", w);
610       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
611           set_par_n != set_par_d)
612         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
613             set_par_n, set_par_d, NULL);
614
615       goto done;
616     } else if (w) {
617       GstStructure *tmp;
618       gint set_h, set_par_n, set_par_d;
619
620       GST_DEBUG_OBJECT (trans, "width is fixed (%d)", w);
621
622       /* If the PAR is fixed too, there's not much to do
623        * except choosing the height that is nearest to the
624        * height with the same DAR */
625       if (gst_value_is_fixed (to_par)) {
626         to_par_n = gst_value_get_fraction_numerator (to_par);
627         to_par_d = gst_value_get_fraction_denominator (to_par);
628
629         GST_DEBUG_OBJECT (trans, "PAR is fixed %d/%d", to_par_n, to_par_d);
630
631         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
632                 to_par_n, &num, &den)) {
633           GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
634               ("Error calculating the output scaled size - integer overflow"));
635           goto done;
636         }
637
638         h = (guint) gst_util_uint64_scale_int (w, den, num);
639         gst_structure_fixate_field_nearest_int (outs, "height", h);
640
641         goto done;
642       }
643
644       /* The PAR is not fixed and it's quite likely that we can set
645        * an arbitrary PAR. */
646
647       /* Check if we can keep the input height */
648       tmp = gst_structure_copy (outs);
649       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
650       gst_structure_get_int (tmp, "height", &set_h);
651
652       /* Might have failed but try to keep the DAR nonetheless by
653        * adjusting the PAR */
654       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
655               &to_par_n, &to_par_d)) {
656         GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
657             ("Error calculating the output scaled size - integer overflow"));
658         gst_structure_free (tmp);
659         goto done;
660       }
661       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
662         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
663       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
664           to_par_n, to_par_d);
665       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
666           &set_par_d);
667       gst_structure_free (tmp);
668
669       /* Check if the adjusted PAR is accepted */
670       if (set_par_n == to_par_n && set_par_d == to_par_d) {
671         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
672             set_par_n != set_par_d)
673           gst_structure_set (outs, "height", G_TYPE_INT, set_h,
674               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
675               NULL);
676         goto done;
677       }
678
679       /* Otherwise scale the height to the new PAR and check if the
680        * adjusted with is accepted. If all that fails we can't keep
681        * the DAR */
682       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
683               set_par_n, &num, &den)) {
684         GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
685             ("Error calculating the output scaled size - integer overflow"));
686         goto done;
687       }
688
689       h = (guint) gst_util_uint64_scale_int (w, den, num);
690       gst_structure_fixate_field_nearest_int (outs, "height", h);
691       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
692           set_par_n != set_par_d)
693         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
694             set_par_n, set_par_d, NULL);
695
696       goto done;
697     } else if (gst_value_is_fixed (to_par)) {
698       GstStructure *tmp;
699       gint set_h, set_w, f_h, f_w;
700
701       to_par_n = gst_value_get_fraction_numerator (to_par);
702       to_par_d = gst_value_get_fraction_denominator (to_par);
703
704       GST_DEBUG_OBJECT (trans, "PAR is fixed %d/%d", to_par_n, to_par_d);
705
706       /* Calculate scale factor for the PAR change */
707       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
708               to_par_n, &num, &den)) {
709         GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
710             ("Error calculating the output scaled size - integer overflow"));
711         goto done;
712       }
713
714       /* Try to keep the input height (because of interlacing) */
715       tmp = gst_structure_copy (outs);
716       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
717       gst_structure_get_int (tmp, "height", &set_h);
718
719       /* This might have failed but try to scale the width
720        * to keep the DAR nonetheless */
721       w = (guint) gst_util_uint64_scale_int (set_h, num, den);
722       gst_structure_fixate_field_nearest_int (tmp, "width", w);
723       gst_structure_get_int (tmp, "width", &set_w);
724       gst_structure_free (tmp);
725
726       /* We kept the DAR and the height is nearest to the original height */
727       if (set_w == w) {
728         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
729             G_TYPE_INT, set_h, NULL);
730         goto done;
731       }
732
733       f_h = set_h;
734       f_w = set_w;
735
736       /* If the former failed, try to keep the input width at least */
737       tmp = gst_structure_copy (outs);
738       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
739       gst_structure_get_int (tmp, "width", &set_w);
740
741       /* This might have failed but try to scale the width
742        * to keep the DAR nonetheless */
743       h = (guint) gst_util_uint64_scale_int (set_w, den, num);
744       gst_structure_fixate_field_nearest_int (tmp, "height", h);
745       gst_structure_get_int (tmp, "height", &set_h);
746       gst_structure_free (tmp);
747
748       /* We kept the DAR and the width is nearest to the original width */
749       if (set_h == h) {
750         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
751             G_TYPE_INT, set_h, NULL);
752         goto done;
753       }
754
755       /* If all this failed, keep the height that was nearest to the orignal
756        * height and the nearest possible width. This changes the DAR but
757        * there's not much else to do here.
758        */
759       gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
760           f_h, NULL);
761       goto done;
762     } else {
763       GstStructure *tmp;
764       gint set_h, set_w, set_par_n, set_par_d, tmp2;
765
766       /* width, height and PAR are not fixed but passthrough is not possible */
767
768       /* First try to keep the height and width as good as possible
769        * and scale PAR */
770       tmp = gst_structure_copy (outs);
771       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
772       gst_structure_get_int (tmp, "height", &set_h);
773       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
774       gst_structure_get_int (tmp, "width", &set_w);
775
776       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
777               &to_par_n, &to_par_d)) {
778         GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
779             ("Error calculating the output scaled size - integer overflow"));
780         gst_structure_free (tmp);
781         goto done;
782       }
783
784       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
785         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
786       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
787           to_par_n, to_par_d);
788       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
789           &set_par_d);
790       gst_structure_free (tmp);
791
792       if (set_par_n == to_par_n && set_par_d == to_par_d) {
793         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
794             G_TYPE_INT, set_h, NULL);
795
796         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
797             set_par_n != set_par_d)
798           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
799               set_par_n, set_par_d, NULL);
800         goto done;
801       }
802
803       /* Otherwise try to scale width to keep the DAR with the set
804        * PAR and height */
805       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
806               set_par_n, &num, &den)) {
807         GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
808             ("Error calculating the output scaled size - integer overflow"));
809         goto done;
810       }
811
812       w = (guint) gst_util_uint64_scale_int (set_h, num, den);
813       tmp = gst_structure_copy (outs);
814       gst_structure_fixate_field_nearest_int (tmp, "width", w);
815       gst_structure_get_int (tmp, "width", &tmp2);
816       gst_structure_free (tmp);
817
818       if (tmp2 == w) {
819         gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
820             G_TYPE_INT, set_h, NULL);
821         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
822             set_par_n != set_par_d)
823           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
824               set_par_n, set_par_d, NULL);
825         goto done;
826       }
827
828       /* ... or try the same with the height */
829       h = (guint) gst_util_uint64_scale_int (set_w, den, num);
830       tmp = gst_structure_copy (outs);
831       gst_structure_fixate_field_nearest_int (tmp, "height", h);
832       gst_structure_get_int (tmp, "height", &tmp2);
833       gst_structure_free (tmp);
834
835       if (tmp2 == h) {
836         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
837             G_TYPE_INT, tmp2, NULL);
838         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
839             set_par_n != set_par_d)
840           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
841               set_par_n, set_par_d, NULL);
842         goto done;
843       }
844
845       /* If all fails we can't keep the DAR and take the nearest values
846        * for everything from the first try */
847       gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
848           G_TYPE_INT, set_h, NULL);
849       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
850           set_par_n != set_par_d)
851         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
852             set_par_n, set_par_d, NULL);
853     }
854   }
855
856 done:
857   GST_DEBUG_OBJECT (trans, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
858
859   if (from_par == &fpar)
860     g_value_unset (&fpar);
861   if (to_par == &tpar)
862     g_value_unset (&tpar);
863
864   /* fixate remaining fields */
865   othercaps = gst_caps_fixate (othercaps);
866
867   if (direction == GST_PAD_SINK) {
868     if (gst_caps_is_subset (caps, othercaps)) {
869       gst_caps_replace (&othercaps, caps);
870     }
871   }
872
873   return othercaps;
874 }
875
876 static GstFlowReturn
877 gst_v4l2_transform_prepare_output_buffer (GstBaseTransform * trans,
878     GstBuffer * inbuf, GstBuffer ** outbuf)
879 {
880   GstV4l2Transform *self = GST_V4L2_TRANSFORM (trans);
881   GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool);
882   GstFlowReturn ret = GST_FLOW_OK;
883   GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_CLASS (parent_class);
884
885   if (gst_base_transform_is_passthrough (trans)) {
886     GST_DEBUG_OBJECT (self, "Passthrough, no need to do anything");
887     *outbuf = inbuf;
888     goto beach;
889   }
890
891   /* Ensure input internal pool is active */
892   if (!gst_buffer_pool_is_active (pool)) {
893     GstStructure *config = gst_buffer_pool_get_config (pool);
894     gint min = self->v4l2output->min_buffers == 0 ? GST_V4L2_MIN_BUFFERS :
895         self->v4l2output->min_buffers;
896     gst_buffer_pool_config_set_params (config, self->incaps,
897         self->v4l2output->info.size, min, min);
898
899     /* There is no reason to refuse this config */
900     if (!gst_buffer_pool_set_config (pool, config))
901       goto activate_failed;
902
903     if (!gst_buffer_pool_set_active (pool, TRUE))
904       goto activate_failed;
905   }
906
907   GST_DEBUG_OBJECT (self, "Queue input buffer");
908   ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (pool), &inbuf);
909   if (G_UNLIKELY (ret != GST_FLOW_OK))
910     goto beach;
911
912   do {
913     pool = gst_base_transform_get_buffer_pool (trans);
914
915     if (!gst_buffer_pool_set_active (pool, TRUE))
916       goto activate_failed;
917
918     GST_DEBUG_OBJECT (self, "Dequeue output buffer");
919     ret = gst_buffer_pool_acquire_buffer (pool, outbuf, NULL);
920     g_object_unref (pool);
921
922     if (ret != GST_FLOW_OK)
923       goto alloc_failed;
924
925     pool = self->v4l2capture->pool;
926     ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (pool), outbuf);
927
928   } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER);
929
930   if (ret != GST_FLOW_OK) {
931     gst_buffer_unref (*outbuf);
932     *outbuf = NULL;
933   }
934
935   if (bclass->copy_metadata)
936     if (!bclass->copy_metadata (trans, inbuf, *outbuf)) {
937       /* something failed, post a warning */
938       GST_ELEMENT_WARNING (self, STREAM, NOT_IMPLEMENTED,
939           ("could not copy metadata"), (NULL));
940     }
941
942 beach:
943   return ret;
944
945 activate_failed:
946   GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
947       ("failed to activate bufferpool"), ("failed to activate bufferpool"));
948   g_object_unref (pool);
949   return GST_FLOW_ERROR;
950
951 alloc_failed:
952   GST_DEBUG_OBJECT (self, "could not allocate buffer from pool");
953   return ret;
954 }
955
956 static GstFlowReturn
957 gst_v4l2_transform_transform (GstBaseTransform * trans, GstBuffer * inbuf,
958     GstBuffer * outbuf)
959 {
960   /* Nothing to do */
961   return GST_FLOW_OK;
962 }
963
964 static gboolean
965 gst_v4l2_transform_sink_event (GstBaseTransform * trans, GstEvent * event)
966 {
967   GstV4l2Transform *self = GST_V4L2_TRANSFORM (trans);
968   gboolean ret;
969
970   /* Nothing to flush in passthrough */
971   if (gst_base_transform_is_passthrough (trans))
972     return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
973
974   switch (GST_EVENT_TYPE (event)) {
975     case GST_EVENT_FLUSH_START:
976       GST_DEBUG_OBJECT (self, "flush start");
977       gst_v4l2_object_unlock (self->v4l2output);
978       gst_v4l2_object_unlock (self->v4l2capture);
979       break;
980     default:
981       break;
982   }
983
984   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
985
986   switch (GST_EVENT_TYPE (event)) {
987     case GST_EVENT_FLUSH_STOP:
988       /* Buffer should be back now */
989       GST_DEBUG_OBJECT (self, "flush stop");
990       gst_v4l2_object_unlock_stop (self->v4l2capture);
991       gst_v4l2_object_unlock_stop (self->v4l2output);
992       gst_v4l2_buffer_pool_flush (self->v4l2output->pool);
993       gst_v4l2_buffer_pool_flush (self->v4l2capture->pool);
994       break;
995     default:
996       break;
997   }
998
999   return ret;
1000 }
1001
1002 static GstStateChangeReturn
1003 gst_v4l2_transform_change_state (GstElement * element,
1004     GstStateChange transition)
1005 {
1006   GstV4l2Transform *self = GST_V4L2_TRANSFORM (element);
1007   GstStateChangeReturn ret;
1008
1009   switch (transition) {
1010     case GST_STATE_CHANGE_NULL_TO_READY:
1011       if (!gst_v4l2_transform_open (self))
1012         return GST_STATE_CHANGE_FAILURE;
1013       break;
1014     case GST_STATE_CHANGE_PAUSED_TO_READY:
1015       gst_v4l2_object_unlock (self->v4l2output);
1016       gst_v4l2_object_unlock (self->v4l2capture);
1017       break;
1018     default:
1019       break;
1020   }
1021
1022   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1023
1024   switch (transition) {
1025     case GST_STATE_CHANGE_READY_TO_NULL:
1026       gst_v4l2_transform_close (self);
1027       break;
1028     default:
1029       break;
1030   }
1031
1032   return ret;
1033 }
1034
1035 static void
1036 gst_v4l2_transform_dispose (GObject * object)
1037 {
1038   GstV4l2Transform *self = GST_V4L2_TRANSFORM (object);
1039
1040   gst_caps_replace (&self->probed_sinkcaps, NULL);
1041   gst_caps_replace (&self->probed_srccaps, NULL);
1042
1043   G_OBJECT_CLASS (parent_class)->dispose (object);
1044 }
1045
1046 static void
1047 gst_v4l2_transform_finalize (GObject * object)
1048 {
1049   GstV4l2Transform *self = GST_V4L2_TRANSFORM (object);
1050
1051   gst_v4l2_object_destroy (self->v4l2capture);
1052   gst_v4l2_object_destroy (self->v4l2output);
1053
1054   G_OBJECT_CLASS (parent_class)->finalize (object);
1055 }
1056
1057 static void
1058 gst_v4l2_transform_init (GstV4l2Transform * self)
1059 {
1060   /* V4L2 object are created in subinstance_init */
1061   /* enable QoS */
1062   gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (self), TRUE);
1063 }
1064
1065 static void
1066 gst_v4l2_transform_subinstance_init (GTypeInstance * instance, gpointer g_class)
1067 {
1068   GstV4l2TransformClass *klass = GST_V4L2_TRANSFORM_CLASS (g_class);
1069   GstV4l2Transform *self = GST_V4L2_TRANSFORM (instance);
1070
1071   self->v4l2output = gst_v4l2_object_new (GST_ELEMENT (self),
1072       GST_OBJECT (GST_BASE_TRANSFORM_SINK_PAD (self)),
1073       V4L2_BUF_TYPE_VIDEO_OUTPUT, klass->default_device,
1074       gst_v4l2_get_output, gst_v4l2_set_output, NULL);
1075   self->v4l2output->no_initial_format = TRUE;
1076   self->v4l2output->keep_aspect = FALSE;
1077
1078   self->v4l2capture = gst_v4l2_object_new (GST_ELEMENT (self),
1079       GST_OBJECT (GST_BASE_TRANSFORM_SRC_PAD (self)),
1080       V4L2_BUF_TYPE_VIDEO_CAPTURE, klass->default_device,
1081       gst_v4l2_get_input, gst_v4l2_set_input, NULL);
1082 }
1083
1084 static void
1085 gst_v4l2_transform_class_init (GstV4l2TransformClass * klass)
1086 {
1087   GstElementClass *element_class;
1088   GObjectClass *gobject_class;
1089   GstBaseTransformClass *base_transform_class;
1090
1091   element_class = (GstElementClass *) klass;
1092   gobject_class = (GObjectClass *) klass;
1093   base_transform_class = (GstBaseTransformClass *) klass;
1094
1095   GST_DEBUG_CATEGORY_INIT (gst_v4l2_transform_debug, "v4l2transform", 0,
1096       "V4L2 Converter");
1097
1098   gst_element_class_set_static_metadata (element_class,
1099       "V4L2 Video Converter",
1100       "Filter/Converter/Video/Scaler",
1101       "Transform streams via V4L2 API",
1102       "Nicolas Dufresne <nicolas.dufresne@collabora.com>");
1103
1104   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_v4l2_transform_dispose);
1105   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_v4l2_transform_finalize);
1106   gobject_class->set_property =
1107       GST_DEBUG_FUNCPTR (gst_v4l2_transform_set_property);
1108   gobject_class->get_property =
1109       GST_DEBUG_FUNCPTR (gst_v4l2_transform_get_property);
1110
1111   base_transform_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_transform_stop);
1112   base_transform_class->set_caps =
1113       GST_DEBUG_FUNCPTR (gst_v4l2_transform_set_caps);
1114   base_transform_class->query = GST_DEBUG_FUNCPTR (gst_v4l2_transform_query);
1115   base_transform_class->sink_event =
1116       GST_DEBUG_FUNCPTR (gst_v4l2_transform_sink_event);
1117   base_transform_class->decide_allocation =
1118       GST_DEBUG_FUNCPTR (gst_v4l2_transform_decide_allocation);
1119   base_transform_class->propose_allocation =
1120       GST_DEBUG_FUNCPTR (gst_v4l2_transform_propose_allocation);
1121   base_transform_class->transform_caps =
1122       GST_DEBUG_FUNCPTR (gst_v4l2_transform_transform_caps);
1123   base_transform_class->fixate_caps =
1124       GST_DEBUG_FUNCPTR (gst_v4l2_transform_fixate_caps);
1125   base_transform_class->prepare_output_buffer =
1126       GST_DEBUG_FUNCPTR (gst_v4l2_transform_prepare_output_buffer);
1127   base_transform_class->transform =
1128       GST_DEBUG_FUNCPTR (gst_v4l2_transform_transform);
1129
1130   base_transform_class->passthrough_on_same_caps = TRUE;
1131
1132   element_class->change_state =
1133       GST_DEBUG_FUNCPTR (gst_v4l2_transform_change_state);
1134
1135   gst_v4l2_object_install_m2m_properties_helper (gobject_class);
1136 }
1137
1138 static void
1139 gst_v4l2_transform_subclass_init (gpointer g_class, gpointer data)
1140 {
1141   GstV4l2TransformClass *klass = GST_V4L2_TRANSFORM_CLASS (g_class);
1142   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1143   GstV4l2TransformCData *cdata = data;
1144
1145   klass->default_device = cdata->device;
1146
1147   /* Note: gst_pad_template_new() take the floating ref from the caps */
1148   gst_element_class_add_pad_template (element_class,
1149       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
1150           cdata->sink_caps));
1151   gst_element_class_add_pad_template (element_class,
1152       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
1153           cdata->src_caps));
1154
1155   g_free (cdata);
1156 }
1157
1158 /* Probing functions */
1159 gboolean
1160 gst_v4l2_is_transform (GstCaps * sink_caps, GstCaps * src_caps)
1161 {
1162   gboolean ret = FALSE;
1163
1164   if (gst_caps_is_subset (sink_caps, gst_v4l2_object_get_raw_caps ())
1165       && gst_caps_is_subset (src_caps, gst_v4l2_object_get_raw_caps ()))
1166     ret = TRUE;
1167
1168   return ret;
1169 }
1170
1171 void
1172 gst_v4l2_transform_register (GstPlugin * plugin, const gchar * basename,
1173     const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps)
1174 {
1175   GTypeQuery type_query;
1176   GTypeInfo type_info = { 0, };
1177   GType type, subtype;
1178   gchar *type_name;
1179   GstV4l2TransformCData *cdata;
1180
1181   cdata = g_new0 (GstV4l2TransformCData, 1);
1182   cdata->device = g_strdup (device_path);
1183   cdata->sink_caps = gst_caps_ref (sink_caps);
1184   cdata->src_caps = gst_caps_ref (src_caps);
1185
1186   type = gst_v4l2_transform_get_type ();
1187   g_type_query (type, &type_query);
1188   memset (&type_info, 0, sizeof (type_info));
1189   type_info.class_size = type_query.class_size;
1190   type_info.instance_size = type_query.instance_size;
1191   type_info.class_init = gst_v4l2_transform_subclass_init;
1192   type_info.class_data = cdata;
1193   type_info.instance_init = gst_v4l2_transform_subinstance_init;
1194
1195   type_name = g_strdup_printf ("v4l2%sconvert", basename);
1196   subtype = g_type_register_static (type, type_name, &type_info, 0);
1197
1198   if (!gst_element_register (plugin, type_name, GST_RANK_NONE, subtype))
1199     GST_WARNING ("Failed to register plugin '%s'", type_name);
1200
1201   g_free (type_name);
1202 }