Files
gonic/transcode/transcode_test.go

144 lines
3.4 KiB
Go

package transcode_test
import (
"bytes"
"context"
"io"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.senan.xyz/gonic/transcode"
)
var testProfile = transcode.PCM16le
const (
// assuming above profile is 48kHz 16bit stereo
sampleRate = 48_000
bytesPerSample = 2
numChannels = 2
)
const bytesPerSec = sampleRate * bytesPerSample * numChannels
func TestMain(m *testing.M) {
if _, err := exec.LookPath("ffmpeg"); err != nil {
return // no ffmpeg, skip these tests
}
os.Exit(m.Run())
}
// TestTranscode starts a web server that transcodes a 5s FLAC file to PCM audio. A client
// consumes the result over a 5 second period.
func TestTranscode(t *testing.T) {
t.Parallel()
testFile := "testdata/5s.flac"
testFileLen := 5
tr := transcode.NewFFmpegTranscoder()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.NoError(t, tr.Transcode(r.Context(), testProfile, testFile, w))
w.(http.Flusher).Flush()
}))
defer server.Close()
resp, err := server.Client().Get(server.URL)
require.NoError(t, err)
defer resp.Body.Close()
var buf bytes.Buffer
for {
n, err := io.Copy(&buf, io.LimitReader(resp.Body, bytesPerSec))
require.NoError(t, err)
if n == 0 {
break
}
time.Sleep(1 * time.Second)
}
// we should have 5 seconds of PCM data
require.Equal(t, testFileLen*bytesPerSec, buf.Len())
}
// TestTranscodeWithSeek starts a web server that transcodes a 5s FLAC file to PCM audio, but with a 2 second offset.
// A client consumes the result over a 3 second period.
func TestTranscodeWithSeek(t *testing.T) {
t.Parallel()
testFile := "testdata/5s.flac"
testFileLen := 5
seekSecs := 2
profile := transcode.WithSeek(testProfile, time.Duration(seekSecs)*time.Second)
tr := transcode.NewFFmpegTranscoder()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.NoError(t, tr.Transcode(r.Context(), profile, testFile, w))
w.(http.Flusher).Flush()
}))
defer server.Close()
resp, err := server.Client().Get(server.URL)
require.NoError(t, err)
defer resp.Body.Close()
var buf bytes.Buffer
for {
n, err := io.Copy(&buf, io.LimitReader(resp.Body, bytesPerSec))
require.NoError(t, err)
if n == 0 {
break
}
time.Sleep(1 * time.Second)
}
// since we seeked 2 seconds, we should have 5-2 = 3 seconds of PCM data
require.Equal(t, (testFileLen-seekSecs)*bytesPerSec, buf.Len())
}
func TestCachingParallelism(t *testing.T) {
t.Parallel()
var realTranscodeCount atomic.Uint64
transcoder := callbackTranscoder{
transcoder: transcode.NewFFmpegTranscoder(),
callback: func() { realTranscodeCount.Add(1) },
}
cacheTranscoder := transcode.NewCachingTranscoder(transcoder, t.TempDir())
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
var buf bytes.Buffer
require.NoError(t, cacheTranscoder.Transcode(context.Background(), transcode.PCM16le, "testdata/5s.flac", &buf))
require.Equal(t, 5*bytesPerSec, buf.Len())
}()
}
wg.Wait()
require.Equal(t, 1, int(realTranscodeCount.Load()))
}
type callbackTranscoder struct {
transcoder transcode.Transcoder
callback func()
}
func (ct callbackTranscoder) Transcode(ctx context.Context, profile transcode.Profile, in string, out io.Writer) error {
ct.callback()
return ct.transcoder.Transcode(ctx, profile, in, out)
}