new version

This commit is contained in:
ston1th 2021-02-07 12:02:55 +01:00
commit 9ef7d28c46
25 changed files with 267 additions and 845 deletions

10
Godeps/Godeps.json generated
View file

@ -1,10 +0,0 @@
{
"ImportPath": "git.giftfish.de/ston1th/webcam",
"GoVersion": "go1.6",
"Deps": [
{
"ImportPath": "github.com/creack/goselect",
"Rev": "2b9baf3927bb887c7a904663aa1a1c2dcf02ca61"
}
]
}

5
Godeps/Readme generated
View file

@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

2
Godeps/_workspace/.gitignore generated vendored
View file

@ -1,2 +0,0 @@
/pkg
/bin

View file

@ -1,29 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
go-select*
goselect*
example-*
example/example

View file

@ -1,5 +0,0 @@
FROM google/golang:stable
MAINTAINER Guillaume J. Charmes <guillaume@charmes.net>
CMD /tmp/a.out
ADD . /src
RUN cd /src && go build -o /tmp/a.out

View file

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Guillaume J. Charmes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,30 +0,0 @@
# go-select
select(2) implementation in Go
## Supported platforms
| | 386 | amd64 | arm |
|---------------|-----|-------|-----|
| **linux** | yes | yes | yes |
| **darwin** | yes | yes | n/a |
| **freebsd** | yes | yes | yes |
| **openbsd** | yes | yes | yes |
| **netbsd** | yes | yes | yes |
| **dragonfly** | n/a | yes | n/a |
| **solaris** | n/a | no | n/a |
| **plan9** | no | no | n/a |
| **windows** | yes | yes | n/a |
| **android** | n/a | n/a | no |
*n/a: platform not supported by Go
Go on `plan9` and `solaris` do not implement `syscall.Select` not `syscall.SYS_SELECT`.
## Cross compile
Using davecheney's https://github.com/davecheney/golang-crosscompile
```
export PLATFORMS="darwin/386 darwin/amd64 freebsd/386 freebsd/amd64 freebsd/arm linux/386 linux/amd64 linux/arm windows/386 windows/amd64 openbsd/386 openbsd/amd64 netbsd/386 netbsd/amd64 dragonfly/amd64 plan9/386 plan9/amd64 solaris/amd64"
```

View file

