Device Enumeration

This section describes device enumeration, querying information about attached devices and their connection topology.

Simple Device Enumeration

The simplest way to gather information about the available video capture devices is to create a device enumerator (IC4_DEVICE_ENUM) and query the global device list.

The following code snippet shows how to get the list of devices and print device information to the console:

void print_device_list()
{
    printf("Enumerating all attached video capture devices in a single list...\n");
 
    struct IC4_DEVICE_ENUM* enumerator = NULL;
    if (!ic4_devenum_create(&enumerator))
    {
        printf("Failed to create device enumerator");
        goto print_error;
    }
 
    if (!ic4_devenum_update_device_list(enumerator))
    {
        printf("Failed to update device list");
        goto print_error;
    }
 
    int count = ic4_devenum_get_device_count(enumerator);
    if (count == 0)
    {
        printf("No devices found\n");
        goto cleanup;
    }
    
    printf("Found %d devices:\n", count);
    for (int i = 0; i < count; ++i)
    {
        struct IC4_DEVICE_INFO* info = NULL;
        if (!ic4_devenum_get_devinfo(enumerator, i, &info))
        {
            printf("Failed to query device info for index %d\n", i);
            print_last_error();
            continue;
        }
 
        printf("\t");
        print_device_info(info);
        printf("\n");
 
        ic4_devinfo_unref(info);
    }
 
    printf("\n");
    goto cleanup;
 
print_error:
    print_last_error();
 
cleanup:
    ic4_devenum_unref(enumerator);
}
bool ic4_devenum_create(struct IC4_DEVICE_ENUM **ppEnumerator)
Creates a new device enumerator.
Device enumerator type.
bool ic4_devenum_update_device_list(struct IC4_DEVICE_ENUM *pEnumerator)
Searches for video capture devices and populates the enumerator's internal device list.
bool ic4_devenum_get_devinfo(const struct IC4_DEVICE_ENUM *pEnumerator, int index, struct IC4_DEVICE_INFO **ppInfo)
Returns a IC4_DEVICE_INFO object describing one of the discovered video capture devices.
Device information type.
void ic4_devinfo_unref(struct IC4_DEVICE_INFO *pInfo)
Decreases the device information's internal reference count by one.
void ic4_devenum_unref(struct IC4_DEVICE_ENUM *pEnumerator)
Decreases the device enumerator's internal reference count by one.

The device information is printed using a simple helper function:

void print_device_info(const struct IC4_DEVICE_INFO* device_info)
{
    printf("Model: %s ", ic4_devinfo_get_model_name(device_info));
    printf("Serial: %s", ic4_devinfo_get_serial(device_info));
    printf("Version: %s", ic4_devinfo_get_version(device_info));
}
const char * ic4_devinfo_get_version(const struct IC4_DEVICE_INFO *pInfo)
Get the device version from a device information object.
const char * ic4_devinfo_get_serial(const struct IC4_DEVICE_INFO *pInfo)
Get the textual representation of the serial number from a device information object.
const char * ic4_devinfo_get_model_name(const struct IC4_DEVICE_INFO *pInfo)
Get the model name from a device information object.

Query Interface/Device Topology

Device enumeration can also be performed on a per-interface basis. Interfaces are the physical pieces of hardware that cameras are attached to, e.g. USB controllers or network adapters.

Each interface can in turn be queried for the currently attached video capture devices.

The following code snippet shows how to get the list of interfaces. It then prints interface information and device information to the console:

