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
).