From febb1de5a86349b53e849a26e760ad31e3a1223f Mon Sep 17 00:00:00 2001 From: Brad P Date: Tue, 27 May 2025 17:52:04 -0500 Subject: [PATCH 1/8] add duration check for transcoding --- ffmpeg/ffmpeg.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ffmpeg/ffmpeg.go b/ffmpeg/ffmpeg.go index 2ceef33e5d..04c6fb93bf 100755 --- a/ffmpeg/ffmpeg.go +++ b/ffmpeg/ffmpeg.go @@ -33,6 +33,7 @@ var ErrTranscoderHw = errors.New("TranscoderInvalidHardware") var ErrTranscoderInp = errors.New("TranscoderInvalidInput") var ErrTranscoderClipConfig = errors.New("TranscoderInvalidClipConfig") var ErrTranscoderVid = errors.New("TranscoderInvalidVideo") +var ErrTranscoderDuration = errors.New("TranscoderInvalidDuration") var ErrTranscoderStp = errors.New("TranscoderStopped") var ErrTranscoderFmt = errors.New("TranscoderUnrecognizedFormat") var ErrTranscoderPrf = errors.New("TranscoderUnrecognizedProfile") @@ -880,6 +881,10 @@ func (t *Transcoder) Transcode(input *TranscodeOptionsIn, ps []TranscodeOptions) if err != nil { return nil, err } + if format.DurSecs > 300 { + glog.Errorf("Input file %s has duration of %d seconds, which is more than 5 minutes. This is not supported by the transcoder.", input.Fname, format.DurSecs) + return nil, ErrTranscoderDuration + } // TODO hoist the rest of this into C so we don't have to invoke GetCodecInfo if !t.started { // NeedsBypass is state where video is present in container & without any frames From e771d00a88ce3ca270dd4bae5c12f7a7c1f72c89 Mon Sep 17 00:00:00 2001 From: Brad P Date: Tue, 27 May 2025 20:03:57 -0500 Subject: [PATCH 2/8] add test for duration limit --- ffmpeg/ffmpeg_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go index 5501c35478..3860708d72 100644 --- a/ffmpeg/ffmpeg_test.go +++ b/ffmpeg/ffmpeg_test.go @@ -2424,3 +2424,60 @@ func TestTranscoder_PNGDemuxerOpts(t *testing.T) { assert.Equal(t, 3, res.Decoded.Frames) assert.Equal(t, 180, res.Encoded[0].Frames) } + +func TestTranscode_DurationLimit(t *testing.T) { + run, dir := setupTest(t) + defer os.RemoveAll(dir) + cmd := ` + ffmpeg -f lavfi -i color=c=blue:s=1280x720 -r 1 -frames:v 301 -c:v libx264 test-dur-bad.ts + ffmpeg -f lavfi -i color=c=blue:s=1280x720 -r 1 -frames:v 300 -c:v libx264 test-dur-good.ts + ` + run(cmd) + + // Create a transcoder instance + transcoder := NewTranscoder() + defer transcoder.StopTranscoder() + + // Set up transcode options + badInput := &TranscodeOptionsIn{ + Fname: fmt.Sprintf("%v/test-dur-bad.ts", dir), + Accel: Software, + } + + goodInput := &TranscodeOptionsIn{ + Fname: fmt.Sprintf("%v/test-dur-good.ts", dir), + Accel: Software, + } + + profiles := []VideoProfile{ + { + Name: "test_profile", + Resolution: "854x480", + Bitrate: "1000k", + }, + } + + options := []TranscodeOptions{ + { + Oname: fmt.Sprintf("%s/out-test-dur.ts", dir), + Profile: profiles[0], + Accel: Software, + }, + } + + // transcode bad input + _, errBadInput := transcoder.Transcode(badInput, options) + + // Check that the correct error was returned + if errBadInput != ErrTranscoderDuration { + t.Errorf("Expected ErrTranscoderDuration for video longer than 5 minutes, got %v", errBadInput) + } + + // transcode good input + _, errGoodInput := transcoder.Transcode(goodInput, options) + + // Check that the correct error was returned + if errGoodInput != nil { + t.Error(errGoodInput) + } +} From eaa08302be5ac8c6ead96ab809f1d56a0b95078d Mon Sep 17 00:00:00 2001 From: Brad P Date: Tue, 27 May 2025 20:12:10 -0500 Subject: [PATCH 3/8] add test with pipe: input that does not trigger duration limit --- ffmpeg/ffmpeg_test.go | 52 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go index 3860708d72..d832a089f3 100644 --- a/ffmpeg/ffmpeg_test.go +++ b/ffmpeg/ffmpeg_test.go @@ -3,6 +3,7 @@ package ffmpeg import ( "bufio" "fmt" + "io" "io/ioutil" "os" "os/exec" @@ -2481,3 +2482,54 @@ func TestTranscode_DurationLimit(t *testing.T) { t.Error(errGoodInput) } } + +func TestTranscoder_NoDurationLimitBytes(t *testing.T) { + run, dir := setupTest(t) + defer os.RemoveAll(dir) + + cmd := `ffmpeg -f lavfi -i color=c=blue:s=1280x720 -r 1 -frames:v 301 -c:v libx264 test-dur-bad.ts` + run(cmd) + // Create a transcoder instance + transcoder := NewTranscoder() + defer transcoder.StopTranscoder() + + ir, iw, err := os.Pipe() + fname := fmt.Sprintf("%s/test-dur-bad.ts", dir) + _, err = os.Stat(fname) + if err != nil { + t.Fatal(err) + return + } + + go func(iw *os.File) { + defer iw.Close() + f, _ := os.Open(fname) + io.Copy(iw, f) + }(iw) + + badInput := &TranscodeOptionsIn{ + Fname: fmt.Sprintf("pipe:%d", ir.Fd()), + Accel: Software, + } + + profiles := []VideoProfile{ + { + Name: "test_profile", + Resolution: "854x480", + Bitrate: "1000k", + }, + } + + options := []TranscodeOptions{ + { + Oname: fmt.Sprintf("%s/out-test-dur.ts", dir), + Profile: profiles[0], + Accel: Software, + }, + } + + // transcode bad input + _, errBadInput := transcoder.Transcode(badInput, options) + + assert.Nil(t, errBadInput) +} From acc215a3444cf88457e6ab2d1ba3646525b69581 Mon Sep 17 00:00:00 2001 From: Brad | ad-astra <99882368+ad-astra-video@users.noreply.github.com> Date: Wed, 28 May 2025 05:21:09 -0500 Subject: [PATCH 4/8] Update ffmpeg/ffmpeg_test.go Co-authored-by: Josh Allmann --- ffmpeg/ffmpeg_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go index d832a089f3..f07009f65d 100644 --- a/ffmpeg/ffmpeg_test.go +++ b/ffmpeg/ffmpeg_test.go @@ -2470,9 +2470,7 @@ func TestTranscode_DurationLimit(t *testing.T) { _, errBadInput := transcoder.Transcode(badInput, options) // Check that the correct error was returned - if errBadInput != ErrTranscoderDuration { - t.Errorf("Expected ErrTranscoderDuration for video longer than 5 minutes, got %v", errBadInput) - } + assert.Equal(t, ErrTranscoderDuration, errBadInput) // transcode good input _, errGoodInput := transcoder.Transcode(goodInput, options) From e2729f51a939e903a17fe338f3b6c7a3d1e79a57 Mon Sep 17 00:00:00 2001 From: Brad | ad-astra <99882368+ad-astra-video@users.noreply.github.com> Date: Wed, 28 May 2025 05:22:15 -0500 Subject: [PATCH 5/8] Update ffmpeg/ffmpeg_test.go Co-authored-by: Josh Allmann --- ffmpeg/ffmpeg_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go index f07009f65d..c6ccd27d1f 100644 --- a/ffmpeg/ffmpeg_test.go +++ b/ffmpeg/ffmpeg_test.go @@ -2430,8 +2430,10 @@ func TestTranscode_DurationLimit(t *testing.T) { run, dir := setupTest(t) defer os.RemoveAll(dir) cmd := ` - ffmpeg -f lavfi -i color=c=blue:s=1280x720 -r 1 -frames:v 301 -c:v libx264 test-dur-bad.ts - ffmpeg -f lavfi -i color=c=blue:s=1280x720 -r 1 -frames:v 300 -c:v libx264 test-dur-good.ts + # generate a 1fps sample + ffmpeg -i $1../transcoder/test.ts -c copy -bsf:v setts=ts=N/TB_OUT/1 -frames:v 301 -y test.ts + # double check the sample actually has the characteristics we expect + ffprobe -show_format test.ts | grep duration=301.00 ` run(cmd) From c876c42205ceab6328ebc1da42f0b68619c156c1 Mon Sep 17 00:00:00 2001 From: Brad P Date: Wed, 28 May 2025 06:20:02 -0500 Subject: [PATCH 6/8] update tests --- ffmpeg/ffmpeg_test.go | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go index c6ccd27d1f..b309c6d64e 100644 --- a/ffmpeg/ffmpeg_test.go +++ b/ffmpeg/ffmpeg_test.go @@ -2431,7 +2431,7 @@ func TestTranscode_DurationLimit(t *testing.T) { defer os.RemoveAll(dir) cmd := ` # generate a 1fps sample - ffmpeg -i $1../transcoder/test.ts -c copy -bsf:v setts=ts=N/TB_OUT/1 -frames:v 301 -y test.ts + ffmpeg -i "$1"/../transcoder/test.ts -an -c copy -bsf:v setts=ts=N/TB/1 -frames:v 301 -y test.ts # double check the sample actually has the characteristics we expect ffprobe -show_format test.ts | grep duration=301.00 ` @@ -2443,26 +2443,19 @@ func TestTranscode_DurationLimit(t *testing.T) { // Set up transcode options badInput := &TranscodeOptionsIn{ - Fname: fmt.Sprintf("%v/test-dur-bad.ts", dir), + Fname: fmt.Sprintf("%v/test.ts", dir), Accel: Software, } - - goodInput := &TranscodeOptionsIn{ - Fname: fmt.Sprintf("%v/test-dur-good.ts", dir), - Accel: Software, - } - profiles := []VideoProfile{ { Name: "test_profile", - Resolution: "854x480", + Resolution: "16x16", Bitrate: "1000k", }, } - options := []TranscodeOptions{ { - Oname: fmt.Sprintf("%s/out-test-dur.ts", dir), + Oname: fmt.Sprintf("%s/out-test.ts", dir), Profile: profiles[0], Accel: Software, }, @@ -2473,28 +2466,26 @@ func TestTranscode_DurationLimit(t *testing.T) { // Check that the correct error was returned assert.Equal(t, ErrTranscoderDuration, errBadInput) - - // transcode good input - _, errGoodInput := transcoder.Transcode(goodInput, options) - - // Check that the correct error was returned - if errGoodInput != nil { - t.Error(errGoodInput) - } } func TestTranscoder_NoDurationLimitBytes(t *testing.T) { run, dir := setupTest(t) defer os.RemoveAll(dir) - cmd := `ffmpeg -f lavfi -i color=c=blue:s=1280x720 -r 1 -frames:v 301 -c:v libx264 test-dur-bad.ts` + cmd := ` + # generate a 1fps sample + ffmpeg -i "$1"/../transcoder/test.ts -an -c copy -bsf:v setts=ts=N/TB/1 -frames:v 301 -y test.ts + # double check the sample actually has the characteristics we expect + ffprobe -show_format test.ts | grep duration=301.00 + ` run(cmd) + // Create a transcoder instance transcoder := NewTranscoder() defer transcoder.StopTranscoder() ir, iw, err := os.Pipe() - fname := fmt.Sprintf("%s/test-dur-bad.ts", dir) + fname := fmt.Sprintf("%s/test.ts", dir) _, err = os.Stat(fname) if err != nil { t.Fatal(err) @@ -2515,7 +2506,7 @@ func TestTranscoder_NoDurationLimitBytes(t *testing.T) { profiles := []VideoProfile{ { Name: "test_profile", - Resolution: "854x480", + Resolution: "16x16", Bitrate: "1000k", }, } From 02a850889d834dbb5179737335fd6ca02e1175c0 Mon Sep 17 00:00:00 2001 From: Brad P Date: Wed, 28 May 2025 06:22:06 -0500 Subject: [PATCH 7/8] fix test --- ffmpeg/ffmpeg_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go index b309c6d64e..c53529685a 100644 --- a/ffmpeg/ffmpeg_test.go +++ b/ffmpeg/ffmpeg_test.go @@ -2431,7 +2431,7 @@ func TestTranscode_DurationLimit(t *testing.T) { defer os.RemoveAll(dir) cmd := ` # generate a 1fps sample - ffmpeg -i "$1"/../transcoder/test.ts -an -c copy -bsf:v setts=ts=N/TB/1 -frames:v 301 -y test.ts + ffmpeg -i "$1"/../transcoder/test.ts -c copy -bsf:v setts=ts=N/TB/1 -frames:v 301 -y test.ts # double check the sample actually has the characteristics we expect ffprobe -show_format test.ts | grep duration=301.00 ` @@ -2474,7 +2474,7 @@ func TestTranscoder_NoDurationLimitBytes(t *testing.T) { cmd := ` # generate a 1fps sample - ffmpeg -i "$1"/../transcoder/test.ts -an -c copy -bsf:v setts=ts=N/TB/1 -frames:v 301 -y test.ts + ffmpeg -i "$1"/../transcoder/test.ts -c copy -bsf:v setts=ts=N/TB/1 -frames:v 301 -y test.ts # double check the sample actually has the characteristics we expect ffprobe -show_format test.ts | grep duration=301.00 ` From 93a85e7127b31a7202f7fede09c774e037d7bed7 Mon Sep 17 00:00:00 2001 From: Josh Allmann Date: Thu, 29 May 2025 06:11:56 +0000 Subject: [PATCH 8/8] simplify tests --- ffmpeg/ffmpeg_test.go | 83 +++++++------------------------------------ 1 file changed, 13 insertions(+), 70 deletions(-) diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go index c53529685a..923f694b13 100644 --- a/ffmpeg/ffmpeg_test.go +++ b/ffmpeg/ffmpeg_test.go @@ -2437,90 +2437,33 @@ func TestTranscode_DurationLimit(t *testing.T) { ` run(cmd) - // Create a transcoder instance - transcoder := NewTranscoder() - defer transcoder.StopTranscoder() - // Set up transcode options - badInput := &TranscodeOptionsIn{ + fileInput := &TranscodeOptionsIn{ Fname: fmt.Sprintf("%v/test.ts", dir), - Accel: Software, - } - profiles := []VideoProfile{ - { - Name: "test_profile", - Resolution: "16x16", - Bitrate: "1000k", - }, } options := []TranscodeOptions{ { - Oname: fmt.Sprintf("%s/out-test.ts", dir), - Profile: profiles[0], - Accel: Software, + Oname: fmt.Sprintf("%s/out-test.ts", dir), + VideoEncoder: ComponentOptions{Name: "copy"}, + AudioEncoder: ComponentOptions{Name: "copy"}, + Muxer: ComponentOptions{Name: "md5"}, }, } - // transcode bad input - _, errBadInput := transcoder.Transcode(badInput, options) - - // Check that the correct error was returned - assert.Equal(t, ErrTranscoderDuration, errBadInput) -} - -func TestTranscoder_NoDurationLimitBytes(t *testing.T) { - run, dir := setupTest(t) - defer os.RemoveAll(dir) - - cmd := ` - # generate a 1fps sample - ffmpeg -i "$1"/../transcoder/test.ts -c copy -bsf:v setts=ts=N/TB/1 -frames:v 301 -y test.ts - # double check the sample actually has the characteristics we expect - ffprobe -show_format test.ts | grep duration=301.00 - ` - run(cmd) - - // Create a transcoder instance - transcoder := NewTranscoder() - defer transcoder.StopTranscoder() + // transcode long input from file, should error out + _, err := Transcode3(fileInput, options) + assert.Equal(t, ErrTranscoderDuration, err) + // transcode long input from pipe, should *not* error out ir, iw, err := os.Pipe() - fname := fmt.Sprintf("%s/test.ts", dir) - _, err = os.Stat(fname) - if err != nil { - t.Fatal(err) - return - } - go func(iw *os.File) { defer iw.Close() - f, _ := os.Open(fname) + f, _ := os.Open(fileInput.Fname) io.Copy(iw, f) }(iw) - - badInput := &TranscodeOptionsIn{ + pipeInput := &TranscodeOptionsIn{ Fname: fmt.Sprintf("pipe:%d", ir.Fd()), - Accel: Software, - } - - profiles := []VideoProfile{ - { - Name: "test_profile", - Resolution: "16x16", - Bitrate: "1000k", - }, } - - options := []TranscodeOptions{ - { - Oname: fmt.Sprintf("%s/out-test-dur.ts", dir), - Profile: profiles[0], - Accel: Software, - }, - } - - // transcode bad input - _, errBadInput := transcoder.Transcode(badInput, options) - - assert.Nil(t, errBadInput) + _, err = Transcode3(pipeInput, options) + assert.Nil(t, err) }