Browse Source

Support for extended device controls

This patch introduces support for extended control. It builds on the previous work
for user controls to provide the following features:

- Set/get extended control values
- Query information about extended controls
- Retrieve all supported extend controls
- Retrieve extended control menus
Vladimir Vivien 3 years ago
parent
commit
9468ad1e25

+ 52 - 0
examples/ext_ctrls/extctrls.go

@@ -0,0 +1,52 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"os"
+
+	dev "github.com/vladimirvivien/go4vl/device"
+	"github.com/vladimirvivien/go4vl/v4l2"
+)
+
+func main() {
+	devName := "/dev/video0"
+	flag.StringVar(&devName, "d", devName, "device name (path)")
+	flag.Parse()
+
+	device, err := dev.Open(devName)
+	if err != nil {
+		log.Fatalf("failed to open device: %s", err)
+	}
+	defer device.Close()
+
+	ctrls, err := v4l2.QueryAllExtControls(device.Fd())
+	if err != nil {
+		log.Fatalf("failed to get ext controls: %s", err)
+	}
+	if len(ctrls) == 0 {
+		log.Println("Device does not have extended controls")
+		os.Exit(0)
+	}
+	for _, ctrl := range ctrls {
+		printControl(ctrl)
+	}
+}
+
+func printControl(ctrl v4l2.Control) {
+	fmt.Printf("Control id (%d) name: %s\t[min: %d; max: %d; step: %d; default: %d current_val: %d]\n",
+		ctrl.ID, ctrl.Name, ctrl.Minimum, ctrl.Maximum, ctrl.Step, ctrl.Default, ctrl.Value)
+
+	if ctrl.IsMenu() {
+		menus, err := ctrl.GetMenuItems()
+		if err != nil {
+			return
+		}
+
+		for _, m := range menus {
+			fmt.Printf("\t(%d) Menu %s: [%d]\n", m.Index, m.Name, m.Value)
+		}
+	}
+
+}

+ 1 - 1
examples/user_ctrl/ctrl.go

@@ -93,7 +93,7 @@ func printUserControl(ctrl v4l2.Control) {
 		}
 		}
 
 
 		for _, m := range menus {
 		for _, m := range menus {
-			fmt.Printf("\tMenu items for %s: %#v", ctrl.Name, m)
+			fmt.Printf("\tMenu items for %s: %#v\n", ctrl.Name, m)
 		}
 		}
 	}
 	}
 
 

+ 2 - 2
v4l2/control.go

@@ -141,12 +141,12 @@ func GetControl(fd uintptr, id CtrlID) (Control, error) {
 }
 }
 
 
 // QueryAllControls loop through all available user controls and returns information for
 // QueryAllControls loop through all available user controls and returns information for
