@@ -5,6 +5,7 @@ package ffmpeg
55
66import (
77 "context"
8+ "errors"
89 "fmt"
910 "io"
1011 "os/exec"
@@ -16,7 +17,7 @@ import (
1617
1718// Default, Maximum and Minimum Values for encoder configuration. Change these if your needs differ.
1819//
19- //nolint:gochecknoglobals
20+ //nolint:gochecknoglobals,mnd // these are constants, not variables, but configurable by a consumer.
2021var (
2122 DefaultFrameRate = 5
2223 MinimumFrameRate = 1
@@ -30,17 +31,22 @@ var (
3031 MaximumEncodeCRF = 30
3132 DefaultCaptureTime = 15
3233 MaximumCaptureTime = 1200 // 10 minute max.
33- DefaultCaptureSize = int64 (2500000 ) //nolint:gomnd,nolintlint // 2.5MB default (roughly 5-10 seconds)
34- MaximumCaptureSize = int64 (104857600 ) //nolint:gomnd,nolintlint // 100MB max.
34+ DefaultCaptureSize = int64 (2500000 ) // 2.5MB default (roughly 5-10 seconds)
35+ MaximumCaptureSize = int64 (104857600 ) // 100MB max.
3536 DefaultFFmpegPath = "/usr/local/bin/ffmpeg"
3637 DefaultProfile = "main"
3738 DefaultLevel = "3.0"
3839)
3940
4041// Custom errors that this library outputs. The library also outputs errors created elsewhere.
4142var (
42- ErrInvalidOutput = fmt .Errorf ("output path is not valid" )
43- ErrInvalidInput = fmt .Errorf ("input path is not valid" )
43+ ErrInvalidOutput = errors .New ("output path is not valid" )
44+ ErrInvalidInput = errors .New ("input path is not valid" )
45+ )
46+
47+ const (
48+ bits64 = 64
49+ base10 = 10
4450)
4551
4652// Config defines how ffmpeg shall transcode a stream.
@@ -165,65 +171,13 @@ func (e *Encoder) SetRate(rate string) int {
165171// SetSize sets the maximum transcode file size as a string.
166172// This can also be passed into Get() as an int64.
167173func (e * Encoder ) SetSize (size string ) int64 {
168- e .config .Size , _ = strconv .ParseInt (size , 10 , 64 ) //nolint:gomnd,nolintlint
174+ e .config .Size , _ = strconv .ParseInt (size , base10 , bits64 )
169175
170176 e .fixValues ()
171177
172178 return e .config .Size
173179}
174180
175- // getVideoHandle is a helper function that creates and returns an ffmpeg command.
176- // This is used by higher level function to cobble together an input stream.
177- func (e * Encoder ) getVideoHandle (ctx context.Context , input , output , title string ) (string , * exec.Cmd ) {
178- if title == "" {
179- title = filepath .Base (output )
180- }
181-
182- // the order of these values is important.
183- arg := []string {
184- e .config .FFMPEG ,
185- "-v" , "16" , // log level
186- "-rtsp_transport" , "tcp" ,
187- "-i" , input ,
188- "-f" , "mov" ,
189- "-metadata" , `title="` + title + `"` ,
190- "-y" , "-map" , "0" ,
191- }
192-
193- if e .config .Size > 0 {
194- arg = append (arg , "-fs" , strconv .FormatInt (e .config .Size , 10 )) //nolint:gomnd,nolintlint
195- }
196-
197- if e .config .Time > 0 {
198- arg = append (arg , "-t" , strconv .Itoa (e .config .Time ))
199- }
200-
201- if ! e .config .Copy {
202- arg = append (arg , "-vcodec" , "libx264" ,
203- "-profile:v" , e .config .Prof ,
204- "-level" , e .config .Level ,
205- "-pix_fmt" , "yuv420p" ,
206- "-movflags" , "faststart" ,
207- "-s" , strconv .Itoa (e .config .Width )+ "x" + strconv .Itoa (e .config .Height ),
208- "-preset" , "superfast" ,
209- "-crf" , strconv .Itoa (e .config .CRF ),
210- "-r" , strconv .Itoa (e .config .Rate ),
211- )
212- } else {
213- arg = append (arg , "-c" , "copy" )
214- }
215-
216- if ! e .config .Audio {
217- arg = append (arg , "-an" )
218- } else {
219- arg = append (arg , "-c:a" , "copy" )
220- }
221-
222- arg = append (arg , output ) // save file path goes last.
223-
224- return strings .Join (arg , " " ), exec .CommandContext (ctx , arg [0 ], arg [1 :]... ) //nolint:Gosec
225- }
226-
227181// GetVideo retreives video from an input and returns an io.ReadCloser to consume the output.
228182// Input must be an RTSP URL. Title is encoded into the video as the "movie title."
229183// Returns command used, io.ReadCloser and error or nil.
@@ -259,7 +213,8 @@ func (e *Encoder) GetVideoContext(ctx context.Context, input, title string) (str
259213 return cmdStr , nil , fmt .Errorf ("subcommand failed: %w" , err )
260214 }
261215
262- if err := cmd .Run (); err != nil {
216+ err = cmd .Run ()
217+ if err != nil {
263218 return cmdStr , stdoutpipe , fmt .Errorf ("run failed: %w" , err )
264219 }
265220
@@ -272,7 +227,9 @@ func (e *Encoder) GetVideoContext(ctx context.Context, input, title string) (str
272227// This will automatically create a context with a timeout equal to the time duration requested plus 1 second.
273228// If no time duration is requested the context has no timeout.
274229// If you want to control the context, use SaveVideoContext().
275- func (e * Encoder ) SaveVideo (input , output , title string ) (string , string , error ) {
230+ //
231+ //nolint:nonamedreturns // the names help readability.
232+ func (e * Encoder ) SaveVideo (input , output , title string ) (cmdStr , outputStr string , err error ) {
276233 ctx := context .Background ()
277234
278235 if e .config .Time > 0 {
@@ -289,7 +246,11 @@ func (e *Encoder) SaveVideo(input, output, title string) (string, string, error)
289246// Input must be an RTSP URL and output must be a file path. It will be overwritten.
290247// Returns command used, command output and error or nil.
291248// Use the context to add a timeout value (max run duration) to the ffmpeg command.
292- func (e * Encoder ) SaveVideoContext (ctx context.Context , input , output , title string ) (string , string , error ) {
249+ //
250+ //nolint:nonamedreturns // the names help readability.
251+ func (e * Encoder ) SaveVideoContext (
252+ ctx context.Context , input , output , title string ,
253+ ) (cmdStr , outputStr string , err error ) {
293254 if input == "" {
294255 return "" , "" , ErrInvalidInput
295256 } else if output == "" || output == "-" {
@@ -308,7 +269,7 @@ func (e *Encoder) SaveVideoContext(ctx context.Context, input, output, title str
308269}
309270
310271// fixValues makes sure video request values are sane.
311- func (e * Encoder ) fixValues () { //nolint:cyclop
272+ func (e * Encoder ) fixValues () { //nolint:cyclop // it's a simple switch statement.
312273 switch {
313274 case e .config .Height == 0 :
314275 e .config .Height = DefaultFrameHeight
@@ -358,3 +319,56 @@ func (e *Encoder) fixValues() { //nolint:cyclop
358319 e .config .Size = MaximumCaptureSize
359320 }
360321}
322+
323+ // getVideoHandle is a helper function that creates and returns an ffmpeg command.
324+ // This is used by higher level function to cobble together an input stream.
325+ func (e * Encoder ) getVideoHandle (ctx context.Context , input , output , title string ) (string , * exec.Cmd ) {
326+ if title == "" {
327+ title = filepath .Base (output )
328+ }
329+
330+ // the order of these values is important.
331+ arg := []string {
332+ e .config .FFMPEG ,
333+ "-v" , "16" , // log level
334+ "-rtsp_transport" , "tcp" ,
335+ "-i" , input ,
336+ "-f" , "mov" ,
337+ "-metadata" , `title="` + title + `"` ,
338+ "-y" , "-map" , "0" ,
339+ }
340+
341+ if e .config .Size > 0 {
342+ arg = append (arg , "-fs" , strconv .FormatInt (e .config .Size , base10 ))
343+ }
344+
345+ if e .config .Time > 0 {
346+ arg = append (arg , "-t" , strconv .Itoa (e .config .Time ))
347+ }
348+
349+ if ! e .config .Copy {
350+ arg = append (arg , "-vcodec" , "libx264" ,
351+ "-profile:v" , e .config .Prof ,
352+ "-level" , e .config .Level ,
353+ "-pix_fmt" , "yuv420p" ,
354+ "-movflags" , "faststart" ,
355+ "-s" , strconv .Itoa (e .config .Width )+ "x" + strconv .Itoa (e .config .Height ),
356+ "-preset" , "superfast" ,
357+ "-crf" , strconv .Itoa (e .config .CRF ),
358+ "-r" , strconv .Itoa (e .config .Rate ),
359+ )
360+ } else {
361+ arg = append (arg , "-c" , "copy" )
362+ }
363+
364+ if ! e .config .Audio {
365+ arg = append (arg , "-an" )
366+ } else {
367+ arg = append (arg , "-c:a" , "copy" )
368+ }
369+
370+ arg = append (arg , output ) // save file path goes last.
371+
372+ //nolint:gosec // it's ok, but maybe it's not.
373+ return strings .Join (arg , " " ), exec .CommandContext (ctx , arg [0 ], arg [1 :]... )
374+ }
0 commit comments