Initial commit: Null DRM Official

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.
This commit is contained in:
404errordeveloper 2026-10-06 00:25:35 +02:00
commit 2fa8f2435f
121 changed files with 17802 additions and 0 deletions

280
apps/pkg/mpd/synthetic.go Normal file
View file

@ -0,0 +1,280 @@
package mpd
import (
"fmt"
"strings"
"sync"
"time"
)
// SyntheticConfig describes a live encoder's DASH grid well enough to publish a
// synthetic manifest for it. Every value a provider could differ on lives here;
// nothing in this package names a provider.
type SyntheticConfig struct {
// Timescales.
MSSTimescale int64 `yaml:"mss_timescale"`
VideoTimescale int64 `yaml:"video_timescale"`
AudioTimescale int64 `yaml:"audio_timescale"`
// Smooth Streaming stream names used to learn the segment grid.
ManifestPath string `yaml:"manifest_path"`
VideoName string `yaml:"video_name"`
AudioName string `yaml:"audio_name"`
// Segment path appended to the origin base for DASH segments.
SegmentPathSuffix string `yaml:"segment_path_suffix"`
// Representation attributes.
VideoBandwidth int `yaml:"video_bandwidth"`
AudioBandwidth int `yaml:"audio_bandwidth"`
VideoCodecs string `yaml:"video_codecs"`
AudioCodecs string `yaml:"audio_codecs"`
Width int `yaml:"width"`
Height int `yaml:"height"`
AudioSamplingRate int `yaml:"audio_sampling_rate"`
AudioChannels int `yaml:"audio_channels"`
Lang string `yaml:"lang"`
// Segment naming. "{channel}" is substituted; empty means derive as
// <channel>-<stream name>=<bandwidth>[-$Time$].<ext>.
VideoInitTemplate string `yaml:"video_init_template"`
VideoMediaTemplate string `yaml:"video_media_template"`
AudioInitTemplate string `yaml:"audio_init_template"`
AudioMediaTemplate string `yaml:"audio_media_template"`
SegmentExt string `yaml:"segment_ext"`
// Timeline shape.
LiveEdgeOffset float64 `yaml:"live_edge_offset_s"`
WindowSegments int64 `yaml:"window_segments"`
FallbackSegmentDuration int64 `yaml:"fallback_segment_duration"`
// MPD-level durations, as ISO-8601 strings.
MinUpdatePeriod string `yaml:"min_update_period"`
MinBufferTime string `yaml:"min_buffer_time"`
TimeShiftBufferDepth string `yaml:"time_shift_buffer_depth"`
MaxSegmentDuration string `yaml:"max_segment_duration"`
}
// WithDefaults fills anything left zero with the common Smooth-to-DASH values.
// These are container mechanics, not provider identity, so defaulting is safe.
func (c SyntheticConfig) WithDefaults() SyntheticConfig {
if c.MSSTimescale == 0 {
c.MSSTimescale = DefaultMSSTimescale
}
if c.VideoTimescale == 0 {
c.VideoTimescale = 600
}
if c.AudioTimescale == 0 {
c.AudioTimescale = 48_000
}
if c.ManifestPath == "" {
c.ManifestPath = "Manifest"
}
if c.VideoName == "" {
c.VideoName = "video"
}
if c.AudioName == "" {
c.AudioName = "audio_128k"
}
if c.SegmentPathSuffix == "" {
c.SegmentPathSuffix = "dash/"
}
if c.VideoBandwidth == 0 {
c.VideoBandwidth = 6_000_000
}
if c.AudioBandwidth == 0 {
c.AudioBandwidth = 128_000
}
if c.VideoCodecs == "" {
c.VideoCodecs = "avc1.640028"
}
if c.AudioCodecs == "" {
c.AudioCodecs = "mp4a.40.2"
}
if c.Width == 0 {
c.Width = 1920
}
if c.Height == 0 {
c.Height = 1080
}
if c.AudioSamplingRate == 0 {
c.AudioSamplingRate = 48_000
}
if c.AudioChannels == 0 {
c.AudioChannels = 2
}
if c.Lang == "" {
c.Lang = "en"
}
if c.SegmentExt == "" {
c.SegmentExt = "dash"
}
if c.LiveEdgeOffset == 0 {
c.LiveEdgeOffset = 12
}
if c.WindowSegments == 0 {
c.WindowSegments = 6
}
if c.FallbackSegmentDuration == 0 {
c.FallbackSegmentDuration = 38_400_000
}
if c.MinUpdatePeriod == "" {
c.MinUpdatePeriod = "PT2S"
}
if c.MinBufferTime == "" {
c.MinBufferTime = "PT8S"
}
if c.TimeShiftBufferDepth == "" {
c.TimeShiftBufferDepth = "PT1M"
}
if c.MaxSegmentDuration == "" {
c.MaxSegmentDuration = "PT4S"
}
return c
}
// GridSpec is the LearnGrid view of this config, exported so a provider module can
// learn the same encoder grid the builder uses.
func (c SyntheticConfig) GridSpec() GridSpec {
return GridSpec{
ManifestPath: c.ManifestPath,
VideoName: c.VideoName,
AudioName: c.AudioName,
VideoTimescale: c.VideoTimescale,
AudioTimescale: c.AudioTimescale,
MSSTimescale: c.MSSTimescale,
FallbackSegmentDuration: c.FallbackSegmentDuration,
}
}
func (c SyntheticConfig) segName(tmpl, channel, stream string, bandwidth int, withTime bool) string {
if tmpl != "" {
return strings.ReplaceAll(tmpl, "{channel}", channel)
}
if withTime {
return fmt.Sprintf("%s-%s=%d-$Time$.%s", channel, stream, bandwidth, c.SegmentExt)
}
return fmt.Sprintf("%s-%s=%d.%s", channel, stream, bandwidth, c.SegmentExt)
}
type anchor struct {
V, A int64
}
var (
anchorMu sync.Mutex
anchors = map[string]*anchor{}
)
const syntheticTemplate = `<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:cenc="urn:mpeg:cenc:2013"
profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic"
availabilityStartTime="1970-01-01T00:00:00Z" publishTime="%s"
minimumUpdatePeriod="%s" minBufferTime="%s" timeShiftBufferDepth="%s"
maxSegmentDuration="%s">
<BaseURL>%s</BaseURL>
<Period id="1" start="PT0S">
<AdaptationSet id="1" contentType="video" mimeType="video/mp4" lang="%s">
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="%s"/>
<Representation id="r0" bandwidth="%d" codecs="%s" width="%d" height="%d">
<SegmentTemplate timescale="%d" initialization="%s" media="%s" startNumber="1">
<SegmentTimeline><S t="%d" d="%d" r="%d"/></SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
<AdaptationSet id="2" contentType="audio" mimeType="audio/mp4" lang="%s">
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="%s"/>
<Representation id="r1" bandwidth="%d" codecs="%s" audioSamplingRate="%d">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="%d"/>
<SegmentTemplate timescale="%d" initialization="%s" media="%s" startNumber="1">
<SegmentTimeline><S t="%d" d="%d" r="%d"/></SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>
`
// BuildSynthetic publishes a dynamic MPD on the encoder's own segment grid, so a
// downloader keeps fetching live segments even once the upstream (ad-stitched)
// manifest session has expired.
func BuildSynthetic(cfg SyntheticConfig, channel, originBase, kid string) ([]byte, error) {
cfg = cfg.WithDefaults()
if !strings.HasSuffix(originBase, "/") {
originBase += "/"
}
segBase := originBase + cfg.SegmentPathSuffix
kidU, err := KIDToUUID(kid)
if err != nil {
return nil, err
}
grid, err := LearnGrid(channel, originBase, cfg.GridSpec())
if err != nil {
return nil, err
}
// Float seconds so sub-second phase stays close to the encoder.
now := float64(time.Now().UTC().UnixNano())/1e9 - cfg.LiveEdgeOffset
align := func(ts, dur, mod int64) int64 {
raw := int64(now * float64(ts))
return raw - (raw-mod)%dur
}
vt := align(cfg.VideoTimescale, grid.VDur, grid.VMod)
at := align(cfg.AudioTimescale, grid.ADur, grid.AMod)
maxCount := cfg.WindowSegments
anchorMu.Lock()
a := anchors[channel]
if a == nil {
a = &anchor{
V: vt - (maxCount-1)*grid.VDur,
A: at - (maxCount-1)*grid.ADur,
}
anchors[channel] = a
}
vStart, aStart := a.V, a.A
if vt > vStart+(maxCount-1)*grid.VDur {
vStart = vt - (maxCount-1)*grid.VDur
aStart = at - (maxCount-1)*grid.ADur
a.V, a.A = vStart, aStart
} else if vt < vStart {
vStart = vt - (maxCount-1)*grid.VDur
aStart = at - (maxCount-1)*grid.ADur
a.V, a.A = vStart, aStart
}
anchorMu.Unlock()
vCount := (vt-vStart)/grid.VDur + 1
aCount := (at-aStart)/grid.ADur + 1
if vCount < 1 {
vCount = 1
}
if aCount < 1 {
aCount = 1
}
if vCount > maxCount {
vStart = vt - (maxCount-1)*grid.VDur
aStart = at - (maxCount-1)*grid.ADur
vCount, aCount = maxCount, maxCount
anchorMu.Lock()
anchors[channel] = &anchor{V: vStart, A: aStart}
anchorMu.Unlock()
}
published := time.Now().UTC().Format("2006-01-02T15:04:05.000000Z")
vInit := cfg.segName(cfg.VideoInitTemplate, channel, cfg.VideoName, cfg.VideoBandwidth, false)
vMedia := cfg.segName(cfg.VideoMediaTemplate, channel, cfg.VideoName, cfg.VideoBandwidth, true)
aInit := cfg.segName(cfg.AudioInitTemplate, channel, cfg.AudioName, cfg.AudioBandwidth, false)
aMedia := cfg.segName(cfg.AudioMediaTemplate, channel, cfg.AudioName, cfg.AudioBandwidth, true)
out := fmt.Sprintf(syntheticTemplate,
published, cfg.MinUpdatePeriod, cfg.MinBufferTime, cfg.TimeShiftBufferDepth, cfg.MaxSegmentDuration,
XMLEscape(segBase),
cfg.Lang, kidU,
cfg.VideoBandwidth, cfg.VideoCodecs, cfg.Width, cfg.Height,
cfg.VideoTimescale, vInit, vMedia, vStart, grid.VDur, vCount-1,
cfg.Lang, kidU,
cfg.AudioBandwidth, cfg.AudioCodecs, cfg.AudioSamplingRate, cfg.AudioChannels,
cfg.AudioTimescale, aInit, aMedia, aStart, grid.ADur, aCount-1,
)
return []byte(out), nil
}