@ -1,369 +0,0 @@
package main
import (
"fmt"
"io"
"log"
"net"
"os"
"sync"
"time"
"github.com/creack/goselect"
)
type fder interface {
Fd() uintptr
}
// ErrNotFder .
var ErrNotFder = fmt.Errorf("Not a fder")
type readFder interface {
io.Reader
fder
}
type writeFder interface {
io.Reader
fder
}
// SelectReader .
type selectReader struct {
readFder
ready chan struct{}
stop chan struct{}
}
func (r *selectReader) Read(buf []byte) (int, error) {
select {
case <-r.stop:
return 0, io.EOF
case <-r.ready:
return r.readFder.Read(buf)
}
}
// Select .
type Select struct {
readers []*selectReader
writers []writeFder
controlPipeR *os.File
controlPipeW *os.File
}
// NewSelect .
func NewSelect() (*Select, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
s := &Select{
readers: []*selectReader{},
writers: []writeFder{},
controlPipeR: r,
controlPipeW: w,
}
return s, nil
}
func (s *Select) run() {
rFDSet := &goselect.FDSet{}
var max uintptr
var fd uintptr
for {
// TODO: this can be cached and changed only upon controlePipe event.
rFDSet.Zero()
fd = s.controlPipeR.Fd()
max = fd
rFDSet.Set(fd)
for _, r := range s.readers {
fd = r.Fd()
if max < fd {
max = fd
}
rFDSet.Set(fd)
}
println("-------> preselect")
if err := goselect.Select(int(max)+1, rFDSet, nil, nil, -1); err != nil {
log.Fatal(err)
}
for i := uintptr(0); i < goselect.FD_SETSIZE; i++ {
if rFDSet.IsSet(i) {
println(i, "is ready")
}
}
println("<-------- postselect")
for _, r := range s.readers {
if rFDSet.IsSet(r.Fd()) {
func(r *selectReader) { r.ready <- struct{}{} }(r)
}
}
if rFDSet.IsSet(s.controlPipeR.Fd()) {
buf := make([]byte, 1024)
n, err := s.controlPipeR.Read(buf)
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
fmt.Printf("----> control: %s\n", buf[:n])
}
}
}
// NewReader .
func (s *Select) NewReader(r io.Reader) (io.Reader, error) {
rr, ok := r.(readFder)
if !ok {
return nil, ErrNotFder
}
ret := &selectReader{
readFder: rr,
ready: make(chan struct{}),
stop: make(chan struct{}),
}
s.readers = append(s.readers, ret)
if _, err := s.controlPipeW.Write([]byte("newreader")); err != nil {
return nil, err
}
return ret, nil
}
func test6() error {
s, err := NewSelect()
if err != nil {
return err
}
_ = s
fmt.Printf("%d\n", os.Getpid())
const MAX = 4
rs := []io.Reader{}
ws := []io.Writer{}
for i := 0; i < MAX; i++ {
r, w, _ := os.Pipe()
println(i, "--->", r.Fd(), w.Fd())
rs = append(rs, r)
ws = append(ws, w)
}
c := make(chan struct{})
wg := sync.WaitGroup{}
for i := 0; i < MAX; i++ {
wg.Add(1)
go func() {
time.Sleep(2 * time.Second)
<-c
wg.Done()
}()
}
time.Sleep(4 * time.Second)
close(c)
wg.Wait()
for i := 0; i < MAX; i++ {
time.Sleep(5 * time.Second)
wg.Add(1)
go func(r io.Reader, i int) {
rr, _ := s.NewReader(r)
r = rr
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)
if err != nil {
log.Printf("[%d] error read: %s\n", i, err)
break
}
_ = n
fmt.Printf("[%d] %s\n", i, buf[:n])
}
wg.Done()
}(rs[i], i)
}
for i := 0; i < 1; i++ {
for i := 0; i < MAX; i++ {
fmt.Fprintf(ws[i], "{%d} hello", i)
}
time.Sleep(5 * time.Second)
}
wg.Wait()
return nil
}
func test4() error {
rFDSet := &goselect.FDSet{}
buf := make([]byte, 1024)
for {
// TODO: this can be cached and changed only upon controlePipe event.
rFDSet.Zero()
rFDSet.Set(os.Stdin.Fd())
if err := goselect.Select(int(os.Stdin.Fd())+1, rFDSet, nil, nil, -1); err != nil {
return err
}
for i := uintptr(0); i < goselect.FD_SETSIZE; i++ {
if rFDSet.IsSet(i) {
println(i, "is ready")
}
}
if rFDSet.IsSet(os.Stdin.Fd()) {
n, err := os.Stdin.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return err
}
fmt.Printf("---->: %s\n", buf[:n])
}
}
return nil
}
type Client struct {
f *os.File
queue [][]byte
}
func (c *Client) Push(msg []byte) {
c.queue = append(c.queue, msg)
println(len(c.queue))
}
func (c *Client) Pop() []byte {
if len(c.queue) == 0 {
return nil
}
tmp := c.queue[0]
c.queue = c.queue[1:]
return tmp
}
func testServer() error {
l, err := net.Listen("tcp", "0.0.0.0:8080")
if err != nil {
return err
}
ll, ok := l.(*net.TCPListener)
if !ok {
return fmt.Errorf("wrong listener type")
}
llf, err := ll.File()
if err != nil {
return err
}
rFDSet := &goselect.FDSet{}
wFDSet := &goselect.FDSet{}
buf := make([]byte, 1024)
var max, fd uintptr
clients := []*Client{}
for {
// TODO: this can be cached and changed only upon controlePipe event.
rFDSet.Zero()
wFDSet.Zero()
fd = llf.Fd()
max = fd
rFDSet.Set(fd)
for _, c := range clients {
fd = c.f.Fd()
rFDSet.Set(fd)
if len(c.queue) > 0 {
wFDSet.Set(fd)
}
if max < fd {
max = fd
}
}
if err := goselect.Select(int(max)+1, rFDSet, wFDSet, nil, -1); err != nil {
return err
}
println("-->")
// Watch for new clients
if rFDSet.IsSet(llf.Fd()) {
c, err := ll.AcceptTCP()
if err != nil {
return err
}
f, err := c.File()
if err != nil {
return err
}
fd = f.Fd()
println("New client:", fd)
clients = append(clients, &Client{f: f})
}
// Watch for client activity
for _, c := range clients {
fd = c.f.Fd()
if rFDSet.IsSet(fd) {
n, err := c.f.Read(buf)
if err != nil {
return err
}
fmt.Printf("%s", buf[:n])
for _, cc := range clients {
if c != cc {
cc.Push(buf[:n])
}
}
}
}
// Send message to clients
for _, c := range clients {
fd = c.f.Fd()
if wFDSet.IsSet(fd) {
msg := c.Pop()
fmt.Printf("%d write ready: %v\n", fd, msg)
if msg != nil {
_, err := c.f.Write(msg)
if err != nil {
return err
}
}
}
}
}
}
func main() {
if err := testServer(); err != nil {
log.Fatal(err)
}
}
func test5() error {
wg := sync.WaitGroup{}
fmt.Printf("%d\n", os.Getpid())
for i := 0; i < 200; i++ {
r, w, _ := os.Pipe()
go func(w io.Writer) {
for {
w.Write([]byte("hello"))
time.Sleep(time.Second)
}
}(w)
wg.Add(1)
go func(r io.Reader) {
buf := make([]byte, 1024)
for {
_, err := r.Read(buf)
if err != nil {
log.Fatal(err)
}
}
}(r)
}
wg.Wait()
return nil
}

