install go2rtc on bob

This commit is contained in:
2026-04-04 19:36:14 +02:00
parent f0b56e63d1
commit ccf88187b8
537 changed files with 69213 additions and 0 deletions
@@ -0,0 +1,21 @@
# Video For Linux Two
Build on Ubuntu
```bash
sudo apt install gcc-x86-64-linux-gnu
sudo apt install gcc-i686-linux-gnu
sudo apt install gcc-aarch64-linux-gnu binutils
sudo apt install gcc-arm-linux-gnueabihf
sudo apt install gcc-mipsel-linux-gnu
x86_64-linux-gnu-gcc -w -static videodev2_arch.c -o videodev2_x86_64
i686-linux-gnu-gcc -w -static videodev2_arch.c -o videodev2_i686
aarch64-linux-gnu-gcc -w -static videodev2_arch.c -o videodev2_aarch64
arm-linux-gnueabihf-gcc -w -static videodev2_arch.c -o videodev2_armhf
mipsel-linux-gnu-gcc -w -static videodev2_arch.c -o videodev2_mipsel -D_TIME_BITS=32
```
## Useful links
- https://github.com/torvalds/linux/blob/master/include/uapi/linux/videodev2.h
@@ -0,0 +1,252 @@
//go:build linux
package device
import (
"bytes"
"errors"
"fmt"
"syscall"
"unsafe"
)
type Device struct {
fd int
bufs [][]byte
pixFmt uint32
}
func Open(path string) (*Device, error) {
fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CLOEXEC, 0)
if err != nil {
return nil, err
}
return &Device{fd: fd}, nil
}
const buffersCount = 2
type Capability struct {
Driver string
Card string
BusInfo string
Version string
}
func (d *Device) Capability() (*Capability, error) {
c := v4l2_capability{}
if err := ioctl(d.fd, VIDIOC_QUERYCAP, unsafe.Pointer(&c)); err != nil {
return nil, err
}
return &Capability{
Driver: str(c.driver[:]),
Card: str(c.card[:]),
BusInfo: str(c.bus_info[:]),
Version: fmt.Sprintf("%d.%d.%d", byte(c.version>>16), byte(c.version>>8), byte(c.version)),
}, nil
}
func (d *Device) ListFormats() ([]uint32, error) {
var items []uint32
for i := uint32(0); ; i++ {
fd := v4l2_fmtdesc{
index: i,
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
}
if err := ioctl(d.fd, VIDIOC_ENUM_FMT, unsafe.Pointer(&fd)); err != nil {
if !errors.Is(err, syscall.EINVAL) {
return nil, err
}
break
}
items = append(items, fd.pixelformat)
}
return items, nil
}
func (d *Device) ListSizes(pixFmt uint32) ([][2]uint32, error) {
var items [][2]uint32
for i := uint32(0); ; i++ {
fs := v4l2_frmsizeenum{
index: i,
pixel_format: pixFmt,
}
if err := ioctl(d.fd, VIDIOC_ENUM_FRAMESIZES, unsafe.Pointer(&fs)); err != nil {
if !errors.Is(err, syscall.EINVAL) {
return nil, err
}
break
}
if fs.typ != V4L2_FRMSIZE_TYPE_DISCRETE {
continue
}
items = append(items, [2]uint32{fs.discrete.width, fs.discrete.height})
}
return items, nil
}
func (d *Device) ListFrameRates(pixFmt, width, height uint32) ([]uint32, error) {
var items []uint32
for i := uint32(0); ; i++ {
fi := v4l2_frmivalenum{
index: i,
pixel_format: pixFmt,
width: width,
height: height,
}
if err := ioctl(d.fd, VIDIOC_ENUM_FRAMEINTERVALS, unsafe.Pointer(&fi)); err != nil {
if !errors.Is(err, syscall.EINVAL) {
return nil, err
}
break
}
if fi.typ != V4L2_FRMIVAL_TYPE_DISCRETE || fi.discrete.numerator != 1 {
continue
}
items = append(items, fi.discrete.denominator)
}
return items, nil
}
func (d *Device) SetFormat(width, height, pixFmt uint32) error {
d.pixFmt = pixFmt
f := v4l2_format{
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
pix: v4l2_pix_format{
width: width,
height: height,
pixelformat: pixFmt,
field: V4L2_FIELD_NONE,
colorspace: V4L2_COLORSPACE_DEFAULT,
},
}
return ioctl(d.fd, VIDIOC_S_FMT, unsafe.Pointer(&f))
}
func (d *Device) SetParam(fps uint32) error {
p := v4l2_streamparm{
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
capture: v4l2_captureparm{
timeperframe: v4l2_fract{numerator: 1, denominator: fps},
},
}
return ioctl(d.fd, VIDIOC_S_PARM, unsafe.Pointer(&p))
}
func (d *Device) StreamOn() (err error) {
rb := v4l2_requestbuffers{
count: buffersCount,
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: V4L2_MEMORY_MMAP,
}
if err = ioctl(d.fd, VIDIOC_REQBUFS, unsafe.Pointer(&rb)); err != nil {
return err
}
d.bufs = make([][]byte, buffersCount)
for i := uint32(0); i < buffersCount; i++ {
qb := v4l2_buffer{
index: i,
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: V4L2_MEMORY_MMAP,
}
if err = ioctl(d.fd, VIDIOC_QUERYBUF, unsafe.Pointer(&qb)); err != nil {
return err
}
if d.bufs[i], err = syscall.Mmap(
d.fd, int64(qb.offset), int(qb.length), syscall.PROT_READ, syscall.MAP_SHARED,
); nil != err {
return err
}
if err = ioctl(d.fd, VIDIOC_QBUF, unsafe.Pointer(&qb)); err != nil {
return err
}
}
typ := uint32(V4L2_BUF_TYPE_VIDEO_CAPTURE)
return ioctl(d.fd, VIDIOC_STREAMON, unsafe.Pointer(&typ))
}
func (d *Device) StreamOff() (err error) {
typ := uint32(V4L2_BUF_TYPE_VIDEO_CAPTURE)
if err = ioctl(d.fd, VIDIOC_STREAMOFF, unsafe.Pointer(&typ)); err != nil {
return err
}
for i := range d.bufs {
_ = syscall.Munmap(d.bufs[i])
}
rb := v4l2_requestbuffers{
count: 0,
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: V4L2_MEMORY_MMAP,
}
return ioctl(d.fd, VIDIOC_REQBUFS, unsafe.Pointer(&rb))
}
func (d *Device) Capture() ([]byte, error) {
dec := v4l2_buffer{
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: V4L2_MEMORY_MMAP,
}
if err := ioctl(d.fd, VIDIOC_DQBUF, unsafe.Pointer(&dec)); err != nil {
return nil, err
}
src := d.bufs[dec.index][:dec.bytesused]
dst := make([]byte, dec.bytesused)
switch d.pixFmt {
case V4L2_PIX_FMT_YUYV:
YUYVtoYUV(dst, src)
case V4L2_PIX_FMT_NV12:
NV12toYUV(dst, src)
default:
copy(dst, d.bufs[dec.index][:dec.bytesused])
}
enc := v4l2_buffer{
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: V4L2_MEMORY_MMAP,
index: dec.index,
}
if err := ioctl(d.fd, VIDIOC_QBUF, unsafe.Pointer(&enc)); err != nil {
return nil, err
}
return dst, nil
}
func (d *Device) Close() error {
return syscall.Close(d.fd)
}
func ioctl(fd int, req uint, arg unsafe.Pointer) error {
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
if err != 0 {
return err
}
return nil
}
func str(b []byte) string {
if i := bytes.IndexByte(b, 0); i >= 0 {
return string(b[:i])
}
return string(b)
}
@@ -0,0 +1,62 @@
package device
const (
V4L2_PIX_FMT_YUYV = 'Y' | 'U'<<8 | 'Y'<<16 | 'V'<<24
V4L2_PIX_FMT_NV12 = 'N' | 'V'<<8 | '1'<<16 | '2'<<24
V4L2_PIX_FMT_MJPEG = 'M' | 'J'<<8 | 'P'<<16 | 'G'<<24
V4L2_PIX_FMT_H264 = 'H' | '2'<<8 | '6'<<16 | '4'<<24
V4L2_PIX_FMT_HEVC = 'H' | 'E'<<8 | 'V'<<16 | 'C'<<24
)
type Format struct {
FourCC uint32
Name string
FFmpeg string
}
var Formats = []Format{
{V4L2_PIX_FMT_YUYV, "YUV 4:2:2", "yuyv422"},
{V4L2_PIX_FMT_NV12, "Y/UV 4:2:0", "nv12"},
{V4L2_PIX_FMT_MJPEG, "Motion-JPEG", "mjpeg"},
{V4L2_PIX_FMT_H264, "H.264", "h264"},
{V4L2_PIX_FMT_HEVC, "HEVC", "hevc"},
}
func YUYVtoYUV(dst, src []byte) {
n := len(src)
i0 := 0
iy := 0
iu := n / 2
iv := n / 4 * 3
for i0 < n {
dst[iy] = src[i0]
i0++
iy++
dst[iu] = src[i0]
i0++
iu++
dst[iy] = src[i0]
i0++
iy++
dst[iv] = src[i0]
i0++
iv++
}
}
func NV12toYUV(dst, src []byte) {
n := len(src)
k := n / 6
i0 := k * 4
iu := i0
iv := i0 + k
copy(dst, src[:i0]) // copy Y
for i0 < n {
dst[iu] = src[i0]
i0++
iu++
dst[iv] = src[i0]
i0++
iv++
}
}
@@ -0,0 +1,149 @@
package device
const (
VIDIOC_QUERYCAP = 0x80685600
VIDIOC_ENUM_FMT = 0xc0405602
VIDIOC_G_FMT = 0xc0cc5604
VIDIOC_S_FMT = 0xc0cc5605
VIDIOC_REQBUFS = 0xc0145608
VIDIOC_QUERYBUF = 0xc0445609
VIDIOC_QBUF = 0xc044560f
VIDIOC_DQBUF = 0xc0445611
VIDIOC_STREAMON = 0x40045612
VIDIOC_STREAMOFF = 0x40045613
VIDIOC_G_PARM = 0xc0cc5615
VIDIOC_S_PARM = 0xc0cc5616
VIDIOC_ENUM_FRAMESIZES = 0xc02c564a
VIDIOC_ENUM_FRAMEINTERVALS = 0xc034564b
)
const (
V4L2_BUF_TYPE_VIDEO_CAPTURE = 1
V4L2_COLORSPACE_DEFAULT = 0
V4L2_FIELD_NONE = 1
V4L2_FRMIVAL_TYPE_DISCRETE = 1
V4L2_FRMSIZE_TYPE_DISCRETE = 1
V4L2_MEMORY_MMAP = 1
)
type v4l2_capability struct { // size 104
driver [16]byte // offset 0, size 16
card [32]byte // offset 16, size 32
bus_info [32]byte // offset 48, size 32
version uint32 // offset 80, size 4
capabilities uint32 // offset 84, size 4
device_caps uint32 // offset 88, size 4
reserved [3]uint32 // offset 92, size 12
}
type v4l2_format struct { // size 204
typ uint32 // offset 0, size 4
_ [0]byte // align
pix v4l2_pix_format // offset 4, size 48
_ [152]byte // filler
}
type v4l2_pix_format struct { // size 48
width uint32 // offset 0, size 4
height uint32 // offset 4, size 4
pixelformat uint32 // offset 8, size 4
field uint32 // offset 12, size 4
bytesperline uint32 // offset 16, size 4
sizeimage uint32 // offset 20, size 4
colorspace uint32 // offset 24, size 4
priv uint32 // offset 28, size 4
flags uint32 // offset 32, size 4
ycbcr_enc uint32 // offset 36, size 4
quantization uint32 // offset 40, size 4
xfer_func uint32 // offset 44, size 4
}
type v4l2_streamparm struct { // size 204
typ uint32 // offset 0, size 4
capture v4l2_captureparm // offset 4, size 40
_ [160]byte // filler
}
type v4l2_captureparm struct { // size 40
capability uint32 // offset 0, size 4
capturemode uint32 // offset 4, size 4
timeperframe v4l2_fract // offset 8, size 8
extendedmode uint32 // offset 16, size 4
readbuffers uint32 // offset 20, size 4
reserved [4]uint32 // offset 24, size 16
}
type v4l2_fract struct { // size 8
numerator uint32 // offset 0, size 4
denominator uint32 // offset 4, size 4
}
type v4l2_requestbuffers struct { // size 20
count uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
memory uint32 // offset 8, size 4
capabilities uint32 // offset 12, size 4
flags uint8 // offset 16, size 1
reserved [3]uint8 // offset 17, size 3
}
type v4l2_buffer struct { // size 68
index uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
bytesused uint32 // offset 8, size 4
flags uint32 // offset 12, size 4
field uint32 // offset 16, size 4
_ [8]byte // align
timecode v4l2_timecode // offset 28, size 16
sequence uint32 // offset 44, size 4
memory uint32 // offset 48, size 4
offset uint32 // offset 52, size 4
_ [0]byte // align
length uint32 // offset 56, size 4
_ [8]byte // filler
}
type v4l2_timecode struct { // size 16
typ uint32 // offset 0, size 4
flags uint32 // offset 4, size 4
frames uint8 // offset 8, size 1
seconds uint8 // offset 9, size 1
minutes uint8 // offset 10, size 1
hours uint8 // offset 11, size 1
userbits [4]uint8 // offset 12, size 4
}
type v4l2_fmtdesc struct { // size 64
index uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
flags uint32 // offset 8, size 4
description [32]byte // offset 12, size 32
pixelformat uint32 // offset 44, size 4
mbus_code uint32 // offset 48, size 4
reserved [3]uint32 // offset 52, size 12
}
type v4l2_frmsizeenum struct { // size 44
index uint32 // offset 0, size 4
pixel_format uint32 // offset 4, size 4
typ uint32 // offset 8, size 4
discrete v4l2_frmsize_discrete // offset 12, size 8
_ [24]byte // filler
}
type v4l2_frmsize_discrete struct { // size 8
width uint32 // offset 0, size 4
height uint32 // offset 4, size 4
}
type v4l2_frmivalenum struct { // size 52
index uint32 // offset 0, size 4
pixel_format uint32 // offset 4, size 4
width uint32 // offset 8, size 4
height uint32 // offset 12, size 4
typ uint32 // offset 16, size 4
discrete v4l2_fract // offset 20, size 8
_ [24]byte // filler
}
@@ -0,0 +1,164 @@
//go:build ignore
#include <stdio.h>
#include <stddef.h>
#include <linux/videodev2.h>
#define printconst1(con) printf("\t%s = 0x%08lx\n", #con, con)
#define printconst2(con) printf("\t%s = %d\n", #con, con)
#define printstruct(str) printf("type %s struct { // size %lu\n", #str, sizeof(struct str))
#define printmember(str, mem, typ) printf("\t%s %s // offset %lu, size %lu\n", #mem == "type" ? "typ" : #mem, typ, offsetof(struct str, mem), sizeof((struct str){0}.mem))
#define printunimem(str, uni, mem, typ) printf("\t%s %s // offset %lu, size %lu\n", #mem, typ, offsetof(struct str, uni.mem), sizeof((struct str){0}.uni.mem))
#define printalign1(str, mem2, mem1) printf("\t_ [%lu]byte // align\n", offsetof(struct str, mem2) - offsetof(struct str, mem1) - sizeof((struct str){0}.mem1))
#define printfiller(str, mem) printf("\t_ [%lu]byte // filler\n", sizeof(struct str) - offsetof(struct str, mem) - sizeof((struct str){0}.mem))
int main() {
printf("const (\n");
printconst1(VIDIOC_QUERYCAP);
printconst1(VIDIOC_ENUM_FMT);
printconst1(VIDIOC_G_FMT);
printconst1(VIDIOC_S_FMT);
printconst1(VIDIOC_REQBUFS);
printconst1(VIDIOC_QUERYBUF);
printf("\n");
printconst1(VIDIOC_QBUF);
printconst1(VIDIOC_DQBUF);
printconst1(VIDIOC_STREAMON);
printconst1(VIDIOC_STREAMOFF);
printconst1(VIDIOC_G_PARM);
printconst1(VIDIOC_S_PARM);
printf("\n");
printconst1(VIDIOC_ENUM_FRAMESIZES);
printconst1(VIDIOC_ENUM_FRAMEINTERVALS);
printf(")\n\n");
printf("const (\n");
printconst2(V4L2_BUF_TYPE_VIDEO_CAPTURE);
printconst2(V4L2_COLORSPACE_DEFAULT);
printconst2(V4L2_FIELD_NONE);
printconst2(V4L2_FRMIVAL_TYPE_DISCRETE);
printconst2(V4L2_FRMSIZE_TYPE_DISCRETE);
printconst2(V4L2_MEMORY_MMAP);
printf(")\n\n");
printstruct(v4l2_capability);
printmember(v4l2_capability, driver, "[16]byte");
printmember(v4l2_capability, card, "[32]byte");
printmember(v4l2_capability, bus_info, "[32]byte");
printmember(v4l2_capability, version, "uint32");
printmember(v4l2_capability, capabilities, "uint32");
printmember(v4l2_capability, device_caps, "uint32");
printmember(v4l2_capability, reserved, "[3]uint32");
printf("}\n\n");
printstruct(v4l2_format);
printmember(v4l2_format, type, "uint32");
printalign1(v4l2_format, fmt, type);
printunimem(v4l2_format, fmt, pix, "v4l2_pix_format");
printfiller(v4l2_format, fmt.pix);
printf("}\n\n");
printstruct(v4l2_pix_format);
printmember(v4l2_pix_format, width, "uint32");
printmember(v4l2_pix_format, height, "uint32");
printmember(v4l2_pix_format, pixelformat, "uint32");
printmember(v4l2_pix_format, field, "uint32");
printmember(v4l2_pix_format, bytesperline, "uint32");
printmember(v4l2_pix_format, sizeimage, "uint32");
printmember(v4l2_pix_format, colorspace, "uint32");
printmember(v4l2_pix_format, priv, "uint32");
printmember(v4l2_pix_format, flags, "uint32");
printmember(v4l2_pix_format, ycbcr_enc, "uint32");
printmember(v4l2_pix_format, quantization, "uint32");
printmember(v4l2_pix_format, xfer_func, "uint32");
printf("}\n\n");
printstruct(v4l2_streamparm);
printmember(v4l2_streamparm, type, "uint32");
printunimem(v4l2_streamparm, parm, capture, "v4l2_captureparm");
printfiller(v4l2_streamparm, parm.capture);
printf("}\n\n");
printstruct(v4l2_captureparm);
printmember(v4l2_captureparm, capability, "uint32");
printmember(v4l2_captureparm, capturemode, "uint32");
printmember(v4l2_captureparm, timeperframe, "v4l2_fract");
printmember(v4l2_captureparm, extendedmode, "uint32");
printmember(v4l2_captureparm, readbuffers, "uint32");
printmember(v4l2_captureparm, reserved, "[4]uint32");
printf("}\n\n");
printstruct(v4l2_fract);
printmember(v4l2_fract, numerator, "uint32");
printmember(v4l2_fract, denominator, "uint32");
printf("}\n\n");
printstruct(v4l2_requestbuffers);
printmember(v4l2_requestbuffers, count, "uint32");
printmember(v4l2_requestbuffers, type, "uint32");
printmember(v4l2_requestbuffers, memory, "uint32");
printmember(v4l2_requestbuffers, capabilities, "uint32");
printmember(v4l2_requestbuffers, flags, "uint8");
printmember(v4l2_requestbuffers, reserved, "[3]uint8");
printf("}\n\n");
printstruct(v4l2_buffer);
printmember(v4l2_buffer, index, "uint32");
printmember(v4l2_buffer, type, "uint32");
printmember(v4l2_buffer, bytesused, "uint32");
printmember(v4l2_buffer, flags, "uint32");
printmember(v4l2_buffer, field, "uint32");
printalign1(v4l2_buffer, timecode, field);
printmember(v4l2_buffer, timecode, "v4l2_timecode");
printmember(v4l2_buffer, sequence, "uint32");
printmember(v4l2_buffer, memory, "uint32");
printunimem(v4l2_buffer, m, offset, "uint32");
printalign1(v4l2_buffer, length, m.offset);
printmember(v4l2_buffer, length, "uint32");
printfiller(v4l2_buffer, length);
printf("}\n\n");
printstruct(v4l2_timecode);
printmember(v4l2_timecode, type, "uint32");
printmember(v4l2_timecode, flags, "uint32");
printmember(v4l2_timecode, frames, "uint8");
printmember(v4l2_timecode, seconds, "uint8");
printmember(v4l2_timecode, minutes, "uint8");
printmember(v4l2_timecode, hours, "uint8");
printmember(v4l2_timecode, userbits, "[4]uint8");
printf("}\n\n");
printstruct(v4l2_fmtdesc);
printmember(v4l2_fmtdesc, index, "uint32");
printmember(v4l2_fmtdesc, type, "uint32");
printmember(v4l2_fmtdesc, flags, "uint32");
printmember(v4l2_fmtdesc, description, "[32]byte");
printmember(v4l2_fmtdesc, pixelformat, "uint32");
printmember(v4l2_fmtdesc, mbus_code, "uint32");
printmember(v4l2_fmtdesc, reserved, "[3]uint32");
printf("}\n\n");
printstruct(v4l2_frmsizeenum);
printmember(v4l2_frmsizeenum, index, "uint32");
printmember(v4l2_frmsizeenum, pixel_format, "uint32");
printmember(v4l2_frmsizeenum, type, "uint32");
printmember(v4l2_frmsizeenum, discrete, "v4l2_frmsize_discrete");
printfiller(v4l2_frmsizeenum, discrete);
printf("}\n\n");
printstruct(v4l2_frmsize_discrete);
printmember(v4l2_frmsize_discrete, width, "uint32");
printmember(v4l2_frmsize_discrete, height, "uint32");
printf("}\n\n");
printstruct(v4l2_frmivalenum);
printmember(v4l2_frmivalenum, index, "uint32");
printmember(v4l2_frmivalenum, pixel_format, "uint32");
printmember(v4l2_frmivalenum, width, "uint32");
printmember(v4l2_frmivalenum, height, "uint32");
printmember(v4l2_frmivalenum, type, "uint32");
printmember(v4l2_frmivalenum, discrete, "v4l2_fract");
printfiller(v4l2_frmivalenum, discrete);
printf("}\n\n");
return 0;
}
@@ -0,0 +1,149 @@
package device
const (
VIDIOC_QUERYCAP = 0x80685600
VIDIOC_ENUM_FMT = 0xc0405602
VIDIOC_G_FMT = 0xc0cc5604
VIDIOC_S_FMT = 0xc0cc5605
VIDIOC_REQBUFS = 0xc0145608
VIDIOC_QUERYBUF = 0xc0505609
VIDIOC_QBUF = 0xc050560f
VIDIOC_DQBUF = 0xc0505611
VIDIOC_STREAMON = 0x40045612
VIDIOC_STREAMOFF = 0x40045613
VIDIOC_G_PARM = 0xc0cc5615
VIDIOC_S_PARM = 0xc0cc5616
VIDIOC_ENUM_FRAMESIZES = 0xc02c564a
VIDIOC_ENUM_FRAMEINTERVALS = 0xc034564b
)
const (
V4L2_BUF_TYPE_VIDEO_CAPTURE = 1
V4L2_COLORSPACE_DEFAULT = 0
V4L2_FIELD_NONE = 1
V4L2_FRMIVAL_TYPE_DISCRETE = 1
V4L2_FRMSIZE_TYPE_DISCRETE = 1
V4L2_MEMORY_MMAP = 1
)
type v4l2_capability struct { // size 104
driver [16]byte // offset 0, size 16
card [32]byte // offset 16, size 32
bus_info [32]byte // offset 48, size 32
version uint32 // offset 80, size 4
capabilities uint32 // offset 84, size 4
device_caps uint32 // offset 88, size 4
reserved [3]uint32 // offset 92, size 12
}
type v4l2_format struct { // size 204
typ uint32 // offset 0, size 4
_ [0]byte // align
pix v4l2_pix_format // offset 4, size 48
_ [152]byte // filler
}
type v4l2_pix_format struct { // size 48
width uint32 // offset 0, size 4
height uint32 // offset 4, size 4
pixelformat uint32 // offset 8, size 4
field uint32 // offset 12, size 4
bytesperline uint32 // offset 16, size 4
sizeimage uint32 // offset 20, size 4
colorspace uint32 // offset 24, size 4
priv uint32 // offset 28, size 4
flags uint32 // offset 32, size 4
ycbcr_enc uint32 // offset 36, size 4
quantization uint32 // offset 40, size 4
xfer_func uint32 // offset 44, size 4
}
type v4l2_streamparm struct { // size 204
typ uint32 // offset 0, size 4
capture v4l2_captureparm // offset 4, size 40
_ [160]byte // filler
}
type v4l2_captureparm struct { // size 40
capability uint32 // offset 0, size 4
capturemode uint32 // offset 4, size 4
timeperframe v4l2_fract // offset 8, size 8
extendedmode uint32 // offset 16, size 4
readbuffers uint32 // offset 20, size 4
reserved [4]uint32 // offset 24, size 16
}
type v4l2_fract struct { // size 8
numerator uint32 // offset 0, size 4
denominator uint32 // offset 4, size 4
}
type v4l2_requestbuffers struct { // size 20
count uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
memory uint32 // offset 8, size 4
capabilities uint32 // offset 12, size 4
flags uint8 // offset 16, size 1
reserved [3]uint8 // offset 17, size 3
}
type v4l2_buffer struct { // size 80
index uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
bytesused uint32 // offset 8, size 4
flags uint32 // offset 12, size 4
field uint32 // offset 16, size 4
_ [20]byte // align
timecode v4l2_timecode // offset 40, size 16
sequence uint32 // offset 56, size 4
memory uint32 // offset 60, size 4
offset uint32 // offset 64, size 4
_ [0]byte // align
length uint32 // offset 68, size 4
_ [8]byte // filler
}
type v4l2_timecode struct { // size 16
typ uint32 // offset 0, size 4
flags uint32 // offset 4, size 4
frames uint8 // offset 8, size 1
seconds uint8 // offset 9, size 1
minutes uint8 // offset 10, size 1
hours uint8 // offset 11, size 1
userbits [4]uint8 // offset 12, size 4
}
type v4l2_fmtdesc struct { // size 64
index uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
flags uint32 // offset 8, size 4
description [32]byte // offset 12, size 32
pixelformat uint32 // offset 44, size 4
mbus_code uint32 // offset 48, size 4
reserved [3]uint32 // offset 52, size 12
}
type v4l2_frmsizeenum struct { // size 44
index uint32 // offset 0, size 4
pixel_format uint32 // offset 4, size 4
typ uint32 // offset 8, size 4
discrete v4l2_frmsize_discrete // offset 12, size 8
_ [24]byte // filler
}
type v4l2_frmsize_discrete struct { // size 8
width uint32 // offset 0, size 4
height uint32 // offset 4, size 4
}
type v4l2_frmivalenum struct { // size 52
index uint32 // offset 0, size 4
pixel_format uint32 // offset 4, size 4
width uint32 // offset 8, size 4
height uint32 // offset 12, size 4
typ uint32 // offset 16, size 4
discrete v4l2_fract // offset 20, size 8
_ [24]byte // filler
}
@@ -0,0 +1,149 @@
package device
const (
VIDIOC_QUERYCAP = 0x40685600
VIDIOC_ENUM_FMT = 0xc0405602
VIDIOC_G_FMT = 0xc0cc5604
VIDIOC_S_FMT = 0xc0cc5605
VIDIOC_REQBUFS = 0xc0145608
VIDIOC_QUERYBUF = 0xc0445609
VIDIOC_QBUF = 0xc044560f
VIDIOC_DQBUF = 0xc0445611
VIDIOC_STREAMON = 0x80045612
VIDIOC_STREAMOFF = 0x80045613
VIDIOC_G_PARM = 0xc0cc5615
VIDIOC_S_PARM = 0xc0cc5616
VIDIOC_ENUM_FRAMESIZES = 0xc02c564a
VIDIOC_ENUM_FRAMEINTERVALS = 0xc034564b
)
const (
V4L2_BUF_TYPE_VIDEO_CAPTURE = 1
V4L2_COLORSPACE_DEFAULT = 0
V4L2_FIELD_NONE = 1
V4L2_FRMIVAL_TYPE_DISCRETE = 1
V4L2_FRMSIZE_TYPE_DISCRETE = 1
V4L2_MEMORY_MMAP = 1
)
type v4l2_capability struct { // size 104
driver [16]byte // offset 0, size 16
card [32]byte // offset 16, size 32
bus_info [32]byte // offset 48, size 32
version uint32 // offset 80, size 4
capabilities uint32 // offset 84, size 4
device_caps uint32 // offset 88, size 4
reserved [3]uint32 // offset 92, size 12
}
type v4l2_format struct { // size 204
typ uint32 // offset 0, size 4
_ [0]byte // align
pix v4l2_pix_format // offset 4, size 48
_ [152]byte // filler
}
type v4l2_pix_format struct { // size 48
width uint32 // offset 0, size 4
height uint32 // offset 4, size 4
pixelformat uint32 // offset 8, size 4
field uint32 // offset 12, size 4
bytesperline uint32 // offset 16, size 4
sizeimage uint32 // offset 20, size 4
colorspace uint32 // offset 24, size 4
priv uint32 // offset 28, size 4
flags uint32 // offset 32, size 4
ycbcr_enc uint32 // offset 36, size 4
quantization uint32 // offset 40, size 4
xfer_func uint32 // offset 44, size 4
}
type v4l2_streamparm struct { // size 204
typ uint32 // offset 0, size 4
capture v4l2_captureparm // offset 4, size 40
_ [160]byte // filler
}
type v4l2_captureparm struct { // size 40
capability uint32 // offset 0, size 4
capturemode uint32 // offset 4, size 4
timeperframe v4l2_fract // offset 8, size 8
extendedmode uint32 // offset 16, size 4
readbuffers uint32 // offset 20, size 4
reserved [4]uint32 // offset 24, size 16
}
type v4l2_fract struct { // size 8
numerator uint32 // offset 0, size 4
denominator uint32 // offset 4, size 4
}
type v4l2_requestbuffers struct { // size 20
count uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
memory uint32 // offset 8, size 4
capabilities uint32 // offset 12, size 4
flags uint8 // offset 16, size 1
reserved [3]uint8 // offset 17, size 3
}
type v4l2_buffer struct { // size 68
index uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
bytesused uint32 // offset 8, size 4
flags uint32 // offset 12, size 4
field uint32 // offset 16, size 4
_ [8]byte // align
timecode v4l2_timecode // offset 28, size 16
sequence uint32 // offset 44, size 4
memory uint32 // offset 48, size 4
offset uint32 // offset 52, size 4
_ [0]byte // align
length uint32 // offset 56, size 4
_ [8]byte // filler
}
type v4l2_timecode struct { // size 16
typ uint32 // offset 0, size 4
flags uint32 // offset 4, size 4
frames uint8 // offset 8, size 1
seconds uint8 // offset 9, size 1
minutes uint8 // offset 10, size 1
hours uint8 // offset 11, size 1
userbits [4]uint8 // offset 12, size 4
}
type v4l2_fmtdesc struct { // size 64
index uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
flags uint32 // offset 8, size 4
description [32]byte // offset 12, size 32
pixelformat uint32 // offset 44, size 4
mbus_code uint32 // offset 48, size 4
reserved [3]uint32 // offset 52, size 12
}
type v4l2_frmsizeenum struct { // size 44
index uint32 // offset 0, size 4
pixel_format uint32 // offset 4, size 4
typ uint32 // offset 8, size 4
discrete v4l2_frmsize_discrete // offset 12, size 8
_ [24]byte // filler
}
type v4l2_frmsize_discrete struct { // size 8
width uint32 // offset 0, size 4
height uint32 // offset 4, size 4
}
type v4l2_frmivalenum struct { // size 52
index uint32 // offset 0, size 4
pixel_format uint32 // offset 4, size 4
width uint32 // offset 8, size 4
height uint32 // offset 12, size 4
typ uint32 // offset 16, size 4
discrete v4l2_fract // offset 20, size 8
_ [24]byte // filler
}
@@ -0,0 +1,151 @@
//go:build amd64 || arm64
package device
const (
VIDIOC_QUERYCAP = 0x80685600
VIDIOC_ENUM_FMT = 0xc0405602
VIDIOC_G_FMT = 0xc0d05604
VIDIOC_S_FMT = 0xc0d05605
VIDIOC_REQBUFS = 0xc0145608
VIDIOC_QUERYBUF = 0xc0585609
VIDIOC_QBUF = 0xc058560f
VIDIOC_DQBUF = 0xc0585611
VIDIOC_STREAMON = 0x40045612
VIDIOC_STREAMOFF = 0x40045613
VIDIOC_G_PARM = 0xc0cc5615
VIDIOC_S_PARM = 0xc0cc5616
VIDIOC_ENUM_FRAMESIZES = 0xc02c564a
VIDIOC_ENUM_FRAMEINTERVALS = 0xc034564b
)
const (
V4L2_BUF_TYPE_VIDEO_CAPTURE = 1
V4L2_COLORSPACE_DEFAULT = 0
V4L2_FIELD_NONE = 1
V4L2_FRMIVAL_TYPE_DISCRETE = 1
V4L2_FRMSIZE_TYPE_DISCRETE = 1
V4L2_MEMORY_MMAP = 1
)
type v4l2_capability struct { // size 104
driver [16]byte // offset 0, size 16
card [32]byte // offset 16, size 32
bus_info [32]byte // offset 48, size 32
version uint32 // offset 80, size 4
capabilities uint32 // offset 84, size 4
device_caps uint32 // offset 88, size 4
reserved [3]uint32 // offset 92, size 12
}
type v4l2_format struct { // size 208
typ uint32 // offset 0, size 4
_ [4]byte // align
pix v4l2_pix_format // offset 8, size 48
_ [152]byte // filler
}
type v4l2_pix_format struct { // size 48
width uint32 // offset 0, size 4
height uint32 // offset 4, size 4
pixelformat uint32 // offset 8, size 4
field uint32 // offset 12, size 4
bytesperline uint32 // offset 16, size 4
sizeimage uint32 // offset 20, size 4
colorspace uint32 // offset 24, size 4
priv uint32 // offset 28, size 4
flags uint32 // offset 32, size 4
ycbcr_enc uint32 // offset 36, size 4
quantization uint32 // offset 40, size 4
xfer_func uint32 // offset 44, size 4
}
type v4l2_streamparm struct { // size 204
typ uint32 // offset 0, size 4
capture v4l2_captureparm // offset 4, size 40
_ [160]byte // filler
}
type v4l2_captureparm struct { // size 40
capability uint32 // offset 0, size 4
capturemode uint32 // offset 4, size 4
timeperframe v4l2_fract // offset 8, size 8
extendedmode uint32 // offset 16, size 4
readbuffers uint32 // offset 20, size 4
reserved [4]uint32 // offset 24, size 16
}
type v4l2_fract struct { // size 8
numerator uint32 // offset 0, size 4
denominator uint32 // offset 4, size 4
}
type v4l2_requestbuffers struct { // size 20
count uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
memory uint32 // offset 8, size 4
capabilities uint32 // offset 12, size 4
flags uint8 // offset 16, size 1
reserved [3]uint8 // offset 17, size 3
}
type v4l2_buffer struct { // size 88
index uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
bytesused uint32 // offset 8, size 4
flags uint32 // offset 12, size 4
field uint32 // offset 16, size 4
_ [20]byte // align
timecode v4l2_timecode // offset 40, size 16
sequence uint32 // offset 56, size 4
memory uint32 // offset 60, size 4
offset uint32 // offset 64, size 4
_ [4]byte // align
length uint32 // offset 72, size 4
_ [12]byte // filler
}
type v4l2_timecode struct { // size 16
typ uint32 // offset 0, size 4
flags uint32 // offset 4, size 4
frames uint8 // offset 8, size 1
seconds uint8 // offset 9, size 1
minutes uint8 // offset 10, size 1
hours uint8 // offset 11, size 1
userbits [4]uint8 // offset 12, size 4
}
type v4l2_fmtdesc struct { // size 64
index uint32 // offset 0, size 4
typ uint32 // offset 4, size 4
flags uint32 // offset 8, size 4
description [32]byte // offset 12, size 32
pixelformat uint32 // offset 44, size 4
mbus_code uint32 // offset 48, size 4
reserved [3]uint32 // offset 52, size 12
}
type v4l2_frmsizeenum struct { // size 44
index uint32 // offset 0, size 4
pixel_format uint32 // offset 4, size 4
typ uint32 // offset 8, size 4
discrete v4l2_frmsize_discrete // offset 12, size 8
_ [24]byte // filler
}
type v4l2_frmsize_discrete struct { // size 8
width uint32 // offset 0, size 4
height uint32 // offset 4, size 4
}
type v4l2_frmivalenum struct { // size 52
index uint32 // offset 0, size 4
pixel_format uint32 // offset 4, size 4
width uint32 // offset 8, size 4
height uint32 // offset 12, size 4
typ uint32 // offset 16, size 4
discrete v4l2_fract // offset 20, size 8
_ [24]byte // filler
}
@@ -0,0 +1,142 @@
//go:build linux
package v4l2
import (
"errors"
"net/url"
"strings"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/h264/annexb"
"github.com/AlexxIT/go2rtc/pkg/v4l2/device"
"github.com/pion/rtp"
)
type Producer struct {
core.Connection
dev *device.Device
}
func Open(rawURL string) (*Producer, error) {
// Example (ffmpeg source compatible):
// v4l2:device?video=/dev/video0&input_format=mjpeg&video_size=1280x720
u, err := url.Parse(rawURL)
if err != nil {
return nil, err
}
query := u.Query()
dev, err := device.Open(query.Get("video"))
if err != nil {
return nil, err
}
codec := &core.Codec{
ClockRate: 90000,
PayloadType: core.PayloadTypeRAW,
}
var width, height, pixFmt uint32
if wh := strings.Split(query.Get("video_size"), "x"); len(wh) == 2 {
codec.FmtpLine = "width=" + wh[0] + ";height=" + wh[1]
width = uint32(core.Atoi(wh[0]))
height = uint32(core.Atoi(wh[1]))
}
switch query.Get("input_format") {
case "yuyv422":
if codec.FmtpLine == "" {
return nil, errors.New("v4l2: invalid video_size")
}
codec.Name = core.CodecRAW
codec.FmtpLine += ";colorspace=422"
pixFmt = device.V4L2_PIX_FMT_YUYV
case "nv12":
if codec.FmtpLine == "" {
return nil, errors.New("v4l2: invalid video_size")
}
codec.Name = core.CodecRAW
codec.FmtpLine += ";colorspace=420mpeg2" // maybe 420jpeg
pixFmt = device.V4L2_PIX_FMT_NV12
case "mjpeg":
codec.Name = core.CodecJPEG
pixFmt = device.V4L2_PIX_FMT_MJPEG
case "h264":
codec.Name = core.CodecH264
pixFmt = device.V4L2_PIX_FMT_H264
case "hevc":
codec.Name = core.CodecH265
pixFmt = device.V4L2_PIX_FMT_HEVC
default:
return nil, errors.New("v4l2: invalid input_format")
}
if err = dev.SetFormat(width, height, pixFmt); err != nil {
return nil, err
}
if fps := core.Atoi(query.Get("framerate")); fps > 0 {
if err = dev.SetParam(uint32(fps)); err != nil {
return nil, err
}
}
medias := []*core.Media{
{
Kind: core.KindVideo,
Direction: core.DirectionRecvonly,
Codecs: []*core.Codec{codec},
},
}
return &Producer{
Connection: core.Connection{
ID: core.NewID(),
FormatName: "v4l2",
Medias: medias,
},
dev: dev,
}, nil
}
func (c *Producer) Start() error {
if err := c.dev.StreamOn(); err != nil {
return err
}
var bitstream bool
switch c.Medias[0].Codecs[0].Name {
case core.CodecH264, core.CodecH265:
bitstream = true
}
for {
buf, err := c.dev.Capture()
if err != nil {
return err
}
c.Recv += len(buf)
if len(c.Receivers) == 0 {
continue
}
if bitstream {
buf = annexb.EncodeToAVCC(buf)
}
pkt := &rtp.Packet{
Header: rtp.Header{Timestamp: core.Now90000()},
Payload: buf,
}
c.Receivers[0].WriteRTP(pkt)
}
}
func (c *Producer) Stop() error {
_ = c.Connection.Stop()
return errors.Join(c.dev.StreamOff(), c.dev.Close())
}