diff --git a/examples/color.go b/examples/color.go index 58f467e..d08cbae 100644 --- a/examples/color.go +++ b/examples/color.go @@ -90,6 +90,14 @@ func check(c color.Color) bool { //} return false } +func check2(c color.Color) bool { + //r, g, b, _ := c.RGBA() + r, g, b, _ := c.RGBA() + if r < 10000 && g > 8000 && b > 8000 { + return true + } + return false +} func set(img *image.RGBA, x, y int) { img.Set(x, y, color.White) @@ -102,6 +110,10 @@ func set(img *image.RGBA, x, y int) { //} } +func set2(img *image.RGBA, x, y int) { + img.Set(x, y, color.RGBA{90, 90, 90, 0}) +} + func mark(img image.Image) { o := jpeg.Options{100} r := img.Bounds() @@ -119,6 +131,9 @@ func mark(img image.Image) { if check(img.At(x, y)) { set(n, x, y) } + if check2(img.At(x, y)) { + set2(n, x, y) + } } } wg.Done() diff --git a/examples/main.go b/examples/main.go index bc38761..334a2cc 100644 --- a/examples/main.go +++ b/examples/main.go @@ -5,8 +5,8 @@ import ( //"encoding/hex" "fmt" "image" + "image/jpeg" "os" - //"image/jpeg" //"net/http" "git.giftfish.de/ston1th/webcam" "time" @@ -22,6 +22,20 @@ func main() { } defer cam.Close() + ctrl := cam.GetControls() + for k, value := range ctrl { + fmt.Fprintln(os.Stderr, k) + for _, v := range value { + fmt.Fprintln(os.Stderr, " "+v) + } + } + + // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html#control-ids + err = cam.SetControl(webcam.V4L2_CID_CONTRAST, 0) + if err != nil { + panic(err.Error()) + } + formats := cam.GetPixelFormats() println("Available formats: ") for k, value := range formats { @@ -65,10 +79,13 @@ func main() { default: panic(err.Error()) } - frm, err := cam.ReadFrame() + //frm, err := cam.ReadFrame() + o := jpeg.Options{100} + img, err := cam.ReadJPEG() + jpeg.Encode(os.Stdout, img, &o) if err == nil { //print(".") - os.Stdout.Write(frm) + //os.Stdout.Write(frm) os.Stdout.Sync() } } diff --git a/webcam.go b/webcam.go index c137fc8..d8dc377 100644 --- a/webcam.go +++ b/webcam.go @@ -151,6 +151,30 @@ type frmsizeenum struct { reserved [2]uint32 } +type queryctrl struct { + id uint32 + _type uint32 + name [32]uint8 + minimum int32 + maximum int32 + step int32 + default_value int32 + flags uint32 + reserved [2]uint32 +} + +type querymenu struct { + id uint32 + index uint32 + name [32]uint8 + reserved uint32 +} + +type control struct { + id uint32 + value int32 +} + const ( _IOC_READ = 2 _IOC_WRITE = 1 @@ -173,6 +197,55 @@ const ( _V4L2_FRMSIZE_TYPE_DISCRETE = 1 _V4L2_FRMSIZE_TYPE_CONTINUOUS = 2 _V4L2_FRMSIZE_TYPE_STEPWISE = 3 + + _V4L2_CTRL_FLAG_NEXT_CTRL = 0x80000000 + _V4L2_CTRL_FLAG_DISABLED = 0x0001 + _V4L2_CTRL_TYPE_MENU = 3 + _V4L2_CTRL_CLASS_USER = 0x00980000 + _V4L2_CID_BASE = _V4L2_CTRL_CLASS_USER | 0x900 + _V4L2_CID_USER_BASE = _V4L2_CID_BASE + _V4L2_CID_USER_CLASS = _V4L2_CTRL_CLASS_USER | 1 + + // Controls + V4L2_CID_BRIGHTNESS = _V4L2_CID_BASE + 0 + V4L2_CID_CONTRAST = _V4L2_CID_BASE + 1 + V4L2_CID_SATURATION = _V4L2_CID_BASE + 2 + V4L2_CID_HUE = _V4L2_CID_BASE + 3 + V4L2_CID_AUDIO_VOLUME = _V4L2_CID_BASE + 5 + V4L2_CID_AUDIO_BALANCE = _V4L2_CID_BASE + 6 + V4L2_CID_AUDIO_BASS = _V4L2_CID_BASE + 7 + V4L2_CID_AUDIO_TREBLE = _V4L2_CID_BASE + 8 + V4L2_CID_AUDIO_MUTE = _V4L2_CID_BASE + 9 + V4L2_CID_AUDIO_LOUDNESS = _V4L2_CID_BASE + 10 + V4L2_CID_AUTO_WHITE_BALANCE = _V4L2_CID_BASE + 12 + V4L2_CID_DO_WHITE_BALANCE = _V4L2_CID_BASE + 13 + V4L2_CID_RED_BALANCE = _V4L2_CID_BASE + 14 + V4L2_CID_BLUE_BALANCE = _V4L2_CID_BASE + 15 + V4L2_CID_GAMMA = _V4L2_CID_BASE + 16 + V4L2_CID_EXPOSURE = _V4L2_CID_BASE + 17 + V4L2_CID_AUTOGAIN = _V4L2_CID_BASE + 18 + V4L2_CID_GAIN = _V4L2_CID_BASE + 19 + V4L2_CID_HFLIP = _V4L2_CID_BASE + 20 + V4L2_CID_VFLIP = _V4L2_CID_BASE + 21 + V4L2_CID_POWER_LINE_FREQUENCY = _V4L2_CID_BASE + 24 + V4L2_CID_HUE_AUTO = _V4L2_CID_BASE + 25 + V4L2_CID_WHITE_BALANCE_TEMPERATURE = _V4L2_CID_BASE + 26 + V4L2_CID_SHARPNESS = _V4L2_CID_BASE + 27 + V4L2_CID_BACKLIGHT_COMPENSATION = _V4L2_CID_BASE + 28 + V4L2_CID_CHROMA_AGC = _V4L2_CID_BASE + 29 + V4L2_CID_COLOR_KILLER = _V4L2_CID_BASE + 30 + V4L2_CID_COLORFX = _V4L2_CID_BASE + 31 + V4L2_CID_AUTOBRIGHTNESS = _V4L2_CID_BASE + 32 + V4L2_CID_BAND_STOP_FILTER = _V4L2_CID_BASE + 33 + V4L2_CID_ROTATE = _V4L2_CID_BASE + 34 + V4L2_CID_BG_COLOR = _V4L2_CID_BASE + 35 + V4L2_CID_CHROMA_GAIN = _V4L2_CID_BASE + 36 + V4L2_CID_ILLUMINATORS_1 = _V4L2_CID_BASE + 37 + V4L2_CID_ILLUMINATORS_2 = _V4L2_CID_BASE + 38 + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE = _V4L2_CID_BASE + 39 + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT = _V4L2_CID_BASE + 40 + V4L2_CID_ALPHA_COMPONENT = _V4L2_CID_BASE + 41 + V4L2_CID_COLORFX_CBCR = _V4L2_CID_BASE + 42 ) var ( @@ -185,6 +258,10 @@ var ( _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{})) + _VIDIOC_QUERYCTRL = _IOWR('V', 36, unsafe.Sizeof(queryctrl{})) + _VIDIOC_QUERYMENU = _IOWR('V', 37, unsafe.Sizeof(querymenu{})) + _VIDIOC_G_CTRL = _IOWR('V', 27, unsafe.Sizeof(control{})) + _VIDIOC_S_CTRL = _IOWR('V', 28, unsafe.Sizeof(control{})) ) func _IOC(dir, t, nr, size uintptr) uintptr { @@ -214,6 +291,17 @@ func ioctl(fd, cmd, ptr uintptr) error { return nil } +func u2s(a [32]uint8) string { + buf := make([]byte, 32) + for i, u := range a { + if u == 0 { + break + } + buf[i] = byte(u) + } + return string(buf) +} + func (w *Webcam) checkCapabilities() (video, stream bool, err error) { c := new(capability) err = ioctl(w.fd, _VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(c))) @@ -222,6 +310,50 @@ func (w *Webcam) checkCapabilities() (video, stream bool, err error) { return } +func (w *Webcam) enumMenu(id uint32, min, max int32) (items []string) { + var qm querymenu + qm.id = id + for qm.index = uint32(min); qm.index <= uint32(max); qm.index++ { + if ioctl(w.fd, _VIDIOC_QUERYMENU, uintptr(unsafe.Pointer(&qm))) == nil { + items = append(items, u2s(qm.name)) + } + } + return +} + +func (w *Webcam) GetControls() (m map[string][]string) { + m = make(map[string][]string) + var qc queryctrl + qc.id = _V4L2_CTRL_FLAG_NEXT_CTRL + for ioctl(w.fd, _VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(&qc))) == nil { + if (qc.flags & _V4L2_CTRL_FLAG_DISABLED) == 0 { + items := []string{} + if qc._type == _V4L2_CTRL_TYPE_MENU { + items = w.enumMenu(qc.id, qc.minimum, qc.maximum) + } + m[u2s(qc.name)] = items + } + qc.id |= _V4L2_CTRL_FLAG_NEXT_CTRL + } + return +} + +func (w *Webcam) SetControl(ctrl uint32, value int32) error { + var qc queryctrl + qc.id = ctrl + err := ioctl(w.fd, _VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(&qc))) + if err != nil { + return err + } + if (qc.flags & _V4L2_CTRL_FLAG_DISABLED) != 0 { + return errors.New("unsupported") + } + var c control + c.id = ctrl + c.value = value + return ioctl(w.fd, _VIDIOC_S_CTRL, uintptr(unsafe.Pointer(&c))) + +} func (w *Webcam) GetPixelFormats() (m map[uint32]string) { m = make(map[uint32]string) for i := uint32(0); ; i++ {