View file

@ -1,33 +0,0 @@
// +build !freebsd,!windows,!plan9
package goselect
import "syscall"
const FD_SETSIZE = syscall.FD_SETSIZE
// FDSet wraps syscall.FdSet with convenience methods
type FDSet syscall.FdSet
// Set adds the fd to the set
func (fds *FDSet) Set(fd uintptr) {
fds.Bits[fd/NFDBITS] |= (1 << (fd % NFDBITS))
}
// Clear remove the fd from the set
func (fds *FDSet) Clear(fd uintptr) {
fds.Bits[fd/NFDBITS] &^= (1 << (fd % NFDBITS))
}
// IsSet check if the given fd is set
func (fds *FDSet) IsSet(fd uintptr) bool {
return fds.Bits[fd/NFDBITS]&(1<<(fd%NFDBITS)) != 0
}
// Keep a null set to avoid reinstatiation
var nullFdSet = &FDSet{}
// Zero empties the Set
func (fds *FDSet) Zero() {
copy(fds.Bits[:], (nullFdSet).Bits[:])
}

View file

@ -1,10 +0,0 @@
// +build darwin openbsd netbsd 386 arm
package goselect
// darwin, netbsd and openbsd uses uint32 on both amd64 and 386
const (
// NFDBITS is the amount of bits per mask
NFDBITS = 4 * 8
)

View file

@ -1,10 +0,0 @@
// +build amd64,!darwin,!netbsd,!openbsd
package goselect
// darwin, netbsd and openbsd uses uint32 on both amd64 and 386
const (
// NFDBITS is the amount of bits per mask
NFDBITS = 8 * 8
)

View file

@ -1,93 +0,0 @@
package goselect
/**
From: XCode's MacOSX10.10.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/sys/select.h
--
// darwin/amd64 / 386
sizeof(__int32_t) == 4
--
typedef __int32_t __fd_mask;
#define FD_SETSIZE 1024
#define __NFDBITS (sizeof(__fd_mask) * 8)
#define __howmany(x, y) ((((x) % (y)) == 0) ? ((x) / (y)) : (((x) / (y)) + 1))
typedef struct fd_set {
__fd_mask fds_bits[__howmany(__FD_SETSIZE, __NFDBITS)];
} fd_set;
#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS))
#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n))
#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0)
*/
/**
From: /usr/include/i386-linux-gnu/sys/select.h
--
// linux/i686
sizeof(long int) == 4
--
typedef long int __fd_mask;
#define FD_SETSIZE 1024
#define __NFDBITS (sizeof(__fd_mask) * 8)
typedef struct fd_set {
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
} fd_set;
#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS))
#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n))
#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0)
*/
/**
From: /usr/include/x86_64-linux-gnu/sys/select.h
--
// linux/amd64
sizeof(long int) == 8
--
typedef long int __fd_mask;
#define FD_SETSIZE 1024
#define __NFDBITS (sizeof(__fd_mask) * 8)
typedef struct fd_set {
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
} fd_set;
#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS))
#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n))
#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0)
*/
/**
From: /usr/include/sys/select.h
--
// freebsd/amd64
sizeof(unsigned long) == 8
--
typedef unsigned long __fd_mask;
#define FD_SETSIZE 1024U
#define __NFDBITS (sizeof(__fd_mask) * 8)
#define _howmany(x, y) (((x) + ((y) - 1)) / (y))
typedef struct fd_set {
__fd_mask fds_bits[_howmany(FD_SETSIZE, __NFDBITS)];
} fd_set;
#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS))
#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n))
#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0)
*/

