remuxer.go
var ErrStopped = errors.New("process stopped")
ErrStopped is returned by Process when the Stop channel was closed. Every segment processed before the return is durable in the output file; Progress reports where a resume should re-request from.
func Decrypt(data []byte, sample *SencSample, block cipher.Block)
--- Logic ---
func ExtractPsshData(data []byte) ([]byte, error)
ExtractPsshData takes a byte slice that may be raw protobuf, a single PSSH box, or nested PSSH boxes, and returns the innermost data payload.
--- Box ---
type Box struct { Moov *MoovBox Moof *MoofBox Mdat *MdatBox Raw []byte }
func DecodeBoxes(data []byte) ([]Box, error)
--- BoxHeader ---
type BoxHeader struct { Size uint32 Type [4]byte }
func DecodeBoxHeader(data []byte) (*BoxHeader, error)
func (h *BoxHeader) Put(buffer []byte)
--- CO64 ---
type Co64Box struct { Offsets []uint64 }
func DecodeCo64Box(data []byte) (*Co64Box, error)
func (b Co64Box) Encode() []byte
--- CTTS ---
type CttsBox struct { Entries []CttsEntry }
func DecodeCttsBox(data []byte) (*CttsBox, error)
func (b CttsBox) Encode() []byte
type CttsEntry struct { SampleCount uint32 SampleOffset int32 }
--- ENC (Encrypted Sample Entry) ---
type EncBox struct { Header *BoxHeader EntryHeader []byte Sinf *SinfBox RawChildren [][]byte }
func DecodeEncBox(data []byte) (*EncBox, error)
func (b *EncBox) Encode() []byte
--- FRMA ---
type FrmaBox struct { Header *BoxHeader DataFormat [4]byte }
func DecodeFrmaBox(data []byte) (*FrmaBox, error)
func (b *FrmaBox) Encode() []byte
--- MDAT ---
type MdatBox struct { Header *BoxHeader Payload []byte }
func DecodeMdatBox(data []byte) (*MdatBox, error)
--- MDHD ---
type MdhdBox struct { Header *BoxHeader Version byte Flags [3]byte CreationTime uint64 ModificationTime uint64 Timescale uint32 Duration uint64 Language [2]byte Quality [2]byte }
func DecodeMdhdBox(data []byte) (*MdhdBox, error)
func (b *MdhdBox) Encode() []byte
func (b *MdhdBox) SetDuration(duration uint64)
--- MDIA ---
type MdiaBox struct { Header *BoxHeader Mdhd *MdhdBox Minf *MinfBox RawChildren [][]byte }
func DecodeMdiaBox(data []byte) (*MdiaBox, error)
func (b *MdiaBox) Encode() []byte
--- MINF ---
type MinfBox struct { Header *BoxHeader Stbl *StblBox RawChildren [][]byte }
func DecodeMinfBox(data []byte) (*MinfBox, error)
func (b *MinfBox) Encode() []byte
--- MOOF ---
type MoofBox struct { Header *BoxHeader Traf *TrafBox RawChildren [][]byte }
func DecodeMoofBox(data []byte) (*MoofBox, error)
--- MOOV ---
type MoovBox struct { Header *BoxHeader Mvhd *MvhdBox Trak []*TrakBox Pssh []*PsshBox RawChildren [][]byte }
func DecodeMoovBox(data []byte) (*MoovBox, error)
func FindMoov(boxes []Box) (*MoovBox, bool)
FindMoov returns the first moov of a decoded box list.
func (b *MoovBox) Encode() []byte
func (b *MoovBox) FindDefaultKID() []byte
FindDefaultKID walks the first track's sample entries and returns the first non-zero default KID declared by a 'tenc' box, following trak > mdia > minf > stbl > stsd > enc > sinf > schi > tenc. An all-zero KID means no key is declared by that box, so it is skipped. Returns nil if no KID is found.
func (b *MoovBox) FindPssh(systemID []byte) (*PsshBox, bool)
func (b *MoovBox) RemoveMvex()
--- MVHD ---
type MvhdBox struct { Header *BoxHeader Version byte Flags [3]byte CreationTime uint64 ModificationTime uint64 Timescale uint32 Duration uint64 RemainingData []byte }
func DecodeMvhdBox(data []byte) (*MvhdBox, error)
func (b *MvhdBox) Encode() []byte
func (b *MvhdBox) SetDuration(duration uint64)
--- PSSH ---
type PsshBox struct { Header *BoxHeader Version byte Flags [3]byte SystemID [16]byte KIDs [][16]byte Data []byte }
func DecodePsshBox(data []byte) (*PsshBox, error)
type RemuxSample struct { Size uint32 Duration uint32 IsSync bool CompositionTimeOffset int32 }
RemuxState is the remuxer bookkeeping recovered from a stopped file. It is exactly the state the remuxer held when it was stopped, derived from the sample tables in the moov that Finish wrote to the file.
type RemuxState struct { Samples []*RemuxSample ChunkOffsets []uint64 SamplesPerChunk []uint32 }
func StateFromMoov(moovData []byte) (*RemuxState, error)
StateFromMoov rebuilds remuxer state from the moov that Finish wrote to a stopped file. It is a pure decode: the input is only the moov bytes, which the caller reads from the file without touching the media data, so memory use is bounded by the moov size, never the file size.
type Remuxer struct { Writer io.WriteSeeker Moov *MoovBox OnSample func(data []byte, sample *SencSample) // OnMoov, when set, is called by Process when it consumes a moov from // the stream, before any segment is processed. Returning an error // aborts Process. This is where a caller fetches its DRM key: the KID // lives in the moov, and the key must be in hand before any sample is // decrypted. While the callback runs the reader is simply not being // read; the open HTTP connection sits backpressured. OnMoov func(moov *MoovBox) error // Stop, when closed, asks Process to return ErrStopped at the next // segment boundary. A nil Stop means Process never stops early. The // caller owns the channel and closes it; sofia only receives. Stop <-chan struct{} // contains filtered or unexported fields }
func (r *Remuxer) AddSegment(segmentData []byte) error
AddSegment processes one complete, standalone segment: every moof+mdat pair inside it becomes a fragment. Trailing bytes that do not form a complete box are ignored.
func (r *Remuxer) AdoptState(initSegment []byte, state *RemuxState, segmentsDone int) error
AdoptState resumes a remuxer from state recovered out of a stopped file (StateFromMoov). The mdat header written by Initialize sits at offset 0 of the original file, where the payloads still begin, so the chunk offsets in the state remain valid as-is. The caller truncates the old moov away and positions the writer at the payload boundary; AdoptState itself performs no I/O.
func (r *Remuxer) Finish() error
func (r *Remuxer) Initialize(initSegment []byte) error
func (r *Remuxer) Process(reader io.Reader) error
Process streams a continuous MP4 from reader: ftyp, sidx, styp and free boxes are skipped without interpretation, the first moov initializes the remuxer (calling OnMoov before any segment is processed; a moov arriving when the remuxer is already initialized — from an init segment or a resume — is skipped), and each moof+mdat pair becomes a segment processed exactly as AddSegment would process it. One segment at a time is held in memory — the moof and mdat bytes — and released before the next segment is read. The reader is consumed directly with no intermediate buffering: it should be the raw HTTP response body. It returns nil at EOF, ErrStopped when Stop was closed, or the first error. Stop is checked between segments, so a segment whose moof was already read still completes.
func (r *Remuxer) Progress() (inputOffset int64, segmentsDone int)
Progress reports the resume point after Process returns: the number of bytes consumed just past the last complete segment, and the number of segments processed. The offset is relative to the first byte this session's Process call read, so a resumed session adds the offset it started from.
--- SCHI (Scheme Information) ---
type SchiBox struct { Header *BoxHeader Tenc *TencBox RawChildren [][]byte }
func DecodeSchiBox(data []byte) (*SchiBox, error)
func (b *SchiBox) Encode() []byte
--- SENC ---
type SencBox struct { Header *BoxHeader Flags uint32 Samples []SencSample }
func DecodeSencBox(data []byte) (*SencBox, error)
type SencSample struct { IV []byte Subsamples []Subsample }
--- SINF ---
type SinfBox struct { Header *BoxHeader Frma *FrmaBox Schi *SchiBox RawChildren [][]byte }
func DecodeSinfBox(data []byte) (*SinfBox, error)
func (b *SinfBox) Encode() []byte
--- STBL ---
type StblBox struct { Header *BoxHeader Stsd *StsdBox Stts *SttsBox Ctts *CttsBox Stsz *StszBox Stsc *StscBox Stco *StcoBox Co64 *Co64Box Stss *StssBox RawChildren [][]byte }
func DecodeStblBox(data []byte) (*StblBox, error)
func (b *StblBox) Encode() []byte
--- STCO ---
type StcoBox struct { Offsets []uint32 }
func DecodeStcoBox(data []byte) (*StcoBox, error)
func (b StcoBox) Encode() []byte
--- STSC ---
type StscBox struct { Entries []StscEntry }
func DecodeStscBox(data []byte) (*StscBox, error)
func (b StscBox) Encode() []byte
type StscEntry struct { FirstChunk uint32 SamplesPerChunk uint32 SampleDescriptionIndex uint32 }
--- STSD ---
type StsdBox struct { Header *BoxHeader HeaderFields [8]byte // Ver(1)+Flags(3)+EntryCount(4) EncChildren []*EncBox RawChildren [][]byte }
func DecodeStsdBox(data []byte) (*StsdBox, error)
func (b *StsdBox) Encode() []byte
func (b *StsdBox) RemoveSinf() error
--- STSS ---
type StssBox struct { Indices []uint32 }
func DecodeStssBox(data []byte) (*StssBox, error)
func (b StssBox) Encode() []byte
--- STSZ ---
type StszBox struct { SampleSize uint32 SampleCount uint32 EntrySizes []uint32 }
func DecodeStszBox(data []byte) (*StszBox, error)
func (b StszBox) Encode() []byte
--- STTS ---
type SttsBox struct { Entries []SttsEntry }
func DecodeSttsBox(data []byte) (*SttsBox, error)
func (b SttsBox) Encode() []byte
type SttsEntry struct { SampleCount uint32 SampleDuration uint32 }
type Subsample struct { BytesOfClearData uint16 BytesOfProtectedData uint32 }
--- TENC --- TencBox defines the Track Encryption Box ('tenc'), which contains default encryption parameters for a track. Specification: ISO/IEC 23001-7
type TencBox struct { Header *BoxHeader Version byte Flags uint32 DefaultIsProtected byte DefaultPerSampleIVSize byte DefaultKID [16]byte DefaultConstantIVSize byte // Present if DefaultIsProtected=1 and DefaultPerSampleIVSize=0 DefaultConstantIV []byte // Present if DefaultIsProtected=1 and DefaultPerSampleIVSize=0 }
func DecodeTencBox(data []byte) (*TencBox, error)
func (b *TencBox) Encode() []byte
--- TFHD ---
type TfhdBox struct { Header *BoxHeader Flags uint32 TrackID uint32 BaseDataOffset uint64 SampleDescriptionIndex uint32 DefaultSampleDuration uint32 DefaultSampleSize uint32 DefaultSampleFlags uint32 }
func DecodeTfhdBox(data []byte) (*TfhdBox, error)
--- TRAF ---
type TrafBox struct { Header *BoxHeader Tfhd *TfhdBox Trun []*TrunBox Senc *SencBox RawChildren [][]byte }
func DecodeTrafBox(data []byte) (*TrafBox, error)
--- TRAK ---
type TrakBox struct { Header *BoxHeader Mdia *MdiaBox RawChildren [][]byte }
func DecodeTrakBox(data []byte) (*TrakBox, error)
func (b *TrakBox) Encode() []byte
func (b *TrakBox) RemoveEdts()
--- TRUN ---
type TrunBox struct { Header *BoxHeader Flags uint32 SampleCount uint32 DataOffset int32 FirstSampleFlags uint32 Samples []TrunSample }
func DecodeTrunBox(data []byte) (*TrunBox, error)
type TrunSample struct { Size uint32 Duration uint32 Flags uint32 CompositionTimeOffset int32 }