initial commit
This commit is contained in:
commit
c67729332c
26 changed files with 1612 additions and 0 deletions
10
Godeps/Godeps.json
generated
Normal file
10
Godeps/Godeps.json
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"ImportPath": "git.giftfish.de/ston1th/webcam",
|
||||||
|
"GoVersion": "go1.6",
|
||||||
|
"Deps": [
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/creack/goselect",
|
||||||
|
"Rev": "2b9baf3927bb887c7a904663aa1a1c2dcf02ca61"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
5
Godeps/Readme
generated
Normal file
5
Godeps/Readme
generated
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
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
Normal file
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/pkg
|
||||||
|
/bin
|
||||||
29
Godeps/_workspace/src/github.com/creack/goselect/.gitignore
generated
vendored
Normal file
29
Godeps/_workspace/src/github.com/creack/goselect/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# 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
|
||||||
5
Godeps/_workspace/src/github.com/creack/goselect/Dockerfile
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/creack/goselect/Dockerfile
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
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
|
||||||
22
Godeps/_workspace/src/github.com/creack/goselect/LICENSE
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/creack/goselect/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
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.
|
||||||
|
|
||||||
30
Godeps/_workspace/src/github.com/creack/goselect/README.md
generated
vendored
Normal file
30
Godeps/_workspace/src/github.com/creack/goselect/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# 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"
|
||||||
|
```
|
||||||
369
Godeps/_workspace/src/github.com/creack/goselect/example/main.go
generated
vendored
Normal file
369
Godeps/_workspace/src/github.com/creack/goselect/example/main.go
generated
vendored
Normal file
|
|
@ -0,0 +1,369 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
33
Godeps/_workspace/src/github.com/creack/goselect/fdset.go
generated
vendored
Normal file
33
Godeps/_workspace/src/github.com/creack/goselect/fdset.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
// +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[:])
|
||||||
|
}
|
||||||
10
Godeps/_workspace/src/github.com/creack/goselect/fdset_32.go
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/creack/goselect/fdset_32.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// +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
|
||||||
|
)
|
||||||
10
Godeps/_workspace/src/github.com/creack/goselect/fdset_64.go
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/creack/goselect/fdset_64.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// +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
|
||||||
|
)
|
||||||
93
Godeps/_workspace/src/github.com/creack/goselect/fdset_doc.go
generated
vendored
Normal file
93
Godeps/_workspace/src/github.com/creack/goselect/fdset_doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
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)
|
||||||
|
*/
|
||||||
33
Godeps/_workspace/src/github.com/creack/goselect/fdset_freebsd.go
generated
vendored
Normal file
33
Godeps/_workspace/src/github.com/creack/goselect/fdset_freebsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
// +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[:])
|
||||||
|
}
|
||||||
20
Godeps/_workspace/src/github.com/creack/goselect/fdset_unsupported.go
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/creack/goselect/fdset_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
// +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() {}
|
||||||
57
Godeps/_workspace/src/github.com/creack/goselect/fdset_windows.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/creack/goselect/fdset_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
// +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
|
||||||
|
}
|
||||||
28
Godeps/_workspace/src/github.com/creack/goselect/select.go
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/creack/goselect/select.go
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
10
Godeps/_workspace/src/github.com/creack/goselect/select_linux.go
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/creack/goselect/select_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// +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
|
||||||
|
}
|
||||||
9
Godeps/_workspace/src/github.com/creack/goselect/select_other.go
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/creack/goselect/select_other.go
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
// +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)
|
||||||
|
}
|
||||||
16
Godeps/_workspace/src/github.com/creack/goselect/select_unsupported.go
generated
vendored
Normal file
16
Godeps/_workspace/src/github.com/creack/goselect/select_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// +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
|
||||||
|
}
|
||||||
13
Godeps/_workspace/src/github.com/creack/goselect/select_windows.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/creack/goselect/select_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +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
|
||||||
|
}
|
||||||
41
Godeps/_workspace/src/github.com/creack/goselect/zselect_windows.go
generated
vendored
Normal file
41
Godeps/_workspace/src/github.com/creack/goselect/zselect_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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
|
||||||
|
}
|
||||||
24
LICENSE
Normal file
24
LICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
Copyright (c) 2015, ston1th
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* The names of the authors and/or contributors may not be used to
|
||||||
|
endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
5
README.md
Normal file
5
README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Webcam library written in pure Go (no cgo bindings)
|
||||||
|
|
||||||
|
This library is for interacting with webcams, using the video4linux (V4L2) framework.
|
||||||
|
|
||||||
|
It is derived work from https://github.com/blackjack/webcam
|
||||||
252
examples/main.go
Normal file
252
examples/main.go
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
//"bytes"
|
||||||
|
//"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"os"
|
||||||
|
//"image/jpeg"
|
||||||
|
//"net/http"
|
||||||
|
"git.giftfish.de/ston1th/webcam"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
//choice := readChoice(fmt.Sprintf("Choose format [1-%d]: ", len(formats)))
|
||||||
|
//format := formats[1]
|
||||||
|
format := uint32(1196444237) // MJPEG
|
||||||
|
//format := PixelFormat(1448695129) // YUV
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
//choice = readChoice(fmt.Sprintf("Choose format [1-%d]: ", len(frames)))
|
||||||
|
s := fs[5]
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
frm, err := cam.ReadFrame()
|
||||||
|
if err == nil {
|
||||||
|
//print(".")
|
||||||
|
os.Stdout.Write(frm)
|
||||||
|
os.Stdout.Sync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
buf := bytes.NewBuffer(frm)
|
||||||
|
img, err = jpeg.Decode(buf)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc("/", handler)
|
||||||
|
panic(http.ListenAndServe(":8090", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := jpeg.Encode(w, img, nil)
|
||||||
|
println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
imgchan := make(chan *image.Image)
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
frm, err := cam.ReadFrame()
|
||||||
|
if err == nil {
|
||||||
|
pw.Write(frm)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
img, err := mjpeg.Decode(pr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
dec <- imgchan
|
||||||
|
|
||||||
|
http.HandleFunc("/", handler)
|
||||||
|
http.HandleFunc("/frame", handler2)
|
||||||
|
panic(http.ListenAndServe(":8090", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler2(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, err := w.Write(frame)
|
||||||
|
println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := jpeg.Encode(w, *dec, nil)
|
||||||
|
println(err)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
fmt "fmt"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var frame []byte
|
||||||
|
var dec image.Image
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cam, err := Open("/dev/video0")
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
defer cam.Close()
|
||||||
|
|
||||||
|
err = cam.StartStreaming()
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
frame, err = cam.ReadFrame()
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dec, err = jpeg.Decode(bytes.NewBuffer(frame))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc("/", handler)
|
||||||
|
panic(http.ListenAndServe(":8090", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := jpeg.Encode(w, dec, nil)
|
||||||
|
println(err)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/jpeg"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const X = 800
|
||||||
|
const Y = 600
|
||||||
|
|
||||||
|
func div(f1, f2 float64) float64 {
|
||||||
|
return ((f1 - f2) / f1) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
func check(i1, i2 uint32) bool {
|
||||||
|
if i1 == i2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if i1 == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if i2 == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
f1 := float64(i1)
|
||||||
|
f2 := float64(i2)
|
||||||
|
if div(f1, f2) > 5.00 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func comp(p1, p2 image.Image) image.Image {
|
||||||
|
red := color.RGBA{255, 0, 0, 0}
|
||||||
|
n := image.NewRGBA(p1.Bounds())
|
||||||
|
for i := 0; i < X; i++ {
|
||||||
|
for j := 0; j < Y; j++ {
|
||||||
|
r1, g1, b1, _ := p1.At(i, j).RGBA()
|
||||||
|
r2, g2, b2, _ := p2.At(i, j).RGBA()
|
||||||
|
if check(r1, r2) || check(g1, g2) || check(b1, b2) {
|
||||||
|
n.Set(i, j, red)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n.Set(i, j, p1.At(i, j))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
var img image.Image
|
||||||
|
var test image.Image
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f1, err := os.Open("pic1.jpeg")
|
||||||
|
fmt.Println(err)
|
||||||
|
f2, err := os.Open("pic2.jpeg")
|
||||||
|
fmt.Println(err)
|
||||||
|
p1, _, err := image.Decode(f1)
|
||||||
|
fmt.Println(err)
|
||||||
|
p2, _, err := image.Decode(f2)
|
||||||
|
fmt.Println(err)
|
||||||
|
img = comp(p1, p2)
|
||||||
|
test = p1
|
||||||
|
http.HandleFunc("/", handler)
|
||||||
|
http.HandleFunc("/test", handler2)
|
||||||
|
panic(http.ListenAndServe(":8090", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println(jpeg.Encode(w, img, nil))
|
||||||
|
}
|
||||||
|
func handler2(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println(jpeg.Encode(w, test, nil))
|
||||||
|
}*/
|
||||||
442
webcam.go
Normal file
442
webcam.go
Normal 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()
|
||||||
|
}
|
||||||
44
webcam_test.go
Normal file
44
webcam_test.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package webcam
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestConstants(t *testing.T) {
|
||||||
|
const (
|
||||||
|
__VIDIOC_QUERYCAP = 2154321408
|
||||||
|
__VIDIOC_ENUM_FMT = 3225441794
|
||||||
|
__VIDIOC_S_FMT = 3234878981
|
||||||
|
__VIDIOC_REQBUFS = 3222558216
|
||||||
|
__VIDIOC_QUERYBUF = 3227014665
|
||||||
|
__VIDIOC_QBUF = 3227014671
|
||||||
|
__VIDIOC_DQBUF = 3227014673
|
||||||
|
__VIDIOC_STREAMON = 1074026002
|
||||||
|
__VIDIOC_ENUM_FRAMESIZES = 3224131146
|
||||||
|
)
|
||||||
|
if __VIDIOC_QUERYCAP != _VIDIOC_QUERYCAP {
|
||||||
|
t.Error("expected _VIDIOC_QUERYCAP", __VIDIOC_QUERYCAP, "got", _VIDIOC_QUERYCAP)
|
||||||
|
}
|
||||||
|
if __VIDIOC_ENUM_FMT != _VIDIOC_ENUM_FMT {
|
||||||
|
t.Error("expected _VIDIOC_ENUM_FMT", __VIDIOC_ENUM_FMT, "got", _VIDIOC_ENUM_FMT)
|
||||||
|
}
|
||||||
|
if __VIDIOC_S_FMT != _VIDIOC_S_FMT {
|
||||||
|
t.Error("expected _VIDIOC_S_FMT", __VIDIOC_S_FMT, "got", _VIDIOC_S_FMT)
|
||||||
|
}
|
||||||
|
if __VIDIOC_REQBUFS != _VIDIOC_REQBUFS {
|
||||||
|
t.Error("expected _VIDIOC_REQBUFS", __VIDIOC_REQBUFS, "got", _VIDIOC_REQBUFS)
|
||||||
|
}
|
||||||
|
if __VIDIOC_QUERYBUF != _VIDIOC_QUERYBUF {
|
||||||
|
t.Error("expected _VIDIOC_QUERYBUF", __VIDIOC_QUERYBUF, "got", _VIDIOC_QUERYBUF)
|
||||||
|
}
|
||||||
|
if __VIDIOC_QBUF != _VIDIOC_QBUF {
|
||||||
|
t.Error("expected _VIDIOC_QBUF", __VIDIOC_QBUF, "got", _VIDIOC_QBUF)
|
||||||
|
}
|
||||||
|
if __VIDIOC_DQBUF != _VIDIOC_DQBUF {
|
||||||
|
t.Error("expected _VIDIOC_DQBUF", __VIDIOC_DQBUF, "got", _VIDIOC_DQBUF)
|
||||||
|
}
|
||||||
|
if __VIDIOC_STREAMON != _VIDIOC_STREAMON {
|
||||||
|
t.Error("expected _VIDIOC_STREAMON", __VIDIOC_STREAMON, "got", _VIDIOC_STREAMON)
|
||||||
|
}
|
||||||
|
if __VIDIOC_ENUM_FRAMESIZES != _VIDIOC_ENUM_FRAMESIZES {
|
||||||
|
t.Error("expected _VIDIOC_ENUM_FRAMESIZES", __VIDIOC_ENUM_FRAMESIZES, "got", _VIDIOC_ENUM_FRAMESIZES)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue