Skip to content

Commit

Permalink
Merge pull request #30 from vladimirvivien/webcam-upgrade
Browse files Browse the repository at this point in the history
Code and example updates
  • Loading branch information
vladimirvivien authored Oct 2, 2022
2 parents b1aac42 + a93d5b2 commit 5e2fbd8
Show file tree
Hide file tree
Showing 17 changed files with 553 additions and 92 deletions.
21 changes: 14 additions & 7 deletions device/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,7 @@ func (d *Device) Start(ctx context.Context) error {
if err != nil {
return fmt.Errorf("device: requested buffer type not be supported: %w", err)
}
if bufReq.Count < 2 {
return fmt.Errorf("device: %s: issuficient buffer memory", d.path)
}

d.config.bufSize = bufReq.Count
d.requestedBuf = bufReq

Expand Down Expand Up @@ -395,10 +393,10 @@ func (d *Device) startStreamLoop(ctx context.Context) error {
defer close(d.output)

fd := d.Fd()
var frame []byte
ioMemType := d.MemIOType()
bufType := d.BufferType()
waitForRead := v4l2.WaitForRead(d)

for {
select {
// handle stream capture (read from driver)
Expand All @@ -411,12 +409,21 @@ func (d *Device) startStreamLoop(ctx context.Context) error {
panic(fmt.Sprintf("device: stream loop dequeue: %s", err))
}

// copy mapped buffer (copying avoids polluted data from subsequent dequeue ops)
if buff.Flags&v4l2.BufFlagMapped != 0 && buff.Flags&v4l2.BufFlagError == 0 {
frame = make([]byte, buff.BytesUsed)
if n := copy(frame, d.buffers[buff.Index][:buff.BytesUsed]); n == 0 {
d.output <- []byte{}
}
d.output <- frame
frame = nil
} else {
d.output <- []byte{}
}

if _, err := v4l2.QueueBuffer(fd, ioMemType, bufType, buff.Index); err != nil {
panic(fmt.Sprintf("device: stream loop queue: %s: buff: %#v", err, buff))
}

d.output <- d.Buffers()[buff.Index][:buff.BytesUsed]

case <-ctx.Done():
d.Stop()
return
Expand Down
4 changes: 3 additions & 1 deletion examples/capture0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ func main() {
// open device
device, err := device.Open(
devName,
device.WithPixFormat(v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMPEG, Width: 640, Height: 480}),
device.WithPixFormat(
v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMPEG, Width: 640, Height: 480},
),
)
...
}
Expand Down
4 changes: 3 additions & 1 deletion examples/capture0/capture0.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"log"
"os"
"time"

"github.com/vladimirvivien/go4vl/device"
"github.com/vladimirvivien/go4vl/v4l2"
Expand All @@ -19,7 +20,7 @@ func main() {
// open device
device, err := device.Open(
devName,
device.WithPixFormat(v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMJPEG, Width: 640, Height: 480, Field: v4l2.FieldInterlaced}),
device.WithPixFormat(v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMJPEG, Width: 640, Height: 480}),
)
if err != nil {
log.Fatalf("failed to open device: %s", err)
Expand Down Expand Up @@ -56,6 +57,7 @@ func main() {
if count >= totalFrames {
break
}
time.Sleep(1 * time.Second)
}

stop() // stop capture
Expand Down
49 changes: 41 additions & 8 deletions examples/capture1/capture1.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package main

import (
"bytes"
"context"
"flag"
"fmt"
"image"
"image/jpeg"
"log"
"os"

Expand All @@ -13,7 +16,13 @@ import (

func main() {
devName := "/dev/video0"
totalFrames := 3
width := 640
height := 480
flag.StringVar(&devName, "d", devName, "device name (path)")
flag.IntVar(&totalFrames, "c", totalFrames, "number of frames to caputure")
flag.IntVar(&width, "w", width, "picture width")
flag.IntVar(&height, "h", height, "picture height")
flag.Parse()

// open device
Expand Down Expand Up @@ -59,6 +68,7 @@ func main() {
log.Fatalf("device does not support any of %#v", preferredFmts)
}
log.Printf("Found preferred fmt: %s", fmtDesc)

frameSizes, err := v4l2.GetFormatFrameSizes(device.Fd(), fmtDesc.PixelFormat)
if err != nil {
log.Fatalf("failed to get framesize info: %s", err)
Expand All @@ -67,16 +77,18 @@ func main() {
// select size 640x480 for format
var frmSize v4l2.FrameSizeEnum
for _, size := range frameSizes {
if size.Size.MinWidth == 640 && size.Size.MinHeight == 480 {
if size.Size.MinWidth == uint32(width) && size.Size.MinHeight == uint32(height) {
frmSize = size
break
}
}

if frmSize.Size.MinWidth == 0 {
log.Fatalf("Size 640x480 not supported for fmt: %s", fmtDesc)
log.Fatalf("Size %dx%d not supported for fmt: %s", width, height, fmtDesc)
}

log.Printf("Found preferred size: %#v", frmSize)

// configure device with preferred fmt

if err := device.SetPixFormat(v4l2.PixFormat{
Expand All @@ -101,28 +113,49 @@ func main() {
}

// process frames from capture channel
totalFrames := 10
count := 0
log.Printf("Capturing %d frames at %d fps...", totalFrames, fps)
log.Printf("Capturing %d frames (buffers: %d, %d fps)...", totalFrames, device.BufferCount(), fps)
for frame := range device.GetOutput() {
if count >= totalFrames {
break
}
count++

if len(frame) == 0 {
log.Println("received frame size 0")
continue
}

log.Printf("captured %d bytes", len(frame))
img, fmtName, err := image.Decode(bytes.NewReader(frame))
if err != nil {
log.Printf("failed to decode jpeg: %s", err)
continue
}
log.Printf("decoded image format: %s", fmtName)

var imgBuf bytes.Buffer
if err := jpeg.Encode(&imgBuf, img, nil); err != nil {
log.Printf("failed to encode jpeg: %s", err)
continue
}

fileName := fmt.Sprintf("capture_%d.jpg", count)
file, err := os.Create(fileName)
if err != nil {
log.Printf("failed to create file %s: %s", fileName, err)
continue
}

if _, err := file.Write(frame); err != nil {
log.Printf("failed to write file %s: %s", fileName, err)
file.Close()
continue
}
log.Printf("Saved file: %s", fileName)
if err := file.Close(); err != nil {
log.Printf("failed to close file %s: %s", fileName, err)
}
count++
if count >= totalFrames {
break
}
}

cancel() // stop capture
Expand Down
2 changes: 1 addition & 1 deletion examples/ccapture/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# V4L2 video capture example in C

This an example in C showing a minimally required steps to capture video using V4L2. This is can be used to run tests on devices and compare results with the Go4VL code.
This an example in C showing the minimally required steps to capture video using the V4L2 framework. This can be used as a test tool to compare results between C and the Go4VL Go code.

## Build and run
On a Linux machine, run the following:
Expand Down
9 changes: 9 additions & 0 deletions examples/fileserv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# fileserv

A simple file server that can be used to view generated images in a remote environment.

## Run

```
go run fileserv.go ":port"
```
24 changes: 24 additions & 0 deletions examples/fileserv/fileserv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"log"
"net/http"
"os"
)

var (
port = ":5050"
)

func main() {
if len(os.Args) > 2 {
port = os.Args[1]
}

// serve examples dir
log.Printf("serving files on port %s", port)
http.Handle("/", http.FileServer(http.Dir("../")))
if err := http.ListenAndServe(port, nil); err != nil {
log.Fatal(err)
}
}
4 changes: 4 additions & 0 deletions examples/simplecam/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# camserv

This is a simple example shows how easy it is to use go4vl to
create a simple web application to stream camera images.
64 changes: 64 additions & 0 deletions examples/simplecam/simplecam.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"context"
"flag"
"fmt"
"log"
"mime/multipart"
"net/http"
"net/textproto"

"github.com/vladimirvivien/go4vl/device"
"github.com/vladimirvivien/go4vl/v4l2"
)

var (
frames <-chan []byte
)

func imageServ(w http.ResponseWriter, req *http.Request) {
mimeWriter := multipart.NewWriter(w)
w.Header().Set("Content-Type", fmt.Sprintf("multipart/x-mixed-replace; boundary=%s", mimeWriter.Boundary()))
partHeader := make(textproto.MIMEHeader)
partHeader.Add("Content-Type", "image/jpeg")

var frame []byte
for frame = range frames {
partWriter, err := mimeWriter.CreatePart(partHeader)
if err != nil {
log.Printf("failed to create multi-part writer: %s", err)
return
}

if _, err := partWriter.Write(frame); err != nil {
log.Printf("failed to write image: %s", err)
}
}
}

func main() {
port := ":9090"
devName := "/dev/video0"
flag.StringVar(&devName, "d", devName, "device name (path)")
flag.StringVar(&port, "p", port, "webcam service port")

camera, err := device.Open(
devName,
device.WithPixFormat(v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMJPEG, Width: 640, Height: 480}),
)
if err != nil {
log.Fatalf("failed to open device: %s", err)
}
defer camera.Close()

if err := camera.Start(context.TODO()); err != nil {
log.Fatalf("camera start: %s", err)
}

frames = camera.GetOutput()

log.Printf("Serving images: [%s/stream]", port)
http.HandleFunc("/stream", imageServ)
log.Fatal(http.ListenAndServe(port, nil))
}
33 changes: 33 additions & 0 deletions examples/snapshot/snap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"context"
"log"
"os"

"github.com/vladimirvivien/go4vl/device"
)

func main() {
dev, err := device.Open("/dev/video0", device.WithBufferSize(1))
if err != nil {
log.Fatal(err)
}
defer dev.Close()

if err := dev.Start(context.TODO()); err != nil {
log.Fatal(err)
}

frame := <-dev.GetOutput()

file, err := os.Create("pic.jpg")
if err != nil {
log.Fatal(err)
}
defer file.Close()

if _, err := file.Write(frame); err != nil {
log.Fatal(err)
}
}
9 changes: 9 additions & 0 deletions examples/webcam/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#! /bin/bash
# Run the following once to pull correct dependencies
go get github.com/vladimirvivien/go4vl@latest
go get github.com/esimov/pigo/core@latest
go get github.com/fogleman/gg@8febc0f526adecda6f8ae80f3869b7cd77e52984

go mod tidy

go build .
Binary file added examples/webcam/facefinder.model
Binary file not shown.
14 changes: 14 additions & 0 deletions examples/webcam/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/vladimirvivien/go4vl/exampels/webcam

go 1.19

require (
github.com/esimov/pigo v1.4.5
github.com/fogleman/gg v1.3.1-0.20210928143535-8febc0f526ad
)

require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
)
Loading

0 comments on commit 5e2fbd8

Please sign in to comment.