View file

@ -1,33 +0,0 @@
// +build freebsd
package goselect
import "syscall"
const FD_SETSIZE = syscall.FD_SETSIZE
// FDSet wraps syscall.FdSet with convenience methods
type FDSet syscall.FdSet
// Set adds the fd to the set
func (fds *FDSet) Set(fd uintptr) {
fds.X__fds_bits[fd/NFDBITS] |= (1 << (fd % NFDBITS))
}
// Clear remove the fd from the set
func (fds *FDSet) Clear(fd uintptr) {
fds.X__fds_bits[fd/NFDBITS] &^= (1 << (fd % NFDBITS))
}
// IsSet check if the given fd is set
func (fds *FDSet) IsSet(fd uintptr) bool {
return fds.X__fds_bits[fd/NFDBITS]&(1<<(fd%NFDBITS)) != 0
}
// Keep a null set to avoid reinstatiation
var nullFdSet = &FDSet{}
// Zero empties the Set
func (fds *FDSet) Zero() {
copy(fds.X__fds_bits[:], (nullFdSet).X__fds_bits[:])
}

View file

@ -1,20 +0,0 @@
// +build plan9
package goselect
const FD_SETSIZE = 0
// FDSet wraps syscall.FdSet with convenience methods
type FDSet struct{}
// Set adds the fd to the set
func (fds *FDSet) Set(fd uintptr) {}
// Clear remove the fd from the set
func (fds *FDSet) Clear(fd uintptr) {}
// IsSet check if the given fd is set
func (fds *FDSet) IsSet(fd uintptr) bool { return false }
// Zero empties the Set
func (fds *FDSet) Zero() {}

View file

@ -1,57 +0,0 @@
// +build windows
package goselect
import "syscall"
const FD_SETSIZE = 64
// FDSet extracted from mingw libs source code
type FDSet struct {
fd_count uint
fd_array [FD_SETSIZE]uintptr
}
// Set adds the fd to the set
func (fds *FDSet) Set(fd uintptr) {
var i uint
for i = 0; i < fds.fd_count; i++ {
if fds.fd_array[i] == fd {
break
}
}
if i == fds.fd_count {
if fds.fd_count < FD_SETSIZE {
fds.fd_array[i] = fd
fds.fd_count++
}
}
}
// Clear remove the fd from the set
func (fds *FDSet) Clear(fd uintptr) {
var i uint
for i = 0; i < fds.fd_count; i++ {
if fds.fd_array[i] == fd {
for i < fds.fd_count-1 {
fds.fd_array[i] = fds.fd_array[i+1]
i++
}
fds.fd_count--
break
}
}
}
// IsSet check if the given fd is set
func (fds *FDSet) IsSet(fd uintptr) bool {
if isset, err := __WSAFDIsSet(syscall.Handle(fd), fds); err == nil && isset != 0 {
return true
}
return false
}
// Zero empties the Set
func (fds *FDSet) Zero() {
fds.fd_count = 0
}

View file

@ -1,28 +0,0 @@
package goselect
import (
"syscall"
"time"
)
// Select wraps syscall.Select with Go types
func Select(n int, r, w, e *FDSet, timeout time.Duration) error {
var timeval *syscall.Timeval
if timeout >= 0 {
t := syscall.NsecToTimeval(timeout.Nanoseconds())
timeval = &t
}
return sysSelect(n, r, w, e, timeval)
}
// RetrySelect wraps syscall.Select with Go types, and retries a number of times, with a given retryDelay.
func RetrySelect(n int, r, w, e *FDSet, timeout time.Duration, retries int, retryDelay time.Duration) (err error) {
for i := 0; i < retries; i++ {
if err = Select(n, r, w, e, timeout); err != syscall.EINTR {
return err
}
time.Sleep(retryDelay)
}
return err
}

