gstreamer/sys/applemedia/miovideodevice.c

847 lines
24 KiB
C
Raw Normal View History

/*
2013-02-16 01:44:19 +00:00
* Copyright (C) 2009 Ole André Vadla Ravnås <oleavr@soundrop.com>
2010-11-04 13:29:37 +00:00
* 2009 Knut Inge Hvidsten <knuhvids@cisco.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "miovideodevice.h"
#include <gst/video/video.h>
#include <unistd.h>
GST_DEBUG_CATEGORY_EXTERN (gst_mio_video_src_debug);
#define GST_CAT_DEFAULT gst_mio_video_src_debug
enum
{
PROP_0,
PROP_CONTEXT,
PROP_HANDLE,
PROP_UID,
PROP_NAME,
PROP_TRANSPORT
};
G_DEFINE_TYPE (GstMIOVideoDevice, gst_mio_video_device, G_TYPE_OBJECT);
typedef struct _GstMIOVideoFormat GstMIOVideoFormat;
typedef struct _GstMIOSetFormatCtx GstMIOSetFormatCtx;
typedef struct _GstMIOFindRateCtx GstMIOFindRateCtx;
struct _GstMIOVideoFormat
{
TundraObjectID stream;
CMFormatDescriptionRef desc;
UInt32 type;
CMVideoDimensions dim;
};
struct _GstMIOSetFormatCtx
{
UInt32 format;
gint width, height;
gint fps_n, fps_d;
gboolean success;
};
struct _GstMIOFindRateCtx
{
gdouble needle;
gdouble closest_match;
gboolean success;
};
static void gst_mio_video_device_collect_format (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, gpointer user_data);
static GstStructure *gst_mio_video_device_format_basics_to_structure
(GstMIOVideoDevice * self, GstMIOVideoFormat * format);
static gboolean gst_mio_video_device_add_framerates_to_structure
(GstMIOVideoDevice * self, GstMIOVideoFormat * format, GstStructure * s);
static void gst_mio_video_device_add_pixel_aspect_to_structure
(GstMIOVideoDevice * self, GstMIOVideoFormat * format, GstStructure * s);
static void gst_mio_video_device_append_framerate (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, TundraFramerate * rate, gpointer user_data);
static void gst_mio_video_device_framerate_to_fraction_value
(TundraFramerate * rate, GValue * fract);
static gdouble gst_mio_video_device_round_to_whole_hundreths (gdouble value);
static void gst_mio_video_device_guess_pixel_aspect_ratio
(gint width, gint height, gint * par_width, gint * par_height);
static void gst_mio_video_device_activate_matching_format
(GstMIOVideoDevice * self, GstMIOVideoFormat * format, gpointer user_data);
static void gst_mio_video_device_find_closest_framerate
(GstMIOVideoDevice * self, GstMIOVideoFormat * format,
TundraFramerate * rate, gpointer user_data);
typedef void (*GstMIOVideoDeviceEachFormatFunc) (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, gpointer user_data);
typedef void (*GstMIOVideoDeviceEachFramerateFunc) (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, TundraFramerate * rate, gpointer user_data);
static void gst_mio_video_device_formats_foreach (GstMIOVideoDevice * self,
GstMIOVideoDeviceEachFormatFunc func, gpointer user_data);
static void gst_mio_video_device_format_framerates_foreach
(GstMIOVideoDevice * self, GstMIOVideoFormat * format,
GstMIOVideoDeviceEachFramerateFunc func, gpointer user_data);
static gint gst_mio_video_device_compare (GstMIOVideoDevice * a,
GstMIOVideoDevice * b);
static gint gst_mio_video_device_calculate_score (GstMIOVideoDevice * device);
static void
gst_mio_video_device_init (GstMIOVideoDevice * self)
{
}
static void
gst_mio_video_device_dispose (GObject * object)
{
GstMIOVideoDevice *self = GST_MIO_VIDEO_DEVICE_CAST (object);
if (self->cached_caps != NULL) {
gst_caps_unref (self->cached_caps);
self->cached_caps = NULL;
}
G_OBJECT_CLASS (gst_mio_video_device_parent_class)->dispose (object);
}
static void
gst_mio_video_device_finalize (GObject * object)
{
GstMIOVideoDevice *self = GST_MIO_VIDEO_DEVICE_CAST (object);
g_free (self->cached_uid);
g_free (self->cached_name);
G_OBJECT_CLASS (gst_mio_video_device_parent_class)->finalize (object);
}
static void
gst_mio_video_device_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstMIOVideoDevice *self = GST_MIO_VIDEO_DEVICE (object);
switch (prop_id) {
case PROP_CONTEXT:
g_value_set_pointer (value, self->ctx);
break;
case PROP_HANDLE:
g_value_set_int (value, gst_mio_video_device_get_handle (self));
break;
case PROP_UID:
g_value_set_string (value, gst_mio_video_device_get_uid (self));
break;
case PROP_NAME:
g_value_set_string (value, gst_mio_video_device_get_name (self));
break;
case PROP_TRANSPORT:
g_value_set_uint (value, gst_mio_video_device_get_transport_type (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_mio_video_device_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstMIOVideoDevice *self = GST_MIO_VIDEO_DEVICE (object);
switch (prop_id) {
case PROP_CONTEXT:
self->ctx = g_value_get_pointer (value);
break;
case PROP_HANDLE:
self->handle = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
TundraObjectID
gst_mio_video_device_get_handle (GstMIOVideoDevice * self)
{
return self->handle;
}
const gchar *
gst_mio_video_device_get_uid (GstMIOVideoDevice * self)
{
if (self->cached_uid == NULL) {
TundraTargetSpec pspec = { 0, };
pspec.name = kTundraObjectPropertyUID;
pspec.scope = kTundraScopeGlobal;
self->cached_uid =
gst_mio_object_get_string (self->handle, &pspec, self->ctx->mio);
}
return self->cached_uid;
}
const gchar *
gst_mio_video_device_get_name (GstMIOVideoDevice * self)
{
if (self->cached_name == NULL) {
TundraTargetSpec pspec = { 0, };
pspec.name = kTundraObjectPropertyName;
pspec.scope = kTundraScopeGlobal;
self->cached_name =
gst_mio_object_get_string (self->handle, &pspec, self->ctx->mio);
}
return self->cached_name;
}
TundraDeviceTransportType
gst_mio_video_device_get_transport_type (GstMIOVideoDevice * self)
{
if (self->cached_transport == kTundraDeviceTransportInvalid) {
TundraTargetSpec pspec = { 0, };
pspec.name = kTundraDevicePropertyTransportType;
pspec.scope = kTundraScopeGlobal;
self->cached_transport =
gst_mio_object_get_uint32 (self->handle, &pspec, self->ctx->mio);
}
return self->cached_transport;
}
gboolean
gst_mio_video_device_open (GstMIOVideoDevice * self)
{
/* nothing for now */
return TRUE;
}
void
gst_mio_video_device_close (GstMIOVideoDevice * self)
{
/* nothing for now */
}
GstCaps *
gst_mio_video_device_get_available_caps (GstMIOVideoDevice * self)
{
if (self->cached_caps == NULL) {
GstCaps *caps;
caps = gst_caps_new_empty ();
gst_mio_video_device_formats_foreach (self,
gst_mio_video_device_collect_format, caps);
self->cached_caps = caps;
}
return self->cached_caps;
}
static void
gst_mio_video_device_collect_format (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, gpointer user_data)
{
GstCaps *caps = user_data;
GstStructure *s;
s = gst_mio_video_device_format_basics_to_structure (self, format);
if (s == NULL)
goto unsupported_format;
if (!gst_mio_video_device_add_framerates_to_structure (self, format, s))
goto no_framerates;
gst_mio_video_device_add_pixel_aspect_to_structure (self, format, s);
gst_caps_append_structure (caps, s);
return;
/* ERRORS */
unsupported_format:
{
gchar *fcc;
fcc = gst_mio_fourcc_to_string (format->type);
GST_WARNING ("skipping unsupported format %s", fcc);
g_free (fcc);
return;
}
no_framerates:
{
GST_WARNING ("no framerates?");
gst_structure_free (s);
return;
}
}
static GstStructure *
gst_mio_video_device_format_basics_to_structure (GstMIOVideoDevice * self,
GstMIOVideoFormat * format)
{
GstStructure *s;
switch (format->type) {
case kCVPixelFormatType_422YpCbCr8:
case kCVPixelFormatType_422YpCbCr8Deprecated:
{
guint fcc;
if (format->type == kCVPixelFormatType_422YpCbCr8)
fcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y');
else
fcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
s = gst_structure_new ("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, fcc,
"width", G_TYPE_INT, format->dim.width,
"height", G_TYPE_INT, format->dim.height, NULL);
break;
}
case kFigVideoCodecType_JPEG_OpenDML:
{
s = gst_structure_new ("image/jpeg",
"width", G_TYPE_INT, format->dim.width,
"height", G_TYPE_INT, format->dim.height, NULL);
break;
}
default:
s = NULL;
break;
}
return s;
}
static gboolean
gst_mio_video_device_add_framerates_to_structure (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, GstStructure * s)
{
GValue rates = { 0, };
const GValue *rates_value;
g_value_init (&rates, GST_TYPE_LIST);
gst_mio_video_device_format_framerates_foreach (self, format,
gst_mio_video_device_append_framerate, &rates);
if (gst_value_list_get_size (&rates) == 0)
goto no_framerates;
if (gst_value_list_get_size (&rates) > 1)
rates_value = &rates;
else
rates_value = gst_value_list_get_value (&rates, 0);
gst_structure_set_value (s, "framerate", rates_value);
g_value_unset (&rates);
return TRUE;
/* ERRORS */
no_framerates:
{
g_value_unset (&rates);
return FALSE;
}
}
static void
gst_mio_video_device_add_pixel_aspect_to_structure (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, GstStructure * s)
{
gint par_width, par_height;
gst_mio_video_device_guess_pixel_aspect_ratio
(format->dim.width, format->dim.height, &par_width, &par_height);
gst_structure_set (s, "pixel-aspect-ratio",
GST_TYPE_FRACTION, par_width, par_height, NULL);
}
static void
gst_mio_video_device_append_framerate (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, TundraFramerate * rate, gpointer user_data)
{
GValue *rates = user_data;
GValue value = { 0, };
g_value_init (&value, GST_TYPE_FRACTION);
gst_mio_video_device_framerate_to_fraction_value (rate, &value);
gst_value_list_append_value (rates, &value);
g_value_unset (&value);
}
static void
gst_mio_video_device_framerate_to_fraction_value (TundraFramerate * rate,
GValue * fract)
{
gdouble rounded;
gint n, d;
rounded = gst_mio_video_device_round_to_whole_hundreths (rate->value);
gst_util_double_to_fraction (rounded, &n, &d);
gst_value_set_fraction (fract, n, d);
}
static gdouble
gst_mio_video_device_round_to_whole_hundreths (gdouble value)
{
gdouble m, x, y, z;
m = 0.01;
x = value;
y = floor ((x / m) + 0.5);
z = y * m;
return z;
}
static void
gst_mio_video_device_guess_pixel_aspect_ratio (gint width, gint height,
gint * par_width, gint * par_height)
{
/*
* As we dont have access to the actual pixel aspect, we will try to do a
* best-effort guess. The guess is based on most sensors being either 4/3
* or 16/9, and most pixel aspects being close to 1/1.
*/
if (width == 768 && height == 448) { /* special case for w448p */
*par_width = 28;
*par_height = 27;
} else {
if (((gdouble) width / (gdouble) height) < 1.2778) {
*par_width = 12;
*par_height = 11;
} else {
*par_width = 1;
*par_height = 1;
}
}
}
gboolean
gst_mio_video_device_set_caps (GstMIOVideoDevice * self, GstCaps * caps)
{
GstVideoFormat format;
GstMIOSetFormatCtx ctx = { 0, };
if (gst_video_format_parse_caps (caps, &format, &ctx.width, &ctx.height)) {
if (format == GST_VIDEO_FORMAT_UYVY)
ctx.format = kCVPixelFormatType_422YpCbCr8;
else if (format == GST_VIDEO_FORMAT_YUY2)
ctx.format = kCVPixelFormatType_422YpCbCr8Deprecated;
else
g_assert_not_reached ();
} else {
GstStructure *s;
s = gst_caps_get_structure (caps, 0);
g_assert (gst_structure_has_name (s, "image/jpeg"));
gst_structure_get_int (s, "width", &ctx.width);
gst_structure_get_int (s, "height", &ctx.height);
ctx.format = kFigVideoCodecType_JPEG_OpenDML;
}
gst_video_parse_caps_framerate (caps, &ctx.fps_n, &ctx.fps_d);
gst_mio_video_device_formats_foreach (self,
gst_mio_video_device_activate_matching_format, &ctx);
return ctx.success;
}
static void
gst_mio_video_device_activate_matching_format (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, gpointer user_data)
{
GstMIOSetFormatCtx *ctx = user_data;
GstMIOFindRateCtx find_ctx;
TundraTargetSpec spec = { 0, };
TundraStatus status;
if (format->type != ctx->format)
return;
else if (format->dim.width != ctx->width)
return;
else if (format->dim.height != ctx->height)
return;
find_ctx.needle = (gdouble) ctx->fps_n / (gdouble) ctx->fps_d;
find_ctx.closest_match = 0.0;
find_ctx.success = FALSE;
gst_mio_video_device_format_framerates_foreach (self, format,
gst_mio_video_device_find_closest_framerate, &find_ctx);
if (!find_ctx.success)
goto no_matching_framerate_found;
spec.scope = kTundraScopeInput;
spec.name = kTundraStreamPropertyFormatDescription;
status = self->ctx->mio->TundraObjectSetPropertyData (format->stream, &spec,
NULL, NULL, sizeof (format->desc), &format->desc);
if (status != kTundraSuccess)
goto failed_to_set_format;
spec.name = kTundraStreamPropertyFrameRate;
status = self->ctx->mio->TundraObjectSetPropertyData (format->stream, &spec,
NULL, NULL, sizeof (find_ctx.closest_match), &find_ctx.closest_match);
if (status != kTundraSuccess)
goto failed_to_set_framerate;
self->selected_format = format->desc;
self->selected_fps_n = ctx->fps_n;
self->selected_fps_d = ctx->fps_d;
ctx->success = TRUE;
return;
/* ERRORS */
no_matching_framerate_found:
{
GST_ERROR ("no matching framerate found");
return;
}
failed_to_set_format:
{
GST_ERROR ("failed to set format: 0x%08x", status);
return;
}
failed_to_set_framerate:
{
GST_ERROR ("failed to set framerate: 0x%08x", status);
return;
}
}
static void
gst_mio_video_device_find_closest_framerate (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, TundraFramerate * rate, gpointer user_data)
{
GstMIOFindRateCtx *ctx = user_data;
if (fabs (rate->value - ctx->needle) <= 0.1) {
ctx->closest_match = rate->value;
ctx->success = TRUE;
}
}
CMFormatDescriptionRef
gst_mio_video_device_get_selected_format (GstMIOVideoDevice * self)
{
return self->selected_format;
}
GstClockTime
gst_mio_video_device_get_duration (GstMIOVideoDevice * self)
{
return gst_util_uint64_scale_int (GST_SECOND,
self->selected_fps_d, self->selected_fps_n);
}
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);
}
static void
gst_mio_video_device_format_framerates_foreach (GstMIOVideoDevice * self,
GstMIOVideoFormat * format, GstMIOVideoDeviceEachFramerateFunc func,
gpointer user_data)
{
TundraTargetSpec spec = { 0, };
GArray *rates;
guint rate_idx;
spec.name = kTundraStreamPropertyFrameRates;
spec.scope = kTundraScopeInput;
rates = gst_mio_object_get_array_full (format->stream, &spec,
sizeof (format->desc), &format->desc, sizeof (TundraFramerate),
self->ctx->mio);
for (rate_idx = 0; rate_idx != rates->len; rate_idx++) {
TundraFramerate *rate;
rate = &g_array_index (rates, TundraFramerate, rate_idx);
func (self, format, rate, user_data);
}
g_array_free (rates, 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);
}
static void
gst_mio_video_device_class_init (GstMIOVideoDeviceClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gst_mio_video_device_dispose;
gobject_class->finalize = gst_mio_video_device_finalize;
gobject_class->get_property = gst_mio_video_device_get_property;
gobject_class->set_property = gst_mio_video_device_set_property;
g_object_class_install_property (gobject_class, PROP_CONTEXT,
g_param_spec_pointer ("context", "CoreMedia Context",
"CoreMedia context to use",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_HANDLE,
g_param_spec_int ("handle", "Handle",
"MIO handle of this video capture device",
G_MININT, G_MAXINT, -1,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_UID,
g_param_spec_string ("uid", "Unique ID",
"Unique ID of this video capture device", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_NAME,
g_param_spec_string ("name", "Device Name",
"Name of this video capture device", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TRANSPORT,
g_param_spec_uint ("transport", "Transport",
"Transport type of this video capture device",
0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
GList *
gst_mio_video_device_list_create (GstCoreMediaCtx * ctx)
{
GList *devices = NULL;
TundraTargetSpec pspec = { 0, };
GArray *handles;
guint handle_idx;
pspec.name = kTundraSystemPropertyDevices;
pspec.scope = kTundraScopeGlobal;
handles = gst_mio_object_get_array (TUNDRA_SYSTEM_OBJECT_ID, &pspec,
sizeof (TundraObjectID), ctx->mio);
if (handles == NULL)
goto beach;
for (handle_idx = 0; handle_idx != handles->len; handle_idx++) {
TundraObjectID handle;
GstMIOVideoDevice *device;
handle = g_array_index (handles, TundraObjectID, handle_idx);
device = g_object_new (GST_TYPE_MIO_VIDEO_DEVICE,
"context", ctx, "handle", handle, NULL);
/* TODO: Skip screen input devices for now */
if (gst_mio_video_device_get_transport_type (device) !=
kTundraDeviceTransportScreen) {
devices = g_list_prepend (devices, device);
} else {
g_object_unref (device);
}
}
devices = g_list_sort (devices, (GCompareFunc) gst_mio_video_device_compare);
g_array_free (handles, TRUE);
beach:
return devices;
}
void
gst_mio_video_device_list_destroy (GList * devices)
{
g_list_foreach (devices, (GFunc) g_object_unref, NULL);
g_list_free (devices);
}
static gint
gst_mio_video_device_compare (GstMIOVideoDevice * a, GstMIOVideoDevice * b)
{
gint score_a, score_b;
score_a = gst_mio_video_device_calculate_score (a);
score_b = gst_mio_video_device_calculate_score (b);
if (score_a > score_b)
return -1;
else if (score_a < score_b)
return 1;
return g_ascii_strcasecmp (gst_mio_video_device_get_name (a),
gst_mio_video_device_get_name (b));
}
static gint
gst_mio_video_device_calculate_score (GstMIOVideoDevice * device)
{
switch (gst_mio_video_device_get_transport_type (device)) {
case kTundraDeviceTransportScreen:
return 0;
case kTundraDeviceTransportBuiltin:
return 1;
case kTundraDeviceTransportUSB:
return 2;
default:
return 3;
}
}