Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / sys / applemedia / miovideodevice.c
1 /*
2  * Copyright (C) 2009 Ole André Vadla Ravnås <oravnas@cisco.com>
3  *               2009 Knut Inge Hvidsten <knuhvids@cisco.com>
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 #include "miovideodevice.h"
22
23 #include <gst/video/video.h>
24
25 #include <unistd.h>
26
27 GST_DEBUG_CATEGORY_EXTERN (gst_mio_video_src_debug);
28 #define GST_CAT_DEFAULT gst_mio_video_src_debug
29
30 enum
31 {
32   PROP_0,
33   PROP_CONTEXT,
34   PROP_HANDLE,
35   PROP_UID,
36   PROP_NAME,
37   PROP_TRANSPORT
38 };
39
40 G_DEFINE_TYPE (GstMIOVideoDevice, gst_mio_video_device, G_TYPE_OBJECT);
41
42 typedef struct _GstMIOVideoFormat GstMIOVideoFormat;
43 typedef struct _GstMIOSetFormatCtx GstMIOSetFormatCtx;
44 typedef struct _GstMIOFindRateCtx GstMIOFindRateCtx;
45
46 struct _GstMIOVideoFormat
47 {
48   TundraObjectID stream;
49   CMFormatDescriptionRef desc;
50
51   UInt32 type;
52   CMVideoDimensions dim;
53 };
54
55 struct _GstMIOSetFormatCtx
56 {
57   UInt32 format;
58   gint width, height;
59   gint fps_n, fps_d;
60   gboolean success;
61 };
62
63 struct _GstMIOFindRateCtx
64 {
65   gdouble needle;
66   gdouble closest_match;
67   gboolean success;
68 };
69
70 static void gst_mio_video_device_collect_format (GstMIOVideoDevice * self,
71     GstMIOVideoFormat * format, gpointer user_data);
72 static GstStructure *gst_mio_video_device_format_basics_to_structure
73     (GstMIOVideoDevice * self, GstMIOVideoFormat * format);
74 static gboolean gst_mio_video_device_add_framerates_to_structure
75     (GstMIOVideoDevice * self, GstMIOVideoFormat * format, GstStructure * s);
76 static void gst_mio_video_device_add_pixel_aspect_to_structure
77     (GstMIOVideoDevice * self, GstMIOVideoFormat * format, GstStructure * s);
78
79 static void gst_mio_video_device_append_framerate (GstMIOVideoDevice * self,
80     GstMIOVideoFormat * format, TundraFramerate * rate, gpointer user_data);
81 static void gst_mio_video_device_framerate_to_fraction_value
82     (TundraFramerate * rate, GValue * fract);
83 static gdouble gst_mio_video_device_round_to_whole_hundreths (gdouble value);
84 static void gst_mio_video_device_guess_pixel_aspect_ratio
85     (gint width, gint height, gint * par_width, gint * par_height);
86
87 static void gst_mio_video_device_activate_matching_format
88     (GstMIOVideoDevice * self, GstMIOVideoFormat * format, gpointer user_data);
89 static void gst_mio_video_device_find_closest_framerate
90     (GstMIOVideoDevice * self, GstMIOVideoFormat * format,
91     TundraFramerate * rate, gpointer user_data);
92
93 typedef void (*GstMIOVideoDeviceEachFormatFunc) (GstMIOVideoDevice * self,
94     GstMIOVideoFormat * format, gpointer user_data);
95 typedef void (*GstMIOVideoDeviceEachFramerateFunc) (GstMIOVideoDevice * self,
96     GstMIOVideoFormat * format, TundraFramerate * rate, gpointer user_data);
97 static void gst_mio_video_device_formats_foreach (GstMIOVideoDevice * self,
98     GstMIOVideoDeviceEachFormatFunc func, gpointer user_data);
99 static void gst_mio_video_device_format_framerates_foreach
100     (GstMIOVideoDevice * self, GstMIOVideoFormat * format,
101     GstMIOVideoDeviceEachFramerateFunc func, gpointer user_data);
102
103 static gint gst_mio_video_device_compare (GstMIOVideoDevice * a,
104     GstMIOVideoDevice * b);
105 static gint gst_mio_video_device_calculate_score (GstMIOVideoDevice * device);
106
107 static void
108 gst_mio_video_device_init (GstMIOVideoDevice * self)
109 {
110 }
111
112 static void
113 gst_mio_video_device_dispose (GObject * object)
114 {
115   GstMIOVideoDevice *self = GST_MIO_VIDEO_DEVICE_CAST (object);
116
117   if (self->cached_caps != NULL) {
118     gst_caps_unref (self->cached_caps);
119     self->cached_caps = NULL;
120   }
121
122   G_OBJECT_CLASS (gst_mio_video_device_parent_class)->dispose (object);
123 }
124
125 static void
126 gst_mio_video_device_finalize (GObject * object)
127 {
128   GstMIOVideoDevice *self = GST_MIO_VIDEO_DEVICE_CAST (object);
129
130   g_free (self->cached_uid);
131   g_free (self->cached_name);
132
133   G_OBJECT_CLASS (gst_mio_video_device_parent_class)->finalize (object);
134 }
135
136 static void
137 gst_mio_video_device_get_property (GObject * object, guint prop_id,
138     GValue * value, GParamSpec * pspec)
139 {
140   GstMIOVideoDevice *self = GST_MIO_VIDEO_DEVICE (object);
141
142   switch (prop_id) {
143     case PROP_CONTEXT:
144       g_value_set_pointer (value, self->ctx);
145       break;
146     case PROP_HANDLE:
147       g_value_set_int (value, gst_mio_video_device_get_handle (self));
148       break;
149     case PROP_UID:
150       g_value_set_string (value, gst_mio_video_device_get_uid (self));
151       break;
152     case PROP_NAME:
153       g_value_set_string (value, gst_mio_video_device_get_name (self));
154       break;
155     case PROP_TRANSPORT:
156       g_value_set_uint (value, gst_mio_video_device_get_transport_type (self));
157       break;
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160       break;
161   }
162 }
163
164 static void
165 gst_mio_video_device_set_property (GObject * object, guint prop_id,
166     const GValue * value, GParamSpec * pspec)
167 {
168   GstMIOVideoDevice *self = GST_MIO_VIDEO_DEVICE (object);
169
170   switch (prop_id) {
171     case PROP_CONTEXT:
172       self->ctx = g_value_get_pointer (value);
173       break;
174     case PROP_HANDLE:
175       self->handle = g_value_get_int (value);
176       break;
177     default:
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179       break;
180   }
181 }
182
183 TundraObjectID
184 gst_mio_video_device_get_handle (GstMIOVideoDevice * self)
185 {
186   return self->handle;
187 }
188
189 const gchar *
190 gst_mio_video_device_get_uid (GstMIOVideoDevice * self)
191 {
192   if (self->cached_uid == NULL) {
193     TundraTargetSpec pspec = { 0, };
194
195     pspec.name = kTundraObjectPropertyUID;
196     pspec.scope = kTundraScopeGlobal;
197     self->cached_uid =
198         gst_mio_object_get_string (self->handle, &pspec, self->ctx->mio);
199   }
200
201   return self->cached_uid;
202 }
203
204 const gchar *
205 gst_mio_video_device_get_name (GstMIOVideoDevice * self)
206 {
207   if (self->cached_name == NULL) {
208     TundraTargetSpec pspec = { 0, };
209
210     pspec.name = kTundraObjectPropertyName;
211     pspec.scope = kTundraScopeGlobal;
212     self->cached_name =
213         gst_mio_object_get_string (self->handle, &pspec, self->ctx->mio);
214   }
215
216   return self->cached_name;
217 }
218
219 TundraDeviceTransportType
220 gst_mio_video_device_get_transport_type (GstMIOVideoDevice * self)
221 {
222   if (self->cached_transport == kTundraDeviceTransportInvalid) {
223     TundraTargetSpec pspec = { 0, };
224
225     pspec.name = kTundraDevicePropertyTransportType;
226     pspec.scope = kTundraScopeGlobal;
227     self->cached_transport =
228         gst_mio_object_get_uint32 (self->handle, &pspec, self->ctx->mio);
229   }
230
231   return self->cached_transport;
232 }
233
234 gboolean
235 gst_mio_video_device_open (GstMIOVideoDevice * self)
236 {
237   /* nothing for now */
238   return TRUE;
239 }
240
241 void
242 gst_mio_video_device_close (GstMIOVideoDevice * self)
243 {
244   /* nothing for now */
245 }
246
247 GstCaps *
248 gst_mio_video_device_get_available_caps (GstMIOVideoDevice * self)
249 {
250   if (self->cached_caps == NULL) {
251     GstCaps *caps;
252
253     caps = gst_caps_new_empty ();
254     gst_mio_video_device_formats_foreach (self,
255         gst_mio_video_device_collect_format, caps);
256
257     self->cached_caps = caps;
258   }
259
260   return self->cached_caps;
261 }
262
263 static void
264 gst_mio_video_device_collect_format (GstMIOVideoDevice * self,
265     GstMIOVideoFormat * format, gpointer user_data)
266 {
267   GstCaps *caps = user_data;
268   GstStructure *s;
269
270   s = gst_mio_video_device_format_basics_to_structure (self, format);
271   if (s == NULL)
272     goto unsupported_format;
273
274   if (!gst_mio_video_device_add_framerates_to_structure (self, format, s))
275     goto no_framerates;
276
277   gst_mio_video_device_add_pixel_aspect_to_structure (self, format, s);
278
279   gst_caps_append_structure (caps, s);
280
281   return;
282
283   /* ERRORS */
284 unsupported_format:
285   {
286     gchar *fcc;
287
288     fcc = gst_mio_fourcc_to_string (format->type);
289     GST_WARNING ("skipping unsupported format %s", fcc);
290     g_free (fcc);
291
292     return;
293   }
294 no_framerates:
295   {
296     GST_WARNING ("no framerates?");
297
298     gst_structure_free (s);
299
300     return;
301   }
302 }
303
304 static GstStructure *
305 gst_mio_video_device_format_basics_to_structure (GstMIOVideoDevice * self,
306     GstMIOVideoFormat * format)
307 {
308   GstStructure *s;
309
310   switch (format->type) {
311     case kCVPixelFormatType_422YpCbCr8:
312     case kCVPixelFormatType_422YpCbCr8Deprecated:
313     {
314       guint fcc;
315
316       if (format->type == kCVPixelFormatType_422YpCbCr8)
317         fcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y');
318       else
319         fcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
320
321       s = gst_structure_new ("video/x-raw-yuv",
322           "format", GST_TYPE_FOURCC, fcc,
323           "width", G_TYPE_INT, format->dim.width,
324           "height", G_TYPE_INT, format->dim.height, NULL);
325       break;
326     }
327     case kFigVideoCodecType_JPEG_OpenDML:
328     {
329       s = gst_structure_new ("image/jpeg",
330           "width", G_TYPE_INT, format->dim.width,
331           "height", G_TYPE_INT, format->dim.height, NULL);
332       break;
333     }
334     default:
335       s = NULL;
336       break;
337   }
338
339   return s;
340 }
341
342 static gboolean
343 gst_mio_video_device_add_framerates_to_structure (GstMIOVideoDevice * self,
344     GstMIOVideoFormat * format, GstStructure * s)
345 {
346   GValue rates = { 0, };
347   const GValue *rates_value;
348
349   g_value_init (&rates, GST_TYPE_LIST);
350
351   gst_mio_video_device_format_framerates_foreach (self, format,
352       gst_mio_video_device_append_framerate, &rates);
353   if (gst_value_list_get_size (&rates) == 0)
354     goto no_framerates;
355
356   if (gst_value_list_get_size (&rates) > 1)
357     rates_value = &rates;
358   else
359     rates_value = gst_value_list_get_value (&rates, 0);
360   gst_structure_set_value (s, "framerate", rates_value);
361
362   g_value_unset (&rates);
363
364   return TRUE;
365
366   /* ERRORS */
367 no_framerates:
368   {
369     g_value_unset (&rates);
370     return FALSE;
371   }
372 }
373
374 static void
375 gst_mio_video_device_add_pixel_aspect_to_structure (GstMIOVideoDevice * self,
376     GstMIOVideoFormat * format, GstStructure * s)
377 {
378   gint par_width, par_height;
379
380   gst_mio_video_device_guess_pixel_aspect_ratio
381       (format->dim.width, format->dim.height, &par_width, &par_height);
382
383   gst_structure_set (s, "pixel-aspect-ratio",
384       GST_TYPE_FRACTION, par_width, par_height, NULL);
385 }
386
387 static void
388 gst_mio_video_device_append_framerate (GstMIOVideoDevice * self,
389     GstMIOVideoFormat * format, TundraFramerate * rate, gpointer user_data)
390 {
391   GValue *rates = user_data;
392   GValue value = { 0, };
393
394   g_value_init (&value, GST_TYPE_FRACTION);
395   gst_mio_video_device_framerate_to_fraction_value (rate, &value);
396   gst_value_list_append_value (rates, &value);
397   g_value_unset (&value);
398 }
399
400 static void
401 gst_mio_video_device_framerate_to_fraction_value (TundraFramerate * rate,
402     GValue * fract)
403 {
404   gdouble rounded;
405   gint n, d;
406
407   rounded = gst_mio_video_device_round_to_whole_hundreths (rate->value);
408   gst_util_double_to_fraction (rounded, &n, &d);
409   gst_value_set_fraction (fract, n, d);
410 }
411
412 static gdouble
413 gst_mio_video_device_round_to_whole_hundreths (gdouble value)
414 {
415   gdouble m, x, y, z;
416
417   m = 0.01;
418   x = value;
419   y = floor ((x / m) + 0.5);
420   z = y * m;
421
422   return z;
423 }
424
425 static void
426 gst_mio_video_device_guess_pixel_aspect_ratio (gint width, gint height,
427     gint * par_width, gint * par_height)
428 {
429   /*
430    * As we dont have access to the actual pixel aspect, we will try to do a
431    * best-effort guess. The guess is based on most sensors being either 4/3
432    * or 16/9, and most pixel aspects being close to 1/1.
433    */
434
435   if (width == 768 && height == 448) {  /* special case for w448p */
436     *par_width = 28;
437     *par_height = 27;
438   } else {
439     if (((gdouble) width / (gdouble) height) < 1.2778) {
440       *par_width = 12;
441       *par_height = 11;
442     } else {
443       *par_width = 1;
444       *par_height = 1;
445     }
446   }
447 }
448
449 gboolean
450 gst_mio_video_device_set_caps (GstMIOVideoDevice * self, GstCaps * caps)
451 {
452   GstVideoFormat format;
453   GstMIOSetFormatCtx ctx = { 0, };
454
455   if (gst_video_format_parse_caps (caps, &format, &ctx.width, &ctx.height)) {
456     if (format == GST_VIDEO_FORMAT_UYVY)
457       ctx.format = kCVPixelFormatType_422YpCbCr8;
458     else if (format == GST_VIDEO_FORMAT_YUY2)
459       ctx.format = kCVPixelFormatType_422YpCbCr8Deprecated;
460     else
461       g_assert_not_reached ();
462   } else {
463     GstStructure *s;
464
465     s = gst_caps_get_structure (caps, 0);
466     g_assert (gst_structure_has_name (s, "image/jpeg"));
467     gst_structure_get_int (s, "width", &ctx.width);
468     gst_structure_get_int (s, "height", &ctx.height);
469
470     ctx.format = kFigVideoCodecType_JPEG_OpenDML;
471   }
472
473   gst_video_parse_caps_framerate (caps, &ctx.fps_n, &ctx.fps_d);
474
475   gst_mio_video_device_formats_foreach (self,
476       gst_mio_video_device_activate_matching_format, &ctx);
477
478   return ctx.success;
479 }
480
481 static void
482 gst_mio_video_device_activate_matching_format (GstMIOVideoDevice * self,
483     GstMIOVideoFormat * format, gpointer user_data)
484 {
485   GstMIOSetFormatCtx *ctx = user_data;
486   GstMIOFindRateCtx find_ctx;
487   TundraTargetSpec spec = { 0, };
488   TundraStatus status;
489
490   if (format->type != ctx->format)
491     return;
492   else if (format->dim.width != ctx->width)
493     return;
494   else if (format->dim.height != ctx->height)
495     return;
496
497   find_ctx.needle = (gdouble) ctx->fps_n / (gdouble) ctx->fps_d;
498   find_ctx.closest_match = 0.0;
499   find_ctx.success = FALSE;
500   gst_mio_video_device_format_framerates_foreach (self, format,
501       gst_mio_video_device_find_closest_framerate, &find_ctx);
502   if (!find_ctx.success)
503     goto no_matching_framerate_found;
504
505   spec.scope = kTundraScopeInput;
506
507   spec.name = kTundraStreamPropertyFormatDescription;
508   status = self->ctx->mio->TundraObjectSetPropertyData (format->stream, &spec,
509       NULL, NULL, sizeof (format->desc), &format->desc);
510   if (status != kTundraSuccess)
511     goto failed_to_set_format;
512
513   spec.name = kTundraStreamPropertyFrameRate;
514   status = self->ctx->mio->TundraObjectSetPropertyData (format->stream, &spec,
515       NULL, NULL, sizeof (find_ctx.closest_match), &find_ctx.closest_match);
516   if (status != kTundraSuccess)
517     goto failed_to_set_framerate;
518
519   self->selected_format = format->desc;
520   self->selected_fps_n = ctx->fps_n;
521   self->selected_fps_d = ctx->fps_d;
522
523   ctx->success = TRUE;
524   return;
525
526   /* ERRORS */
527 no_matching_framerate_found:
528   {
529     GST_ERROR ("no matching framerate found");
530     return;
531   }
532 failed_to_set_format:
533   {
534     GST_ERROR ("failed to set format: 0x%08x", status);
535     return;
536   }
537 failed_to_set_framerate:
538   {
539     GST_ERROR ("failed to set framerate: 0x%08x", status);
540     return;
541   }
542 }
543
544 static void
545 gst_mio_video_device_find_closest_framerate (GstMIOVideoDevice * self,
546     GstMIOVideoFormat * format, TundraFramerate * rate, gpointer user_data)
547 {
548   GstMIOFindRateCtx *ctx = user_data;
549
550   if (fabs (rate->value - ctx->needle) <= 0.1) {
551     ctx->closest_match = rate->value;
552     ctx->success = TRUE;
553   }
554 }
555
556 CMFormatDescriptionRef
557 gst_mio_video_device_get_selected_format (GstMIOVideoDevice * self)
558 {
559   return self->selected_format;
560 }
561
562 GstClockTime
563 gst_mio_video_device_get_duration (GstMIOVideoDevice * self)
564 {
565   return gst_util_uint64_scale_int (GST_SECOND,
566       self->selected_fps_d, self->selected_fps_n);
567 }
568
569 static void
570 gst_mio_video_device_formats_foreach (GstMIOVideoDevice * self,
571     GstMIOVideoDeviceEachFormatFunc func, gpointer user_data)
572 {
573   GstCMApi *cm = self->ctx->cm;
574   GstMIOApi *mio = self->ctx->mio;
575   TundraTargetSpec spec = { 0, };
576   GArray *streams;
577   guint stream_idx;
578
579   spec.name = kTundraDevicePropertyStreams;
580   spec.scope = kTundraScopeInput;
581   streams = gst_mio_object_get_array (self->handle, &spec,
582       sizeof (TundraObjectID), mio);
583
584   /* TODO: We only consider the first stream for now */
585   for (stream_idx = 0; stream_idx != MIN (streams->len, 1); stream_idx++) {
586     TundraObjectID stream;
587     CFArrayRef formats;
588     CFIndex num_formats, fmt_idx;
589
590     stream = g_array_index (streams, TundraObjectID, stream_idx);
591
592     spec.name = kTundraStreamPropertyFormatDescriptions;
593     spec.scope = kTundraScopeInput;
594
595     formats = gst_mio_object_get_pointer (stream, &spec, mio);
596     num_formats = CFArrayGetCount (formats);
597
598     for (fmt_idx = 0; fmt_idx != num_formats; fmt_idx++) {
599       GstMIOVideoFormat fmt;
600
601       fmt.stream = stream;
602       fmt.desc = (CMFormatDescriptionRef)
603           CFArrayGetValueAtIndex (formats, fmt_idx);
604       if (cm->CMFormatDescriptionGetMediaType (fmt.desc) != kFigMediaTypeVideo)
605         continue;
606       fmt.type = cm->CMFormatDescriptionGetMediaSubType (fmt.desc);
607       fmt.dim = cm->CMVideoFormatDescriptionGetDimensions (fmt.desc);
608
609       func (self, &fmt, user_data);
610     }
611   }
612
613   g_array_free (streams, TRUE);
614 }
615
616 static void
617 gst_mio_video_device_format_framerates_foreach (GstMIOVideoDevice * self,
618     GstMIOVideoFormat * format, GstMIOVideoDeviceEachFramerateFunc func,
619     gpointer user_data)
620 {
621   TundraTargetSpec spec = { 0, };
622   GArray *rates;
623   guint rate_idx;
624
625   spec.name = kTundraStreamPropertyFrameRates;
626   spec.scope = kTundraScopeInput;
627   rates = gst_mio_object_get_array_full (format->stream, &spec,
628       sizeof (format->desc), &format->desc, sizeof (TundraFramerate),
629       self->ctx->mio);
630
631   for (rate_idx = 0; rate_idx != rates->len; rate_idx++) {
632     TundraFramerate *rate;
633
634     rate = &g_array_index (rates, TundraFramerate, rate_idx);
635
636     func (self, format, rate, user_data);
637   }
638
639   g_array_free (rates, TRUE);
640 }
641
642 void
643 gst_mio_video_device_print_debug_info (GstMIOVideoDevice * self)
644 {
645   GstCMApi *cm = self->ctx->cm;
646   GstMIOApi *mio = self->ctx->mio;
647   TundraTargetSpec spec = { 0, };
648   gchar *str;
649   GArray *streams;
650   guint stream_idx;
651
652   g_print ("Device %p with handle %d\n", self, self->handle);
653
654   spec.scope = kTundraScopeGlobal;
655
656   spec.name = kTundraObjectPropertyClass;
657   str = gst_mio_object_get_fourcc (self->handle, &spec, mio);
658   g_print ("  Class: '%s'\n", str);
659   g_free (str);
660
661   spec.name = kTundraObjectPropertyCreator;
662   str = gst_mio_object_get_string (self->handle, &spec, mio);
663   g_print ("  Creator: \"%s\"\n", str);
664   g_free (str);
665
666   spec.name = kTundraDevicePropertyModelUID;
667   str = gst_mio_object_get_string (self->handle, &spec, mio);
668   g_print ("  Model UID: \"%s\"\n", str);
669   g_free (str);
670
671   spec.name = kTundraDevicePropertyTransportType;
672   str = gst_mio_object_get_fourcc (self->handle, &spec, mio);
673   g_print ("  Transport Type: '%s'\n", str);
674   g_free (str);
675
676   g_print ("  Streams:\n");
677   spec.name = kTundraDevicePropertyStreams;
678   spec.scope = kTundraScopeInput;
679   streams = gst_mio_object_get_array (self->handle, &spec,
680       sizeof (TundraObjectID), mio);
681   for (stream_idx = 0; stream_idx != streams->len; stream_idx++) {
682     TundraObjectID stream;
683     CFArrayRef formats;
684     CFIndex num_formats, fmt_idx;
685
686     stream = g_array_index (streams, TundraObjectID, stream_idx);
687
688     g_print ("    stream[%u] = %d\n", stream_idx, stream);
689
690     spec.scope = kTundraScopeInput;
691     spec.name = kTundraStreamPropertyFormatDescriptions;
692
693     formats = gst_mio_object_get_pointer (stream, &spec, mio);
694     num_formats = CFArrayGetCount (formats);
695
696     g_print ("      <%u formats>\n", (guint) num_formats);
697
698     for (fmt_idx = 0; fmt_idx != num_formats; fmt_idx++) {
699       CMFormatDescriptionRef fmt;
700       gchar *media_type;
701       gchar *media_sub_type;
702       CMVideoDimensions dim;
703       GArray *rates;
704       guint rate_idx;
705
706       fmt = CFArrayGetValueAtIndex (formats, fmt_idx);
707       media_type = gst_mio_fourcc_to_string
708           (cm->CMFormatDescriptionGetMediaType (fmt));
709       media_sub_type = gst_mio_fourcc_to_string
710           (cm->CMFormatDescriptionGetMediaSubType (fmt));
711       dim = cm->CMVideoFormatDescriptionGetDimensions (fmt);
712
713       g_print ("      format[%u]: MediaType='%s' MediaSubType='%s' %ux%u\n",
714           (guint) fmt_idx, media_type, media_sub_type,
715           (guint) dim.width, (guint) dim.height);
716
717       spec.name = kTundraStreamPropertyFrameRates;
718       rates = gst_mio_object_get_array_full (stream, &spec, sizeof (fmt), &fmt,
719           sizeof (TundraFramerate), mio);
720       for (rate_idx = 0; rate_idx != rates->len; rate_idx++) {
721         TundraFramerate *rate;
722
723         rate = &g_array_index (rates, TundraFramerate, rate_idx);
724         g_print ("        %f\n", rate->value);
725       }
726       g_array_free (rates, TRUE);
727
728       g_free (media_sub_type);
729       g_free (media_type);
730     }
731   }
732
733   g_array_free (streams, TRUE);
734 }
735
736 static void
737 gst_mio_video_device_class_init (GstMIOVideoDeviceClass * klass)
738 {
739   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
740
741   gobject_class->dispose = gst_mio_video_device_dispose;
742   gobject_class->finalize = gst_mio_video_device_finalize;
743   gobject_class->get_property = gst_mio_video_device_get_property;
744   gobject_class->set_property = gst_mio_video_device_set_property;
745
746   g_object_class_install_property (gobject_class, PROP_CONTEXT,
747       g_param_spec_pointer ("context", "CoreMedia Context",
748           "CoreMedia context to use",
749           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
750   g_object_class_install_property (gobject_class, PROP_HANDLE,
751       g_param_spec_int ("handle", "Handle",
752           "MIO handle of this video capture device",
753           G_MININT, G_MAXINT, -1,
754           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
755   g_object_class_install_property (gobject_class, PROP_UID,
756       g_param_spec_string ("uid", "Unique ID",
757           "Unique ID of this video capture device", NULL,
758           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
759   g_object_class_install_property (gobject_class, PROP_NAME,
760       g_param_spec_string ("name", "Device Name",
761           "Name of this video capture device", NULL,
762           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
763   g_object_class_install_property (gobject_class, PROP_TRANSPORT,
764       g_param_spec_uint ("transport", "Transport",
765           "Transport type of this video capture device",
766           0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
767 }
768
769 GList *
770 gst_mio_video_device_list_create (GstCoreMediaCtx * ctx)
771 {
772   GList *devices = NULL;
773   TundraTargetSpec pspec = { 0, };
774   GArray *handles;
775   guint handle_idx;
776
777   pspec.name = kTundraSystemPropertyDevices;
778   pspec.scope = kTundraScopeGlobal;
779   handles = gst_mio_object_get_array (TUNDRA_SYSTEM_OBJECT_ID, &pspec,
780       sizeof (TundraObjectID), ctx->mio);
781   if (handles == NULL)
782     goto beach;
783
784   for (handle_idx = 0; handle_idx != handles->len; handle_idx++) {
785     TundraObjectID handle;
786     GstMIOVideoDevice *device;
787
788     handle = g_array_index (handles, TundraObjectID, handle_idx);
789     device = g_object_new (GST_TYPE_MIO_VIDEO_DEVICE,
790         "context", ctx, "handle", handle, NULL);
791
792     /* TODO: Skip screen input devices for now */
793     if (gst_mio_video_device_get_transport_type (device) !=
794         kTundraDeviceTransportScreen) {
795       devices = g_list_prepend (devices, device);
796     } else {
797       g_object_unref (device);
798     }
799   }
800
801   devices = g_list_sort (devices, (GCompareFunc) gst_mio_video_device_compare);
802
803   g_array_free (handles, TRUE);
804
805 beach:
806   return devices;
807 }
808
809 void
810 gst_mio_video_device_list_destroy (GList * devices)
811 {
812   g_list_foreach (devices, (GFunc) g_object_unref, NULL);
813   g_list_free (devices);
814 }
815
816 static gint
817 gst_mio_video_device_compare (GstMIOVideoDevice * a, GstMIOVideoDevice * b)
818 {
819   gint score_a, score_b;
820
821   score_a = gst_mio_video_device_calculate_score (a);
822   score_b = gst_mio_video_device_calculate_score (b);
823
824   if (score_a > score_b)
825     return -1;
826   else if (score_a < score_b)
827     return 1;
828
829   return g_ascii_strcasecmp (gst_mio_video_device_get_name (a),
830       gst_mio_video_device_get_name (b));
831 }
832
833 static gint
834 gst_mio_video_device_calculate_score (GstMIOVideoDevice * device)
835 {
836   switch (gst_mio_video_device_get_transport_type (device)) {
837     case kTundraDeviceTransportScreen:
838       return 0;
839     case kTundraDeviceTransportBuiltin:
840       return 1;
841     case kTundraDeviceTransportUSB:
842       return 2;
843     default:
844       return 3;
845   }
846 }