View file

@ -1,10 +0,0 @@
// +build linux
package goselect
import "syscall"
func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error {
_, err := syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), timeout)
return err
}

View file

@ -1,9 +0,0 @@
// +build !linux,!windows,!plan9,!solaris
package goselect
import "syscall"
func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error {
return syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), timeout)
}

View file

@ -1,16 +0,0 @@
// +build plan9 solaris
package goselect
import (
"fmt"
"runtime"
"syscall"
)
// ErrUnsupported .
var ErrUnsupported = fmt.Errorf("Platofrm %s/%s unsupported", runtime.GOOS, runtime.GOARCH)
func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error {
return ErrUnsupported
}

View file

@ -1,13 +0,0 @@
// +build windows
package goselect
import "syscall"
//sys _select(nfds int, readfds *FDSet, writefds *FDSet, exceptfds *FDSet, timeout *syscall.Timeval) (total int, err error) = ws2_32.select
//sys __WSAFDIsSet(handle syscall.Handle, fdset *FDSet) (isset int, err error) = ws2_32.__WSAFDIsSet
func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error {
_, err := _select(n, r, w, e, timeout)
return err
}

View file

@ -1,41 +0,0 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
package goselect
import "unsafe"
import "syscall"
var _ unsafe.Pointer
var (
modws2_32 = syscall.NewLazyDLL("ws2_32.dll")
procselect = modws2_32.NewProc("select")
proc__WSAFDIsSet = modws2_32.NewProc("__WSAFDIsSet")
)
func _select(nfds int, readfds *FDSet, writefds *FDSet, exceptfds *FDSet, timeout *syscall.Timeval) (total int, err error) {
r0, _, e1 := syscall.Syscall6(procselect.Addr(), 5, uintptr(nfds), uintptr(unsafe.Pointer(readfds)), uintptr(unsafe.Pointer(writefds)), uintptr(unsafe.Pointer(exceptfds)), uintptr(unsafe.Pointer(timeout)), 0)
total = int(r0)
if total == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func __WSAFDIsSet(handle syscall.Handle, fdset *FDSet) (isset int, err error) {
r0, _, e1 := syscall.Syscall(proc__WSAFDIsSet.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(fdset)), 0)
isset = int(r0)
if isset == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}

131
examples/color.go Normal file
View file

@ -0,0 +1,131 @@
package main
import (
"fmt"
"git.giftfish.de/ston1th/webcam"
"image"
"image/color"
"image/jpeg"
"os"
"sync"
"time"
)
const min = 3300
// noise
//const min = 3000
// minimum
//const min = 2570
var frame []byte
var img image.Image
func main() {
cam, err := webcam.Open("/dev/video0", time.Second*5)
if err != nil {
panic(err.Error())
}
defer cam.Close()
formats := cam.GetPixelFormats()
println("Available formats: ")
for k, value := range formats {
fmt.Fprintln(os.Stderr, k, value)
}
format := uint32(1196444237) // MJPEG
fmt.Fprintf(os.Stderr, "Supported frame sizes for format %s\n", formats[format])
fs := cam.GetFrameSizes(format)
for _, f := range fs {
fmt.Fprintln(os.Stderr, f)
}
//s := fs[6]
s := fs[2]
f, w, h, err := cam.SetImageFormat(format, s.MaxWidth, s.MaxHeight)
if err != nil {
panic(err.Error())
} else {
fmt.Fprintf(os.Stderr, "Resulting image format: %s (%dx%d)\n", formats[f], w, h)
}
fmt.Fprintf(os.Stderr, "Press Enter to start streaming\n")
fmt.Scanf("\n")
err = cam.StartStreaming()
if err != nil {
panic(err.Error())
}
for {
err = cam.WaitForFrame()
switch err.(type) {
case nil:
case webcam.TimeoutErr:
continue
default:
panic(err.Error())
}
img, err := cam.ReadJPEG()
if err == nil {
mark(img)
os.Stdout.Sync()
}
}
}
func check(c color.Color) bool {
//r, g, b, _ := c.RGBA()
r, g, b, _ := c.RGBA()
if r > 30000 && g < 20000 && b < 20000 {
return true
}
//if g > 50000 {
// return true
//}
//if b > min {
// return true
//}
return false
}
func set(img *image.RGBA, x, y int) {
img.Set(x, y, color.White)
//m := 10
//for i := x - m; i < x+m; i++ {
// img.Set(i, y, color.White)
//}
//for i := y - m; i < y+m; i++ {
// img.Set(x, i, color.White)
//}
}
func mark(img image.Image) {
o := jpeg.Options{100}
r := img.Bounds()
n := image.NewRGBA(r)
var wg sync.WaitGroup
threads := 4
wx := 0
work := r.Max.X / threads
wmax := work
for i := 0; i < threads; i++ {
wg.Add(1)
go func(x, max int) {
for ; x < max; x++ {
for y := 0; y < r.Max.Y; y++ {
if check(img.At(x, y)) {
set(n, x, y)
}
}
}
wg.Done()
}(wx, wmax)
wx += work
wmax += work
}
wg.Wait()
jpeg.Encode(os.Stdout, n, &o)
}

