Add real ffmpeg-backed transcoding subsystem (bounded concurrency, streaming, HLS segmenting)
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
package transcoding
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func genTestFile(t *testing.T, dir string) string {
|
||||
t.Helper()
|
||||
if _, err := exec.LookPath("ffmpeg"); err != nil {
|
||||
t.Skip("ffmpeg not installed")
|
||||
}
|
||||
out := filepath.Join(dir, "src.mp3")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "ffmpeg", "-f", "lavfi", "-i", "sine=frequency=440:duration=2", "-codec:a", "libmp3lame", out)
|
||||
if combined, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("ffmpeg: %v\n%s", err, combined)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestStreamProducesOutput(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := genTestFile(t, dir)
|
||||
|
||||
m := NewManager("")
|
||||
var buf bytes.Buffer
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
if err := m.Stream(ctx, &buf, src, Options{Format: FormatMP3, MaxBitRateKbps: 64}); err != nil {
|
||||
t.Fatalf("Stream: %v", err)
|
||||
}
|
||||
if buf.Len() == 0 {
|
||||
t.Fatal("transcoded output is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamRespectsContextCancellation(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := genTestFile(t, dir)
|
||||
|
||||
m := NewManager("")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // already cancelled before starting
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := m.Stream(ctx, &buf, src, Options{Format: FormatMP3})
|
||||
if err == nil {
|
||||
t.Fatal("expected an error when the context is already cancelled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFormat(t *testing.T) {
|
||||
cases := map[string]Format{
|
||||
"mp3": FormatMP3,
|
||||
"opus": FormatOpus,
|
||||
"aac": FormatAAC,
|
||||
"unknown": FormatMP3,
|
||||
"": FormatMP3,
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := ParseFormat(in); got != want {
|
||||
t.Errorf("ParseFormat(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildHLS(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := genTestFile(t, dir)
|
||||
|
||||
m := NewManager("")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
session, err := m.BuildHLS(ctx, src, filepath.Join(dir, "cache"), Options{Format: FormatAAC, MaxBitRateKbps: 96})
|
||||
if err != nil {
|
||||
t.Fatalf("BuildHLS: %v", err)
|
||||
}
|
||||
if session.SegmentCount == 0 {
|
||||
t.Fatal("expected at least one HLS segment")
|
||||
}
|
||||
playlist := session.Playlist(func(i int) string { return "seg" + string(rune('0'+i)) })
|
||||
if playlist == "" {
|
||||
t.Fatal("empty playlist")
|
||||
}
|
||||
if session.SegmentPath(0) == "" {
|
||||
t.Fatal("SegmentPath(0) should resolve")
|
||||
}
|
||||
if session.SegmentPath(9999) != "" {
|
||||
t.Fatal("SegmentPath out of range should return empty")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user