4#include <mediacapture/qgstreamercamera_p.h>
6#include <QtMultimedia/qcameradevice.h>
7#include <QtMultimedia/qmediacapturesession.h>
8#include <QtMultimedia/private/qcameradevice_p.h>
9#include <QtMultimedia/private/qmultimediautils_p.h>
10#include <QtCore/qdebug.h>
12#include <common/qgst_debug_p.h>
13#include <qgstreamervideodevices_p.h>
14#include <qgstreamerintegration_p.h>
16#if QT_CONFIG(linux_v4l)
17#include <linux/videodev2.h>
18#include <private/qcore_unix_p.h>
24q23::expected<QPlatformCamera *, QString> QGstreamerCamera::create(QCamera *camera)
26 static const auto error = qGstErrorMessageIfElementsNotAvailable(
27 "videotestsrc",
"capsfilter",
"videoconvert",
"videoscale",
"identity");
29 return q23::unexpected{ *error };
31 return new QGstreamerCamera(camera);
37 QGstBin::create(
"camerabin"),
40 QGstElement::createFromFactory(
"videotestsrc"),
43 QGstElement::createFromFactory(
"capsfilter",
"videoCapsFilter"),
46 QGstElement::createFromFactory(
"identity"),
49 QGstElement::createFromFactory(
"videoconvert",
"videoConvert"),
52 QGstElement::createFromFactory(
"videoscale",
"videoScale"),
55 gstCameraBin.add(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
56 qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
57 gstCameraBin.addGhostPad(gstVideoScale,
"src");
62 gstCameraBin.setStateSync(GST_STATE_NULL);
72 if (m_active == active)
74 if (m_cameraDevice.isNull() && active)
79 Q_EMIT activeChanged(active);
84 using namespace Qt::Literals;
86 if (m_cameraDevice == camera)
89 m_cameraDevice = camera;
92 if (camera.isNull()) {
96 GstDevice *device = integration->videoDevice(camera.id());
99 updateError(QCamera::Error::CameraError,
100 u"Failed to create GstDevice for camera: "_s
101 + QString::fromUtf8(camera.id()));
105 newGstCamera = QGstElement::createFromDevice(device,
"camerasrc");
107 static const auto ioMode = [] {
109 int ioMode = qEnvironmentVariableIntValue(
"QT_GSTREAMER_V4L2SRC_IOMODE", &ok);
110 return ok ? std::optional(ioMode) : std::nullopt;
112 if (ioMode && newGstCamera.factoryName() ==
"v4l2src")
113 newGstCamera.set(
"io-mode", *ioMode);
115 QUniqueGstStructureHandle properties{
116 gst_device_get_properties(device),
121 if (propertiesView.name() ==
"v4l2deviceprovider")
122 m_v4l2DevicePath = QString::fromUtf8(propertiesView[
"device.path"].toString());
126 QCameraFormat f = findBestCameraFormat(camera);
127 m_currentCameraFormat = f;
130 if (f.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
132 QGstElement::findFactory(
"v4l2jpegdec") ?
"v4l2jpegdec" :
"jpegdec");
138 qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
139 gstCameraBin.stopAndRemoveElements(gstCamera, gstDecode);
141 gstCapsFilter.set(
"caps", caps);
143 gstCamera = std::move(newGstCamera);
144 gstDecode = std::move(newGstDecode);
146 gstCameraBin.add(gstCamera, gstDecode);
147 qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
148 gstCameraBin.syncChildrenState();
151 updateCameraProperties();
156 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
159 QCameraFormat f = format;
161 f = findBestCameraFormat(m_cameraDevice);
163 if (f == m_currentCameraFormat)
166 m_currentCameraFormat = f;
169 if (f.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
171 QGstElement::findFactory(
"v4l2jpegdec") ?
"v4l2jpegdec" :
"jpegdec");
177 qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
178 gstCameraBin.stopAndRemoveElements(gstDecode);
180 gstCapsFilter.set(
"caps", caps);
182 gstDecode = std::move(newGstDecode);
184 gstCameraBin.add(gstDecode);
185 qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
186 gstCameraBin.syncChildrenState();
194#if QT_CONFIG(linux_v4l)
195 if (isV4L2Camera()) {
200#if QT_CONFIG(gstreamer_photography)
201 if (
auto *p = photography())
202 gst_photography_set_white_balance_mode(p, GST_PHOTOGRAPHY_WB_MODE_AUTO);
203 QCamera::Features f = QCamera::Feature::ColorTemperature | QCamera::Feature::ExposureCompensation |
204 QCamera::Feature::IsoSensitivity | QCamera::Feature::ManualExposureTime;
205 supportedFeaturesChanged(f);
210#if QT_CONFIG(gstreamer_photography)
211GstPhotography *QGstreamerCamera::photography()
const
213 if (gstCamera && GST_IS_PHOTOGRAPHY(gstCamera.element()))
214 return GST_PHOTOGRAPHY(gstCamera.element());
221 if (mode == focusMode())
224#if QT_CONFIG(gstreamer_photography)
225 auto p = photography();
227 GstPhotographyFocusMode photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL;
230 case QCamera::FocusModeAutoNear:
231 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MACRO;
233 case QCamera::FocusModeAutoFar:
236 case QCamera::FocusModeHyperfocal:
237 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_HYPERFOCAL;
239 case QCamera::FocusModeInfinity:
240 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY;
242 case QCamera::FocusModeManual:
243 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MANUAL;
249 if (gst_photography_set_focus_mode(p, photographyMode))
250 focusModeChanged(mode);
257#if QT_CONFIG(gstreamer_photography)
261 return mode == QCamera::FocusModeAuto;
268#if QT_CONFIG(gstreamer_photography)
269 if (
auto *p = photography()) {
270 GstPhotographyFlashMode flashMode;
271 gst_photography_get_flash_mode(p, &flashMode);
274 case QCamera::FlashAuto:
275 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO;
277 case QCamera::FlashOff:
278 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_OFF;
280 case QCamera::FlashOn:
281 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_ON;
285 if (gst_photography_set_flash_mode(p, flashMode))
286 flashModeChanged(mode);
293#if QT_CONFIG(gstreamer_photography)
298 return mode == QCamera::FlashAuto;
303#if QT_CONFIG(gstreamer_photography)
314#if QT_CONFIG(linux_v4l)
315 if (isV4L2Camera() && v4l2AutoExposureSupported && v4l2ManualExposureSupported) {
316 if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
318 int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
319 setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
320 exposureModeChanged(mode);
325#if QT_CONFIG(gstreamer_photography)
326 auto *p = photography();
330 GstPhotographySceneMode sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
333 case QCamera::ExposureManual:
334 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_MANUAL;
336 case QCamera::ExposurePortrait:
337 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT;
339 case QCamera::ExposureSports:
340 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SPORT;
342 case QCamera::ExposureNight:
343 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT;
345 case QCamera::ExposureAuto:
346 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
348 case QCamera::ExposureLandscape:
349 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE;
351 case QCamera::ExposureSnow:
352 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SNOW;
354 case QCamera::ExposureBeach:
355 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BEACH;
357 case QCamera::ExposureAction:
358 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_ACTION;
360 case QCamera::ExposureNightPortrait:
361 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT_PORTRAIT;
363 case QCamera::ExposureTheatre:
364 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_THEATRE;
366 case QCamera::ExposureSunset:
367 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SUNSET;
369 case QCamera::ExposureSteadyPhoto:
370 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_STEADY_PHOTO;
372 case QCamera::ExposureFireworks:
373 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_FIREWORKS;
375 case QCamera::ExposureParty:
376 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PARTY;
378 case QCamera::ExposureCandlelight:
379 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_CANDLELIGHT;
381 case QCamera::ExposureBarcode:
382 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BARCODE;
388 if (gst_photography_set_scene_mode(p, sceneMode))
389 exposureModeChanged(mode);
395 if (mode == QCamera::ExposureAuto)
397#if QT_CONFIG(linux_v4l)
398 if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported)
399 return mode == QCamera::ExposureManual;
401#if QT_CONFIG(gstreamer_photography)
411 Q_UNUSED(compensation);
412#if QT_CONFIG(linux_v4l)
413 if (isV4L2Camera() && (v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) {
414 int value = qBound(v4l2MinExposureAdjustment, (
int)(compensation*1000), v4l2MaxExposureAdjustment);
415 setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
416 exposureCompensationChanged(value/1000.);
421#if QT_CONFIG(gstreamer_photography)
422 if (
auto *p = photography()) {
423 if (gst_photography_set_ev_compensation(p, compensation))
424 exposureCompensationChanged(compensation);
432#if QT_CONFIG(linux_v4l)
433 if (isV4L2Camera()) {
434 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
436 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
438 iso = qBound(minIso(), iso, maxIso());
439 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso);
444#if QT_CONFIG(gstreamer_photography)
445 if (
auto *p = photography()) {
446 if (gst_photography_set_iso_speed(p, iso))
447 isoSensitivityChanged(iso);
454#if QT_CONFIG(linux_v4l)
455 if (isV4L2Camera()) {
456 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
458 return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
461#if QT_CONFIG(gstreamer_photography)
462 if (
auto *p = photography()) {
464 if (gst_photography_get_iso_speed(p, &speed))
474#if QT_CONFIG(linux_v4l)
475 if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) {
476 int exposure = qBound(v4l2MinExposure, qRound(secs*10000.), v4l2MaxExposure);
477 setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure);
478 exposureTimeChanged(exposure/10000.);
483#if QT_CONFIG(gstreamer_photography)
484 if (
auto *p = photography()) {
485 if (gst_photography_set_exposure(p, guint(secs*1000000)))
486 exposureTimeChanged(secs);
493#if QT_CONFIG(linux_v4l)
494 if (isV4L2Camera()) {
495 return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
498#if QT_CONFIG(gstreamer_photography)
499 if (
auto *p = photography()) {
500 guint32 exposure = 0;
501 if (gst_photography_get_exposure(p, &exposure))
502 return exposure/1000000.;
510 if (mode == QCamera::WhiteBalanceAuto)
513#if QT_CONFIG(linux_v4l)
514 if (isV4L2Camera()) {
515 if (v4l2AutoWhiteBalanceSupported && v4l2ColorTemperatureSupported)
519#if QT_CONFIG(gstreamer_photography)
520 if (
auto *p = photography()) {
523 case QCamera::WhiteBalanceAuto:
524 case QCamera::WhiteBalanceSunlight:
525 case QCamera::WhiteBalanceCloudy:
526 case QCamera::WhiteBalanceShade:
527 case QCamera::WhiteBalanceSunset:
528 case QCamera::WhiteBalanceTungsten:
529 case QCamera::WhiteBalanceFluorescent:
531 case QCamera::WhiteBalanceManual: {
532 GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
533 if (iface->set_color_temperature && iface->get_color_temperature)
543 return mode == QCamera::WhiteBalanceAuto;
548 Q_ASSERT(isWhiteBalanceModeSupported(mode));
550#if QT_CONFIG(linux_v4l)
551 if (isV4L2Camera()) {
552 int temperature = colorTemperatureForWhiteBalance(mode);
553 int t = setV4L2ColorTemperature(temperature);
555 mode = QCamera::WhiteBalanceAuto;
556 whiteBalanceModeChanged(mode);
561#if QT_CONFIG(gstreamer_photography)
562 if (
auto *p = photography()) {
563 GstPhotographyWhiteBalanceMode gstMode = GST_PHOTOGRAPHY_WB_MODE_AUTO;
565 case QCamera::WhiteBalanceSunlight:
566 gstMode = GST_PHOTOGRAPHY_WB_MODE_DAYLIGHT;
568 case QCamera::WhiteBalanceCloudy:
569 gstMode = GST_PHOTOGRAPHY_WB_MODE_CLOUDY;
571 case QCamera::WhiteBalanceShade:
572 gstMode = GST_PHOTOGRAPHY_WB_MODE_SHADE;
574 case QCamera::WhiteBalanceSunset:
575 gstMode = GST_PHOTOGRAPHY_WB_MODE_SUNSET;
577 case QCamera::WhiteBalanceTungsten:
578 gstMode = GST_PHOTOGRAPHY_WB_MODE_TUNGSTEN;
580 case QCamera::WhiteBalanceFluorescent:
581 gstMode = GST_PHOTOGRAPHY_WB_MODE_FLUORESCENT;
583 case QCamera::WhiteBalanceAuto:
587 if (gst_photography_set_white_balance_mode(p, gstMode)) {
588 whiteBalanceModeChanged(mode);
597 if (temperature == 0) {
598 setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
602 Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual));
604#if QT_CONFIG(linux_v4l)
605 if (isV4L2Camera()) {
606 int t = setV4L2ColorTemperature(temperature);
608 colorTemperatureChanged(t);
613#if QT_CONFIG(gstreamer_photography)
614 if (
auto *p = photography()) {
615 GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
616 Q_ASSERT(iface->set_color_temperature);
617 iface->set_color_temperature(p, temperature);
623#if QT_CONFIG(linux_v4l)
624bool QGstreamerCamera::isV4L2Camera()
const
626 return !m_v4l2DevicePath.isEmpty();
629void QGstreamerCamera::initV4L2Controls()
631 using namespace QtMultimediaPrivate;
633 v4l2AutoWhiteBalanceSupported =
false;
634 v4l2ColorTemperatureSupported =
false;
635 QCamera::Features features{};
637 Q_ASSERT(!m_v4l2DevicePath.isEmpty());
640 withV4L2DeviceFileDescriptor([&](
int fd) {
641 struct v4l2_queryctrl queryControl = {};
642 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
644 if (qt_safe_ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
645 v4l2AutoWhiteBalanceSupported =
true;
646 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE,
true);
650 queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
651 if (qt_safe_ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
652 v4l2MinColorTemp = queryControl.minimum;
653 v4l2MaxColorTemp = queryControl.maximum;
654 v4l2ColorTemperatureSupported =
true;
655 features |= QCamera::Feature::ColorTemperature;
659 queryControl.id = V4L2_CID_EXPOSURE_AUTO;
660 if (qt_safe_ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
661 v4l2AutoExposureSupported =
true;
665 queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
666 if (qt_safe_ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
667 v4l2ManualExposureSupported =
true;
668 v4l2MinExposure = queryControl.minimum;
669 v4l2MaxExposure = queryControl.maximum;
670 features |= QCamera::Feature::ManualExposureTime;
674 queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
675 if (qt_safe_ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
676 v4l2MinExposureAdjustment = queryControl.minimum;
677 v4l2MaxExposureAdjustment = queryControl.maximum;
678 features |= QCamera::Feature::ExposureCompensation;
682 queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
683 if (qt_safe_ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
684 queryControl.id = V4L2_CID_ISO_SENSITIVITY;
685 if (qt_safe_ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
686 features |= QCamera::Feature::IsoSensitivity;
687 minIsoChanged(queryControl.minimum);
688 maxIsoChanged(queryControl.minimum);
693 supportedFeaturesChanged(features);
696int QGstreamerCamera::setV4L2ColorTemperature(
int temperature)
698 if (v4l2AutoWhiteBalanceSupported) {
699 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ?
true :
false);
700 }
else if (temperature == 0) {
704 if (temperature != 0 && v4l2ColorTemperatureSupported) {
705 temperature = qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp);
706 if (!setV4L2Parameter(V4L2_CID_WHITE_BALANCE_TEMPERATURE, qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp)))
715bool QGstreamerCamera::setV4L2Parameter(quint32 id, qint32 value)
717 return withV4L2DeviceFileDescriptor([&](
int fd) {
718 v4l2_control control{ id, value };
719 if (QtMultimediaPrivate::qt_safe_ioctl(fd, VIDIOC_S_CTRL, &control) != 0) {
720 qWarning() <<
"Unable to set the V4L2 Parameter" << Qt::hex << id <<
"to" << value
721 << qt_error_string(errno);
728int QGstreamerCamera::getV4L2Parameter(quint32 id)
const
730 return withV4L2DeviceFileDescriptor([&](
int fd) {
731 v4l2_control control{ id, 0 };
732 if (QtMultimediaPrivate::qt_safe_ioctl(fd, VIDIOC_G_CTRL, &control) != 0) {
733 qWarning() <<
"Unable to get the V4L2 Parameter" << Qt::hex << id
734 << qt_error_string(errno);
737 return control.value;
755 if (m_active == active)
760 Q_EMIT activeChanged(active);
static QGstCaps fromCameraFormat(const QCameraFormat &format)
QGstElement & operator=(QGstElement &&) noexcept=default
QGstElement & operator=(const QGstElement &)=default
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override
~QGstreamerCamera() override
bool isFocusModeSupported(QCamera::FocusMode mode) const override
bool isActive() const override
bool isFlashModeSupported(QCamera::FlashMode mode) const override
bool isExposureModeSupported(QCamera::ExposureMode mode) const override
bool setCameraFormat(const QCameraFormat &format) override
void setColorTemperature(int temperature) override
void setExposureMode(QCamera::ExposureMode) override
bool isFlashReady() const override
int isoSensitivity() const override
float exposureTime() const override
bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override
void setFlashMode(QCamera::FlashMode mode) override
void setFocusMode(QCamera::FocusMode mode) override
void setActive(bool active) override
void setManualIsoSensitivity(int) override
void setCamera(const QCameraDevice &camera) override
void setManualExposureTime(float) override
void setExposureCompensation(float) override
void setActive(bool) override
QGstreamerCustomCamera(QObject *, QGstElement element)
bool isActive() const override
static QGstreamerIntegration * instance()
Combined button and popup list for selecting options.