Sort of working vidplayback
This commit is contained in:
parent
4181d984be
commit
95e7203450
|
@ -18,7 +18,7 @@ func main() {
|
|||
w := a.NewWindow("Video")
|
||||
|
||||
videoWidget = vidplayback.NewVidPlayback()
|
||||
videoWidget.VideoFilename = "network.mp4"
|
||||
videoWidget.VideoFilename = "1 Minute Timer-CH50zuS8DD0.mp4"
|
||||
layout := layouts.NewFloatingControlsLayout()
|
||||
layout.FloatingControlsLocation = layouts.FloatingControlsCenter
|
||||
|
||||
|
|
|
@ -6,9 +6,8 @@ import (
|
|||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
|
@ -17,7 +16,16 @@ import (
|
|||
"github.com/zergon321/reisen"
|
||||
)
|
||||
|
||||
var _ fyne.WidgetRenderer = (*vidPlaybackRenderer)(nil)
|
||||
func staticNoiseImage(w, h int) *image.RGBA {
|
||||
i := image.NewRGBA(image.Rect(0, 0, w, h))
|
||||
for x := 0; x < w; x++ {
|
||||
for y := 0; y < h; y++ {
|
||||
lum := uint8(rand.Float32() * 255)
|
||||
i.Set(x, y, color.RGBA{lum, lum, lum, 255})
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
const (
|
||||
width = 1280
|
||||
|
@ -36,12 +44,16 @@ type player struct {
|
|||
ticker <-chan time.Time
|
||||
errs <-chan error
|
||||
frameBuffer <-chan *image.RGBA
|
||||
audioBuffer <-chan [2]float64
|
||||
last time.Time
|
||||
deltaTime float64
|
||||
fps int
|
||||
paused bool
|
||||
wid *VidPlayback
|
||||
frameCount int64
|
||||
}
|
||||
|
||||
// readVideoAndAudio reads video and audio frames
|
||||
// from the opened media and sends the decoded
|
||||
// data to che channels to be played.
|
||||
func (p *player) readVideoAndAudio(media *reisen.Media) (<-chan *image.RGBA, <-chan [2]float64, chan error, error) {
|
||||
frameBuffer := make(chan *image.RGBA, frameBufferSize)
|
||||
sampleBuffer := make(chan [2]float64, sampleBufferSize)
|
||||
|
@ -55,6 +67,7 @@ func (p *player) readVideoAndAudio(media *reisen.Media) (<-chan *image.RGBA, <-c
|
|||
|
||||
videoStream := media.VideoStreams()[0]
|
||||
err = videoStream.Open()
|
||||
p.frameCount = videoStream.FrameCount()
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
@ -167,7 +180,6 @@ func (p *player) readVideoAndAudio(media *reisen.Media) (<-chan *image.RGBA, <-c
|
|||
// Starts reading samples and frames
|
||||
// of the media file.
|
||||
func (p *player) open(fname string) error {
|
||||
fmt.Printf("Opening %s...\n", fname)
|
||||
// Sprite for drawing video frames.
|
||||
p.pix = image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||
|
||||
|
@ -175,18 +187,28 @@ func (p *player) open(fname string) error {
|
|||
media, err := reisen.NewMedia(fname)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Open error %s\n", err.Error())
|
||||
fmt.Printf("Error %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the FPS for playing
|
||||
// video frames.
|
||||
videoFPS, _ := media.Streams()[0].FrameRate()
|
||||
fmt.Printf("FPS %d\n", videoFPS)
|
||||
fmt.Printf("Detected FPS of %d\n", videoFPS)
|
||||
if videoFPS == 0 {
|
||||
fmt.Printf("Assuming 60fps as FPS was 0.\n")
|
||||
fmt.Println("Setting fps to 60 as no FPS found")
|
||||
videoFPS = 60
|
||||
}
|
||||
if videoFPS > 150 {
|
||||
fmt.Printf("FPS of %d seems way too high, assuming it's *1000, so we'll use %d", videoFPS, videoFPS/1000)
|
||||
videoFPS = videoFPS / 1000
|
||||
}
|
||||
p.fps = videoFPS
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Error %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// SPF for frame ticker.
|
||||
spf := 1.0 / float64(videoFPS)
|
||||
|
@ -199,20 +221,13 @@ func (p *player) open(fname string) error {
|
|||
}
|
||||
|
||||
// Start decoding streams.
|
||||
var sampleSource <-chan [2]float64
|
||||
p.frameBuffer, sampleSource,
|
||||
p.frameBuffer, p.audioBuffer,
|
||||
p.errs, err = p.readVideoAndAudio(media)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Error %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// Start playing audio samples.
|
||||
//speaker.Play(p.streamSamples(sampleSource))
|
||||
// nooope.
|
||||
_ = sampleSource
|
||||
|
||||
p.ticker = time.Tick(frameDuration)
|
||||
|
||||
// Setup metrics.
|
||||
|
@ -220,104 +235,118 @@ func (p *player) open(fname string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *player) update(screen *canvas.Image) error {
|
||||
// Compute dt.
|
||||
p.deltaTime = time.Since(p.last).Seconds()
|
||||
p.last = time.Now()
|
||||
|
||||
// Check for incoming errors.
|
||||
select {
|
||||
case err, ok := <-p.errs:
|
||||
if ok {
|
||||
fmt.Printf("Error %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
// Read video frames and draw them.
|
||||
select {
|
||||
case <-p.ticker:
|
||||
frame, ok := <-p.frameBuffer
|
||||
|
||||
if ok {
|
||||
p.pix.Pix = frame.Pix
|
||||
screen.Refresh()
|
||||
fmt.Print(".")
|
||||
p.wid.Refresh()
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
var _ fyne.WidgetRenderer = (*vidPlaybackRenderer)(nil)
|
||||
|
||||
type VidPlayback struct {
|
||||
widget.BaseWidget
|
||||
UpdateFPS int64
|
||||
VideoFilename string
|
||||
|
||||
play player
|
||||
fpsTimer *time.Ticker
|
||||
videoOpened bool
|
||||
playerStruct player
|
||||
currentFrameID int64
|
||||
}
|
||||
|
||||
func NewVidPlayback() *VidPlayback {
|
||||
v := &VidPlayback{}
|
||||
v.play.wid = v
|
||||
return v
|
||||
w := &VidPlayback{}
|
||||
w.ExtendBaseWidget(w)
|
||||
w.UpdateFPS = 25
|
||||
w.videoOpened = false
|
||||
w.currentFrameID = 0
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func (v *VidPlayback) Resize(s fyne.Size) {
|
||||
v.BaseWidget.Resize(s)
|
||||
func fileIsOpenable(path string) bool {
|
||||
_, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *VidPlayback) CreateRenderer() fyne.WidgetRenderer {
|
||||
return newVidPlaybackRenderer(v)
|
||||
func (w *VidPlayback) Play() {
|
||||
if !w.videoOpened {
|
||||
if fileIsOpenable(w.VideoFilename) {
|
||||
w.playerStruct.open(w.VideoFilename)
|
||||
w.videoOpened = true
|
||||
w.fpsTimer.Stop()
|
||||
spf := 1.0 / float64(w.playerStruct.fps)
|
||||
frameDuration, err := time.
|
||||
ParseDuration(fmt.Sprintf("%fs", spf))
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Error %s\n", err.Error())
|
||||
}
|
||||
w.fpsTimer.Reset(frameDuration)
|
||||
go func() {
|
||||
// constantly empty the audio buffer in case that's what's jamming things up.
|
||||
for {
|
||||
_ = <-w.playerStruct.audioBuffer
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
w.BaseWidget.Refresh()
|
||||
}
|
||||
|
||||
func (v *VidPlayback) Play() {
|
||||
_ = v.play.open(v.VideoFilename)
|
||||
v.play.paused = false
|
||||
func (w *VidPlayback) Resize(s fyne.Size) {
|
||||
w.BaseWidget.Resize(s)
|
||||
}
|
||||
|
||||
func (w *VidPlayback) CreateRenderer() fyne.WidgetRenderer {
|
||||
w.fpsTimer = time.NewTicker(time.Duration(1000/25) * time.Millisecond)
|
||||
go func(v *VidPlayback) {
|
||||
for {
|
||||
_ = <-w.fpsTimer.C
|
||||
v.Refresh()
|
||||
}
|
||||
}(w)
|
||||
return newVidPlaybackRenderer(w)
|
||||
}
|
||||
|
||||
type vidPlaybackRenderer struct {
|
||||
vidPlayback *VidPlayback
|
||||
currentframe *canvas.Image
|
||||
background *canvas.Rectangle
|
||||
currentframe *canvas.Raster
|
||||
}
|
||||
|
||||
func newVidPlaybackRenderer(v *VidPlayback) *vidPlaybackRenderer {
|
||||
renderer := &vidPlaybackRenderer{
|
||||
vidPlayback: v,
|
||||
}
|
||||
i := image.NewRGBA(image.Rect(0, 0, 640, 480))
|
||||
for x := 0; x < 640; x++ {
|
||||
for y := 0; y < 480; y++ {
|
||||
lum := uint8(rand.Float32() * 255)
|
||||
i.Set(x, y, color.RGBA{lum, lum, lum, 255})
|
||||
func (r *vidPlaybackRenderer) actualrenderframe(w, h int) image.Image {
|
||||
var frameImage *image.RGBA
|
||||
if r.vidPlayback.videoOpened {
|
||||
frameImage = <-r.vidPlayback.playerStruct.frameBuffer
|
||||
r.vidPlayback.currentFrameID = r.vidPlayback.currentFrameID + 1
|
||||
fmt.Print(".")
|
||||
if r.vidPlayback.currentFrameID%int64(r.vidPlayback.playerStruct.fps) == 0 {
|
||||
fmt.Println(r.vidPlayback.currentFrameID)
|
||||
}
|
||||
if r.vidPlayback.currentFrameID > r.vidPlayback.playerStruct.frameCount-5 { //definitely stopping before the end
|
||||
r.vidPlayback.videoOpened = false
|
||||
fmt.Println("Almost at end of file!")
|
||||
}
|
||||
} else {
|
||||
frameImage = staticNoiseImage(w, h) //no point doing anything other than returning anyway!
|
||||
}
|
||||
renderer.currentframe = canvas.NewImageFromImage(i)
|
||||
go func() {
|
||||
for {
|
||||
if v.play.paused {
|
||||
time.Sleep(time.Millisecond * 8)
|
||||
continue
|
||||
}
|
||||
return frameImage
|
||||
}
|
||||
|
||||
err := v.play.update(renderer.currentframe)
|
||||
if err != nil {
|
||||
log.Println("Error playing:", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
func newVidPlaybackRenderer(w *VidPlayback) *vidPlaybackRenderer {
|
||||
renderer := &vidPlaybackRenderer{
|
||||
vidPlayback: w,
|
||||
background: canvas.NewRectangle(color.RGBA{255, 0, 255, 255}),
|
||||
}
|
||||
renderer.currentframe = canvas.NewRaster(renderer.actualrenderframe)
|
||||
return renderer
|
||||
}
|
||||
|
||||
func (r *vidPlaybackRenderer) Objects() []fyne.CanvasObject {
|
||||
return []fyne.CanvasObject{r.currentframe}
|
||||
// The order is critical, rect is drawn first then currentframe
|
||||
return []fyne.CanvasObject{r.background, r.currentframe}
|
||||
}
|
||||
|
||||
func (r *vidPlaybackRenderer) Layout(s fyne.Size) {
|
||||
r.background.Resize(s)
|
||||
r.currentframe.Resize(s)
|
||||
}
|
||||
|
||||
|
@ -326,22 +355,9 @@ func (r *vidPlaybackRenderer) MinSize() fyne.Size {
|
|||
}
|
||||
|
||||
func (r *vidPlaybackRenderer) Refresh() {
|
||||
|
||||
b := r.currentframe.Image.Bounds()
|
||||
fmt.Print("*")
|
||||
m := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
|
||||
draw.Draw(m, m.Bounds(), r.currentframe.Image, b.Min, draw.Src)
|
||||
|
||||
for x := 0; x < b.Dx(); x++ {
|
||||
for y := 0; y < b.Dy(); y++ {
|
||||
lum := uint8(rand.Float32() * 255)
|
||||
m.Set(x, y, color.RGBA{lum, lum, lum, 255})
|
||||
}
|
||||
}
|
||||
fmt.Print("+")
|
||||
r.currentframe.Image = m
|
||||
r.currentframe.Refresh()
|
||||
}
|
||||
|
||||
func (r *vidPlaybackRenderer) Destroy() {
|
||||
r.vidPlayback.fpsTimer.Stop()
|
||||
} // Called when the renderer is destroyed
|
||||
|
|
Loading…
Reference in New Issue