Devices

Goal

This tutorial elaborates on how the interface for devices provides abstractions for physical or virtual devices that may be present in the system, such as cameras, microphones, DVD drives, or screen captures. After reading the tutorial you will know how to:

  • Enumerate the available devices

  • List devices properties

  • List the eventual output stream formats provided devices

Prerequisites

Walkthrough

Device enumeration is quite straightforward. flu_device_list_get returns a double-linked GList of pointers to FluDevice. Remember that, as usual, this list has to be freed calling its specific cleanup function, in this case flu_device_list_free

Enumerate devices
static void _enumerate_devices()
{
    GList *devices = flu_device_list_get();

    guint size = g_list_length(devices);
    if (size > 0)
    {
        g_print("Devices list (%u):\n", size);

        gint idx = 0;
        for (GList *item = devices; item; item = item->next)
        {
            FluDevice *device = (FluDevice *)item->data;
            _device_display_properties(device, idx++);
        }
    }
    else
    {
        g_print("No devices available.\n");
    }

    flu_device_list_free(devices);
}

Device’s properties can be queried by calling flu_device_<property>_get functions, passing them a pointer to FluDevice.

Querying device properties
static void _device_display_properties(const FluDevice *device, gint idx)
{
    g_print("\n  #%03d => name: %s\n"
            "          fake: %s\n"
            "          type: %s\n"
            "         label: %s\n"
            "            id: %s\n"
            "           uri: %s\n",
            idx++,
            STRING_OR_NONE(flu_device_name_get(device)),
            YES_OR_NO(flu_device_is_fake(device)),
            flu_device_type_name_get(flu_device_type_get(device)),
            STRING_OR_NONE(flu_device_label_get(device)),
            STRING_OR_NONE(flu_device_current_media_id_get(device)),
            STRING_OR_NONE(flu_device_uri_get(device)));

Some devices are fake devices. These devices are intended for debug, and can be used, for example, to impersonate real devices in testing, when the real devices are not available.

A device can provide zero or many output formats. To get a list of available formats, use flu_device_output_formats_get.

Getting available formats
static void _device_display_output_formats(const FluDevice *device)
{
    const GList *formats = flu_device_output_formats_get((FluDevice *)device);
    if (formats)
    {
        const GList *l;
        for (l = formats; l != NULL; l = l->next)
        {
            const FluStreamInfo *format_info = (const FluStreamInfo *)l->data;
            const FluStreamInfoData *format_data = &format_info->data;

            _stream_info_display(format_info);

            switch (format_info->type)
            {
            case FLU_STREAM_TYPE_VIDEO:
                _stream_info_video_display_format((const FluStreamVideoInfo *)format_data);
                break;
            case FLU_STREAM_TYPE_AUDIO:
                _stream_info_audio_display_format((const FluStreamAudioInfo *)format_data);
                break;

            case FLU_STREAM_TYPE_TEXT:
                break;

            case FLU_STREAM_TYPE_DATA:
                break;

            default:
                g_abort();
            }
        }
    }
}

The returned FluStreamInfoData is an union, and has to be cast to the corresponding FluStream<Type>Info struct by querying the type member. A similar logic is also done in the Print for each stream part of the Stream Management tutorial.

Finally, it’s possible to inspect specific features of a given format. Please keep in mind that not all struct fields have a sense for all devices. In real-world code, you must inspect only those fields that have a role in your device.

Display format details
static void _stream_info_video_display_format(const FluStreamVideoInfo *info)
{
    g_print("           "
            " video format: %dx%d@%.1f,"
            " bitrate: %d,"
            " aspect ratio: %.1f,"
            " codec: %s\n",
            info->width,
            info->height,
            _fraction(info->fps_n, info->fps_d),
            info->bitrate,
            _fraction(info->par_n, info->par_d),
            STRING_OR_NONE(info->codec));
}

Full source code

Full source code
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <fluendo-sdk.h>
#include <stdio.h>

#define STRING_OR_NONE(str) ((str) && (str)[0]) ? (str) : "none"
#define YES_OR_NO(val) ((val)) ? "yes" : "no"

static float _fraction(gint d, gint n)
{
    if (n == 0)
    {
        return 0.0f;
    }

    return (float)d / (float)n;
}

static const char *_verbose_channels(gint channels)
{
    switch (channels)
    {
    case 0:
        return "none";
    case 1:
        return "mono";
    case 2:
        return "stereo";
    case 6:
        return "5.1";
    default:
        return "Unknow";
    }
}

static void _stream_info_video_display_format(const FluStreamVideoInfo *info)
{
    g_print("           "
            " video format: %dx%d@%.1f,"
            " bitrate: %d,"
            " aspect ratio: %.1f,"
            " codec: %s\n",
            info->width,
            info->height,
            _fraction(info->fps_n, info->fps_d),
            info->bitrate,
            _fraction(info->par_n, info->par_d),
            STRING_OR_NONE(info->codec));
}

static void _stream_info_audio_display_format(const FluStreamAudioInfo *info)
{
    g_print("           "
            " audio format: %s@%dhz,"
            " width: %d"
            " bitrate: %d,"
            " lang: %s[%s],"
            " codec: %s,"
            " extension: %d,"
            " description: %d\n",
            _verbose_channels(info->channels),
            info->rate,
            info->width,
            info->bitrate,
            STRING_OR_NONE(info->language),
            STRING_OR_NONE(info->iso),
            STRING_OR_NONE(info->codec),
            info->extension,
            info->audio_description);
}

static void _stream_info_display(const FluStreamInfo *info)
{
    g_print("        "
            "        "
            " Stream index: %d,"
            " group index: %d,"
            " encrypted: %s\n",
            info->index,
            info->group_index,
            YES_OR_NO(info->is_encrypted));
}

static void _device_display_output_formats(const FluDevice *device)
{
    const GList *formats = flu_device_output_formats_get((FluDevice *)device);
    if (formats)
    {
        const GList *l;
        for (l = formats; l != NULL; l = l->next)
        {
            const FluStreamInfo *format_info = (const FluStreamInfo *)l->data;
            const FluStreamInfoData *format_data = &format_info->data;

            _stream_info_display(format_info);

            switch (format_info->type)
            {
            case FLU_STREAM_TYPE_VIDEO:
                _stream_info_video_display_format((const FluStreamVideoInfo *)format_data);
                break;
            case FLU_STREAM_TYPE_AUDIO:
                _stream_info_audio_display_format((const FluStreamAudioInfo *)format_data);
                break;

            case FLU_STREAM_TYPE_TEXT:
                break;

            case FLU_STREAM_TYPE_DATA:
                break;

            default:
                g_abort();
            }
        }
    }
}

static void _device_display_properties(const FluDevice *device, gint idx)
{
    g_print("\n  #%03d => name: %s\n"
            "          fake: %s\n"
            "          type: %s\n"
            "         label: %s\n"
            "            id: %s\n"
            "           uri: %s\n",
            idx++,
            STRING_OR_NONE(flu_device_name_get(device)),
            YES_OR_NO(flu_device_is_fake(device)),
            flu_device_type_name_get(flu_device_type_get(device)),
            STRING_OR_NONE(flu_device_label_get(device)),
            STRING_OR_NONE(flu_device_current_media_id_get(device)),
            STRING_OR_NONE(flu_device_uri_get(device)));

    _device_display_output_formats(device);
}

static void _enumerate_devices()
{
    GList *devices = flu_device_list_get();

    guint size = g_list_length(devices);
    if (size > 0)
    {
        g_print("Devices list (%u):\n", size);

        gint idx = 0;
        for (GList *item = devices; item; item = item->next)
        {
            FluDevice *device = (FluDevice *)item->data;
            _device_display_properties(device, idx++);
        }
    }
    else
    {
        g_print("No devices available.\n");
    }

    flu_device_list_free(devices);
}

int main(void)
{
    /* Initialize Fluendo SDK */
    flu_initialize();

    _enumerate_devices();

    /* Shutdowns library */
    flu_shutdown();
    return 0;
}

You can download it here.

Building

This source code along with the rest of tutorials can be compiled using the following commands.

On Linux:

mkdir fluendo-sdk-tutorials && cd fluendo-sdk-tutorials
meson /opt/fluendo-sdk/share/doc/fluendo-sdk/tutorials/src
ninja

On Windows:

mkdir fluendo-sdk-tutorials
cd fluendo-sdk-tutorials
meson C:\fluendo-sdk\<version>\<x86/x86_64>\share\doc\fluendo-sdk\tutorials\src
ninja

To generate a Visual Studio project, you can pass the --backend=vs option to meson.

Conclusions

During this tutorial the following concepts have been consolidated: