Skip to content

Commit 7c573f3

Browse files
authored
Merge pull request #12 from golift/dn2_context
Add context inputs for exec.Cmd
2 parents 04e248a + b15d8d5 commit 7c573f3

5 files changed

Lines changed: 60 additions & 22 deletions

File tree

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2023 Go Lift - Building Strong Go Tools
3+
Copyright (c) 2019-2023 Go Lift - Building Strong Go Tools
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

encode.go

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
package ffmpeg
55

66
import (
7-
"bytes"
7+
"context"
88
"fmt"
99
"io"
1010
"os/exec"
1111
"path/filepath"
1212
"strconv"
1313
"strings"
14+
"time"
1415
)
1516

1617
// Default, Maximum and Minimum Values for encoder configuration. Change these if your needs differ.
@@ -173,7 +174,7 @@ func (e *Encoder) SetSize(size string) int64 {
173174

174175
// getVideoHandle is a helper function that creates and returns an ffmpeg command.
175176
// This is used by higher level function to cobble together an input stream.
176-
func (e *Encoder) getVideoHandle(input, output, title string) (string, *exec.Cmd) {
177+
func (e *Encoder) getVideoHandle(ctx context.Context, input, output, title string) (string, *exec.Cmd) {
177178
if title == "" {
178179
title = filepath.Base(output)
179180
}
@@ -220,18 +221,38 @@ func (e *Encoder) getVideoHandle(input, output, title string) (string, *exec.Cmd
220221

221222
arg = append(arg, output) // save file path goes last.
222223

223-
return strings.Join(arg, " "), exec.Command(arg[0], arg[1:]...) //nolint:Gosec
224+
return strings.Join(arg, " "), exec.CommandContext(ctx, arg[0], arg[1:]...) //nolint:Gosec
224225
}
225226

226227
// GetVideo retreives video from an input and returns an io.ReadCloser to consume the output.
227228
// Input must be an RTSP URL. Title is encoded into the video as the "movie title."
228229
// Returns command used, io.ReadCloser and error or nil.
230+
// This will automatically create a context with a timeout equal to the time duration requested plus 1 second.
231+
// If no time duration is requested the context has no timeout.
232+
// If you want to control the context, use GetVideoContext().
229233
func (e *Encoder) GetVideo(input, title string) (string, io.ReadCloser, error) {
234+
ctx := context.Background()
235+
236+
if e.config.Time > 0 {
237+
var cancel func()
238+
239+
ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(e.config.Time+1))
240+
defer cancel()
241+
}
242+
243+
return e.GetVideoContext(ctx, input, title)
244+
}
245+
246+
// GetVideoContext retreives video from an input and returns an io.ReadCloser to consume the output.
247+
// Input must be an RTSP URL. Title is encoded into the video as the "movie title."
248+
// Returns command used, io.ReadCloser and error or nil.
249+
// Use the context to add a timeout value (max run duration) to the ffmpeg command.
250+
func (e *Encoder) GetVideoContext(ctx context.Context, input, title string) (string, io.ReadCloser, error) {
230251
if input == "" {
231252
return "", nil, ErrInvalidInput
232253
}
233254

234-
cmdStr, cmd := e.getVideoHandle(input, "-", title)
255+
cmdStr, cmd := e.getVideoHandle(ctx, input, "-", title)
235256

236257
stdoutpipe, err := cmd.StdoutPipe()
237258
if err != nil {
@@ -248,26 +269,42 @@ func (e *Encoder) GetVideo(input, title string) (string, io.ReadCloser, error) {
248269
// SaveVideo saves a video snippet to a file.
249270
// Input must be an RTSP URL and output must be a file path. It will be overwritten.
250271
// Returns command used, command output and error or nil.
272+
// This will automatically create a context with a timeout equal to the time duration requested plus 1 second.
273+
// If no time duration is requested the context has no timeout.
274+
// If you want to control the context, use SaveVideoContext().
251275
func (e *Encoder) SaveVideo(input, output, title string) (string, string, error) {
276+
ctx := context.Background()
277+
278+
if e.config.Time > 0 {
279+
var cancel func()
280+
281+
ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(e.config.Time+1))
282+
defer cancel()
283+
}
284+
285+
return e.SaveVideoContext(ctx, input, output, title)
286+
}
287+
288+
// SaveVideoContext saves a video snippet to a file using a provided context.
289+
// Input must be an RTSP URL and output must be a file path. It will be overwritten.
290+
// Returns command used, command output and error or nil.
291+
// 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) {
252293
if input == "" {
253294
return "", "", ErrInvalidInput
254295
} else if output == "" || output == "-" {
255296
return "", "", ErrInvalidOutput
256297
}
257298

258-
cmdStr, cmd := e.getVideoHandle(input, output, title)
299+
cmdStr, cmd := e.getVideoHandle(ctx, input, output, title)
259300
// log.Println(cmdStr) // DEBUG
260301

261-
var out bytes.Buffer
262-
cmd.Stderr, cmd.Stdout = &out, &out
263-
264-
if err := cmd.Start(); err != nil {
265-
return cmdStr, strings.TrimSpace(out.String()), fmt.Errorf("subcommand failed: %w", err)
266-
} else if err := cmd.Wait(); err != nil {
267-
return cmdStr, strings.TrimSpace(out.String()), fmt.Errorf("subcommand failed: %w", err)
302+
out, err := cmd.CombinedOutput()
303+
if err != nil {
304+
return cmdStr, string(out), fmt.Errorf("subcommand failed: %w", err)
268305
}
269306

270-
return cmdStr, strings.TrimSpace(out.String()), nil
307+
return cmdStr, string(out), nil
271308
}
272309

273310
// fixValues makes sure video request values are sane.

encode_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func TestSaveVideo(t *testing.T) {
6868
assert.True(strings.HasPrefix(cmd, "echo"), "The command does not - but should - begin with the Encoder value.")
6969
assert.True(strings.HasSuffix(cmd, fileTemp),
7070
"The command does not - but should - end with a dash to indicate output to stdout.")
71-
assert.EqualValues(cmd, "echo "+out, "Somehow the wrong value was written")
71+
assert.EqualValues(cmd, "echo "+strings.TrimSpace(out), "Somehow the wrong value was written")
7272

7373
// Make sure audio can be turned on.
7474
encode = Get(&Config{FFMPEG: "echo", Audio: true})

go.mod

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
module golift.io/ffmpeg
22

3-
go 1.15
3+
go 1.19
44

55
require github.com/stretchr/testify v1.8.4
6+
7+
require (
8+
github.com/davecgh/go-spew v1.1.1 // indirect
9+
github.com/pmezard/go-difflib v1.0.0 // indirect
10+
gopkg.in/yaml.v3 v3.0.1 // indirect
11+
)

go.sum

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
21
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
32
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
43
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -7,20 +6,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
76
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
87
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
98
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
10-
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
11-
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
129
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
1310
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
14-
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
15-
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
1611
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
1712
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
13+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1814
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
1915
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
2016
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
2117
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
2218
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
23-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
2419
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2520
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2621
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)