Skip to content

Instantly share code, notes, and snippets.

@metal3d
Last active September 5, 2023 06:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save metal3d/d6e295bef424ad18f801bf7f026f0019 to your computer and use it in GitHub Desktop.
Save metal3d/d6e295bef424ad18f801bf7f026f0019 to your computer and use it in GitHub Desktop.
Go + Fyne + V4L
package main
import (
"bytes"
"context"
"image"
"log"
"sync"
_ "image/jpeg"
_ "image/png"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"github.com/vladimirvivien/go4vl/device"
)
const (
bufferSize = 4
maxWidth = 960
)
func main() {
cam := NewCamView(nil)
dev, err := device.Open("/dev/video0", device.WithBufferSize(bufferSize))
if err != nil {
log.Fatal(err)
}
defer dev.Close()
dev.SetFrameRate(30)
if err := dev.Start(context.TODO()); err != nil {
log.Fatal(err)
}
go func() {
buff := &bytes.Buffer{}
for frame := range dev.GetOutput() {
buff.Reset()
buff.Write(frame)
im, _, err := image.Decode(buff)
if err != nil {
return
}
if im == nil {
return
}
cam.SetImage(im)
}
}()
app := app.New()
window := app.NewWindow("Cam")
window.SetContent(container.NewVBox(
cam,
))
window.Resize(fyne.NewSize(1024, 768))
window.ShowAndRun()
}
// Ensure CamView implements fyne.Widget
var _ fyne.Widget = (*CamView)(nil)
// CamView is a widget that displays an image.
type CamView struct {
widget.BaseWidget
locker sync.Mutex
img *canvas.Image
minSize fyne.Size
}
// NewCamView creates a new CamView widget. It implements fyne.Widget.
func NewCamView(im image.Image) *CamView {
c := &CamView{
locker: sync.Mutex{},
img: canvas.NewImageFromImage(im),
}
c.SetImage(im)
c.ExtendBaseWidget(c)
return c
}
// SetImage sets the image to display.
func (c *CamView) SetImage(im image.Image) {
if im == nil {
return
}
c.locker.Lock()
defer c.Refresh()
defer c.locker.Unlock()
c.img.Image = im
// simply set the minSize and ensure it is not too large for the screen (limiting to 800px width)
size := fyne.NewSize(float32(im.Bounds().Dx()), float32(im.Bounds().Dy()))
if size.Width > maxWidth {
ratio := size.Width / maxWidth
size.Width = maxWidth
size.Height /= ratio
}
c.img.SetMinSize(size)
}
// Refresh refreshes the widget (the image).
func (c *CamView) Refresh() {
c.locker.Lock()
defer c.locker.Unlock()
c.img.Refresh()
}
// GetImage returns the image used by this widget.
func (c *CamView) GetImage() *canvas.Image {
c.locker.Lock()
defer c.locker.Unlock()
return c.img
}
// CreateRenderer is a private method to Fyne which links this widget to its renderer.
//
// implement fyne.Widget
func (c *CamView) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(container.NewCenter(c.GetImage()))
}
@metal3d
Copy link
Author

metal3d commented Sep 4, 2023

To make it work (on Linux):

cd /tmp
git clone https://gist.github.com/d6e295bef424ad18f801bf7f026f0019.git camview
cd camview
go mod init camview
go mod tidy
go run -v .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment