Browse Source

Device API refactor; documentation and example updates

Vladimir Vivien 3 years ago
parent
commit
94af4eafb7
5 changed files with 89 additions and 38 deletions
  1. 16 16
      README.md
  2. 9 8
      device/device.go
  3. 64 0
      examples/capture0/capture0.go
  4. 0 0
      examples/capture1/capture.go
  5. 0 14
      v4l2/streaming_loop.go

+ 16 - 16
README.md

@@ -11,47 +11,47 @@ It hides all the complexities of working with V4L2 and provides idiomatic Go typ
 
 ## Features
 * Capture and control video data from your Go programs
-* Idiomatic Go API for device access and video capture
-* Use cgo-generated types for correct data representation in Go
-* Use familiar types such as channels to stream video data
+* Idiomatic Go types such as channels to access and stream video data
 * Exposes device enumeration and information
 * Provides device capture control
 * Access to video format information
-* Streaming support using memory map (other methods coming later)
+* Streaming users zero-copy IO using memory mapped buffers
 
-### Not working/supported yet
-* Inherent support for video output
-* Only support MMap memory stream (user pointers, DMA not working)
-* Device control not implemented yet
-
-## Prerequisites
+## Compilation Requirements
 * Go compiler/tools
-* Linux OS (32- or 64-bit)
 * Kernel minimum v5.10.x
 * A locally configured C compiler (i.e. gcc)
 * Header files for V4L2 (i.e. /usr/include/linux/videodev2.h)
 
-All examples have been tested using a Rasperry PI 3, running 32-bit Raspberry PI OS.
+All examples have been tested using a Raspberry PI 3, running 32-bit Raspberry PI OS.
 The package should work with no problem on your 64-bit Linux OS.
 
 ## Getting started
+
+### System upgrade
+
 To avoid issues with old header files on your machine, upgrade your system to pull down the latest OS packages
-with something similar to the following (follow directions for your system to properly upgrade):
+with something similar to the following (follow directions for your system for proper upgrade):
 
 ```shell
 sudo apt update
 sudo apt full-upgrade
 ```
 
-To include `go4vl` in your own code, pull the package
+### Using the go4vl package
+
+To include `go4vl` in your own code, `go get` the package:
 
 ```bash
 go get github.com/vladimirvivien/go4vl/v4l2
 ```
 
-## Examples
+## Video capture example
+
 The following is a simple example that captures video data from an attached camera device to
-and saves them as JPEG files. The example assumes the attached device supports JPEG (MJPEG) output format inherently.
+and saves the captured frames as JPEG files. 
+
+The example assumes the attached device supports JPEG (MJPEG) output format inherently.
 
 ```go
 package main

+ 9 - 8
device/device.go

@@ -365,13 +365,16 @@ func (d *Device) Start(ctx context.Context)  error {
 }
 
 func (d *Device) Stop() error {
-	d.streaming = false
+	if !d.streaming {
+		return nil
+	}
 	if err := v4l2.UnmapMemoryBuffers(d); err != nil {
-		return fmt.Errorf("device: stop: %s", err)
+		return fmt.Errorf("device: stop: %w", err)
 	}
 	if err := v4l2.StreamOff(d); err != nil {
 		return fmt.Errorf("device: stop: %w", err)
 	}
+	d.streaming = false
 	return nil
 }
 
@@ -395,15 +398,13 @@ func (d *Device) startStreamLoop(ctx context.Context) error {
 				//TODO add better error-handling, for now just panic
 				buff, err  := v4l2.CaptureBuffer(fd, ioMemType, bufType)
 				if err != nil {
-					panic(fmt.Errorf("stream loop: buffer capture: %s", err).Error())
+					panic(fmt.Errorf("stream loop: capture buffer: %s", err).Error())
 				}
 
-				select {
-				case d.output <-d.Buffers()[buff.Index][:buff.BytesUsed]:
-				case <-ctx.Done():
-					return
-				}
+				d.output <-d.Buffers()[buff.Index][:buff.BytesUsed]
+
 			case <-ctx.Done():
+				d.Stop()
 				return
 			}
 		}

+ 64 - 0
examples/capture0/capture0.go

@@ -0,0 +1,64 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/vladimirvivien/go4vl/device"
+	"github.com/vladimirvivien/go4vl/v4l2"
+)
+
+func main() {
+	devName := "/dev/video0"
+	flag.StringVar(&devName, "d", devName, "device name (path)")
+	flag.Parse()
+
+	// open device
+	device, err := device.Open(
+		devName,
+		device.WithPixFormat(v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMPEG, Width: 640, Height: 480}),
+	)
+	if err != nil {
+		log.Fatalf("failed to open device: %s", err)
+	}
+	defer device.Close()
+
+	// start stream
+	ctx, stop := context.WithCancel(context.TODO())
+	if err := device.Start(ctx); err != nil {
+		log.Fatalf("failed to start stream: %s", err)
+	}
+
+	// process frames from capture channel
+	totalFrames := 10
+	count := 0
+	log.Printf("Capturing %d frames...", totalFrames)
+
+	for frame := range device.GetOutput() {
+		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)
+			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
+		}
+	}
+
+	stop() // stop capture	
+	fmt.Println("Done.")
+
+}

+ 0 - 0
examples/capture/capture.go → examples/capture1/capture.go


+ 0 - 14
v4l2/streaming_loop.go

@@ -1,23 +1,9 @@
 package v4l2
 
 import (
-	"fmt"
-
 	sys "golang.org/x/sys/unix"
 )
 
-// StopStreamLoop unmaps allocated IO memory and signal device to stop streaming
-func StopStreamLoop(dev StreamingDevice) error {
-	if dev.Buffers() == nil {
-		return fmt.Errorf("stop loop: failed to stop loop: buffers uninitialized")
-	}
-
-	if err := StreamOff(dev); err != nil {
-		return fmt.Errorf("stop loop: stream off: %w", err)
-	}
-	return nil
-}
-
 func WaitForRead(dev Device) <-chan struct{} {
 	sigChan := make(chan struct{})