static gboolean
gst_cel_video_src_parse_stream_format (GstCelVideoSrc * self,
    guint index, CFDictionaryRef stream_format, GstCelVideoFormat * format)
{
  GstCMApi *cm = self->ctx->cm;
  GstMTApi *mt = self->ctx->mt;
  CMFormatDescriptionRef desc;
  CMVideoDimensions dim;
  UInt32 subtype;
  CFNumberRef framerate_value;
  SInt32 fps_n;

  format->index = index;

  desc = CFDictionaryGetValue (stream_format,
      *(mt->kFigSupportedFormat_FormatDescription));

  dim = cm->CMVideoFormatDescriptionGetDimensions (desc);
  format->width = dim.width;
  format->height = dim.height;

  subtype = cm->CMFormatDescriptionGetMediaSubType (desc);

  switch (subtype) {
    case kComponentVideoUnsigned:
      format->video_format = GST_VIDEO_FORMAT_YUY2;
      format->fourcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
      break;
    case kYUV420vCodecType:
      format->video_format = GST_VIDEO_FORMAT_NV12;
      format->fourcc = GST_MAKE_FOURCC ('N', 'V', '1', '2');
      break;
    default:
      goto unsupported_format;
  }

  framerate_value = CFDictionaryGetValue (stream_format,
      *(mt->kFigSupportedFormat_VideoMaxFrameRate));
  CFNumberGetValue (framerate_value, kCFNumberSInt32Type, &fps_n);
  format->fps_n = fps_n;
  format->fps_d = 1;

  return TRUE;

unsupported_format:
  return FALSE;
}
static void
gst_mio_video_device_formats_foreach (GstMIOVideoDevice * self,
    GstMIOVideoDeviceEachFormatFunc func, gpointer user_data)
{
  GstCMApi *cm = self->ctx->cm;
  GstMIOApi *mio = self->ctx->mio;
  TundraTargetSpec spec = { 0, };
  GArray *streams;
  guint stream_idx;

  spec.name = kTundraDevicePropertyStreams;
  spec.scope = kTundraScopeInput;
  streams = gst_mio_object_get_array (self->handle, &spec,
      sizeof (TundraObjectID), mio);

  /* TODO: We only consider the first stream for now */
  for (stream_idx = 0; stream_idx != MIN (streams->len, 1); stream_idx++) {
    TundraObjectID stream;
    CFArrayRef formats;
    CFIndex num_formats, fmt_idx;

    stream = g_array_index (streams, TundraObjectID, stream_idx);

    spec.name = kTundraStreamPropertyFormatDescriptions;
    spec.scope = kTundraScopeInput;

    formats = gst_mio_object_get_pointer (stream, &spec, mio);
    num_formats = CFArrayGetCount (formats);

    for (fmt_idx = 0; fmt_idx != num_formats; fmt_idx++) {
      GstMIOVideoFormat fmt;

      fmt.stream = stream;
      fmt.desc = (CMFormatDescriptionRef)
          CFArrayGetValueAtIndex (formats, fmt_idx);
      if (cm->CMFormatDescriptionGetMediaType (fmt.desc) != kFigMediaTypeVideo)
        continue;
      fmt.type = cm->CMFormatDescriptionGetMediaSubType (fmt.desc);
      fmt.dim = cm->CMVideoFormatDescriptionGetDimensions (fmt.desc);

      func (self, &fmt, user_data);
    }
  }

  g_array_free (streams, TRUE);
}
void
gst_mio_video_device_print_debug_info (GstMIOVideoDevice * self)
{
  GstCMApi *cm = self->ctx->cm;
  GstMIOApi *mio = self->ctx->mio;
  TundraTargetSpec spec = { 0, };
  gchar *str;
  GArray *streams;
  guint stream_idx;

  g_print ("Device %p with handle %d\n", self, self->handle);

  spec.scope = kTundraScopeGlobal;

  spec.name = kTundraObjectPropertyClass;
  str = gst_mio_object_get_fourcc (self->handle, &spec, mio);
  g_print ("  Class: '%s'\n", str);
  g_free (str);

  spec.name = kTundraObjectPropertyCreator;
  str = gst_mio_object_get_string (self->handle, &spec, mio);
  g_print ("  Creator: \"%s\"\n", str);
  g_free (str);

  spec.name = kTundraDevicePropertyModelUID;
  str = gst_mio_object_get_string (self->handle, &spec, mio);
  g_print ("  Model UID: \"%s\"\n", str);
  g_free (str);

  spec.name = kTundraDevicePropertyTransportType;
  str = gst_mio_object_get_fourcc (self->handle, &spec, mio);
  g_print ("  Transport Type: '%s'\n", str);
  g_free (str);

  g_print ("  Streams:\n");
  spec.name = kTundraDevicePropertyStreams;
  spec.scope = kTundraScopeInput;
  streams = gst_mio_object_get_array (self->handle, &spec,
      sizeof (TundraObjectID), mio);
  for (stream_idx = 0; stream_idx != streams->len; stream_idx++) {
    TundraObjectID stream;
    CFArrayRef formats;
    CFIndex num_formats, fmt_idx;

    stream = g_array_index (streams, TundraObjectID, stream_idx);

    g_print ("    stream[%u] = %d\n", stream_idx, stream);

    spec.scope = kTundraScopeInput;
    spec.name = kTundraStreamPropertyFormatDescriptions;

    formats = gst_mio_object_get_pointer (stream, &spec, mio);
    num_formats = CFArrayGetCount (formats);

    g_print ("      <%u formats>\n", (guint) num_formats);

    for (fmt_idx = 0; fmt_idx != num_formats; fmt_idx++) {
      CMFormatDescriptionRef fmt;
      gchar *media_type;
      gchar *media_sub_type;
      CMVideoDimensions dim;
      GArray *rates;
      guint rate_idx;

      fmt = CFArrayGetValueAtIndex (formats, fmt_idx);
      media_type = gst_mio_fourcc_to_string
          (cm->CMFormatDescriptionGetMediaType (fmt));
      media_sub_type = gst_mio_fourcc_to_string
          (cm->CMFormatDescriptionGetMediaSubType (fmt));
      dim = cm->CMVideoFormatDescriptionGetDimensions (fmt);

      g_print ("      format[%u]: MediaType='%s' MediaSubType='%s' %ux%u\n",
          (guint) fmt_idx, media_type, media_sub_type,
          (guint) dim.width, (guint) dim.height);

      spec.name = kTundraStreamPropertyFrameRates;
      rates = gst_mio_object_get_array_full (stream, &spec, sizeof (fmt), &fmt,
          sizeof (TundraFramerate), mio);
      for (rate_idx = 0; rate_idx != rates->len; rate_idx++) {
        TundraFramerate *rate;

        rate = &g_array_index (rates, TundraFramerate, rate_idx);
        g_print ("        %f\n", rate->value);
      }
      g_array_free (rates, TRUE);

      g_free (media_sub_type);
      g_free (media_type);
    }
  }

  g_array_free (streams, TRUE);
}