install go2rtc on bob
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user