Accessing UVC camera controls from PyGST

199 views Asked by At

I'm using PyGST to display the feed from a UVC webcam inside a PyQt application. I can access some camera controls, such as brightness and contrast, directly using the corresponding properties of the v4l2src elements. However, I'd like to access additional controls, namely focus, available through v4l2-ctl. My understanding is that such controls should be accessible through the extra-controls property (extra_controls in Python) of the v4l2src element. However, the property is empty at runtime after the pipeline is started. What am I missing?

EDIT: minimal sample

import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from PyQt5 import uic
import subprocess
import re

#GStreamer libraries
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gst, GObject, GstVideo
Gst.init(None)

class WebcamTest(qtw.QMainWindow):
    def __init__(self):
        super().__init__()

        self.resize(640,480)
        self.gst_video = VideoWidget()
        self.setCentralWidget(self.gst_video)
        self.gst_video.prepare()

class VideoWidget(qtw.QWidget):
    def __init__(self):
        super(VideoWidget, self).__init__()
        self.setAttribute(qtc.Qt.WA_NativeWindow)
        self.windowId = self.winId()
        qtw.qApp.sync()
        self.pipeline = None

    def prepare(self):
        pipeline = "v4l2src device=/dev/video0 name=source ! image/jpeg, width=640, height=480, framerate=30/1, format=MJPG ! jpegdec ! videoconvert ! xvimagesink"

        self.pipeline = Gst.parse_launch(pipeline)
        self.source = self.pipeline.get_child_by_name('source')

        bus = self.pipeline.get_bus()
        bus.add_signal_watch()
        bus.enable_sync_message_emission()
        bus.connect('sync-message::element', self.on_sync_message)
        self.pipeline.set_state(Gst.State.PLAYING)
        print(f'source properties: {self.source.list_properties()}')
        print(f'brightness={self.source.props.brightness}')
        print(f'extra-controls={self.source.props.extra_controls}')

    def on_sync_message(self, bus, msg):
        message_name = msg.get_structure().get_name()
        # qtc.qDebug(message_name)
        if message_name == 'prepare-window-handle':
            win_id = self.windowId
            assert win_id
            imagesink = msg.src
            imagesink.set_window_handle(win_id)

    def dispose(self):
        if (self.pipeline):
            self.pipeline.set_state(Gst.State.NULL)
        self.pipeline = None
    
    def isPlaying(self):
        return self.pipeline.current_state == Gst.State.PLAYING

if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)
    main_ui = WebcamTest()
    main_ui.move(200,100)
    main_ui.show()
    app.exec_()

List of available controls from the camera:

$ v4l2-ctl -l
                     brightness 0x00980900 (int)    : min=-64 max=64 step=1 default=0 value=0
                       contrast 0x00980901 (int)    : min=0 max=64 step=1 default=32 value=32
                     saturation 0x00980902 (int)    : min=0 max=128 step=1 default=64 value=64
                            hue 0x00980903 (int)    : min=-40 max=40 step=1 default=0 value=0
 white_balance_temperature_auto 0x0098090c (bool)   : default=1 value=1
    white_balance_red_component 0x0098090e (int)    : min=1 max=500 step=1 default=100 value=100 flags=inactive
   white_balance_blue_component 0x0098090f (int)    : min=1 max=500 step=1 default=100 value=100 flags=inactive
                          gamma 0x00980910 (int)    : min=72 max=500 step=1 default=100 value=100
                           gain 0x00980913 (int)    : min=0 max=100 step=1 default=0 value=0
           power_line_frequency 0x00980918 (menu)   : min=0 max=2 default=1 value=1
                       hue_auto 0x00980919 (bool)   : default=0 value=0
      white_balance_temperature 0x0098091a (int)    : min=2800 max=6500 step=1 default=4600 value=4600 flags=inactive
                      sharpness 0x0098091b (int)    : min=0 max=6 step=1 default=3 value=3
         backlight_compensation 0x0098091c (int)    : min=0 max=2 step=1 default=1 value=1
                  exposure_auto 0x009a0901 (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute 0x009a0902 (int)    : min=1 max=5000 step=1 default=157 value=157 flags=inactive
         exposure_auto_priority 0x009a0903 (bool)   : default=0 value=1
                 focus_absolute 0x009a090a (int)    : min=1 max=1023 step=1 default=1 value=688
                     focus_auto 0x009a090c (bool)   : default=0 value=0
                zoom_continuous 0x009a090f (int)    : min=0 max=0 step=0 default=0 value=0 flags=write-only
                        privacy 0x009a0910 (bool)   : default=0 value=0
                  iris_absolute 0x009a0911 (int)    : min=0 max=0 step=0 default=0 value=0
                  iris_relative 0x009a0912 (int)    : min=0 max=0 step=0 default=0 value=0 flags=write-only
                      pan_speed 0x009a0920 (int)    : min=0 max=0 step=0 default=0 value=0
                     tilt_speed 0x009a0921 (int)    : min=0 max=0 step=0 default=0 value=0

EDIT: Found a way to change a parameter, for instance this snippet will activate the auto focus:

        extra_controls = Gst.Structure.new_from_string('i,focus_auto=1')
        self.source.set_property('extra_controls', extra_controls)

However, I still don't understand how to query current values of other controls (similarly to what I can get with v4l2-ctl -l).

0

There are 0 answers