using System; using System.Collections.Generic; using System.IO; using System.Text; namespace ytLive.Services.Encoder; /// /// TASK 65: turns the fragmented-MP4 byte stream ffmpeg writes to stdout into a playable ring. /// /// MP4 boxes are length-prefixed, so this needs no muxer cooperation: ftyp+moov are /// the initialisation segment, and each moof+mdat pair is one fragment. The ring only ever /// sees whole fragments, which is what makes a snapshot playable and a trim safe. /// /// Deliberately forgiving: an unparseable header clears the buffer and waits for the next one. A /// clip that fails must never take the encoder down with it — the stream is running through the same /// process. /// public sealed class Fmp4FragmentSplitter { private readonly ClipRing _ring; private readonly List _pending = new(capacity: 64 * 1024); private readonly MemoryStream _init = new(); private readonly MemoryStream _fragment = new(); private bool _inFragment; public Fmp4FragmentSplitter(ClipRing ring) => _ring = ring; public void Write(ReadOnlySpan data) { foreach (var b in data) _pending.Add(b); Drain(); } private void Drain() { while (true) { if (_pending.Count < 8) return; long size = ((long)_pending[0] << 24) | ((long)_pending[1] << 16) | ((long)_pending[2] << 8) | _pending[3]; var type = Encoding.ASCII.GetString(_pending.GetRange(4, 4).ToArray()); var header = 8; if (size == 1) { if (_pending.Count < 16) return; size = 0; for (var i = 8; i < 16; i++) size = (size << 8) | _pending[i]; header = 16; } if (size < header || size > int.MaxValue) { // Not a box boundary any more; drop what we have and resync on the next write. _pending.Clear(); return; } if (_pending.Count < size) return; var box = _pending.GetRange(0, (int)size).ToArray(); _pending.RemoveRange(0, (int)size); Route(type, box); } } private void Route(string type, byte[] box) { switch (type) { case "ftyp": _init.Write(box); break; case "moov": _init.Write(box); _ring.SetInit(_init.ToArray()); // init is complete once moov lands break; case "moof": FlushFragment(); _fragment.Write(box); _inFragment = true; break; case "mdat": if (!_inFragment) return; // mdat without a moof is not replayable _fragment.Write(box); FlushFragment(); break; default: if (_inFragment) _fragment.Write(box); break; } } private void FlushFragment() { if (_inFragment && _fragment.Length > 0) _ring.Append(_fragment.ToArray()); _fragment.SetLength(0); _inFragment = false; } }