void print_interface_device_tree()
{
    printf("Enumerating video capture devices by interface...\n");
 
    struct IC4_DEVICE_ENUM* enumerator = NULL;
    if (!ic4_devenum_create(&enumerator))
    {
        printf("Failed to create device enumerator");
        goto print_error;
    }
 
    if (!ic4_devenum_update_interface_list(enumerator))
    {
        printf("Failed to update device list");
        goto print_error;
    }
 
    int itf_count = ic4_devenum_get_interface_count(enumerator);
    if (itf_count == 0)
    {
        printf("No interfaces found\n");
        goto cleanup;
    }
 
    for (int i = 0; i < itf_count; ++i)
    {
        struct IC4_INTERFACE* itf = NULL;
        if (!ic4_devenum_get_devitf(enumerator, i, &itf))
        {
            printf("Failed to query interface info for index %d\n", i);
            print_last_error();
            continue;
        }
 
        printf("Interface: %s\n", ic4_devitf_get_display_name(itf));
        printf("\tProvided by %s [TLType: %s]\n", ic4_devitf_get_tl_name(itf), tltype_to_string(ic4_devitf_get_tl_type(itf)));
 
        if (!ic4_devitf_update_device_list(itf))
        {
            printf("Failed to update interface device list");
            print_last_error();
            ic4_devitf_unref(itf);
            continue;
        }
 
        int dev_count = ic4_devitf_get_device_count(itf);
        if (dev_count == 0)
        {
            printf("\tNo devices found\n");
            ic4_devitf_unref(itf);
            continue;
        }
 
        printf("\tFound %d devices:\n", dev_count);
        for (int j = 0; j < dev_count; ++j)
        {
            struct IC4_DEVICE_INFO* info = NULL;
            if (!ic4_devitf_get_devinfo(itf, j, &info))
            {
                printf("Failed to query device info for index %d\n", j);
                print_last_error();
                continue;
            }
 
            printf("\t\t");
            print_device_info(info);
            printf("\n");
 
            ic4_devinfo_unref(info);
        }
 
        ic4_devitf_unref(itf);
    }
 
    printf("\n");
    goto cleanup;
 
print_error:
    print_last_error();
 
cleanup:
    ic4_devenum_unref(enumerator);
}
bool ic4_devenum_update_interface_list(struct IC4_DEVICE_ENUM *pEnumerator)
Searches for interfaces and populates the enumerator's internal interface list.
int ic4_devenum_get_interface_count(const struct IC4_DEVICE_ENUM *pEnumerator)
Returns the number of interfaces discovered by the previous call to ic4_devenum_update_interface_list...
bool ic4_devenum_get_devitf(const struct IC4_DEVICE_ENUM *pEnumerator, int index, struct IC4_INTERFACE **ppInterface)
Returns a IC4_INTERFACE object describing one of the discovered interfaces.
Device interface type.
void ic4_devitf_unref(struct IC4_INTERFACE *pInterface)
Decreases the device interface's internal reference count by one.
enum IC4_TL_TYPE ic4_devitf_get_tl_type(const struct IC4_INTERFACE *pInterface)
Returns the type of the transport layer used by this interface.
bool ic4_devitf_update_device_list(struct IC4_INTERFACE *pInterface)
Searches for video capture devices and populates the device interfaces's internal device list.
const char * ic4_devitf_get_display_name(const struct IC4_INTERFACE *pInterface)
Returns the name of the device interface.
const char * ic4_devitf_get_tl_name(const struct IC4_INTERFACE *pInterface)
Returns the name of the transport layer that provides this interface object.
int ic4_devitf_get_device_count(const struct IC4_INTERFACE *pInterface)
Returns the number of devices discovered by the previous call to ic4_devitf_update_device_list().
bool ic4_devitf_get_devinfo(const struct IC4_INTERFACE *pInterface, int index, struct IC4_DEVICE_INFO **ppInfo)
Returns a IC4_DEVICE_INFO object describing one of the discovered video capture devices.

Receiving Device Arrival and Removal Notifications

IC4_DEVICE_ENUM objects allow registering a callback function that notifies the program of changes in the list of available devices. To register a callback function, call ic4_devenum_event_add_device_list_changed.

The following code snippet shows how to register and finally unregister a device-list-changed event handler:

int main()
{
    ic4_init_library(NULL);
 
    struct IC4_DEVICE_ENUM* enumerator = NULL;
    if (!ic4_devenum_create(&enumerator))
    {
        printf("Failed to create device enumerator\n");
        goto print_error;
    }
 
    if (!ic4_devenum_event_add_device_list_changed(enumerator, device_list_changed_handler, NULL, NULL))
    {
        printf("Failed to register device-list-changed event handler\n");
        goto print_error;
    }
 
    if (!ic4_devenum_update_device_list(enumerator))
    {
        printf("Failed to update device list\n");
        goto print_error;
    }
 
    int initial_device_count = ic4_devenum_get_device_count(enumerator);
 
    printf("Press ENTER to exit program\n");
    printf("%d devices connected initially.\n", initial_device_count);
 
    (void)getchar();
 
    // This is technically not necessary, since the DeviceEnum object is destroyed at the end of this function.
    if (!ic4_devenum_event_remove_device_list_changed(enumerator, device_list_changed_handler, NULL))
    {
        printf("Failed to unregister device-list-changed event handler\n");
        goto print_error;
    }
 
    goto cleanup;
print_error:
    print_last_error();
cleanup:
    ic4_devenum_unref(enumerator);
    ic4_exit_library();
    return 0;
}
bool ic4_init_library(const struct IC4_INIT_CONFIG *init_config)
Initializes the IC Imaging Control 4 C Library.
bool ic4_devenum_event_add_device_list_changed(struct IC4_DEVICE_ENUM *pEnumerator, ic4_devenum_device_list_change_handler handler, void *user_ptr, ic4_devenum_device_list_change_deleter deleter)
Registers a function to be called when the list of available video capture devices has (potentially) ...
bool ic4_devenum_event_remove_device_list_changed(struct IC4_DEVICE_ENUM *pEnumerator, ic4_devenum_device_list_change_handler handler, void *user_ptr)
Unregisters a device-list-changed handler that was previously registered using ic4_devenum_event_add_...
void ic4_exit_library()
Un-initializes the library.

An example device-list-changed handler can look like this:

void device_list_changed_handler(struct IC4_DEVICE_ENUM* enumerator, void* user_ptr)
{
    ic4_devenum_update_device_list(enumerator);
    int new_device_count = ic4_devenum_get_device_count(enumerator);
 
    printf("Device list has changed!\n");
    printf("Found %d devices\n", new_device_count);
    printf("\n");
}