Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgstreamervideodevices.cpp
Go to the documentation of this file.
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
5
6#include <QtMultimedia/qmediadevices.h>
7#include <QtMultimedia/private/qcameradevice_p.h>
8#include <QtCore/qloggingcategory.h>
9#include <QtCore/qset.h>
10#include <QtCore/private/quniquehandle_types_p.h>
11
12#include <common/qgst_p.h>
13#include <common/qgst_debug_p.h>
14#include <common/qgstutils_p.h>
15#include <common/qglist_helper_p.h>
16
17#if QT_CONFIG(linux_v4l)
18# include <linux/videodev2.h>
19# include <errno.h>
20#endif
21
23
24Q_STATIC_LOGGING_CATEGORY(ltVideoDevices, "qt.multimedia.gstreamer.videodevices");
25
26QGstreamerVideoDevices::QGstreamerVideoDevices(QPlatformMediaIntegration *integration)
31 },
36 },
37 }
38{
39 gst_device_monitor_add_filter(m_deviceMonitor.get(), "Video/Source", nullptr);
40
41 m_busObserver.installMessageFilter(this);
42 gst_device_monitor_start(m_deviceMonitor.get());
43
44 GList *devices = gst_device_monitor_get_devices(m_deviceMonitor.get());
45
46 for (GstDevice *device : QGstUtils::GListRangeAdaptor<GstDevice *>(devices)) {
47 addDevice(QGstDeviceHandle{
48 device,
49 QGstDeviceHandle::HasRef,
50 });
51 }
52
53 g_list_free(devices);
54}
55
56QGstreamerVideoDevices::~QGstreamerVideoDevices()
57{
58 gst_device_monitor_stop(m_deviceMonitor.get());
59}
60
61QList<QCameraDevice> QGstreamerVideoDevices::findVideoInputs() const
62{
63 QList<QCameraDevice> devices;
64
65 for (const auto &device : m_videoSources) {
66 QCameraDevicePrivate *info = new QCameraDevicePrivate;
67
68 QGString desc{
69 gst_device_get_display_name(device.gstDevice.get()),
70 };
71 info->description = desc.toQString();
72 info->id = device.id;
73
74 QUniqueGstStructureHandle properties{
75 gst_device_get_properties(device.gstDevice.get()),
76 };
77 if (properties) {
78 QGstStructureView view{ properties };
79 auto def = view["is-default"].toBool();
80 info->isDefault = def && *def;
81 }
82
83 if (info->isDefault)
84 devices.prepend(info->create());
85 else
86 devices.append(info->create());
87
88 auto caps = QGstCaps(gst_device_get_caps(device.gstDevice.get()), QGstCaps::HasRef);
89 if (caps) {
90 QList<QCameraFormat> formats;
91 QSet<QSize> photoResolutions;
92
93 int size = caps.size();
94 for (int i = 0; i < size; ++i) {
95 auto cap = caps.at(i);
96 auto pixelFormat = cap.pixelFormat();
97 auto frameRate = cap.frameRateRange();
98
99 if (pixelFormat == QVideoFrameFormat::PixelFormat::Format_Invalid) {
100 qCDebug(ltVideoDevices) << "pixel format not supported:" << cap;
101 continue; // skip pixel formats that we don't support
102 }
103
104 auto addFormatForResolution = [&](QSize resolution) {
105 auto *f = new QCameraFormatPrivate{
106 QSharedData(), pixelFormat, resolution, frameRate.min, frameRate.max,
107 };
108 formats.append(f->create());
109 photoResolutions.insert(resolution);
110 };
111
112 std::optional<QGRange<QSize>> resolutionRange = cap.resolutionRange();
113 if (resolutionRange) {
114 addFormatForResolution(resolutionRange->min);
115 addFormatForResolution(resolutionRange->max);
116 } else {
117 QSize resolution = cap.resolution();
118 if (resolution.isValid())
119 addFormatForResolution(resolution);
120 }
121 }
122 info->videoFormats = formats;
123 // ### sort resolutions?
124 info->photoResolutions = photoResolutions.values();
125 }
126 }
127 return devices;
128}
129
130void QGstreamerVideoDevices::addDevice(QGstDeviceHandle device)
131{
132 Q_ASSERT(gst_device_has_classes(device.get(), "Video/Source"));
133
134#if QT_CONFIG(linux_v4l)
135 QUniqueGstStructureHandle structureHandle{
136 gst_device_get_properties(device.get()),
137 };
138
139 const auto *p = QGstStructureView(structureHandle.get())["device.path"].toString();
140 if (p) {
141 QUniqueFileDescriptorHandle fd{
142 qt_safe_open(p, O_RDONLY),
143 };
144
145 if (!fd) {
146 qCDebug(ltVideoDevices) << "Cannot open v4l2 device:" << p;
147 return;
148 }
149
150 struct v4l2_capability cap;
151 if (::ioctl(fd.get(), VIDIOC_QUERYCAP, &cap) < 0) {
152 qCWarning(ltVideoDevices)
153 << "ioctl failed: VIDIOC_QUERYCAP" << qt_error_string(errno) << p;
154 return;
155 }
156
157 if (cap.device_caps & V4L2_CAP_META_CAPTURE) {
158 qCDebug(ltVideoDevices) << "V4L2_CAP_META_CAPTURE device detected" << p;
159 return;
160 }
161
162 constexpr uint32_t videoCaptureCapabilities =
163 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
164
165 if (!(cap.capabilities & videoCaptureCapabilities)) {
166 qCDebug(ltVideoDevices)
167 << "not a V4L2_CAP_VIDEO_CAPTURE or V4L2_CAP_VIDEO_CAPTURE_MPLANE device" << p;
168 return;
169 }
170 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
171 qCDebug(ltVideoDevices) << "not a V4L2_CAP_STREAMING device" << p;
172 return;
173 }
174
175 int index;
176 if (::ioctl(fd.get(), VIDIOC_G_INPUT, &index) < 0) {
177 switch (errno) {
178 case ENOTTY:
179 qCDebug(ltVideoDevices) << "device does not have video inputs" << p;
180 return;
181
182 default:
183 qCWarning(ltVideoDevices)
184 << "ioctl failed: VIDIOC_G_INPUT" << qt_error_string(errno) << p;
185 return;
186 }
187 }
188 } else {
189 qCDebug(ltVideoDevices) << "Video device not a v4l2 device:" << structureHandle;
190 }
191#endif
192
193 auto it = std::find_if(m_videoSources.begin(), m_videoSources.end(),
194 [&](const QGstRecordDevice &a) { return a.gstDevice == device; });
195
196 if (it != m_videoSources.end())
197 return;
198
199 m_videoSources.push_back(QGstRecordDevice{
200 std::move(device),
201 QByteArray::number(m_idGenerator),
202 });
203
204 m_idGenerator++;
205
206 onVideoInputsChanged();
207}
208
209void QGstreamerVideoDevices::removeDevice(QGstDeviceHandle device)
210{
211 auto it = std::find_if(m_videoSources.begin(), m_videoSources.end(),
212 [&](const QGstRecordDevice &a) { return a.gstDevice == device; });
213
214 if (it != m_videoSources.end()) {
215 m_videoSources.erase(it);
216 onVideoInputsChanged();
217 }
218}
219
220bool QGstreamerVideoDevices::processBusMessage(const QGstreamerMessage &message)
221{
222 QGstDeviceHandle device;
223
224 switch (message.type()) {
225 case GST_MESSAGE_DEVICE_ADDED:
226 gst_message_parse_device_added(message.message(), &device);
227 addDevice(std::move(device));
228 break;
229 case GST_MESSAGE_DEVICE_REMOVED:
230 gst_message_parse_device_removed(message.message(), &device);
231 removeDevice(std::move(device));
232 break;
233 default:
234 break;
235 }
236
237 return false;
238}
239
240GstDevice *QGstreamerVideoDevices::videoDevice(const QByteArray &id) const
241{
242 auto it = std::find_if(m_videoSources.begin(), m_videoSources.end(),
243 [&](const QGstRecordDevice &a) { return a.id == id; });
244 return it != m_videoSources.end() ? it->gstDevice.get() : nullptr;
245}
246
247QT_END_NAMESPACE
GstDevice * videoDevice(const QByteArray &id) const
bool processBusMessage(const QGstreamerMessage &message) override
QGstreamerVideoDevices(QPlatformMediaIntegration *integration)
void removeDevice(QGstDeviceHandle)
void addDevice(QGstDeviceHandle)
QList< QCameraDevice > findVideoInputs() const override