@@ -46,7 +46,8 @@ COMMON_SRCS = mixer.c videoinput.c rtctimer.c leetft.c osdtools.c tvtimeconf.c \
cpuinfo.h cpuinfo.c menu.c menu.h \
outputfilter.h outputfilter.c xmltv.h xmltv.c gettext.h tvtimeglyphs.h \
copyfunctions.h copyfunctions.c alsa_stream.c alsa_stream.h \
- epg.h epg.c mixer-oss.c $(ALSA_SRCS)
+ epg.h epg.c mixer-oss.c get_media_devices.c get_media_devices.h \
+ $(ALSA_SRCS)
if ARCH_X86
DSCALER_SRCS = ../plugins/dscalerapi.h \
@@ -94,7 +95,8 @@ tvtime_configure_SOURCES = utils.h utils.c tvtimeconf.h tvtimeconf.c \
tvtime_configure_CFLAGS = $(OPT_CFLAGS) $(XML2_FLAG) $(AM_CFLAGS)
tvtime_configure_LDFLAGS = $(ZLIB_LIBS) $(XML2_LIBS)
tvtime_scanner_SOURCES = utils.h utils.c videoinput.h videoinput.c \
- tvtimeconf.h tvtimeconf.c station.h station.c tvtime-scanner.c
+ tvtimeconf.h tvtimeconf.c station.h station.c tvtime-scanner.c \
+ get_media_devices.c get_media_devices.h
tvtime_scanner_CFLAGS = $(OPT_CFLAGS) $(XML2_FLAG) $(ALSA_CFLAGS) $(AM_CFLAGS)
tvtime_scanner_LDFLAGS = $(ZLIB_LIBS) $(XML2_LIBS) $(ALSA_LIBS)
new file mode 100644
@@ -0,0 +1,597 @@
+/*
+ Copyright © 2011 by Mauro Carvalho Chehab <mchehab@redhat.com>
+
+ The get_media_devices is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The libv4l2util 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the libv4l2util Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA
+ 02110-1335 USA.
+ */
+
+#ifdef __linux__ /* This depends on sysfs, so it is linux only */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <dirent.h>
+#include "get_media_devices.h"
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/**
+ * struct media_device_entry - Describes one device entry got via sysfs
+ *
+ * @device: sysfs name for a device.
+ * PCI devices are like: pci0000:00/0000:00:1b.0
+ * USB devices are like: pci0000:00/0000:00:1d.7/usb1/1-8
+ * @node: Device node, in sysfs or alsa hw identifier
+ * @device_type: Type of the device (V4L_*, DVB_*, SND_*)
+ */
+struct media_device_entry {
+ char *device;
+ char *node;
+ enum device_type type;
+ enum bus_type bus;
+ unsigned major, minor; /* Device major/minor */
+};
+
+/**
+ * struct media_devices - Describes all devices found
+ *
+ * @device: sysfs name for a device.
+ * PCI devices are like: pci0000:00/0000:00:1b.0
+ * USB devices are like: pci0000:00/0000:00:1d.7/usb1/1-8
+ * @node: Device node, in sysfs or alsa hw identifier
+ * @device_type: Type of the device (V4L_*, DVB_*, SND_*)
+ */
+struct media_devices {
+ struct media_device_entry *md_entry;
+ unsigned int md_size;
+};
+
+typedef int (*fill_data_t)(struct media_device_entry *md);
+
+#define DEVICE_STR "devices"
+
+static void get_uevent_info(struct media_device_entry *md_ptr, char *dname)
+{
+ FILE *fd;
+ char file[PATH_MAX], *name, *p;
+ char s[1024];
+
+ snprintf(file, PATH_MAX, "%s/%s/uevent", dname, md_ptr->node);
+ fd = fopen(file, "r");
+ if (!fd)
+ return;
+ while (fgets(s, sizeof(s), fd)) {
+ p = strtok(s, "=");
+ if (!p)
+ continue;
+ name = p;
+ p = strtok(NULL, "\n");
+ if (!p)
+ continue;
+ if (!strcmp(name, "MAJOR"))
+ md_ptr->major = atol(p);
+ else if (!strcmp(name, "MINOR"))
+ md_ptr->minor = atol(p);
+ }
+
+ fclose(fd);
+}
+
+static enum bus_type get_bus(char *device)
+{
+ char file[PATH_MAX];
+ char s[1024];
+ FILE *f;
+
+ if (!strcmp(device, "/sys/devices/virtual"))
+ return MEDIA_BUS_VIRTUAL;
+
+ snprintf(file, PATH_MAX, "%s/modalias", device);
+ f = fopen(file, "r");
+ if (!f)
+ return MEDIA_BUS_UNKNOWN;
+ if (!fgets(s, sizeof(s), f))
+ return MEDIA_BUS_UNKNOWN;
+ fclose(f);
+
+ if (!strncmp(s, "pci", 3))
+ return MEDIA_BUS_PCI;
+ if (!strncmp(s, "usb", 3))
+ return MEDIA_BUS_USB;
+
+ return MEDIA_BUS_UNKNOWN;
+}
+
+static int get_class(char *class,
+ struct media_device_entry **md,
+ unsigned int *md_size,
+ fill_data_t fill)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char dname[PATH_MAX];
+ char fname[PATH_MAX];
+ char link[PATH_MAX];
+ char virt_dev[60];
+ int err = -2;
+ struct media_device_entry *md_ptr = NULL;
+ char *p, *device;
+ enum bus_type bus;
+ static int virtual = 0;
+
+ snprintf(dname, PATH_MAX, "/sys/class/%s", class);
+ dir = opendir(dname);
+ if (!dir) {
+ return 0;
+ }
+ for (entry = readdir(dir); entry; entry = readdir(dir)) {
+ /* Skip . and .. */
+ if (entry->d_name[0] == '.')
+ continue;
+ /* Canonicalize the device name */
+ snprintf(fname, PATH_MAX, "%s/%s", dname, entry->d_name);
+ if (realpath(fname, link)) {
+ device = link;
+
+ /* Remove the subsystem/class_name from the string */
+ p = strstr(device, class);
+ if (!p)
+ continue;
+ *(p - 1) = '\0';
+
+ bus = get_bus(device);
+
+ /* remove the /sys/devices/ from the name */
+ device += 13;
+
+ switch (bus) {
+ case MEDIA_BUS_PCI:
+ /* Remove the device function nr */
+ p = strrchr(device, '.');
+ if (!p)
+ continue;
+ *p = '\0';
+ break;
+ case MEDIA_BUS_USB:
+ /* Remove USB interface from the path */
+ p = strrchr(device, '/');
+ if (!p)
+ continue;
+ /* In case we have a device where the driver
+ attaches directly to the usb device rather
+ then to an interface */
+ if (!strchr(p, ':'))
+ break;
+ *p = '\0';
+ break;
+ case MEDIA_BUS_VIRTUAL:
+ /* Don't group virtual devices */
+ sprintf(virt_dev, "virtual%d", virtual++);
+ device = virt_dev;
+ break;
+ case MEDIA_BUS_UNKNOWN:
+ break;
+ }
+
+ /* Add one more element to the devices struct */
+ *md = realloc(*md, (*md_size + 1) * sizeof(*md_ptr));
+ if (!*md)
+ goto error;
+ md_ptr = (*md) + *md_size;
+ (*md_size)++;
+
+ /* Cleans previous data and fills it with device/node */
+ memset(md_ptr, 0, sizeof(*md_ptr));
+ md_ptr->type = UNKNOWN;
+ md_ptr->device = strdup(device);
+ md_ptr->node = strdup(entry->d_name);
+
+ /* Retrieve major and minor information */
+ get_uevent_info(md_ptr, dname);
+
+ /* Used to identify the type of node */
+ fill(md_ptr);
+ }
+ }
+ err = 0;
+error:
+ closedir(dir);
+ return err;
+}
+
+static int add_v4l_class(struct media_device_entry *md)
+{
+ if (strstr(md->node, "video"))
+ md->type = MEDIA_V4L_VIDEO;
+ else if (strstr(md->node, "vbi"))
+ md->type = MEDIA_V4L_VBI;
+ else if (strstr(md->node, "radio"))
+ md->type = MEDIA_V4L_RADIO;
+ else if (strstr(md->node, "v4l-subdev"))
+ md->type = MEDIA_V4L_SUBDEV;
+
+ return 0;
+}
+
+static int add_snd_class(struct media_device_entry *md)
+{
+ unsigned c = 65535, d = 65535;
+ char node[64];
+
+ if (strstr(md->node, "timer")) {
+ md->type = MEDIA_SND_TIMER;
+ return 0;
+ } else if (strstr(md->node, "seq")) {
+ md->type = MEDIA_SND_SEQ;
+ return 0;
+ } if (strstr(md->node, "card")) {
+ sscanf(md->node, "card%u", &c);
+ md->type = MEDIA_SND_CARD;
+ } else if (strstr(md->node, "hw")) {
+ sscanf(md->node, "hwC%uD%u", &c, &d);
+ md->type = MEDIA_SND_HW;
+ } else if (strstr(md->node, "control")) {
+ sscanf(md->node, "controlC%u", &c);
+ md->type = MEDIA_SND_CONTROL;
+ } else if (strstr(md->node, "pcm")) {
+ sscanf(md->node, "pcmC%uD%u", &c, &d);
+ if (md->node[strlen(md->node) - 1] == 'p')
+ md->type = MEDIA_SND_OUT;
+ else if (md->node[strlen(md->node) - 1] == 'c')
+ md->type = MEDIA_SND_CAP;
+ }
+
+ if (c == 65535)
+ return 0;
+
+ /* Reformat device to be useful for alsa userspace library */
+ if (d == 65535)
+ snprintf(node, sizeof(node), "hw:%u", c);
+ else
+ snprintf(node, sizeof(node), "hw:%u,%u", c, d);
+
+ free(md->node);
+ md->node = strdup(node);
+
+ return 0;
+}
+
+static int add_dvb_class(struct media_device_entry *md)
+{
+ if (strstr(md->node, "video"))
+ md->type = MEDIA_DVB_VIDEO;
+ if (strstr(md->node, "audio"))
+ md->type = MEDIA_DVB_AUDIO;
+ if (strstr(md->node, "sec"))
+ md->type = MEDIA_DVB_SEC;
+ if (strstr(md->node, "frontend"))
+ md->type = MEDIA_DVB_FRONTEND;
+ else if (strstr(md->node, "demux"))
+ md->type = MEDIA_DVB_DEMUX;
+ else if (strstr(md->node, "dvr"))
+ md->type = MEDIA_DVB_DVR;
+ else if (strstr(md->node, "net"))
+ md->type = MEDIA_DVB_NET;
+ else if (strstr(md->node, "ca"))
+ md->type = MEDIA_DVB_CA;
+ else if (strstr(md->node, "osd"))
+ md->type = MEDIA_DVB_OSD;
+
+ return 0;
+}
+
+static int sort_media_device_entry(const void *a, const void *b)
+{
+ const struct media_device_entry *md_a = a;
+ const struct media_device_entry *md_b = b;
+ int cmp;
+
+ cmp = strcmp(md_a->device, md_b->device);
+ if (cmp)
+ return cmp;
+ cmp = (int)md_a->type - (int)md_b->type;
+ if (cmp)
+ return cmp;
+
+ return strcmp(md_a->node, md_b->node);
+}
+
+
+/* Public functions */
+
+void free_media_devices(void *opaque)
+{
+ struct media_devices *md = opaque;
+ struct media_device_entry *md_ptr = md->md_entry;
+ int i;
+ for (i = 0; i < md->md_size; i++) {
+ free(md_ptr->node);
+ free(md_ptr->device);
+ md_ptr++;
+ }
+ free(md->md_entry);
+ free(md);
+}
+
+void *discover_media_devices(void)
+{
+ struct media_devices *md = NULL;
+ struct media_device_entry *md_entry = NULL;
+
+ md = calloc(1, sizeof(*md));
+ if (!md)
+ return NULL;
+
+ md->md_size = 0;
+ if (get_class("video4linux", &md_entry, &md->md_size, add_v4l_class))
+ goto error;
+ if (get_class("sound", &md_entry, &md->md_size, add_snd_class))
+ goto error;
+ if (get_class("dvb", &md_entry, &md->md_size, add_dvb_class))
+ goto error;
+
+ /* There's no media device */
+ if (!md_entry)
+ goto error;
+
+ qsort(md_entry, md->md_size, sizeof(*md_entry), sort_media_device_entry);
+
+ md->md_entry = md_entry;
+
+ return md;
+
+error:
+ free_media_devices(md);
+ return NULL;
+}
+
+const char *media_device_type(enum device_type type)
+{
+ switch(type) {
+ /* V4L nodes */
+ case MEDIA_V4L_VIDEO:
+ return "video";
+ case MEDIA_V4L_VBI:
+ return "vbi";
+ case MEDIA_V4L_RADIO:
+ return "radio";
+ case MEDIA_V4L_SUBDEV:
+ return "v4l subdevice";
+
+ /* DVB nodes */
+ case MEDIA_DVB_VIDEO:
+ return "dvb video";
+ case MEDIA_DVB_AUDIO:
+ return "dvb audio";
+ case MEDIA_DVB_SEC:
+ return "dvb sec";
+ case MEDIA_DVB_FRONTEND:
+ return "dvb frontend";
+ case MEDIA_DVB_DEMUX:
+ return "dvb demux";
+ case MEDIA_DVB_DVR:
+ return "dvb dvr";
+ case MEDIA_DVB_NET:
+ return "dvb net";
+ case MEDIA_DVB_CA:
+ return "dvb conditional access";
+ case MEDIA_DVB_OSD:
+ return "dvb OSD";
+
+ /* Alsa nodes */
+ case MEDIA_SND_CARD:
+ return "sound card";
+ case MEDIA_SND_CAP:
+ return "pcm capture";
+ case MEDIA_SND_OUT:
+ return "pcm output";
+ case MEDIA_SND_CONTROL:
+ return "mixer";
+ case MEDIA_SND_HW:
+ return "sound hardware";
+ case MEDIA_SND_TIMER:
+ return "sound timer";
+ case MEDIA_SND_SEQ:
+ return "sound sequencer";
+
+ default:
+ return "unknown";
+ };
+}
+
+void display_media_devices(void *opaque)
+{
+ struct media_devices *md = opaque;
+ struct media_device_entry *md_ptr = md->md_entry;
+ int i;
+ char *prev = "";
+
+ for (i = 0; i < md->md_size; i++) {
+ if (strcmp(prev, md_ptr->device)) {
+ printf("\nDevice %s:\n\t", md_ptr->device);
+ prev = md_ptr->device;
+ }
+ printf("%s(%s, dev %i:%i) ", md_ptr->node,
+ media_device_type(md_ptr->type),
+ md_ptr->major, md_ptr->minor);
+ md_ptr++;
+ }
+ printf("\n");
+}
+
+const char *get_associated_device(void *opaque,
+ const char *last_seek,
+ const enum device_type desired_type,
+ const char *seek_device,
+ const enum device_type seek_type)
+{
+ struct media_devices *md = opaque;
+ struct media_device_entry *md_ptr = md->md_entry;
+ int i, found = 0;
+ char *prev, *p;
+
+ if (seek_type != NONE && seek_device[0]) {
+ /* Get just the device name */
+ p = strrchr(seek_device, '/');
+ if (p)
+ seek_device = p + 1;
+
+ /* Step 1: Find the seek node */
+ for (i = 0; i < md->md_size; i++, md_ptr++) {
+ if (last_seek && md_ptr->type == seek_type &&
+ !strcmp(md_ptr->node, last_seek)) {
+ found = 1;
+ continue;
+ }
+ if (last_seek && !found)
+ continue;
+ if (md_ptr->type == seek_type &&
+ !strcmp(seek_device, md_ptr->node))
+ break;
+ }
+ if (i == md->md_size)
+ return NULL;
+ i++;
+ prev = md_ptr->device;
+ md_ptr++;
+ /* Step 2: find the associated node */
+ for (; i < md->md_size && !strcmp(prev, md_ptr->device); i++, md_ptr++) {
+ if (last_seek && md_ptr->type == seek_type &&
+ !strcmp(md_ptr->node, last_seek)) {
+ found = 1;
+ continue;
+ }
+ if (last_seek && !found)
+ continue;
+ if (md_ptr->type == desired_type)
+ return md_ptr->node;
+ }
+ } else {
+ for (i = 0; i < md->md_size; i++, md_ptr++) {
+ if (last_seek && !strcmp(md_ptr->node, last_seek)) {
+ found = 1;
+ continue;
+ }
+ if (last_seek && !found)
+ continue;
+ if (md_ptr->type == desired_type)
+ return md_ptr->node;
+ }
+ }
+
+ return NULL;
+}
+
+const char *fget_associated_device(void *opaque,
+ const char *last_seek,
+ const enum device_type desired_type,
+ const int fd_seek_device,
+ const enum device_type seek_type)
+{
+ struct media_devices *md = opaque;
+ struct media_device_entry *md_ptr = md->md_entry;
+ struct stat f_status;
+ unsigned int dev_major, dev_minor;
+ int i, found = 0;
+ char *prev;
+
+ if (fstat(fd_seek_device, &f_status)) {
+ perror("Can't get file status");
+ return NULL;
+ }
+ if (!S_ISCHR(f_status.st_mode)) {
+ fprintf(stderr, "File descriptor is not a char device\n");
+ return NULL;
+ }
+ dev_major = major(f_status.st_rdev);
+ dev_minor = minor(f_status.st_rdev);
+
+ /* Step 1: Find the seek node */
+ for (i = 0; i < md->md_size; i++, md_ptr++) {
+ if (last_seek && md_ptr->type == seek_type
+ && md_ptr->major == dev_major
+ && md_ptr->minor == dev_minor) {
+ found = 1;
+ continue;
+ }
+ if (last_seek && !found)
+ continue;
+ if (md_ptr->type == seek_type
+ && md_ptr->major == dev_major
+ && md_ptr->minor == dev_minor)
+ break;
+ }
+ if (i == md->md_size)
+ return NULL;
+ i++;
+ prev = md_ptr->device;
+ md_ptr++;
+ /* Step 2: find the associated node */
+ for (; i < md->md_size && !strcmp(prev, md_ptr->device); i++, md_ptr++) {
+ if (last_seek && md_ptr->type == seek_type
+ && md_ptr->major == dev_major
+ && md_ptr->minor == dev_minor) {
+ found = 1;
+ continue;
+ }
+ if (last_seek && !found)
+ continue;
+ if (md_ptr->type == desired_type)
+ return md_ptr->node;
+ }
+ return NULL;
+}
+
+const char *get_not_associated_device(void *opaque,
+ const char *last_seek,
+ const enum device_type desired_type,
+ const enum device_type not_desired_type)
+{
+ struct media_devices *md = opaque;
+ struct media_device_entry *md_ptr = md->md_entry;
+ int i, skip = 0, found = 0;
+ char *prev = "", *result = NULL;
+
+ /* Step 1: Find a device without seek_type node */
+ for (i = 0; i < md->md_size; i++, md_ptr++) {
+ if (last_seek && !strcmp(md_ptr->node, last_seek)) {
+ found = 1;
+ continue;
+ }
+ if (last_seek && !found)
+ continue;
+ if (strcmp(prev, md_ptr->device)) {
+ if (!skip && result)
+ break;
+ prev = md_ptr->device;
+ skip = 0;
+ result = NULL;
+ }
+ if (md_ptr->type == not_desired_type)
+ skip = 1;
+ else if (!skip && !result && md_ptr->type == desired_type)
+ result = md_ptr->node;
+ }
+ if (skip)
+ result = NULL;
+
+ return result;
+}
+
+#endif
new file mode 100644
@@ -0,0 +1,168 @@
+/*
+ Copyright © 2011 by Mauro Carvalho Chehab <mchehab@redhat.com>
+
+ The get_media_devices is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The libv4l2util 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the libv4l2util Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA
+ 02110-1335 USA.
+ */
+
+/*
+ * Version of the API
+ */
+#define GET_MEDIA_DEVICES_VERSION 0x0105
+
+/**
+ * enum device_type - Enumerates the type for each device
+ *
+ * The device_type is used to sort the media devices array.
+ * So, the order is relevant. The first device should be
+ * MEDIA_V4L_VIDEO.
+ */
+enum device_type {
+ UNKNOWN = 65535,
+ NONE = 65534,
+ MEDIA_V4L_VIDEO = 0,
+ MEDIA_V4L_VBI,
+ MEDIA_V4L_RADIO,
+ MEDIA_V4L_SUBDEV,
+
+ MEDIA_DVB_VIDEO = 100,
+ MEDIA_DVB_AUDIO,
+ MEDIA_DVB_SEC,
+ MEDIA_DVB_FRONTEND,
+ MEDIA_DVB_DEMUX,
+ MEDIA_DVB_DVR,
+ MEDIA_DVB_CA,
+ MEDIA_DVB_NET,
+ MEDIA_DVB_OSD,
+
+ MEDIA_SND_CARD = 200,
+ MEDIA_SND_CAP,
+ MEDIA_SND_OUT,
+ MEDIA_SND_CONTROL,
+ MEDIA_SND_HW,
+ MEDIA_SND_TIMER,
+ MEDIA_SND_SEQ,
+ /*
+ * FIXME: not all alsa devices were mapped. missing things like
+ * midi, midiC%iD%i and timer interfaces
+ */
+};
+
+enum bus_type {
+ MEDIA_BUS_UNKNOWN,
+ MEDIA_BUS_VIRTUAL,
+ MEDIA_BUS_PCI,
+ MEDIA_BUS_USB,
+};
+
+/**
+ * discover_media_devices() - Returns a list of the media devices
+ * @md_size: Returns the size of the media devices found
+ *
+ * This function reads the /sys/class nodes for V4L, DVB and sound,
+ * and returns an opaque desciptor that keeps a list of the devices.
+ * The fields on this list is opaque, as they can be changed on newer
+ * releases of this library. So, all access to it should be done via
+ * a function provided by the API. The devices are ordered by device,
+ * type and node. At return, md_size is updated.
+ */
+void *discover_media_devices(void);
+
+/**
+ * free_media_devices() - Frees the media devices array
+ *
+ * @opaque: media devices opaque descriptor
+ *
+ * As discover_media_devices() dynamically allocate space for the
+ * strings, feeing the list requires also to free those data. So,
+ * the safest and recommended way is to call this function.
+ */
+void free_media_devices(void *opaque);
+
+/**
+ * media_device_type() - returns a string with the name of a given type
+ *
+ * @type: media device type
+ */
+const char *media_device_type(const enum device_type type);
+
+/**
+ * display_media_devices() - prints a list of media devices
+ *
+ * @opaque: media devices opaque descriptor
+ */
+void display_media_devices(void *opaque);
+
+/**
+ * get_associated_device() - Return the next device associated with another one
+ *
+ * @opaque: media devices opaque descriptor
+ * @last_seek: last seek result. Use NULL to get the first result
+ * @desired_type: type of the desired device
+ * @seek_device: name of the device with you want to get an association.
+ *@ seek_type: type of the seek device. Using NONE produces the same
+ * result of using NULL for the seek_device.
+ *
+ * This function seeks inside the media_devices struct for the next device
+ * that it is associated with a seek parameter.
+ * It can be used to get an alsa device associated with a video device. If
+ * the seek_device is NULL or seek_type is NONE, it will just search for
+ * devices of the desired_type.
+ */
+const char *get_associated_device(void *opaque,
+ const char *last_seek,
+ const enum device_type desired_type,
+ const char *seek_device,
+ const enum device_type seek_type);
+
+/**
+ * fget_associated_device() - Return the next device associated with another one
+ *
+ * @opaque: media devices opaque descriptor
+ * @last_seek: last seek result. Use NULL to get the first result
+ * @desired_type: type of the desired device
+ * @fd_seek_device: file handler for the device where the association will
+ be made
+ *@ seek_type: type of the seek device. Using NONE produces the same
+ * result of using NULL for the seek_device.
+ *
+ * This function seeks inside the media_devices struct for the next device
+ * that it is associated with a seek parameter.
+ * It can be used to get an alsa device associated with an open file descriptor
+ */
+const char *fget_associated_device(void *opaque,
+ const char *last_seek,
+ const enum device_type desired_type,
+ const int fd_seek_device,
+ const enum device_type seek_type);
+
+/**
+ * get_not_associated_device() - Return the next device not associated with
+ * an specific device type.
+ *
+ * @opaque: media devices opaque descriptor
+ * @last_seek: last seek result. Use NULL to get the first result
+ * @desired_type: type of the desired device
+ * @not_desired_type: type of the seek device
+ *
+ * This function seeks inside the media_devices struct for the next physical
+ * device that doesn't support a non_desired type.
+ * This method is useful for example to return the audio devices that are
+ * provided by the motherboard.
+ */
+const char *get_not_associated_device(void *opaque,
+ const char *last_seek,
+ const enum device_type desired_type,
+ const enum device_type not_desired_type);
This will allow us to automatically detect the tv-card and the matching alsa capture device for the alsa digital loopback. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- src/Makefile.am | 6 +- src/get_media_devices.c | 597 ++++++++++++++++++++++++++++++++++++++++++++++++ src/get_media_devices.h | 168 ++++++++++++++ 3 files changed, 769 insertions(+), 2 deletions(-) create mode 100644 src/get_media_devices.c create mode 100644 src/get_media_devices.h