1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qgstreamervideodevices_p.h" |
5 | #include "qmediadevices.h" |
6 | #include "private/qcameradevice_p.h" |
7 | |
8 | #include "qgstreameraudiosource_p.h" |
9 | #include "qgstreameraudiosink_p.h" |
10 | #include "qgstreameraudiodevice_p.h" |
11 | #include "qgstutils_p.h" |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | static gboolean deviceMonitor(GstBus *, GstMessage *message, gpointer m) |
16 | { |
17 | auto *manager = static_cast<QGstreamerVideoDevices *>(m); |
18 | GstDevice *device = nullptr; |
19 | |
20 | switch (GST_MESSAGE_TYPE (message)) { |
21 | case GST_MESSAGE_DEVICE_ADDED: |
22 | gst_message_parse_device_added(message, device: &device); |
23 | manager->addDevice(device); |
24 | break; |
25 | case GST_MESSAGE_DEVICE_REMOVED: |
26 | gst_message_parse_device_removed(message, device: &device); |
27 | manager->removeDevice(device); |
28 | break; |
29 | default: |
30 | break; |
31 | } |
32 | if (device) |
33 | gst_object_unref (object: device); |
34 | |
35 | return G_SOURCE_CONTINUE; |
36 | } |
37 | |
38 | QGstreamerVideoDevices::QGstreamerVideoDevices(QPlatformMediaIntegration *integration) |
39 | : QPlatformVideoDevices(integration) |
40 | { |
41 | GstDeviceMonitor *monitor; |
42 | GstBus *bus; |
43 | |
44 | monitor = gst_device_monitor_new(); |
45 | |
46 | gst_device_monitor_add_filter (monitor, classes: nullptr, caps: nullptr); |
47 | |
48 | bus = gst_device_monitor_get_bus(monitor); |
49 | gst_bus_add_watch(bus, func: deviceMonitor, user_data: this); |
50 | gst_object_unref(object: bus); |
51 | |
52 | gst_device_monitor_start(monitor); |
53 | |
54 | auto devices = gst_device_monitor_get_devices(monitor); |
55 | |
56 | while (devices) { |
57 | GstDevice *device = static_cast<GstDevice *>(devices->data); |
58 | addDevice(device); |
59 | gst_object_unref(object: device); |
60 | devices = g_list_delete_link(list: devices, link_: devices); |
61 | } |
62 | } |
63 | |
64 | QList<QCameraDevice> QGstreamerVideoDevices::videoDevices() const |
65 | { |
66 | QList<QCameraDevice> devices; |
67 | |
68 | for (auto device : m_videoSources) { |
69 | QCameraDevicePrivate *info = new QCameraDevicePrivate; |
70 | auto *desc = gst_device_get_display_name(device: device.gstDevice); |
71 | info->description = QString::fromUtf8(utf8: desc); |
72 | g_free(mem: desc); |
73 | info->id = device.id; |
74 | |
75 | if (QGstStructure properties = gst_device_get_properties(device: device.gstDevice); !properties.isNull()) { |
76 | auto def = properties["is-default" ].toBool(); |
77 | info->isDefault = def && *def; |
78 | properties.free(); |
79 | } |
80 | |
81 | if (info->isDefault) |
82 | devices.prepend(t: info->create()); |
83 | else |
84 | devices.append(t: info->create()); |
85 | |
86 | auto caps = QGstCaps(gst_device_get_caps(device: device.gstDevice), QGstCaps::HasRef); |
87 | if (!caps.isNull()) { |
88 | QList<QCameraFormat> formats; |
89 | QSet<QSize> photoResolutions; |
90 | |
91 | int size = caps.size(); |
92 | for (int i = 0; i < size; ++i) { |
93 | auto cap = caps.at(index: i); |
94 | |
95 | QSize resolution = cap.resolution(); |
96 | if (!resolution.isValid()) |
97 | continue; |
98 | |
99 | auto pixelFormat = cap.pixelFormat(); |
100 | auto frameRate = cap.frameRateRange(); |
101 | |
102 | auto *f = new QCameraFormatPrivate{ |
103 | QSharedData(), |
104 | .pixelFormat: pixelFormat, |
105 | .resolution: resolution, |
106 | .minFrameRate: frameRate.min, |
107 | .maxFrameRate: frameRate.max |
108 | }; |
109 | formats << f->create(); |
110 | photoResolutions.insert(value: resolution); |
111 | } |
112 | info->videoFormats = formats; |
113 | // ### sort resolutions? |
114 | info->photoResolutions = photoResolutions.values(); |
115 | } |
116 | } |
117 | return devices; |
118 | } |
119 | |
120 | void QGstreamerVideoDevices::addDevice(GstDevice *device) |
121 | { |
122 | if (gst_device_has_classes(device, classes: "Video/Source" )) { |
123 | gst_object_ref(object: device); |
124 | m_videoSources.push_back(x: {.gstDevice: device, .id: QByteArray::number(m_idGenerator)}); |
125 | emit videoInputsChanged(); |
126 | m_idGenerator++; |
127 | } |
128 | } |
129 | |
130 | void QGstreamerVideoDevices::removeDevice(GstDevice *device) |
131 | { |
132 | auto it = std::find_if(first: m_videoSources.begin(), last: m_videoSources.end(), |
133 | pred: [=](const QGstDevice &a) { return a.gstDevice == device; }); |
134 | |
135 | if (it != m_videoSources.end()) { |
136 | m_videoSources.erase(position: it); |
137 | emit videoInputsChanged(); |
138 | } |
139 | |
140 | gst_object_unref(object: device); |
141 | } |
142 | |
143 | GstDevice *QGstreamerVideoDevices::videoDevice(const QByteArray &id) const |
144 | { |
145 | auto it = std::find_if(first: m_videoSources.begin(), last: m_videoSources.end(), |
146 | pred: [=](const QGstDevice &a) { return a.id == id; }); |
147 | return it != m_videoSources.end() ? it->gstDevice : nullptr; |
148 | } |
149 | |
150 | QT_END_NAMESPACE |
151 | |