-// all enabled controls and their values.
+// all controls without their current values (use GetControlValue to get current values).
 // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html
 // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html
 func QueryAllControls(fd uintptr) (result []Control, err error) {
 func QueryAllControls(fd uintptr) (result []Control, err error) {
 	cid := uint32(C.V4L2_CTRL_FLAG_NEXT_CTRL)
 	cid := uint32(C.V4L2_CTRL_FLAG_NEXT_CTRL)
 	for {
 	for {
-		control, err := GetControl(fd, cid)
+		control, err := QueryControlInfo(fd, cid)
 		if err != nil {
 		if err != nil {
 			if errors.Is(err, ErrorBadArgument) && len(result) > 0 {
 			if errors.Is(err, ErrorBadArgument) && len(result) > 0 {
 				break
 				break

+ 133 - 69
v4l2/control_values.go

@@ -9,20 +9,72 @@ import "C"
 
 
 // ControlClass
 // ControlClass
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L56
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L56
-type ControlClass = uint32
+type CtrlClass = uint32
 
 
 const (
 const (
-	UserControlClass            ControlClass = C.V4L2_CTRL_CLASS_USER
-	CodecControlClass           ControlClass = C.V4L2_CTRL_CLASS_CODEC
-	CameraControlClass          ControlClass = C.V4L2_CTRL_CLASS_CAMERA
-	CameraFlashControlClass     ControlClass = C.V4L2_CTRL_CLASS_FLASH
-	JPEGControlClass            ControlClass = C.V4L2_CTRL_CLASS_JPEG
-	ImageSourceControlClass     ControlClass = C.V4L2_CTRL_CLASS_IMAGE_SOURCE
-	ImageProcessingControlClass ControlClass = C.V4L2_CTRL_CLASS_IMAGE_PROC
-	DigitalVideoControlClass    ControlClass = C.V4L2_CTRL_CLASS_DV
-	DetectionControlClass       ControlClass = C.V4L2_CTRL_CLASS_DETECT
-	CodecStatelessControlClass  ControlClass = C.V4L2_CTRL_CLASS_CODEC_STATELESS
-	ColorimitryControlClass     ControlClass = C.V4L2_CTRL_CLASS_COLORIMETRY
+	CtrlClassUser            CtrlClass = C.V4L2_CTRL_CLASS_USER
+	CtrlClassCodec           CtrlClass = C.V4L2_CTRL_CLASS_CODEC
+	CtrlClassCamera          CtrlClass = C.V4L2_CTRL_CLASS_CAMERA
+	CtrlClassFlash           CtrlClass = C.V4L2_CTRL_CLASS_FLASH
+	CtrlClassJPEG            CtrlClass = C.V4L2_CTRL_CLASS_JPEG
+	CtrlClassImageSource     CtrlClass = C.V4L2_CTRL_CLASS_IMAGE_SOURCE
+	CtrlClassImageProcessing CtrlClass = C.V4L2_CTRL_CLASS_IMAGE_PROC
+	CtrlClassDigitalVideo    CtrlClass = C.V4L2_CTRL_CLASS_DV
+	CtrlClassDetection       CtrlClass = C.V4L2_CTRL_CLASS_DETECT
+	CtrlClassCodecStateless  CtrlClass = C.V4L2_CTRL_CLASS_CODEC_STATELESS
+	CtrlClassColorimitry     CtrlClass = C.V4L2_CTRL_CLASS_COLORIMETRY
+)
+
+var (
+	// CtrlClasses is a slice of all Control classes
+	CtrlClasses = []CtrlClass{
+		CtrlClassUser,
+		CtrlClassCodec,
+		CtrlClassCamera,
+		CtrlClassFlash,
+		CtrlClassJPEG,
+		CtrlClassImageSource,
+		CtrlClassDigitalVideo,
+		CtrlClassDetection,
+		CtrlClassCodecStateless,
+		CtrlClassColorimitry,
+	}
+)
+
+// CtrlType constants
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1799
+// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-queryctrl.html?highlight=v4l2_ctrl_type#c.V4L.v4l2_ctrl_type
+type CtrlType uint32
+
+const (
+	CtrlTypeInt                 CtrlType = C.V4L2_CTRL_TYPE_INTEGER
+	CtrlTypeBool                CtrlType = C.V4L2_CTRL_TYPE_BOOLEAN
+	CtrlTypeMenu                CtrlType = C.V4L2_CTRL_TYPE_MENU
+	CtrlTypeButton              CtrlType = C.V4L2_CTRL_TYPE_BUTTON
+	CtrlTypeInt64               CtrlType = C.V4L2_CTRL_TYPE_INTEGER64
+	CtrlTypeClass               CtrlType = C.V4L2_CTRL_TYPE_CTRL_CLASS
+	CtrlTypeString              CtrlType = C.V4L2_CTRL_TYPE_STRING
+	CtrlTypeBitMask             CtrlType = C.V4L2_CTRL_TYPE_BITMASK
+	CtrlTypeIntegerMenu         CtrlType = C.V4L2_CTRL_TYPE_INTEGER_MENU
+	CtrlTypeCompoundTypes       CtrlType = C.V4L2_CTRL_COMPOUND_TYPES
+	CtrlTypeU8                  CtrlType = C.V4L2_CTRL_TYPE_U8
+	CtrlTypeU16                 CtrlType = C.V4L2_CTRL_TYPE_U16
+	CtrlTypeU32                 CtrlType = C.V4L2_CTRL_TYPE_U32
+	CtrlTypeArear               CtrlType = C.V4L2_CTRL_TYPE_AREA
+	CtrlTypeHDR10CLLInfo        CtrlType = C.V4L2_CTRL_TYPE_HDR10_CLL_INFO
+	CtrlTypeHDRMasteringDisplay CtrlType = C.V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY
+	CtrlTypeH264SPS             CtrlType = C.V4L2_CTRL_TYPE_H264_SPS
+	CtrlTypeH264PPS             CtrlType = C.V4L2_CTRL_TYPE_H264_PPS
+	CtrlTypeH264ScalingMatrix   CtrlType = C.V4L2_CTRL_TYPE_H264_SCALING_MATRIX
+	CtrlTypeH264SliceParams     CtrlType = C.V4L2_CTRL_TYPE_H264_SLICE_PARAMS
+	CtrlTypeH264DecodeParams    CtrlType = C.V4L2_CTRL_TYPE_H264_DECODE_PARAMS
+	CtrlTypeFWHTParams          CtrlType = C.V4L2_CTRL_TYPE_FWHT_PARAMS
+	CtrlTypeVP8Frame            CtrlType = C.V4L2_CTRL_TYPE_VP8_FRAME
+	CtrlTypeMPEG2Quantization   CtrlType = C.V4L2_CTRL_TYPE_MPEG2_QUANTISATION
+	CtrlTypeMPEG2Sequence       CtrlType = C.V4L2_CTRL_TYPE_MPEG2_SEQUENCE
+	CtrlTypeMPEG2Picture        CtrlType = C.V4L2_CTRL_TYPE_MPEG2_PICTURE
+	CtrlTypeVP9CompressedHDR    CtrlType = C.V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR
+	CtrlTypeVP9Frame            CtrlType = C.V4L2_CTRL_TYPE_VP9_FRAME
 )
 )
 
 
 // CtrlID type for control values
 // CtrlID type for control values
@@ -30,36 +82,40 @@ type CtrlID = uint32
 
 
 // Powerline frequency control enums
 // Powerline frequency control enums
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L100
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L100
+type PowerlineFrequency = uint32
+
 const (
 const (
-	PowerlineFrequencyDisabled uint32 = iota
-	PowerlineFrequency50Hz
-	PowerlineFrequency60Hz
-	PowerlineFrequencyAuto
+	PowerlineFrequencyDisabled PowerlineFrequency = C.V4L2_CID_POWER_LINE_FREQUENCY_DISABLED
+	PowerlineFrequency50Hz     PowerlineFrequency = C.V4L2_CID_POWER_LINE_FREQUENCY_50HZ
+	PowerlineFrequency60Hz     PowerlineFrequency = C.V4L2_CID_POWER_LINE_FREQUENCY_60HZ
+	PowerlineFrequencyAuto     PowerlineFrequency = C.V4L2_CID_POWER_LINE_FREQUENCY_AUTO
 )
 )
 
 
 // Color FX control enums
 // Color FX control enums
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L114
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L114
+type ColorFX = uint32
+
 const (
 const (
-	ColorFXNone uint32 = iota
-	ColorFXBlackWhite
-	ColorFXSepia
-	ColorFXNegative
-	ColorFXEmboss
-	ColorFXSketch
-	ColorFXSkyBlue
-	ColorFXGrassGreen
-	ColorFXSkinWhiten
-	ColorFXVivid
-	ColorFXAqua
-	ColorFXArtFreeze
-	ColorFXSilhouette
-	ColorFXSolarization
-	ColorFXAntique
-	ColorFXSetCBCR
-	ColorFXSetRGB
+	ColorFXNone         ColorFX = C.V4L2_COLORFX_NONE
+	ColorFXBlackWhite   ColorFX = C.V4L2_COLORFX_BW
+	ColorFXSepia        ColorFX = C.V4L2_COLORFX_SEPIA
+	ColorFXNegative     ColorFX = C.V4L2_COLORFX_NEGATIVE
+	ColorFXEmboss       ColorFX = C.V4L2_COLORFX_EMBOSS
+	ColorFXSketch       ColorFX = C.V4L2_COLORFX_SKETCH
+	ColorFXSkyBlue      ColorFX = C.V4L2_COLORFX_SKY_BLUE
+	ColorFXGrassGreen   ColorFX = C.V4L2_COLORFX_GRASS_GREEN
+	ColorFXSkinWhiten   ColorFX = C.V4L2_COLORFX_SKIN_WHITEN
+	ColorFXVivid        ColorFX = C.V4L2_COLORFX_VIVID
+	ColorFXAqua         ColorFX = C.V4L2_COLORFX_AQUA
+	ColorFXArtFreeze    ColorFX = C.V4L2_COLORFX_ART_FREEZE
+	ColorFXSilhouette   ColorFX = C.V4L2_COLORFX_SILHOUETTE
+	ColorFXSolarization ColorFX = C.V4L2_COLORFX_SOLARIZATION
+	ColorFXAntique      ColorFX = C.V4L2_COLORFX_ANTIQUE
+	ColorFXSetCBCR      ColorFX = C.V4L2_COLORFX_SET_CBCR
+	ColorFXSetRGB       ColorFX = C.V4L2_COLORFX_SET_RGB
 )
 )
 
 
-// User Controls
+// User Controls IDs (CIDs)
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L74
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L74
 // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html#control-id
 // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html#control-id
 const (
 const (
@@ -95,41 +151,49 @@ const (
 	CtrlAlphaComponent          CtrlID = C.V4L2_CID_ALPHA_COMPONENT
 	CtrlAlphaComponent          CtrlID = C.V4L2_CID_ALPHA_COMPONENT
 )
 )
 
 
-// ControlType stores values fround in enum v4l2_ctrl_type
-// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1799
-// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-queryctrl.html?highlight=v4l2_ctrl_type#c.V4L.v4l2_ctrl_type
-type CtrlType uint32
+// Camera control values
+// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/ext-ctrls-camera.html
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L897
+const (
+	CtrlCameraClass        CtrlID = C.V4L2_CID_CAMERA_CLASS
+	CtrlCameraExposureAuto CtrlID = C.V4L2_CID_EXPOSURE_AUTO
 
 
-// CtrlType constants
-// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1799
-// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-queryctrl.html?highlight=v4l2_ctrl_type#c.V4L.v4l2_ctrl_type
+	// TODO add all camera control values
+)
+
+// Flash control values
+// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/ext-ctrls-flash.html
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L1060
 const (
 const (
-	CtrlTypeInt                 CtrlType = C.V4L2_CTRL_TYPE_INTEGER
-	CtrlTypeBool                CtrlType = C.V4L2_CTRL_TYPE_BOOLEAN
-	CtrlTypeMenu                CtrlType = C.V4L2_CTRL_TYPE_MENU
-	CtrlTypeButton              CtrlType = C.V4L2_CTRL_TYPE_BUTTON
-	CtrlTypeInt64               CtrlType = C.V4L2_CTRL_TYPE_INTEGER64
-	CtrlTypeClass               CtrlType = C.V4L2_CTRL_TYPE_CTRL_CLASS
-	CtrlTypeString              CtrlType = C.V4L2_CTRL_TYPE_STRING
-	CtrlTypeBitMask             CtrlType = C.V4L2_CTRL_TYPE_BITMASK
-	CtrlTypeIntegerMenu         CtrlType = C.V4L2_CTRL_TYPE_INTEGER_MENU
-	CtrlTypeCompoundTypes       CtrlType = C.V4L2_CTRL_COMPOUND_TYPES
-	CtrlTypeU8                  CtrlType = C.V4L2_CTRL_TYPE_U8
-	CtrlTypeU16                 CtrlType = C.V4L2_CTRL_TYPE_U16
-	CtrlTypeU32                 CtrlType = C.V4L2_CTRL_TYPE_U32
-	CtrlTypeArear               CtrlType = C.V4L2_CTRL_TYPE_AREA
-	CtrlTypeHDR10CLLInfo        CtrlType = C.V4L2_CTRL_TYPE_HDR10_CLL_INFO
-	CtrlTypeHDRMasteringDisplay CtrlType = C.V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY
-	CtrlTypeH264SPS             CtrlType = C.V4L2_CTRL_TYPE_H264_SPS
-	CtrlTypeH264PPS             CtrlType = C.V4L2_CTRL_TYPE_H264_PPS
-	CtrlTypeH264ScalingMatrix   CtrlType = C.V4L2_CTRL_TYPE_H264_SCALING_MATRIX
-	CtrlTypeH264SliceParams     CtrlType = C.V4L2_CTRL_TYPE_H264_SLICE_PARAMS
-	CtrlTypeH264DecodeParams    CtrlType = C.V4L2_CTRL_TYPE_H264_DECODE_PARAMS
-	CtrlTypeFWHTParams          CtrlType = C.V4L2_CTRL_TYPE_FWHT_PARAMS
-	CtrlTypeVP8Frame            CtrlType = C.V4L2_CTRL_TYPE_VP8_FRAME
-	CtrlTypeMPEG2Quantization   CtrlType = C.V4L2_CTRL_TYPE_MPEG2_QUANTISATION
-	CtrlTypeMPEG2Sequence       CtrlType = C.V4L2_CTRL_TYPE_MPEG2_SEQUENCE
-	CtrlTypeMPEG2Picture        CtrlType = C.V4L2_CTRL_TYPE_MPEG2_PICTURE
-	CtrlTypeVP9CompressedHDR    CtrlType = C.V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR
-	CtrlTypeVP9Frame            CtrlType = C.V4L2_CTRL_TYPE_VP9_FRAME
+	CtrlFlashClass   CtrlID = C.V4L2_CID_FLASH_CLASS
+	CtrlFlashLEDMode CtrlID = C.V4L2_CID_FLASH_LED_MODE
+	// TODO add all flash control const values
 )
 )
+
+// JPEG control values
+// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/ext-ctrls-jpeg.html
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L1104
+const (
+	CtrlJPEGClass          CtrlID = C.V4L2_CID_JPEG_CLASS
+	CtrlJPEGChromaSampling CtrlID = C.V4L2_CID_JPEG_CHROMA_SUBSAMPLING
+	// TODO add all JPEG flash controls
+)
+
+// Image source controls
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L1127
+// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/ext-ctrls-image-source.html
+const (
+	CtrlImgSrcClass         CtrlID = C.V4L2_CID_IMAGE_SOURCE_CLASS
+	CtrlImgSrcVerticalBlank CtrlID = C.V4L2_CID_VBLANK
+)
+
+// Image process controls
+// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/ext-ctrls-image-process.html
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L1144
+const (
+	CtrlImgProcClass = C.V4L2_CID_IMAGE_PROC_CLASS
+	// TODO implement all image process values
+)
+
+// TODO add code for the following controls
+// Stateless codec controls (h264, vp8, fwht, mpeg2, etc)

+ 85 - 0
v4l2/control_values_codecs.go

@@ -0,0 +1,85 @@
+package v4l2
+
+/*
+#cgo linux CFLAGS: -I ${SRCDIR}/../include/
+#include <linux/videodev2.h>
+#include <linux/v4l2-controls.h>
+*/
+import "C"
+
+// MPEGStreamType represents v4l2_mpeg_stream_type
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L237
+type MPEGStreamType = uint32
+
+const (
+	MPEGStreamTypeMPEG2ProgramStream   MPEGStreamType = C.V4L2_MPEG_STREAM_TYPE_MPEG2_PS
+	MPEGStreamTypeMPEG2TransportStream MPEGStreamType = C.V4L2_MPEG_STREAM_TYPE_MPEG2_TS
+	MPEGStreamTypeMPEG1SystemStream    MPEGStreamType = C.V4L2_MPEG_STREAM_TYPE_MPEG1_SS
+	MPEGStreamTypeMPEG2DVD             MPEGStreamType = C.V4L2_MPEG_STREAM_TYPE_MPEG2_DVD
+	MPEGStreamTypeMPEG1VCD             MPEGStreamType = C.V4L2_MPEG_STREAM_TYPE_MPEG1_VCD
+	MPEGStreamTypeMPEG2SVCD            MPEGStreamType = C.V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD
+)
+
+type MPEGVideoEncoding = uint32
+
+const (
+	MPEGVideoEncodingMPEG1    MPEGVideoEncoding = C.V4L2_MPEG_VIDEO_ENCODING_MPEG_1
+	MPEGVideoEncodingMPEG2    MPEGVideoEncoding = C.V4L2_MPEG_VIDEO_ENCODING_MPEG_2
+	MPEGVideoEncodingMPEG4AVC MPEGVideoEncoding = C.V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC
+)
+
+type MPEGVideoAspect = uint32
+
+const (
+	MPEGVideoAspect1x1     MPEGVideoAspect = C.V4L2_MPEG_VIDEO_ASPECT_1x1
+	MPEGVideoAspect4x3     MPEGVideoAspect = C.V4L2_MPEG_VIDEO_ASPECT_4x3
+	MPEGVideoAspect16x9    MPEGVideoAspect = C.V4L2_MPEG_VIDEO_ASPECT_16x9
+	MPEGVideoAspect221x100 MPEGVideoAspect = C.V4L2_MPEG_VIDEO_ASPECT_221x100
+)
+
+type MPEGVideoBitrateMode = uint32
+
+const (
+	MPEGVideoBitrateModeVBR = C.V4L2_MPEG_VIDEO_BITRATE_MODE_VBR
+	MPEGVideoBitrateModeCBR = C.V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
+	MPEGVideoBitrateModeCQ  = C.V4L2_MPEG_VIDEO_BITRATE_MODE_CQ
+)
+
+// Codec control values
+// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/ext-ctrls-codec.html
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/v4l2-controls.h#L228
+const (
+	CtrlCodecClass                         CtrlID               = C.V4L2_CID_CODEC_CLASS
+	CtrlMPEGStreamType                     MPEGStreamType       = C.V4L2_CID_MPEG_STREAM_TYPE
+	CtrlMPEGStreamPIDPMT                   CtrlID               = C.V4L2_CID_MPEG_STREAM_PID_PMT
+	CtrlMPEGStreamPIDAudio                 CtrlID               = C.V4L2_CID_MPEG_STREAM_PID_AUDIO
+	CtrlMPEGStreamPIDVideo                 CtrlID               = C.V4L2_CID_MPEG_STREAM_PID_VIDEO
+	CtrlMPEGStreamPIDPCR                   CtrlID               = C.V4L2_CID_MPEG_STREAM_PID_PCR
+	CtrlMPEGStreamPIDPESAudio              CtrlID               = C.V4L2_CID_MPEG_STREAM_PES_ID_AUDIO
+	CtrlMPEGStreamPESVideo                 CtrlID               = C.V4L2_CID_MPEG_STREAM_PES_ID_VIDEO
+	CtrlMEPGStreamVBIFormat                CtrlID               = C.V4L2_CID_MPEG_STREAM_VBI_FMT
+	CtrlMPEGVideoEncoding                  MPEGVideoEncoding    = C.V4L2_CID_MPEG_VIDEO_ENCODING
+	CtrlMPEGVideoAspect                    MPEGVideoAspect      = C.V4L2_CID_MPEG_VIDEO_ASPECT
+	CtrlMPEGVideoBFrames                   CtrlID               = C.V4L2_CID_MPEG_VIDEO_B_FRAMES
+	CtrlMPEGVideoGOPSize                   CtrlID               = C.V4L2_CID_MPEG_VIDEO_GOP_SIZE
+	CtrlMPEGVideoGOPClosure                CtrlID               = C.V4L2_CID_MPEG_VIDEO_GOP_CLOSURE
+	CtrlMPEGVideoPulldown                  CtrlID               = C.V4L2_CID_MPEG_VIDEO_PULLDOWN
+	CtrlMPEGVideoBitrateMode               MPEGVideoBitrateMode = C.V4L2_CID_MPEG_VIDEO_BITRATE_MODE
+	CtrlMPEGVideoBitrate                   CtrlID               = C.V4L2_CID_MPEG_VIDEO_BITRATE
+	CtrlMPEGVideoBitratePeak               CtrlID               = C.V4L2_CID_MPEG_VIDEO_BITRATE_PEAK
+	CtrlMPEGVideoTemporalDecimation        CtrlID               = C.V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION
+	CtrlMPEGVideoMute                      CtrlID               = C.V4L2_CID_MPEG_VIDEO_MUTE
+	CtrlMPEGVideoMutYUV                    CtrlID               = C.V4L2_CID_MPEG_VIDEO_MUTE_YUV
+	CtrlMPEGVideoDecoderSliceInterface     CtrlID               = C.V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE
+	CtrlMPEGVideoDecoderMPEG4DeblockFilter CtrlID               = C.V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER
+	CtrlMPEGVideoCyclicIntraRefreshMB      CtrlID               = C.V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB
+	CtrlMPEGVideoFrameRCEnable             CtrlID               = C.V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE
+	CtrlMPEGVideoHeaderMode                CtrlID               = C.V4L2_CID_MPEG_VIDEO_HEADER_MODE
+	CtrlMPEGVideoMaxRefPic                 CtrlID               = C.V4L2_CID_MPEG_VIDEO_MAX_REF_PIC
+	CtrlMPEGVideoMBRCEnable                CtrlID               = C.V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE
+	CtrlMPEGVideoMultiSliceMaxBytes        CtrlID               = C.V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES
+	CtrlMPEGVideoMultiSliceMaxMB           CtrlID               = C.V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB
+	CtrlMPEGVideoMultiSliceMode            CtrlID               = C.V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE
+
+	// TODO (vladimir) add remainder codec, there are a lot more!
+)

+ 107 - 75
v4l2/ext_controls.go

@@ -6,43 +6,52 @@ package v4l2
 */
 */
 import "C"
 import "C"
 import (
 import (
+	"errors"
 	"fmt"
 	"fmt"
 	"unsafe"
 	"unsafe"
 )
 )
 
 
-// TODO - Implementation of extended controls (v4l2_ext_control) is paused for now,
-// so that efforts can be focused on other parts of the API. This can resumed
-// later when type v4l2_ext_control and v4l2_ext_controls are better understood.
-
-// ExtControl (v4l2_ext_control)
-// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1730
-// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-g-ext-ctrls.html
-type ExtControl struct {
-	ID      uint32
-	Value   int32
-	Value64 int64
-
-	// NOT USED YET
-	Size               uint32
-	String             string
-	PU8                uint
-	PU16               uint16
-	PU32               uint32
-	PArea              Area
-	PH264SPS           ControlH264SPS
-	PH264PPS           ControlH264PPS
-	PH264ScalingMatrix ControlH264ScalingMatrix
-	H264PredWeights    ControlH264PredictionWeights
-	PH264SliceParams   ControlH264SliceParams
-	PH264DecodeParams  ControlH264DecodeParams
-	PFWHTParams        ControlFWHTParams
-	PVP8Frame          ControlVP8Frame
-	PMPEG2Sequence     ControlMPEG2Sequence
-	PMPEG2Picture      ControlMPEG2Picture
-	PMPEG2Quantization ControlMPEG2Quantization
+// GetExtControlValue retrieves the value for an extended control with the specified id.
+// See https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/extended-controls.html
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1745
+func GetExtControlValue(fd uintptr, ctrlID CtrlID) (CtrlValue, error) {
+	var v4l2Ctrl C.struct_v4l2_ext_control
+	v4l2Ctrl.id = C.uint(ctrlID)
+	v4l2Ctrl.size = 0
+	if err := send(fd, C.VIDIOC_G_EXT_CTRLS, uintptr(unsafe.Pointer(&v4l2Ctrl))); err != nil {
+		return 0, fmt.Errorf("get ext controls: %w", err)
+	}
+	return *(*CtrlValue)(unsafe.Pointer(&v4l2Ctrl.anon0[0])), nil
 }
 }
 
 
-func setExtCtrls(fd uintptr, whichCtrl uint32, ctrls []ExtControl) error {
+// SetExtControlValue saves the value for an extended control with the specified id.
+// See https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/extended-controls.html
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1745
+func SetExtControlValue(fd uintptr, id CtrlID, val CtrlValue) error {
+	ctrlInfo, err := QueryExtControlInfo(fd, id)
+	if err != nil {
+		return fmt.Errorf("set ext control value: id %s: %w", id, err)
+	}
+	if val < ctrlInfo.Minimum || val > ctrlInfo.Maximum {
+		return fmt.Errorf("set ext control value: out-of-range failure: val %d: expected ctrl.Min %d, ctrl.Max %d", val, ctrlInfo.Minimum, ctrlInfo.Maximum)
+	}
+
+	var v4l2Ctrl C.struct_v4l2_ext_control
+	v4l2Ctrl.id = C.uint(id)
+	*(*C.int)(unsafe.Pointer(&v4l2Ctrl.anon0[0])) = *(*C.int)(unsafe.Pointer(&val))
+
+	if err := send(fd, C.VIDIOC_S_CTRL, uintptr(unsafe.Pointer(&v4l2Ctrl))); err != nil {
+		return fmt.Errorf("set ext control value: id %d: %w", id, err)
+	}
+
+	return nil
+}
+
+// SetExtControls implements code to save one or more extended controls at once using the
+// v4l2_ext_controls structure.
+// https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/extended-controls.html
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1774
+func SetExtControlValues(fd uintptr, whichCtrl CtrlClass, ctrls []Control) error {
 	numCtrl := len(ctrls)
 	numCtrl := len(ctrls)
 
 
 	var v4l2CtrlArray []C.struct_v4l2_ext_control
 	var v4l2CtrlArray []C.struct_v4l2_ext_control
@@ -50,7 +59,7 @@ func setExtCtrls(fd uintptr, whichCtrl uint32, ctrls []ExtControl) error {
 	for _, ctrl := range ctrls {
 	for _, ctrl := range ctrls {
 		var v4l2Ctrl C.struct_v4l2_ext_control
 		var v4l2Ctrl C.struct_v4l2_ext_control
 		v4l2Ctrl.id = C.uint(ctrl.ID)
 		v4l2Ctrl.id = C.uint(ctrl.ID)
-		*(*C.uint)(unsafe.Pointer(&v4l2Ctrl.anon0[0])) = *(*C.uint)(unsafe.Pointer(&ctrl.Value))
+		*(*C.int)(unsafe.Pointer(&v4l2Ctrl.anon0[0])) = *(*C.int)(unsafe.Pointer(&ctrl.Value))
 
 
 		v4l2CtrlArray = append(v4l2CtrlArray, v4l2Ctrl)
 		v4l2CtrlArray = append(v4l2CtrlArray, v4l2Ctrl)
 	}
 	}
@@ -67,46 +76,69 @@ func setExtCtrls(fd uintptr, whichCtrl uint32, ctrls []ExtControl) error {
 	return nil
 	return nil
 }
 }
 
 
-// GetExtControls retrieve one or more controls
-// func GetExtControls(fd uintptr, controls []ExtControl) (ExtControls, error) {
-// 	if true {
-// 		// TODO remove when supported
-// 		return ExtControls{}, fmt.Errorf("unsupported")
-// 	}
-
-// 	var ctrls C.struct_v4l2_ext_controls
-// 	ctrls.count = C.uint(len(controls))
-
-// 	// prepare control requests
-// 	var Cctrls []C.struct_v4l2_ext_control
-// 	for _, control := range controls {
-// 		var Cctrl C.struct_v4l2_ext_control
-// 		Cctrl.id = C.uint(control.ID)
-// 		Cctrl.size = C.uint(control.Size)
-// 		*(*ExtControlUnion)(unsafe.Pointer(&Cctrl.anon0[0])) = control.Ctrl
-// 		Cctrls = append(Cctrls, Cctrl)
-// 	}
-// 	ctrls.controls = (*C.struct_v4l2_ext_control)(unsafe.Pointer(&ctrls.controls))
-
-// 	if err := send(fd, C.VIDIOC_G_EXT_CTRLS, uintptr(unsafe.Pointer(&ctrls))); err != nil {
-// 		return ExtControls{}, fmt.Errorf("get ext controls: %w", err)
-// 	}
-
-// 	// gather returned controls
-// 	retCtrls := ExtControls{
-// 		Count:      uint32(ctrls.count),
-// 		ErrorIndex: uint32(ctrls.error_idx),
-// 	}
-// 	// extract controls array
-// 	Cctrls = *(*[]C.struct_v4l2_ext_control)(unsafe.Pointer(&ctrls.controls))
-// 	for _, Cctrl := range Cctrls {
-// 		extCtrl := ExtControl{
-// 			ID:   uint32(Cctrl.id),
-// 			Size: uint32(Cctrl.size),
-// 			Ctrl: *(*ExtControlUnion)(unsafe.Pointer(&Cctrl.anon0[0])),
-// 		}
-// 		retCtrls.Controls = append(retCtrls.Controls, extCtrl)
-// 	}
-
-// 	return retCtrls, nil
-// }
+// GetExtControl retrieves information (query) and current value for the specified control.
+// See https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/extended-controls.html
+func GetExtControl(fd uintptr, id CtrlID) (Control, error) {
+	control, err := QueryExtControlInfo(fd, id)
+	if err != nil {
+		return Control{}, fmt.Errorf("get control: %w", err)
+	}
+
+	// retrieve control value
+	ctrlValue, err := GetExtControlValue(fd, uint32(id))
+	if err != nil {
+		return Control{}, fmt.Errorf("get control: %w", id, err)
+	}
+
+	control.Value = ctrlValue
+	control.fd = fd
+	return control, nil
+}
+
+// QueryExtControlInfo queries information about the specified ext control without its current value.
+func QueryExtControlInfo(fd uintptr, id CtrlID) (Control, error) {
+	// query control information
+	var qryCtrl C.struct_v4l2_query_ext_ctrl
+	qryCtrl.id = C.uint(id)
+
+	if err := send(fd, C.VIDIOC_QUERY_EXT_CTRL, uintptr(unsafe.Pointer(&qryCtrl))); err != nil {
+		return Control{}, fmt.Errorf("query ext control info: VIDIOC_QUERY_EXT_CTRL: id %d: %w", id, err)
+	}
+	control := makeExtControl(qryCtrl)
+	control.fd = fd
+	return control, nil
+}
+
+// QueryExtAllControls loop through all available ext controls and query the information for
+// all controls without their current values (use GetExtControlValue to get current values).
+func QueryAllExtControls(fd uintptr) (result []Control, err error) {
+
+	cid := CtrlClassCodec | uint32(C.V4L2_CTRL_FLAG_NEXT_CTRL)
+	for {
+		control, err := QueryExtControlInfo(fd, cid)
+		if err != nil {
+			if errors.Is(err, ErrorBadArgument) {
+				break
+			}
+			return result, fmt.Errorf("query all ext controls: %w", err)
+		}
+		result = append(result, control)
+		// setup next id
+		cid = control.ID | uint32(C.V4L2_CTRL_FLAG_NEXT_CTRL)
+	}
+
+	return result, nil
+}
+
+func makeExtControl(qryCtrl C.struct_v4l2_query_ext_ctrl) Control {
+	return Control{
+		Type:    CtrlType(qryCtrl._type),
+		ID:      uint32(qryCtrl.id),
+		Name:    C.GoString((*C.char)(unsafe.Pointer(&qryCtrl.name[0]))),
+		Maximum: int32(qryCtrl.maximum),
+		Minimum: int32(qryCtrl.minimum),
+		Step:    int32(qryCtrl.step),
+		Default: int32(qryCtrl.default_value),
+		flags:   uint32(qryCtrl.flags),
+	}
+}