-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathLICENSE
More file actions
161 lines (143 loc) · 4.43 KB
/
LICENSE
File metadata and controls
161 lines (143 loc) · 4.43 KB
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package gompeg
import (
"context"
"errors"
"fmt"
"io"
"os/exec"
"strings"
)
// Command builds and runs an ffmpeg invocation.
// Use New() or preset helpers (e.g., HLS()).
// All exported methods return the same pointer for fluent chaining.
type Command struct {
ffmpegPath string
// basic config
inputs []string
outputs []string
// flags
videoCodec string
audioCodec string
videoBitrate string
audioBitrate string
preset string
format string
// mux
seek string
vframes int
realTime bool
noVideo bool
noAudio bool
// piping + logs
stdin io.Reader
stdout io.Writer
stderr io.Writer
// extra raw args
extra []string
}
// New returns a fresh Command and checks ffmpeg availability.
func New() *Command {
path, _ := exec.LookPath("ffmpeg") // ignore error; handled on Run()
return &Command{ffmpegPath: path}
}
// convenience presets
func HLS() *Command { return New().Format("hls") }
// chainable setters (selection)
func (c *Command) Input(path string) *Command { c.inputs = append(c.inputs, path); return c }
func (c *Command) Output(path string) *Command { c.outputs = append(c.outputs, path); return c }
func (c *Command) Format(f string) *Command { c.format = f; return c }
func (c *Command) VideoCodec(v string) *Command { c.videoCodec = v; return c }
func (c *Command) AudioCodec(a string) *Command { c.audioCodec = a; return c }
func (c *Command) VideoBitrate(k int) *Command { c.videoBitrate = fmt.Sprintf("%dk", k); return c }
func (c *Command) AudioBitrate(k int) *Command { c.audioBitrate = fmt.Sprintf("%dk", k); return c }
func (c *Command) Preset(p string) *Command { c.preset = p; return c }
func (c *Command) Seek(ts string) *Command { c.seek = ts; return c }
func (c *Command) VFrames(n int) *Command { c.vframes = n; return c }
func (c *Command) RealTime() *Command { c.realTime = true; return c }
func (c *Command) NoVideo() *Command { c.noVideo = true; return c }
func (c *Command) NoAudio() *Command { c.noAudio = true; return c }
func (c *Command) PipeInput(r io.Reader) *Command { c.stdin = r; return c }
func (c *Command) PipeOutput(w io.Writer) *Command { c.stdout = w; return c }
func (c *Command) Logs(w io.Writer) *Command { c.stderr = w; return c }
func (c *Command) Extra(args ...string) *Command { c.extra = append(c.extra, args...); return c }
// BuildArgs converts the struct into ffmpeg CLI arguments.
func (c *Command) BuildArgs() ([]string, error) {
if len(c.inputs) == 0 {
return nil, errors.New("no input specified")
}
if len(c.outputs) == 0 {
return nil, errors.New("no output specified")
}
var args []string
if c.realTime {
args = append(args, "-re")
}
for _, in := range c.inputs {
args = append(args, "-i", in)
}
if c.seek != "" {
args = append(args, "-ss", c.seek)
}
if c.noVideo {
args = append(args, "-vn")
}
if c.noAudio {
args = append(args, "-an")
}
if c.videoCodec != "" {
args = append(args, "-c:v", c.videoCodec)
}
if c.audioCodec != "" {
args = append(args, "-c:a", c.audioCodec)
}
if c.videoBitrate != "" {
args = append(args, "-b:v", c.videoBitrate)
}
if c.audioBitrate != "" {
args = append(args, "-b:a", c.audioBitrate)
}
if c.preset != "" {
args = append(args, "-preset", c.preset)
}
if c.vframes > 0 {
args = append(args, "-frames:v", fmt.Sprint(c.vframes))
}
if c.format != "" {
args = append(args, "-f", c.format)
}
// append extras before outputs
args = append(args, c.extra...)
args = append(args, c.outputs...)
return args, nil
}
// String returns the full command for logging / debugging.
func (c *Command) String() string {
a, err := c.BuildArgs()
if err != nil {
return "<invalid command>"
}
return "ffmpeg " + strings.Join(a, " ")
}
// Run executes the command; use RunWithContext for cancellation.
func (c *Command) Run() error { return c.RunWithContext(context.Background()) }
func (c *Command) RunWithContext(ctx context.Context) error {
if c.ffmpegPath == "" {
return errors.New("ffmpeg binary not found in PATH; please install or SetPath")
}
args, err := c.BuildArgs()
if err != nil {
return err
}
cmd := exec.CommandContext(ctx, c.ffmpegPath, args...)
cmd.Stdin = c.stdin
if c.stdout != nil {
cmd.Stdout = c.stdout
}
if c.stderr != nil {
cmd.Stderr = c.stderr
}
return cmd.Run()
}
// SetPath globally overrides the ffmpeg binary location.
func SetPath(p string) { defaultPath = p }
var defaultPath string