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 gstNewCamera = QGstElement::createFromDevice(device,
"camerasrc");
105 QUniqueGstStructureHandle properties{
106 gst_device_get_properties(device),
111 if (propertiesView.name() ==
"v4l2deviceprovider")
112 m_v4l2DevicePath = QString::fromUtf8(propertiesView[
"device.path"].toString());
116 QCameraFormat f = findBestCameraFormat(camera);
118 auto gstNewDecode = QGstElement::createFromFactory(
119 f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ?
"jpegdec" :
"identity");
121 m_currentCameraFormat = f;
124 qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
125 gstCameraBin.stopAndRemoveElements(gstCamera, gstDecode);
127 gstCapsFilter.set(
"caps", caps);
129 gstCamera = std::move(gstNewCamera);
130 gstDecode = std::move(gstNewDecode);
132 gstCameraBin.add(gstCamera, gstDecode);
133 qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
135 gstCameraBin.syncChildrenState();
138 updateCameraProperties();
143 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
146 QCameraFormat f = format;
148 f = findBestCameraFormat(m_cameraDevice);
150 if (f == m_currentCameraFormat)
153 m_currentCameraFormat = f;
157 auto newGstDecode = QGstElement::createFromFactory(
158 f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ?
"jpegdec" :
"identity");
161 qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
162 gstCameraBin.stopAndRemoveElements(gstDecode);
164 gstCapsFilter.set(
"caps", caps);
166 gstDecode = std::move(newGstDecode);
168 gstCameraBin.add(gstDecode);
169 qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
170 gstCameraBin.syncChildrenState();
178#if QT_CONFIG(linux_v4l)
179 if (isV4L2Camera()) {
184#if QT_CONFIG(gstreamer_photography)
185 if (
auto *p = photography())
186 gst_photography_set_white_balance_mode(p, GST_PHOTOGRAPHY_WB_MODE_AUTO);
187 QCamera::Features f = QCamera::Feature::ColorTemperature | QCamera::Feature::ExposureCompensation |
188 QCamera::Feature::IsoSensitivity | QCamera::Feature::ManualExposureTime;
189 supportedFeaturesChanged(f);
194#if QT_CONFIG(gstreamer_photography)
195GstPhotography *QGstreamerCamera::photography()
const
197 if (gstCamera && GST_IS_PHOTOGRAPHY(gstCamera.element()))
198 return GST_PHOTOGRAPHY(gstCamera.element());
205 if (mode == focusMode())
208#if QT_CONFIG(gstreamer_photography)
209 auto p = photography();
211 GstPhotographyFocusMode photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL;
214 case QCamera::FocusModeAutoNear:
215 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MACRO;
217 case QCamera::FocusModeAutoFar:
220 case QCamera::FocusModeHyperfocal:
221 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_HYPERFOCAL;
223 case QCamera::FocusModeInfinity:
224 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY;
226 case QCamera::FocusModeManual:
227 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MANUAL;
233 if (gst_photography_set_focus_mode(p, photographyMode))
234 focusModeChanged(mode);
241#if QT_CONFIG(gstreamer_photography)
245 return mode == QCamera::FocusModeAuto;
252#if QT_CONFIG(gstreamer_photography)
253 if (
auto *p = photography()) {
254 GstPhotographyFlashMode flashMode;
255 gst_photography_get_flash_mode(p, &flashMode);
258 case QCamera::FlashAuto:
259 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO;
261 case QCamera::FlashOff:
262 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_OFF;
264 case QCamera::FlashOn:
265 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_ON;
269 if (gst_photography_set_flash_mode(p, flashMode))
270 flashModeChanged(mode);
277#if QT_CONFIG(gstreamer_photography)
282 return mode == QCamera::FlashAuto;
287#if QT_CONFIG(gstreamer_photography)
298#if QT_CONFIG(linux_v4l)
299 if (isV4L2Camera() && v4l2AutoExposureSupported && v4l2ManualExposureSupported) {
300 if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
302 int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
303 setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
304 exposureModeChanged(mode);
309#if QT_CONFIG(gstreamer_photography)
310 auto *p = photography();
314 GstPhotographySceneMode sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
317 case QCamera::ExposureManual:
318 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_MANUAL;
320 case QCamera::ExposurePortrait:
321 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT;
323 case QCamera::ExposureSports:
324 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SPORT;
326 case QCamera::ExposureNight:
327 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT;
329 case QCamera::ExposureAuto:
330 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
332 case QCamera::ExposureLandscape:
333 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE;
335 case QCamera::ExposureSnow:
336 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SNOW;
338 case QCamera::ExposureBeach:
339 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BEACH;
341 case QCamera::ExposureAction:
342 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_ACTION;
344 case QCamera::ExposureNightPortrait:
345 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT_PORTRAIT;
347 case QCamera::ExposureTheatre:
348 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_THEATRE;
350 case QCamera::ExposureSunset:
351 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SUNSET;
353 case QCamera::ExposureSteadyPhoto:
354 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_STEADY_PHOTO;
356 case QCamera::ExposureFireworks:
357 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_FIREWORKS;
359 case QCamera::ExposureParty:
360 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PARTY;
362 case QCamera::ExposureCandlelight:
363 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_CANDLELIGHT;
365 case QCamera::ExposureBarcode:
366 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BARCODE;
372 if (gst_photography_set_scene_mode(p, sceneMode))
373 exposureModeChanged(mode);
379 if (mode == QCamera::ExposureAuto)
381#if QT_CONFIG(linux_v4l)
382 if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported)
383 return mode == QCamera::ExposureManual;
385#if QT_CONFIG(gstreamer_photography)
395 Q_UNUSED(compensation);
396#if QT_CONFIG(linux_v4l)
397 if (isV4L2Camera() && (v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) {
398 int value = qBound(v4l2MinExposureAdjustment, (
int)(compensation*1000), v4l2MaxExposureAdjustment);
399 setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
400 exposureCompensationChanged(value/1000.);
405#if QT_CONFIG(gstreamer_photography)
406 if (
auto *p = photography()) {
407 if (gst_photography_set_ev_compensation(p, compensation))
408 exposureCompensationChanged(compensation);
416#if QT_CONFIG(linux_v4l)
417 if (isV4L2Camera()) {
418 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
420 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
422 iso = qBound(minIso(), iso, maxIso());
423 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso);
428#if QT_CONFIG(gstreamer_photography)
429 if (
auto *p = photography()) {
430 if (gst_photography_set_iso_speed(p, iso))
431 isoSensitivityChanged(iso);
438#if QT_CONFIG(linux_v4l)
439 if (isV4L2Camera()) {
440 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
442 return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
445#if QT_CONFIG(gstreamer_photography)
446 if (
auto *p = photography()) {
448 if (gst_photography_get_iso_speed(p, &speed))
458#if QT_CONFIG(linux_v4l)
459 if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) {
460 int exposure = qBound(v4l2MinExposure, qRound(secs*10000.), v4l2MaxExposure);
461 setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure);
462 exposureTimeChanged(exposure/10000.);
467#if QT_CONFIG(gstreamer_photography)
468 if (
auto *p = photography()) {
469 if (gst_photography_set_exposure(p, guint(secs*1000000)))
470 exposureTimeChanged(secs);
477#if QT_CONFIG(linux_v4l)
478 if (isV4L2Camera()) {
479 return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
482#if QT_CONFIG(gstreamer_photography)
483 if (
auto *p = photography()) {
484 guint32 exposure = 0;
485 if (gst_photography_get_exposure(p, &exposure))
486 return exposure/1000000.;
494 if (mode == QCamera::WhiteBalanceAuto)
497#if QT_CONFIG(linux_v4l)
498 if (isV4L2Camera()) {
499 if (v4l2AutoWhiteBalanceSupported && v4l2ColorTemperatureSupported)
503#if QT_CONFIG(gstreamer_photography)
504 if (
auto *p = photography()) {
507 case QCamera::WhiteBalanceAuto:
508 case QCamera::WhiteBalanceSunlight:
509 case QCamera::WhiteBalanceCloudy:
510 case QCamera::WhiteBalanceShade:
511 case QCamera::WhiteBalanceSunset:
512 case QCamera::WhiteBalanceTungsten:
513 case QCamera::WhiteBalanceFluorescent:
515 case QCamera::WhiteBalanceManual: {
516 GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
517 if (iface->set_color_temperature && iface->get_color_temperature)
527 return mode == QCamera::WhiteBalanceAuto;
532 Q_ASSERT(isWhiteBalanceModeSupported(mode));
534#if QT_CONFIG(linux_v4l)
535 if (isV4L2Camera()) {
536 int temperature = colorTemperatureForWhiteBalance(mode);
537 int t = setV4L2ColorTemperature(temperature);
539 mode = QCamera::WhiteBalanceAuto;
540 whiteBalanceModeChanged(mode);
545#if QT_CONFIG(gstreamer_photography)
546 if (
auto *p = photography()) {
547 GstPhotographyWhiteBalanceMode gstMode = GST_PHOTOGRAPHY_WB_MODE_AUTO;
549 case QCamera::WhiteBalanceSunlight:
550 gstMode = GST_PHOTOGRAPHY_WB_MODE_DAYLIGHT;
552 case QCamera::WhiteBalanceCloudy:
553 gstMode = GST_PHOTOGRAPHY_WB_MODE_CLOUDY;
555 case QCamera::WhiteBalanceShade:
556 gstMode = GST_PHOTOGRAPHY_WB_MODE_SHADE;
558 case QCamera::WhiteBalanceSunset:
559 gstMode = GST_PHOTOGRAPHY_WB_MODE_SUNSET;
561 case QCamera::WhiteBalanceTungsten:
562 gstMode = GST_PHOTOGRAPHY_WB_MODE_TUNGSTEN;
564 case QCamera::WhiteBalanceFluorescent:
565 gstMode = GST_PHOTOGRAPHY_WB_MODE_FLUORESCENT;
567 case QCamera::WhiteBalanceAuto:
571 if (gst_photography_set_white_balance_mode(p, gstMode)) {
572 whiteBalanceModeChanged(mode);
581 if (temperature == 0) {
582 setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
586 Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual));
588#if QT_CONFIG(linux_v4l)
589 if (isV4L2Camera()) {
590 int t = setV4L2ColorTemperature(temperature);
592 colorTemperatureChanged(t);
597#if QT_CONFIG(gstreamer_photography)
598 if (
auto *p = photography()) {
599 GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
600 Q_ASSERT(iface->set_color_temperature);
601 iface->set_color_temperature(p, temperature);
607#if QT_CONFIG(linux_v4l)
608bool QGstreamerCamera::isV4L2Camera()
const
610 return !m_v4l2DevicePath.isEmpty();
613void QGstreamerCamera::initV4L2Controls()
615 v4l2AutoWhiteBalanceSupported =
false;
616 v4l2ColorTemperatureSupported =
false;
617 QCamera::Features features{};
619 Q_ASSERT(!m_v4l2DevicePath.isEmpty());
622 withV4L2DeviceFileDescriptor([&](
int fd) {
623 struct v4l2_queryctrl queryControl = {};
624 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
626 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
627 v4l2AutoWhiteBalanceSupported =
true;
628 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE,
true);
632 queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
633 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
634 v4l2MinColorTemp = queryControl.minimum;
635 v4l2MaxColorTemp = queryControl.maximum;
636 v4l2ColorTemperatureSupported =
true;
637 features |= QCamera::Feature::ColorTemperature;
641 queryControl.id = V4L2_CID_EXPOSURE_AUTO;
642 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
643 v4l2AutoExposureSupported =
true;
647 queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
648 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
649 v4l2ManualExposureSupported =
true;
650 v4l2MinExposure = queryControl.minimum;
651 v4l2MaxExposure = queryControl.maximum;
652 features |= QCamera::Feature::ManualExposureTime;
656 queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
657 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
658 v4l2MinExposureAdjustment = queryControl.minimum;
659 v4l2MaxExposureAdjustment = queryControl.maximum;
660 features |= QCamera::Feature::ExposureCompensation;
664 queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
665 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
666 queryControl.id = V4L2_CID_ISO_SENSITIVITY;
667 if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
668 features |= QCamera::Feature::IsoSensitivity;
669 minIsoChanged(queryControl.minimum);
670 maxIsoChanged(queryControl.minimum);
675 supportedFeaturesChanged(features);
678int QGstreamerCamera::setV4L2ColorTemperature(
int temperature)
680 if (v4l2AutoWhiteBalanceSupported) {
681 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ?
true :
false);
682 }
else if (temperature == 0) {
686 if (temperature != 0 && v4l2ColorTemperatureSupported) {
687 temperature = qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp);
688 if (!setV4L2Parameter(V4L2_CID_WHITE_BALANCE_TEMPERATURE, qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp)))
697bool QGstreamerCamera::setV4L2Parameter(quint32 id, qint32 value)
699 return withV4L2DeviceFileDescriptor([&](
int fd) {
700 v4l2_control control{ id, value };
701 if (::ioctl(fd, VIDIOC_S_CTRL, &control) != 0) {
702 qWarning() <<
"Unable to set the V4L2 Parameter" << Qt::hex << id <<
"to" << value
703 << qt_error_string(errno);
710int QGstreamerCamera::getV4L2Parameter(quint32 id)
const
712 return withV4L2DeviceFileDescriptor([&](
int fd) {
713 v4l2_control control{ id, 0 };
714 if (::ioctl(fd, VIDIOC_G_CTRL, &control) != 0) {
715 qWarning() <<
"Unable to get the V4L2 Parameter" << Qt::hex << id
716 << qt_error_string(errno);
719 return control.value;
729 m_userProvidedGstElement{
742 m_userProvidedGstElement{
750 if (m_userProvidedGstElement)
753 gstCamera = QGstBin::createFromPipelineDescription(device.id(),
nullptr,
764 if (m_active == active)
769 emit activeChanged(active);
static QGstCaps fromCameraFormat(const QCameraFormat &format)
QGstElement & operator=(QGstElement &&) noexcept=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()