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 <QtCore/qdebug.h>
11#include <common/qgst_debug_p.h>
12#include <qgstreamervideodevices_p.h>
13#include <qgstreamerintegration_p.h>
15#if QT_CONFIG(linux_v4l)
16#include <linux/videodev2.h>
17#include <private/qcore_unix_p.h>
23q23::expected<QPlatformCamera *, QString> QGstreamerCamera::create(QCamera *camera)
25 static const auto error = qGstErrorMessageIfElementsNotAvailable(
26 "videotestsrc",
"capsfilter",
"videoconvert",
"videoscale",
"identity");
28 return q23::unexpected{ *error };
30 return new QGstreamerCamera(camera);
36 QGstBin::create(
"camerabin"),
39 QGstElement::createFromFactory(
"videotestsrc"),
42 QGstElement::createFromFactory(
"capsfilter",
"videoCapsFilter"),
45 QGstElement::createFromFactory(
"identity"),
48 QGstElement::createFromFactory(
"videoconvert",
"videoConvert"),
51 QGstElement::createFromFactory(
"videoscale",
"videoScale"),
54 gstCameraBin.add(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
55 qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
56 gstCameraBin.addGhostPad(gstVideoScale,
"src");
61 gstCameraBin.setStateSync(GST_STATE_NULL);
71 if (m_active == active)
73 if (m_cameraDevice.isNull() && active)
78 emit activeChanged(active);
83 using namespace Qt::Literals;
85 if (m_cameraDevice == camera)
88 m_cameraDevice = camera;
91 if (camera.isNull()) {
95 GstDevice *device = integration->videoDevice(camera.id());
98 updateError(QCamera::Error::CameraError,
99 u"Failed to create GstDevice for camera: "_s
100 + QString::fromUtf8(camera.id()));
104 newGstCamera = QGstElement::createFromDevice(device,
"camerasrc");
106 static const auto ioMode = [] {
108 int ioMode = qEnvironmentVariableIntValue(
"QT_GSTREAMER_V4L2SRC_IOMODE", &ok);
109 return ok ? std::optional(ioMode) : std::nullopt;
111 if (ioMode && newGstCamera.factoryName() ==
"v4l2src")
112 newGstCamera.set(
"io-mode", *ioMode);
114 QUniqueGstStructureHandle properties{
115 gst_device_get_properties(device),
120 if (propertiesView.name() ==
"v4l2deviceprovider")
121 m_v4l2DevicePath = QString::fromUtf8(propertiesView[
"device.path"].toString());
125 QCameraFormat f = findBestCameraFormat(camera);
126 m_currentCameraFormat = f;
129 if (f.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
131 QGstElement::findFactory(
"v4l2jpegdec") ?
"v4l2jpegdec" :
"jpegdec");
137 qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
138 gstCameraBin.stopAndRemoveElements(gstCamera, gstDecode);
140 gstCapsFilter.set(
"caps", caps);
142 gstCamera = std::move(newGstCamera);
143 gstDecode = std::move(newGstDecode);
145 gstCameraBin.add(gstCamera, gstDecode);
146 qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
147 gstCameraBin.syncChildrenState();
150 updateCameraProperties();
155 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
158 QCameraFormat f = format;
160 f = findBestCameraFormat(m_cameraDevice);
162 if (f == m_currentCameraFormat)
165 m_currentCameraFormat = f;
168 if (f.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
170 QGstElement::findFactory(
"v4l2jpegdec") ?
"v4l2jpegdec" :
"jpegdec");
176 qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
177 gstCameraBin.stopAndRemoveElements(gstDecode);
179 gstCapsFilter.set(
"caps", caps);
181 gstDecode = std::move(newGstDecode);
183 gstCameraBin.add(gstDecode);
184 qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
185 gstCameraBin.syncChildrenState();
193#if QT_CONFIG(linux_v4l)
194 if (isV4L2Camera()) {
199#if QT_CONFIG(gstreamer_photography)
200 if (
auto *p = photography())
201 gst_photography_set_white_balance_mode(p, GST_PHOTOGRAPHY_WB_MODE_AUTO);
202 QCamera::Features f = QCamera::Feature::ColorTemperature | QCamera::Feature::ExposureCompensation |
203 QCamera::Feature::IsoSensitivity | QCamera::Feature::ManualExposureTime;
204 supportedFeaturesChanged(f);
209#if QT_CONFIG(gstreamer_photography)
210GstPhotography *QGstreamerCamera::photography()
const
212 if (gstCamera && GST_IS_PHOTOGRAPHY(gstCamera.element()))
213 return GST_PHOTOGRAPHY(gstCamera.element());
220 if (mode == focusMode())
223#if QT_CONFIG(gstreamer_photography)
224 auto p = photography();
226 GstPhotographyFocusMode photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL;
229 case QCamera::FocusModeAutoNear:
230 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MACRO;
232 case QCamera::FocusModeAutoFar:
235 case QCamera::FocusModeHyperfocal:
236 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_HYPERFOCAL;
238 case QCamera::FocusModeInfinity:
239 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY;
241 case QCamera::FocusModeManual:
242 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MANUAL;
248 if (gst_photography_set_focus_mode(p, photographyMode))
249 focusModeChanged(mode);
256#if QT_CONFIG(gstreamer_photography)
260 return mode == QCamera::FocusModeAuto;
267#if QT_CONFIG(gstreamer_photography)
268 if (
auto *p = photography()) {
269 GstPhotographyFlashMode flashMode;
270 gst_photography_get_flash_mode(p, &flashMode);
273 case QCamera::FlashAuto:
274 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO;
276 case QCamera::FlashOff:
277 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_OFF;
279 case QCamera::FlashOn:
280 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_ON;
284 if (gst_photography_set_flash_mode(p, flashMode))
285 flashModeChanged(mode);
292#if QT_CONFIG(gstreamer_photography)
297 return mode == QCamera::FlashAuto;
302#if QT_CONFIG(gstreamer_photography)
313#if QT_CONFIG(linux_v4l)
314 if (isV4L2Camera() && v4l2AutoExposureSupported && v4l2ManualExposureSupported) {
315 if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
317 int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
318 setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
319 exposureModeChanged(mode);
324#if QT_CONFIG(gstreamer_photography)
325 auto *p = photography();
329 GstPhotographySceneMode sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
332 case QCamera::ExposureManual:
333 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_MANUAL;
335 case QCamera::ExposurePortrait:
336 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT;
338 case QCamera::ExposureSports:
339 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SPORT;
341 case QCamera::ExposureNight:
342 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT;
344 case QCamera::ExposureAuto:
345 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
347 case QCamera::ExposureLandscape:
348 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE;
350 case QCamera::ExposureSnow:
351 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SNOW;
353 case QCamera::ExposureBeach:
354 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BEACH;
356 case QCamera::ExposureAction:
357 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_ACTION;
359 case QCamera::ExposureNightPortrait:
360 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT_PORTRAIT;
362 case QCamera::ExposureTheatre:
363 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_THEATRE;
365 case QCamera::ExposureSunset:
366 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SUNSET;
368 case QCamera::ExposureSteadyPhoto:
369 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_STEADY_PHOTO;
371 case QCamera::ExposureFireworks:
372 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_FIREWORKS;
374 case QCamera::ExposureParty:
375 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PARTY;
377 case QCamera::ExposureCandlelight:
378 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_CANDLELIGHT;
380 case QCamera::ExposureBarcode:
381 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BARCODE;
387 if (gst_photography_set_scene_mode(p, sceneMode))
388 exposureModeChanged(mode);
394 if (mode == QCamera::ExposureAuto)
396#if QT_CONFIG(linux_v4l)
397 if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported)
398 return mode == QCamera::ExposureManual;
400#if QT_CONFIG(gstreamer_photography)
410 Q_UNUSED(compensation);
411#if QT_CONFIG(linux_v4l)
412 if (isV4L2Camera() && (v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) {
413 int value = qBound(v4l2MinExposureAdjustment, (
int)(compensation*1000), v4l2MaxExposureAdjustment);
414 setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
415 exposureCompensationChanged(value/1000.);
420#if QT_CONFIG(gstreamer_photography)
421 if (
auto *p = photography()) {
422 if (gst_photography_set_ev_compensation(p, compensation))
423 exposureCompensationChanged(compensation);
431#if QT_CONFIG(linux_v4l)
432 if (isV4L2Camera()) {
433 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
435 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
437 iso = qBound(minIso(), iso, maxIso());
438 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso);
443#if QT_CONFIG(gstreamer_photography)
444 if (
auto *p = photography()) {
445 if (gst_photography_set_iso_speed(p, iso))
446 isoSensitivityChanged(iso);
453#if QT_CONFIG(linux_v4l)
454 if (isV4L2Camera()) {
455 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
457 return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
460#if QT_CONFIG(gstreamer_photography)
461 if (
auto *p = photography()) {
463 if (gst_photography_get_iso_speed(p, &speed))
473#if QT_CONFIG(linux_v4l)
474 if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) {
475 int exposure = qBound(v4l2MinExposure, qRound(secs*10000.), v4l2MaxExposure);
476 setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure);
477 exposureTimeChanged(exposure/10000.);
482#if QT_CONFIG(gstreamer_photography)
483 if (
auto *p = photography()) {
484 if (gst_photography_set_exposure(p, guint(secs*1000000)))
485 exposureTimeChanged(secs);
492#if QT_CONFIG(linux_v4l)
493 if (isV4L2Camera()) {
494 return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
497#if QT_CONFIG(gstreamer_photography)
498 if (
auto *p = photography()) {
499 guint32 exposure = 0;
500 if (gst_photography_get_exposure(p, &exposure))
501 return exposure/1000000.;
509 if (mode == QCamera::WhiteBalanceAuto)
512#if QT_CONFIG(linux_v4l)
513 if (isV4L2Camera()) {
514 if (v4l2AutoWhiteBalanceSupported && v4l2ColorTemperatureSupported)
518#if QT_CONFIG(gstreamer_photography)
519 if (
auto *p = photography()) {
522 case QCamera::WhiteBalanceAuto:
523 case QCamera::WhiteBalanceSunlight:
524 case QCamera::WhiteBalanceCloudy:
525 case QCamera::WhiteBalanceShade:
526 case QCamera::WhiteBalanceSunset:
527 case QCamera::WhiteBalanceTungsten:
528 case QCamera::WhiteBalanceFluorescent:
530 case QCamera::WhiteBalanceManual: {
531 GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
532 if (iface->set_color_temperature && iface->get_color_temperature)
542 return mode == QCamera::WhiteBalanceAuto;
547 Q_ASSERT(isWhiteBalanceModeSupported(mode));
549#if QT_CONFIG(linux_v4l)
550 if (isV4L2Camera()) {
551 int temperature = colorTemperatureForWhiteBalance(mode);
552 int t = setV4L2ColorTemperature(temperature);
554 mode = QCamera::WhiteBalanceAuto;
555 whiteBalanceModeChanged(mode);
560#if QT_CONFIG(gstreamer_photography)
561 if (
auto *p = photography()) {
562 GstPhotographyWhiteBalanceMode gstMode = GST_PHOTOGRAPHY_WB_MODE_AUTO;
564 case QCamera::WhiteBalanceSunlight:
565 gstMode = GST_PHOTOGRAPHY_WB_MODE_DAYLIGHT;
567 case QCamera::WhiteBalanceCloudy:
568 gstMode = GST_PHOTOGRAPHY_WB_MODE_CLOUDY;
570 case QCamera::WhiteBalanceShade:
571 gstMode = GST_PHOTOGRAPHY_WB_MODE_SHADE;
573 case QCamera::WhiteBalanceSunset:
574 gstMode = GST_PHOTOGRAPHY_WB_MODE_SUNSET;
576 case QCamera::WhiteBalanceTungsten:
577 gstMode = GST_PHOTOGRAPHY_WB_MODE_TUNGSTEN;
579 case QCamera::WhiteBalanceFluorescent:
580 gstMode = GST_PHOTOGRAPHY_WB_MODE_FLUORESCENT;
582 case QCamera::WhiteBalanceAuto:
586 if (gst_photography_set_white_balance_mode(p, gstMode)) {
587 whiteBalanceModeChanged(mode);
596 if (temperature == 0) {
597 setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
601 Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual));
603#if QT_CONFIG(linux_v4l)
604 if (isV4L2Camera()) {
605 int t = setV4L2ColorTemperature(temperature);
607 colorTemperatureChanged(t);
612#if QT_CONFIG(gstreamer_photography)
613 if (
auto *p = photography()) {
614 GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
615 Q_ASSERT(iface->set_color_temperature);
616 iface->set_color_temperature(p, temperature);
622#if QT_CONFIG(linux_v4l)
623bool QGstreamerCamera::isV4L2Camera()
const
625 return !m_v4l2DevicePath.isEmpty();
628void QGstreamerCamera::initV4L2Controls()
630 v4l2AutoWhiteBalanceSupported =
false;
631 v4l2ColorTemperatureSupported =
false;
632 QCamera::Features features{};
634 Q_ASSERT(!m_v4l2DevicePath.isEmpty());
637 withV4L2DeviceFileDescriptor([&](
int fd) {
638 struct v4l2_queryctrl queryControl = {};
639 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
641 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
642 v4l2AutoWhiteBalanceSupported =
true;
643 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE,
true);
647 queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
648 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
649 v4l2MinColorTemp = queryControl.minimum;
650 v4l2MaxColorTemp = queryControl.maximum;
651 v4l2ColorTemperatureSupported =
true;
652 features |= QCamera::Feature::ColorTemperature;
656 queryControl.id = V4L2_CID_EXPOSURE_AUTO;
657 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
658 v4l2AutoExposureSupported =
true;
662 queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
663 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
664 v4l2ManualExposureSupported =
true;
665 v4l2MinExposure = queryControl.minimum;
666 v4l2MaxExposure = queryControl.maximum;
667 features |= QCamera::Feature::ManualExposureTime;
671 queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
672 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
673 v4l2MinExposureAdjustment = queryControl.minimum;
674 v4l2MaxExposureAdjustment = queryControl.maximum;
675 features |= QCamera::Feature::ExposureCompensation;
679 queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
680 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
681 queryControl.id = V4L2_CID_ISO_SENSITIVITY;
682 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
683 features |= QCamera::Feature::IsoSensitivity;
684 minIsoChanged(queryControl.minimum);
685 maxIsoChanged(queryControl.minimum);
690 supportedFeaturesChanged(features);
693int QGstreamerCamera::setV4L2ColorTemperature(
int temperature)
695 if (v4l2AutoWhiteBalanceSupported) {
696 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ?
true :
false);
697 }
else if (temperature == 0) {
701 if (temperature != 0 && v4l2ColorTemperatureSupported) {
702 temperature = qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp);
703 if (!setV4L2Parameter(V4L2_CID_WHITE_BALANCE_TEMPERATURE, qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp)))
712bool QGstreamerCamera::setV4L2Parameter(quint32 id, qint32 value)
714 return withV4L2DeviceFileDescriptor([&](
int fd) {
715 v4l2_control control{ id, value };
716 if (::ioctl(fd, VIDIOC_S_CTRL, &control) != 0) {
717 qWarning() <<
"Unable to set the V4L2 Parameter" << Qt::hex << id <<
"to" << value
718 << qt_error_string(errno);
725int QGstreamerCamera::getV4L2Parameter(quint32 id)
const
727 return withV4L2DeviceFileDescriptor([&](
int fd) {
728 v4l2_control control{ id, 0 };
729 if (::ioctl(fd, VIDIOC_G_CTRL, &control) != 0) {
730 qWarning() <<
"Unable to get the V4L2 Parameter" << Qt::hex << id
731 << qt_error_string(errno);
734 return control.value;
744 m_userProvidedGstElement{
757 m_userProvidedGstElement{
765 if (m_userProvidedGstElement)
768 gstCamera = QGstBin::createFromPipelineDescription(device.id(),
nullptr,
779 if (m_active == active)
784 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(QCamera *, QGstElement element)
void setCamera(const QCameraDevice &) override
bool isActive() const override
QGstreamerCustomCamera(QCamera *)
static QGstreamerIntegration * instance()
Combined button and popup list for selecting options.