Capture, decrypt, and restream toolkit with compiled-in app modules (RTE, TG4, BBC), on-device MITM proxy, streamd control plane, and www. BBC module.yaml is published (clear streams); other module values stay local.
129 lines
5.3 KiB
Go
129 lines
5.3 KiB
Go
package supervisor
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"drmdecryption/adb"
|
|
"drmdecryption/app"
|
|
"drmdecryption/apps/streamd/internal/db"
|
|
"drmdecryption/capture"
|
|
"drmdecryption/mpd"
|
|
)
|
|
|
|
// fakeApp stands in for a compiled-in app module: the supervisor must take its
|
|
// declared settings without knowing anything about the provider.
|
|
type fakeApp struct {
|
|
name string
|
|
rewriter string
|
|
liveWait int
|
|
tsBytes int64
|
|
}
|
|
|
|
func (f *fakeApp) Name() string { return f.name }
|
|
func (f *fakeApp) Package() string { return "com.example." + f.name }
|
|
func (f *fakeApp) LicenseURL() string { return "" }
|
|
func (f *fakeApp) Launch(*adb.Client) error { return nil }
|
|
func (f *fakeApp) AutoPlay(*adb.Client, string) error { return nil }
|
|
func (f *fakeApp) CaptureHints() capture.Hints { return capture.DefaultHints() }
|
|
func (f *fakeApp) MPDRewriter() mpd.Rewriter { return mpd.Passthrough{} }
|
|
func (f *fakeApp) Channels() []app.Channel { return nil }
|
|
func (f *fakeApp) HasChannel(string) bool { return false }
|
|
func (f *fakeApp) ProxyBin() string { return "proxy" }
|
|
func (f *fakeApp) CAHash() string { return "deadbeef" }
|
|
func (f *fakeApp) KeyMode() string { return "raw" }
|
|
func (f *fakeApp) VideoSelect() string { return "res=1280x720:for=best" }
|
|
func (f *fakeApp) AudioSelect() string { return "lang=en:for=best" }
|
|
func (f *fakeApp) RewriterName() string { return f.rewriter }
|
|
func (f *fakeApp) LiveWaitSeconds() int { return f.liveWait }
|
|
func (f *fakeApp) TSReadyBytes() int64 { return f.tsBytes }
|
|
|
|
func register(t *testing.T, f *fakeApp) {
|
|
t.Helper()
|
|
app.Register(f.name, func() (app.App, error) { return f, nil })
|
|
}
|
|
|
|
func TestSettingsComeFromTheAppModule(t *testing.T) {
|
|
register(t, &fakeApp{name: "fakedash", rewriter: "fakedash", liveWait: 3, tsBytes: 111})
|
|
got := settingsFor(db.Stream{App: "fakedash", MPD: "https://origin.test/manifest.mpd"})
|
|
if got.Rewriter != "fakedash" {
|
|
t.Errorf("rewriter = %q, want fakedash", got.Rewriter)
|
|
}
|
|
if got.VideoSelect != "res=1280x720:for=best" || got.AudioSelect != "lang=en:for=best" {
|
|
t.Errorf("selectors = %q / %q", got.VideoSelect, got.AudioSelect)
|
|
}
|
|
if got.LiveWait != 3 || got.TSReadyBytes != 111 {
|
|
t.Errorf("liveWait=%d tsReadyBytes=%d", got.LiveWait, got.TSReadyBytes)
|
|
}
|
|
if got.IsHLS {
|
|
t.Error("an .mpd manifest is not HLS")
|
|
}
|
|
}
|
|
|
|
// The stored row wins over the module, so an operator override sticks.
|
|
func TestStoredSettingsOverrideTheModule(t *testing.T) {
|
|
register(t, &fakeApp{name: "fakeoverride", rewriter: "fakeoverride"})
|
|
got := settingsFor(db.Stream{
|
|
App: "fakeoverride",
|
|
MPD: "https://origin.test/manifest.mpd",
|
|
VideoSelect: "res=1920x1080:for=best",
|
|
Rewriter: "none",
|
|
})
|
|
if got.VideoSelect != "res=1920x1080:for=best" {
|
|
t.Errorf("video select = %q, want the stored value", got.VideoSelect)
|
|
}
|
|
if got.Rewriter != "none" {
|
|
t.Errorf("rewriter = %q, want the stored none", got.Rewriter)
|
|
}
|
|
}
|
|
|
|
// An unknown app must not inherit any provider's preferences.
|
|
func TestUnknownAppGetsNeutralDefaults(t *testing.T) {
|
|
got := settingsFor(db.Stream{App: "nosuchapp", MPD: "https://origin.test/manifest.mpd"})
|
|
if got.VideoSelect != "for=best" || got.AudioSelect != "for=best" {
|
|
t.Errorf("selectors = %q / %q, want for=best", got.VideoSelect, got.AudioSelect)
|
|
}
|
|
if got.Rewriter != "none" {
|
|
t.Errorf("rewriter = %q, want none", got.Rewriter)
|
|
}
|
|
if got.LiveWait != defaultLiveWait || got.TSReadyBytes != defaultTSReadyBytes {
|
|
t.Errorf("liveWait=%d tsReadyBytes=%d, want neutral defaults", got.LiveWait, got.TSReadyBytes)
|
|
}
|
|
}
|
|
|
|
// HLS gets more buffering and a longer live wait, by manifest shape and not by
|
|
// matching a stream name.
|
|
func TestHLSDetectionByManifestShape(t *testing.T) {
|
|
got := settingsFor(db.Stream{App: "", MPD: "https://cdn.test/x/playlist-hls-dvr.m3u8"})
|
|
if !got.IsHLS {
|
|
t.Fatal("an .m3u8 manifest should be detected as HLS")
|
|
}
|
|
if got.LiveWait != hlsLiveWait {
|
|
t.Errorf("liveWait = %d, want %d", got.LiveWait, hlsLiveWait)
|
|
}
|
|
if got.TSReadyBytes != hlsTSReadyBytes {
|
|
t.Errorf("tsReadyBytes = %d, want %d", got.TSReadyBytes, hlsTSReadyBytes)
|
|
}
|
|
// The old code keyed this off stream names like "tg4"/"cula"/"plus".
|
|
named := settingsFor(db.Stream{Name: "tg4-ioi", MPD: "https://origin.test/manifest.mpd"})
|
|
if named.TSReadyBytes != defaultTSReadyBytes {
|
|
t.Error("buffering must not be chosen by stream name any more")
|
|
}
|
|
}
|
|
|
|
// A module's rewriter is only used if it registered one under that name.
|
|
func TestRewriterLookupGatesTheRewrite(t *testing.T) {
|
|
register(t, &fakeApp{name: "fakeunregistered", rewriter: "fakeunregistered"})
|
|
cfg := settingsFor(db.Stream{App: "fakeunregistered", MPD: "https://origin.test/manifest.mpd"})
|
|
if _, needed := mpd.Lookup(cfg.Rewriter); needed {
|
|
t.Error("an unregistered rewriter name must not trigger a rewrite")
|
|
}
|
|
mpd.Register("fakeunregistered", func() mpd.Rewriter { return stubRewriter{} })
|
|
if _, needed := mpd.Lookup(cfg.Rewriter); !needed {
|
|
t.Error("a registered rewriter must trigger a rewrite")
|
|
}
|
|
}
|
|
|
|
type stubRewriter struct{}
|
|
|
|
func (stubRewriter) Name() string { return "fakeunregistered" }
|
|
func (stubRewriter) Rewrite(in []byte, _ string) ([]byte, error) { return in, nil }
|