129
examples/radio.go Normal file
View file

@ -0,0 +1,129 @@
package main
import (
"fmt"
"git.giftfish.de/ston1th/webcam"
"image"
"image/color"
"image/jpeg"
"os"
"sync"
"time"
)
const min = 3300
// noise
//const min = 3000
// minimum
//const min = 2570
var frame []byte
var img image.Image
func main() {
cam, err := webcam.Open("/dev/video0", time.Second*5)
if err != nil {
panic(err.Error())
}
defer cam.Close()
formats := cam.GetPixelFormats()
println("Available formats: ")
for k, value := range formats {
fmt.Fprintln(os.Stderr, k, value)
}
format := uint32(1196444237) // MJPEG
fmt.Fprintf(os.Stderr, "Supported frame sizes for format %s\n", formats[format])
fs := cam.GetFrameSizes(format)
for _, f := range fs {
fmt.Fprintln(os.Stderr, f)
}
//s := fs[6]
s := fs[2]
f, w, h, err := cam.SetImageFormat(format, s.MaxWidth, s.MaxHeight)
if err != nil {
panic(err.Error())
} else {
fmt.Fprintf(os.Stderr, "Resulting image format: %s (%dx%d)\n", formats[f], w, h)
}
fmt.Fprintf(os.Stderr, "Press Enter to start streaming\n")
fmt.Scanf("\n")
err = cam.StartStreaming()
if err != nil {
panic(err.Error())
}
for {
err = cam.WaitForFrame()
switch err.(type) {
case nil:
case webcam.TimeoutErr:
continue
default:
panic(err.Error())
}
img, err := cam.ReadJPEG()
if err == nil {
mark(img)
os.Stdout.Sync()
}
}
}
func check(c color.Color) bool {
r, g, b, _ := c.RGBA()
if r > min {
return true
}
if g > min {
return true
}
if b > min {
return true
}
return false
}
func set(img *image.RGBA, x, y int) {
m := 10
for i := x - m; i < x+m; i++ {
img.Set(i, y, color.White)
}
for i := y - m; i < y+m; i++ {
img.Set(x, i, color.White)
}
}
func mark(img image.Image) {
o := jpeg.Options{100}
r := img.Bounds()
n := image.NewRGBA(r)
var wg sync.WaitGroup
threads := 4
wx := 0
work := r.Max.X / threads
wmax := work
for i := 0; i < threads; i++ {
wg.Add(1)
go func(x, max int) {
for ; x < max; x++ {
for y := 0; y < r.Max.Y; y++ {
if check(img.At(x, y)) {
set(n, x, y)
}
}
}
wg.Done()
}(wx, wmax)
wx += work
wmax += work
}
wg.Wait()
jpeg.Encode(os.Stdout, n, &o)
}

5
go.mod Normal file
View file

@ -0,0 +1,5 @@
module git.giftfish.de/ston1th/webcam
go 1.15
require github.com/creack/goselect v0.0.0-20160321032608-2b9baf3927bb

2
go.sum Normal file
View file

@ -0,0 +1,2 @@
github.com/creack/goselect v0.0.0-20160321032608-2b9baf3927bb h1:6kqi/4+wjeZeRr/0eNLOj3B4UcK55HDKUAA6OTi2hO4=
github.com/creack/goselect v0.0.0-20160321032608-2b9baf3927bb/go.mod h1:gHrIcH/9UZDn2qgeTUeW5K9eZsVYCH6/60J/FHysWyE=