using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Jellyfin.Plugin.Enigma2; /// Keep DVB page/region/object versions distinct across independently muxed status and live clips. public sealed class DvbSubtitleVersions { private readonly HashSet pids; private readonly Dictionary<(int Pid, int Type, int Id), int> versions = new(); private readonly Dictionary> plans = new(); public DvbSubtitleVersions(BroadcastTracks tracks) => pids = tracks.Output.Select((s, i) => (s.Codec, Pid: 256 + i)).Where(s => s.Codec == "DVBSUB").Select(s => s.Pid).ToHashSet(); public void Apply(byte[] transport, long sequence) { if (pids.Count == 0) return; if (plans.TryGetValue(sequence, out var known)) { foreach (var edit in known) transport[edit.Position] = edit.Value; return; } var edits = new List<(int, byte)>(); var packets = new Dictionary>(); void Flush(int pid, List positions) { if (positions.Count < 14) return; byte At(int index) => transport[positions[index]]; if (At(0) != 0 || At(1) != 0 || At(2) != 1 || At(3) != 189) return; var length = (At(4) << 8) | At(5); var end = length > 0 ? Math.Min(positions.Count, length + 6) : positions.Count; var p = 9 + At(8); if (p + 2 > end || At(p) != 32) return; p += 2; while (p + 6 <= end && At(p) == 15) { var type = At(p + 1); var size = (At(p + 4) << 8) | At(p + 5); var data = p + 6; if (data + size > end) break; var field = type switch { 16 => 1, 17 => 1, 18 => 1, 19 => 2, 20 => 0, _ => -1 }; if (field >= 0 && size > field) { var id = type switch { 17 or 18 => At(data), 19 => (At(data) << 8) | At(data + 1), _ => 0 }; var key = (pid, (int)type, id); if (!versions.ContainsKey(key) && versions.Count >= 4096) throw new InvalidDataException("Too many DVB subtitle objects"); versions.TryGetValue(key, out var next); versions[key] = next + 1; var index = positions[data + field]; var value = (byte)((transport[index] & 15) | ((next & 15) << 4)); transport[index] = value; edits.Add((index, value)); } p = data + size; } } for (var start = 0; start + 188 <= transport.Length; start += 188) { var pid = ((transport[start + 1] & 31) << 8) | transport[start + 2]; if (!pids.Contains(pid)) continue; if ((transport[start + 3] & 16) == 0) continue; var payload = start + 4 + ((transport[start + 3] & 32) != 0 ? transport[start + 4] + 1 : 0); if (payload >= start + 188) continue; if ((transport[start + 1] & 64) != 0) { if (packets.TryGetValue(pid, out var previous)) Flush(pid, previous); packets[pid] = new(); } if (!packets.TryGetValue(pid, out var positions)) continue; if (positions.Count + 188 > 512 * 1024) throw new InvalidDataException("Oversized DVB subtitle PES"); for (var p = payload; p < start + 188; p++) positions.Add(p); } foreach (var packet in packets) Flush(packet.Key, packet.Value); plans[sequence] = edits; foreach (var old in plans.Keys.Order().Take(Math.Max(0, plans.Count - 8)).ToArray()) plans.Remove(old); } }