Skip to content

Commit

Permalink
Merge pull request #12 from golift/dn2_context
Browse files Browse the repository at this point in the history
Add context inputs for exec.Cmd
  • Loading branch information
davidnewhall authored Jun 22, 2023
2 parents 04e248a + b15d8d5 commit 7c573f3
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 22 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

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

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
63 changes: 50 additions & 13 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
package ffmpeg

import (
"bytes"
"context"
"fmt"
"io"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
)

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

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

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

return strings.Join(arg, " "), exec.Command(arg[0], arg[1:]...) //nolint:Gosec
return strings.Join(arg, " "), exec.CommandContext(ctx, arg[0], arg[1:]...) //nolint:Gosec
}

// GetVideo retreives video from an input and returns an io.ReadCloser to consume the output.
// Input must be an RTSP URL. Title is encoded into the video as the "movie title."
// Returns command used, io.ReadCloser and error or nil.
// This will automatically create a context with a timeout equal to the time duration requested plus 1 second.
// If no time duration is requested the context has no timeout.
// If you want to control the context, use GetVideoContext().
func (e *Encoder) GetVideo(input, title string) (string, io.ReadCloser, error) {
ctx := context.Background()

if e.config.Time > 0 {
var cancel func()

ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(e.config.Time+1))
defer cancel()
}

return e.GetVideoContext(ctx, input, title)
}

// GetVideoContext retreives video from an input and returns an io.ReadCloser to consume the output.
// Input must be an RTSP URL. Title is encoded into the video as the "movie title."
// Returns command used, io.ReadCloser and error or nil.
// Use the context to add a timeout value (max run duration) to the ffmpeg command.
func (e *Encoder) GetVideoContext(ctx context.Context, input, title string) (string, io.ReadCloser, error) {
if input == "" {
return "", nil, ErrInvalidInput
}

cmdStr, cmd := e.getVideoHandle(input, "-", title)
cmdStr, cmd := e.getVideoHandle(ctx, input, "-", title)

stdoutpipe, err := cmd.StdoutPipe()
if err != nil {
Expand All @@ -248,26 +269,42 @@ func (e *Encoder) GetVideo(input, title string) (string, io.ReadCloser, error) {
// SaveVideo saves a video snippet to a file.
// Input must be an RTSP URL and output must be a file path. It will be overwritten.
// Returns command used, command output and error or nil.
// This will automatically create a context with a timeout equal to the time duration requested plus 1 second.
// If no time duration is requested the context has no timeout.
// If you want to control the context, use SaveVideoContext().
func (e *Encoder) SaveVideo(input, output, title string) (string, string, error) {
ctx := context.Background()

if e.config.Time > 0 {
var cancel func()

ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(e.config.Time+1))
defer cancel()
}

return e.SaveVideoContext(ctx, input, output, title)
}

// SaveVideoContext saves a video snippet to a file using a provided context.
// Input must be an RTSP URL and output must be a file path. It will be overwritten.
// Returns command used, command output and error or nil.
// Use the context to add a timeout value (max run duration) to the ffmpeg command.
func (e *Encoder) SaveVideoContext(ctx context.Context, input, output, title string) (string, string, error) {
if input == "" {
return "", "", ErrInvalidInput
} else if output == "" || output == "-" {
return "", "", ErrInvalidOutput
}

cmdStr, cmd := e.getVideoHandle(input, output, title)
cmdStr, cmd := e.getVideoHandle(ctx, input, output, title)
// log.Println(cmdStr) // DEBUG

var out bytes.Buffer
cmd.Stderr, cmd.Stdout = &out, &out

if err := cmd.Start(); err != nil {
return cmdStr, strings.TrimSpace(out.String()), fmt.Errorf("subcommand failed: %w", err)
} else if err := cmd.Wait(); err != nil {
return cmdStr, strings.TrimSpace(out.String()), fmt.Errorf("subcommand failed: %w", err)
out, err := cmd.CombinedOutput()
if err != nil {
return cmdStr, string(out), fmt.Errorf("subcommand failed: %w", err)
}

return cmdStr, strings.TrimSpace(out.String()), nil
return cmdStr, string(out), nil
}

// fixValues makes sure video request values are sane.
Expand Down
2 changes: 1 addition & 1 deletion encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TestSaveVideo(t *testing.T) {
assert.True(strings.HasPrefix(cmd, "echo"), "The command does not - but should - begin with the Encoder value.")
assert.True(strings.HasSuffix(cmd, fileTemp),
"The command does not - but should - end with a dash to indicate output to stdout.")
assert.EqualValues(cmd, "echo "+out, "Somehow the wrong value was written")
assert.EqualValues(cmd, "echo "+strings.TrimSpace(out), "Somehow the wrong value was written")

// Make sure audio can be turned on.
encode = Get(&Config{FFMPEG: "echo", Audio: true})
Expand Down
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
module golift.io/ffmpeg

go 1.15
go 1.19

require github.com/stretchr/testify v1.8.4

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
7 changes: 1 addition & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -7,20 +6,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit 7c573f3

Please sign in to comment.