Last active
September 5, 2023 06:37
-
-
Save metal3d/d6e295bef424ad18f801bf7f026f0019 to your computer and use it in GitHub Desktop.
Go + Fyne + V4L
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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())) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To make it work (on Linux):