Preserve native broadcast video and add patient shared stream startup

This commit is contained in:
2026-10-25 16:59:56 +01:00
parent bd7395f045
commit 11a34a5195
27 changed files with 708 additions and 139 deletions
@@ -26,7 +26,28 @@ public static class TransportStreamClock
}
private static bool Pes(byte[] data, int start, int p) => p >= 0 && (data[start + 1] & 64) != 0 && p + 14 <= start + 188
&& data[p] == 0 && data[p + 1] == 0 && data[p + 2] == 1 && (data[p + 7] & 128) != 0;
public static byte[] Rebase(byte[] input, long videoStart90k, bool alignAudio = true)
public static (long Decode, long Earliest, long Latest)? ClockBounds(byte[] input)
{
if (input.Length % 188 != 0) return null;
long? decode = null;
var times = new List<long>();
for (var start = 0; start < input.Length; start += 188)
{
var p = Payload(input, start);
if (!Pes(input, start, p)) continue;
var pts = ReadPts(input, p + 9);
times.Add(pts);
var dts = (input[p + 7] & 192) == 192 && p + 19 <= start + 188 ? ReadPts(input, p + 14) : pts;
times.Add(dts);
if (decode == null && input[p + 3] >= 224 && input[p + 3] <= 239) decode = dts;
}
if (decode == null) return null;
long Unwrap(long value) => decode.Value + ((value - decode.Value + (1L << 32)) & ClockMask) - (1L << 32);
long min = decode.Value, max = decode.Value;
foreach (var time in times) { var value = Unwrap(time); min = Math.Min(min, value); max = Math.Max(max, value); }
return (decode.Value, min, max);
}
public static byte[] Rebase(byte[] input, long videoStart90k, bool alignAudio = true, long? offset90k = null)
{
if (input.Length % 188 != 0) throw new InvalidDataException("Unaligned MPEG-TS segment");
long? firstVideo = null, firstAudio = null;
@@ -40,7 +61,7 @@ public static class TransportStreamClock
if (firstVideo != null && firstAudio != null) break;
}
if (firstVideo == null) throw new InvalidDataException("MPEG-TS segment lacks a video presentation timestamp");
var offset = videoStart90k - firstVideo.Value;
var offset = offset90k ?? (videoStart90k - firstVideo.Value);
// Independent AAC encoders and HLS boundaries have different priming
// offsets. Anchor the first audio PES of each segment as well, so a
// switch cannot send an audio timestamp backwards by a few milliseconds.