initial commit

This commit is contained in:
ston1th 2016-04-10 11:06:26 +02:00
commit c67729332c
26 changed files with 1612 additions and 0 deletions

442
webcam.go Normal file
View file

@ -0,0 +1,442 @@
package webcam
import (
"errors"
"fmt"
"os"
"sort"
"syscall"
"time"
"unsafe"
"github.com/creack/goselect"
)
type Webcam struct {
fd uintptr
file *os.File
path string
timeout time.Duration
buffers [][]byte
}
func Open(path string, timeout time.Duration) (*Webcam, error) {
f, err := os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0)
if err != nil {
return nil, err
}
w := new(Webcam)
w.fd = f.Fd()
w.file = f
w.path = path
w.timeout = timeout
v, s, err := w.checkCapabilities()
if err != nil {
return nil, err
}
if v {
return nil, errors.New("Not a video capture device")
}
if s {
return nil, errors.New("Device does not support the streaming I/O method")
}
return w, nil
}
type requestbuffers struct {
count uint32
t uint32
memory uint32
reserved [2]uint32
}
type buffer struct {
index uint32
t uint32
bytesused uint32
flags uint32
field uint32
timestamp syscall.Timeval
timecode struct {
t uint32
flags uint32
frames uint8
seconds uint8
minutes uint8
hours uint8
userbits [4]uint8
}
sequence uint32
memory uint32
/*
union {
__u32 offset;
unsigned long userptr;
struct v4l2_plane *planes;
__s32 fd;
} m;
*/
offset int64 // m
length uint32
reserved2 uint32
reserved uint32
}
type format_dummy struct {
t uint32
fmt struct {
raw_data [201]byte
}
}
type format struct {
t uint32
pix pix
raw_data [155]byte // placeholder
}
type pix struct {
empty uint32 // placeholder
width uint32
height uint32
pixelformat uint32
field uint32
bytesperline uint32
sizeimage uint32
colorspace uint32
priv uint32
flags uint32
ycbcr_enc uint32
quantization uint32
// commented out for 'empty' placeholder
//xfer_func uint32
}
type capability struct {
driver [16]byte
card [32]byte
bus_info [32]byte
version uint32
capabilities uint32
device_caps uint32
reserved [3]uint32
}
type fmtdesc struct {
index uint32
t uint32
flags uint32
description [32]byte
pixelformat uint32
reserved [4]uint32
}
type frmsizeenum struct {
index uint32
pixel_format uint32
t uint32
/*
struct v4l2_frmsize_stepwise {
__u32 min_width
__u32 max_width
__u32 step_width
__u32 min_height
__u32 max_height
__u32 step_height
};
struct v4l2_frmsize_discrete {
__u32 width
__u32 height
};
*/
frmsize [6]uint32
reserved [2]uint32
}
const (
_IOC_READ = 2
_IOC_WRITE = 1
_IOC_NRBITS = 8
_IOC_TYPEBITS = 8
_IOC_SIZEBITS = 14
_IOC_NRSHIFT = 0
_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS
_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS
_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS
_V4L2_BUF_TYPE_VIDEO_CAPTURE = 1
_V4L2_MEMORY_MMAP = 1
_V4L2_FIELD_ANY = 0
_V4L2_CAP_STREAMING = 0x04000000
_V4L2_CAP_VIDEO_CAPTURE = 0x00000001
_V4L2_FRMSIZE_TYPE_DISCRETE = 1
_V4L2_FRMSIZE_TYPE_CONTINUOUS = 2
_V4L2_FRMSIZE_TYPE_STEPWISE = 3
)
var (
_VIDIOC_QUERYCAP = _IOR('V', 0, unsafe.Sizeof(capability{}))
_VIDIOC_ENUM_FMT = _IOWR('V', 2, unsafe.Sizeof(fmtdesc{}))
_VIDIOC_S_FMT = _IOWR('V', 5, unsafe.Sizeof(format{}))
_VIDIOC_REQBUFS = _IOWR('V', 8, unsafe.Sizeof(requestbuffers{}))
_VIDIOC_QUERYBUF = _IOWR('V', 9, unsafe.Sizeof(buffer{}))
_VIDIOC_QBUF = _IOWR('V', 15, unsafe.Sizeof(buffer{}))
_VIDIOC_DQBUF = _IOWR('V', 17, unsafe.Sizeof(buffer{}))
_VIDIOC_STREAMON = _IOW('V', 18, unsafe.Sizeof(int32(0)))
_VIDIOC_ENUM_FRAMESIZES = _IOWR('V', 74, unsafe.Sizeof(frmsizeenum{}))
)
func _IOC(dir, t, nr, size uintptr) uintptr {
return dir<<_IOC_DIRSHIFT |
t<<_IOC_TYPESHIFT |
nr<<_IOC_NRSHIFT |
size<<_IOC_SIZESHIFT
}
func _IOWR(t rune, i, size uintptr) uintptr {
return _IOC(_IOC_READ|_IOC_WRITE, uintptr(t), i, size)
}
func _IOW(t rune, i, size uintptr) uintptr {
return _IOC(_IOC_WRITE, uintptr(t), i, size)
}
func _IOR(t rune, i, size uintptr) uintptr {
return _IOC(_IOC_READ, uintptr(t), i, size)
}
func ioctl(fd, cmd, ptr uintptr) error {
_, _, ep := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
if ep != 0 {
return syscall.Errno(ep)
}
return nil
}
func (w *Webcam) checkCapabilities() (video, stream bool, err error) {
c := new(capability)
err = ioctl(w.fd, _VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(c)))
video = (c.capabilities & _V4L2_CAP_VIDEO_CAPTURE) == 0
stream = (c.capabilities & _V4L2_CAP_STREAMING) == 0
return
}
func (w *Webcam) GetPixelFormats() (m map[uint32]string) {
m = make(map[uint32]string)
for i := uint32(0); ; i++ {
vfd := fmtdesc{
index: i,
t: _V4L2_BUF_TYPE_VIDEO_CAPTURE,
}
err := ioctl(w.fd, _VIDIOC_ENUM_FMT, uintptr(unsafe.Pointer(&vfd)))
if err != nil {
return
}
var s string
for _, b := range vfd.description {
s = s + string(b)
}
m[vfd.pixelformat] = s
}
return
}
type FrameSize struct {
MinWidth uint32
MaxWidth uint32
StepWidth uint32
MinHeight uint32
MaxHeight uint32
StepHeight uint32
}
func (s FrameSize) String() string {
if s.StepWidth == 0 && s.StepHeight == 0 {
return fmt.Sprintf("%dx%d", s.MaxWidth, s.MaxHeight)
} else {
return fmt.Sprintf("[%d-%d;%d]x[%d-%d;%d]", s.MinWidth, s.MaxWidth, s.StepWidth, s.MinHeight, s.MaxHeight, s.StepHeight)
}
}
type FrameSizes []FrameSize
func (slice FrameSizes) Len() int { return len(slice) }
func (slice FrameSizes) Less(i, j int) bool {
ls := slice[i].MaxWidth * slice[i].MaxHeight
rs := slice[j].MaxWidth * slice[j].MaxHeight
return ls < rs
}
func (slice FrameSizes) Swap(i, j int) { slice[i], slice[j] = slice[j], slice[i] }
func (w *Webcam) GetFrameSizes(code uint32) (fs FrameSizes) {
var s FrameSize
for i := uint32(0); ; i++ {
vfse := frmsizeenum{
index: i,
pixel_format: code,
}
if err := ioctl(w.fd, _VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(&vfse))); err != nil {
break
}
switch vfse.t {
case _V4L2_FRMSIZE_TYPE_DISCRETE:
s.MinWidth = vfse.frmsize[0]
s.MaxWidth = vfse.frmsize[0]
s.StepWidth = 0
s.MinHeight = vfse.frmsize[1]
s.MaxHeight = vfse.frmsize[1]
s.StepHeight = 0
case _V4L2_FRMSIZE_TYPE_CONTINUOUS, _V4L2_FRMSIZE_TYPE_STEPWISE:
s.MinWidth = vfse.frmsize[0]
s.MaxWidth = vfse.frmsize[1]
s.StepWidth = vfse.frmsize[2]
s.MinHeight = vfse.frmsize[3]
s.MaxHeight = vfse.frmsize[4]
s.StepHeight = vfse.frmsize[5]
}
fs = append(fs, s)
}
sort.Sort(fs)
return
}
func (cam *Webcam) SetImageFormat(w, h, formatcode uint32) (uint32, uint32, uint32, error) {
f := format{
t: _V4L2_BUF_TYPE_VIDEO_CAPTURE,
pix: pix{
width: w,
height: h,
pixelformat: formatcode,
field: _V4L2_FIELD_ANY,
},
}
err := ioctl(cam.fd, _VIDIOC_S_FMT, uintptr(unsafe.Pointer(&f)))
return f.pix.pixelformat, f.pix.width, f.pix.height, err
}
func (w *Webcam) mmapRequestBuffers(bufcount uint32) (int, error) {
req := requestbuffers{
count: bufcount,
t: _V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: _V4L2_MEMORY_MMAP,
}
err := ioctl(w.fd, _VIDIOC_REQBUFS, uintptr(unsafe.Pointer(&req)))
return int(req.count), err
}
func (w *Webcam) mmapQueryBuffer(index uint32) ([]byte, error) {
buf := buffer{
t: _V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: _V4L2_MEMORY_MMAP,
index: index,
}
err := ioctl(w.fd, _VIDIOC_QUERYBUF, uintptr(unsafe.Pointer(&buf)))
if err != nil {
return nil, err
}
return syscall.Mmap(int(w.fd), buf.offset, int(buf.length), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
}
func (w *Webcam) mmapDequeueBuffer() (uint32, uint32, error) {
buf := buffer{
t: _V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: _V4L2_MEMORY_MMAP,
}
err := ioctl(w.fd, _VIDIOC_DQBUF, uintptr(unsafe.Pointer(&buf)))
return buf.index, buf.bytesused, err
}
func (w *Webcam) mmapEnqueueBuffer(index uint32) error {
buf := buffer{
t: _V4L2_BUF_TYPE_VIDEO_CAPTURE,
memory: _V4L2_MEMORY_MMAP,
index: index,
}
return ioctl(w.fd, _VIDIOC_QBUF, uintptr(unsafe.Pointer(&buf)))
}
func (w *Webcam) mmapReleaseBuffers() error {
for _, buf := range w.buffers {
if err := syscall.Munmap(buf); err != nil {
return err
}
}
return nil
}
func (w *Webcam) StartStreaming() error {
n, err := w.mmapRequestBuffers(uint32(256))
if err != nil {
return err
}
w.buffers = make([][]byte, n)
for i := range w.buffers {
buf, err := w.mmapQueryBuffer(uint32(i))
if err != nil {
return err
}
w.buffers[i] = buf
err = w.mmapEnqueueBuffer(uint32(i))
if err != nil {
return err
}
}
t := _V4L2_BUF_TYPE_VIDEO_CAPTURE
return ioctl(w.fd, _VIDIOC_STREAMON, uintptr(unsafe.Pointer(&t)))
}
type TimeoutErr string
func (t TimeoutErr) Error() string {
return string(t)
}
func (w *Webcam) WaitForFrame() (err error) {
fds := new(goselect.FDSet)
for {
fds.Zero()
fds.Set(w.fd)
err = goselect.Select(int(w.fd)+1, fds, nil, nil, w.timeout)
if err == nil {
return
}
e, ok := err.(syscall.Errno)
if ok {
if e.Timeout() {
return TimeoutErr("timeout")
}
if e.Temporary() {
continue
}
}
}
}
func (w *Webcam) ReadFrame() ([]byte, error) {
i, l, err := w.mmapDequeueBuffer()
if err != nil {
return nil, err
}
buf := make([]byte, l)
copy(buf, w.buffers[int(i)])
err = w.mmapEnqueueBuffer(i)
if err != nil {
return nil, err
}
return buf, nil
}
func (w *Webcam) Close() error {
if err := w.mmapReleaseBuffers(); err != nil {
return err
}
return w.file.Close()
}