-
Notifications
You must be signed in to change notification settings - Fork 0
/
ffmpeg.go
102 lines (84 loc) · 2.09 KB
/
ffmpeg.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package io
import (
"fmt"
"github.com/peter-mount/go-anim/util/time"
"github.com/peter-mount/go-kernel/v2/log"
"image"
"io"
"os"
"os/exec"
"strconv"
)
// FFMPegSession handles sending frames to FFMPeg
type FFMPegSession struct {
RenderStreamBase
encoder FFMPegSessionSource // The image encoder
cmd *exec.Cmd // The ffmpeg command
r *io.PipeReader // stdin to ffmpeg
w *io.PipeWriter // writer to send images to ffmpeg
}
type FFMPegSessionSource interface {
Encoder
EncodeFFMPEG(img image.Image) ([]string, error)
}
func (_ Render) ffmpeg(fileName string, frameRate int, encoder FFMPegSessionSource) RenderStream {
s := &FFMPegSession{
RenderStreamBase: RenderStreamBase{
fileName: fileName,
timeCode: time.NewTimeCode(frameRate),
encoder: encoder,
},
encoder: encoder,
}
s.RenderStreamBase.init = s.init
s.RenderStreamBase.write = s.writeBytes
return s
}
func (s *FFMPegSession) init(img image.Image) error {
if s.cmd != nil {
return nil
}
// Get initial source parameters from the encoder, may be nil if none required
args, err := s.encoder.EncodeFFMPEG(img)
if err != nil {
return err
}
frameRateS := strconv.Itoa(s.TimeCode().FrameRate())
args = append(args,
// Required source parameters
"-y",
"-framerate", frameRateS,
// pipe from stdin
"-i", "-",
// Always provide the start time code
"-timecode", s.TimeCode().StartTimeCode().TimeCode(),
// Now the destination parameters
"-r", frameRateS,
"-c:v", "libx264",
"-pix_fmt", "yuv420p",
// Destination file name
s.fileName,
)
fmt.Println("args", args)
s.cmd = exec.Command("ffmpeg", args...)
s.r, s.w = io.Pipe()
s.cmd.Stdin = s.r
if log.IsVerbose() {
s.cmd.Stdout, s.cmd.Stderr = os.Stdout, os.Stderr
}
fmt.Println("cmd", s.cmd)
return s.cmd.Start()
}
func (s *FFMPegSession) writeBytes(b []byte) (int, error) {
return s.w.Write(b)
}
func (s *FFMPegSession) Close() error {
if s.w == nil {
return nil
}
if err := s.w.Close(); err != nil {
fmt.Println("Error closing ffmpeg stream", err)
_ = s.cmd.Process.Kill()
}
return s.cmd.Wait()
}