A file's CONTENT can tell ffmpeg to open other paths or URLs (HLS, concat playlists). ffmpeg 7.0.2 refuses the ones tried, but that is version luck and the image's ffmpeg is still to be chosen (#34). Pin protocol_whitelist to file,pipe and format_whitelist to the demuxers real audio uses, before the input. Red: audio.AllowedDemuxers is undefined. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
80 lines
2.7 KiB
Go
80 lines
2.7 KiB
Go
package audio_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"git.d-ma.be/mathias/tapir/internal/adapters/audio"
|
|
)
|
|
|
|
func TestFFmpegToWAV_AsksForSixteenKilohertzMonoPCMAlways(t *testing.T) {
|
|
// Always transcode, even when the input already is 16 kHz mono: a Teams export
|
|
// often is, and the point is to not ASSUME (ADR-033). Records the argv.
|
|
rec := filepath.Join(t.TempDir(), "argv")
|
|
bin := stub(t, `printf '%s\n' "$@" > `+rec+`
|
|
for a; do last="$a"; done
|
|
printf data > "$last"`)
|
|
|
|
err := (&audio.FFmpeg{Bin: bin}).ToWAV(context.Background(), "/spool/in", filepath.Join(t.TempDir(), "out.wav"))
|
|
|
|
require.NoError(t, err)
|
|
raw, err := os.ReadFile(rec)
|
|
require.NoError(t, err)
|
|
argv := strings.Split(strings.TrimSpace(string(raw)), "\n")
|
|
joined := " " + strings.Join(argv, " ") + " "
|
|
for _, want := range []string{" -ac 1 ", " -ar 16000 ", " -c:a pcm_s16le ", " -vn ", " -i /spool/in "} {
|
|
assert.Contains(t, joined, want)
|
|
}
|
|
assert.True(t, strings.HasSuffix(strings.TrimSpace(joined), "out.wav"), "the output path is the last argument")
|
|
}
|
|
|
|
func TestFFmpegToWAV_NonzeroExitIsAnErrorCarryingStderr(t *testing.T) {
|
|
bin := stub(t, `echo "moov atom not found" >&2; exit 1`)
|
|
|
|
err := (&audio.FFmpeg{Bin: bin}).ToWAV(context.Background(), "/in", filepath.Join(t.TempDir(), "o.wav"))
|
|
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "moov atom not found")
|
|
}
|
|
|
|
func TestFFmpegToWAV_ZeroExitWithNoOutputIsAnError(t *testing.T) {
|
|
bin := stub(t, `exit 0`)
|
|
|
|
err := (&audio.FFmpeg{Bin: bin}).ToWAV(context.Background(), "/in", filepath.Join(t.TempDir(), "never-written.wav"))
|
|
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestFFmpegToWAV_MissingBinaryIsAnError(t *testing.T) {
|
|
err := (&audio.FFmpeg{Bin: filepath.Join(t.TempDir(), "no-ffmpeg")}).ToWAV(context.Background(), "/in", "/out.wav")
|
|
|
|
require.Error(t, err)
|
|
assert.False(t, errors.Is(err, context.Canceled))
|
|
}
|
|
|
|
func TestFFmpegToWAV_RestrictsWhatTheFileMayMakeFFmpegOpen(t *testing.T) {
|
|
rec := filepath.Join(t.TempDir(), "argv")
|
|
bin := stub(t, `printf '%s\n' "$@" > `+rec+`
|
|
for a; do last="$a"; done
|
|
printf data > "$last"`)
|
|
|
|
err := (&audio.FFmpeg{Bin: bin}).ToWAV(context.Background(), "/spool/in", filepath.Join(t.TempDir(), "o.wav"))
|
|
|
|
require.NoError(t, err)
|
|
args := argvOf(t, rec)
|
|
pw, fw, in := indexOf(args, "-protocol_whitelist"), indexOf(args, "-format_whitelist"), indexOf(args, "-i")
|
|
require.GreaterOrEqual(t, pw, 0, "argv: %v", args)
|
|
require.GreaterOrEqual(t, fw, 0, "argv: %v", args)
|
|
assert.Equal(t, "file,pipe", args[pw+1])
|
|
assert.Equal(t, audio.AllowedDemuxers, args[fw+1])
|
|
assert.Less(t, pw, in, "input options must come BEFORE -i or ffmpeg applies them to the output")
|
|
assert.Less(t, fw, in)
|
|
}
|