From ca9ed0f11a15e8a5efc4afd652d1e434299b637b Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Mon, 16 Mar 2026 00:23:25 +0100 Subject: [PATCH] Complete Rewrite of the PsiDecoder. --- .../DataBroadcasting/DataCarouselDecoder.cs | 2 +- skyscraper8/Mpeg2/PsiDecoder.cs | 245 +++++++++--------- 2 files changed, 123 insertions(+), 124 deletions(-) diff --git a/skyscraper8/Dvb/DataBroadcasting/DataCarouselDecoder.cs b/skyscraper8/Dvb/DataBroadcasting/DataCarouselDecoder.cs index cb65642..3353d6c 100644 --- a/skyscraper8/Dvb/DataBroadcasting/DataCarouselDecoder.cs +++ b/skyscraper8/Dvb/DataBroadcasting/DataCarouselDecoder.cs @@ -69,7 +69,7 @@ namespace skyscraper5.Dvb.DataBroadcasting //Undocumented, manufacterer specific data. break; default: - logger.Warn(String.Format("Unknown DSM-CC Sectino type: 0x{0:X2}", dataCopy[0])); + //logger.Warn(String.Format("Unknown DSM-CC Section type: 0x{0:X2}", dataCopy[0])); return; } } diff --git a/skyscraper8/Mpeg2/PsiDecoder.cs b/skyscraper8/Mpeg2/PsiDecoder.cs index 7075378..55760cd 100644 --- a/skyscraper8/Mpeg2/PsiDecoder.cs +++ b/skyscraper8/Mpeg2/PsiDecoder.cs @@ -1,159 +1,158 @@ -using System.IO; +using log4net; +using log4net.Repository.Hierarchy; using skyscraper5.Dvb; using skyscraper5.Skyscraper.IO; +using System.IO; namespace skyscraper5.Mpeg2 { public class PsiDecoder : ITsPacketProcessor, IPayloadUnitDecoder { public IPsiProcessor PsiProcessor { get; } + private int pid; + private int state; + private byte headerA, headerB, headerC; + private byte[] psiPayload; + private int psiPayloadNeeded; + private int psiPayloadOffset; + private int copyOps; + private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name); - public PsiDecoder(int pid, IPsiProcessor psiProcessor) + public PsiDecoder(int pid, IPsiProcessor psiProcessor) { PsiProcessor = psiProcessor; this.pid = pid; } - private int pid; - public void PushPacket(TsPacket packet) { - if (packet.GetPayload() == null) - return; - if (packet.PID != pid) - return; + Span span = packet.GetPayload(); + int automataIterations = 0; + if (packet.PID == 0x0881) + ; - bool psiJustStarted = false; - if (psiSection == null) + while(span.Length > 0) { - if (packet.PayloadUnitStart) + switch(state) { - psiSection = new PsiSection(); - psiSection.Need = 3; - psiSection.HeaderComplete = false; - psiJustStarted = true; - } - else - { - return; - } - } - - MemoryStream payload = new MemoryStream(packet.GetPayload().ToArray(), false); - long available = payload.Length - payload.Position; - if (psiJustStarted) - { - payload.Position = 0; - byte payloadUnitStartOffset = payload.ReadUInt8(); - available--; - payload.Position += payloadUnitStartOffset; - available -= payloadUnitStartOffset; - - long startOffset = 0; - if (startOffset > 0) - { - startOffset = payload.Position + startOffset; - if (startOffset > payload.Length) - { - OnPsiSectionTooLong?.Invoke(packet, psiJustStarted, psiSection); - PacketLoss(); - return; - } - payload.Position += startOffset; - available -= startOffset; - } - } - while (available > 0) - { - if (available > psiSection.Need) - { - //Noch genug im Buffer, um need auf 0 zu bekommen. - byte[] temp1 = new byte[psiSection.Need]; - if (payload.Read(temp1, 0, psiSection.Need) != psiSection.Need) - throw new EndOfStreamException("failed to fill psi buffer"); - available -= psiSection.Need; - psiSection.Append(temp1); - - if (!psiSection.HeaderComplete) - { - psiSection.HeaderComplete = true; - ushort sectionLength = psiSection.SectionLength; - psiSection.Need = sectionLength; - } - else - { - if (psiSection.Valid) + case 0: + //Noch keine PSI am zusammenbauen. + if (!packet.PayloadUnitStart) { - lock (this) - { - PsiProcessor.GatherPsi(psiSection, pid); - } - OnValidSection?.Invoke(psiSection, pid, available); + //im TS Paket beginnt keine neue, also wegwerfen. + return; } - else + byte startOffset = span[0]; + span = span.Slice(1); + span = span.Slice(startOffset); + state = 1; + break; + case 1: + //Section Header Byte 1 extrahieren; + if (span.Length == 0) + return; + if (automataIterations == 0) + { + if (packet.PayloadUnitStart) + span = span.Slice(1); + } + if (span[0] == 0xff) { - if (!DvbCrc32.ValidatePsi(psiSection)) - { - OnCrcError?.Invoke(psiSection, pid,available); - } + return; } - - psiSection = null; - - bool newSection = false; - if (available > 0) + headerA = span[0]; + span = span.Slice(1); + state = 2; + break; + case 2: + //Section Header Byte 2 extrahieren; + headerB = span[0]; + span = span.Slice(1); + state = 3; + break; + case 3: + //Section Header Byte 3 extrahieren; + headerC = span[0]; + span = span.Slice(1); + state = 4; + psiPayloadNeeded = headerB; + psiPayloadNeeded <<= 8; + psiPayloadNeeded += headerC; + psiPayloadNeeded &= 0x0fff; + psiPayload = new byte[psiPayloadNeeded]; + break; + case 4: + if (automataIterations == 0) { - byte payloadPos = payload.ReadUInt8(); - payload.Position--; - if (payloadPos != 0xff) - newSection = true; - if (payload.Position == 0) - { - payload.Position++; - available--; - } + if (packet.PayloadUnitStart) + span = span.Slice(1); } - - if (newSection) + int copyable = Math.Min(psiPayloadNeeded, span.Length); + Copy(span, 0, psiPayload, psiPayloadOffset, copyable); + psiPayloadNeeded -= copyable; + psiPayloadOffset += copyable; + span = span.Slice(copyable); + if (psiPayloadNeeded == 0) { - psiSection = new PsiSection(); - psiSection.Need = 3; - psiSection.HeaderComplete = false; - } - else - { - available = 0; - } - } - } - else - { - //Nicht mehr genug da - byte[] filler = payload.ReadBytes(available); - psiSection.Append(filler); - psiSection.Need -= (int)available; - available = 0; - } + AssemblePsiPayload(headerA, headerB, headerC, psiPayload); + headerA = 0; + headerB = 0; + headerC = 0; + psiPayload = null; + psiPayloadNeeded = 0; + psiPayloadOffset = 0; + copyOps = 0; + state = 1; + } + break; + default: + throw new NotImplementedException(String.Format("PSI Decoder finite automata state {0}", state)); + } + automataIterations++; } } - private PsiSection psiSection; - public void PacketLoss() + private void AssemblePsiPayload(byte headerA, byte headerB, byte headerC, byte[] psiPayload) + { + PsiSection section = new PsiSection(); + section.Append(new byte[] { headerA, headerB, headerC }); + section.HeaderComplete = true; + section.Append(psiPayload); + if (!section.Valid) + { + //logger.ErrorFormat("Invalid MPEG-2 section"); + OnCrcError?.Invoke(section, pid, section.SectionLength); + return; + } + PsiProcessor.GatherPsi(section, pid); + OnValidSection?.Invoke(section, pid, section.SectionLength); + } + + public void PacketLoss() { - psiSection = null; + state = 0; + headerA = 0; + headerB = 0; + headerC = 0; + psiPayload = null; + psiPayloadNeeded = 0; + psiPayloadOffset = 0; + copyOps = 0; } - public bool HeaderComplete => psiSection.HeaderComplete; - public int NeededBytes => psiSection.Need; + private void Copy(Span input, int inputOffset, byte[] output, int outputOffset, int length) + { + for (int i = 0; i < length; i++) + { + output[outputOffset + i] = input[inputOffset + i]; + } + copyOps++; + } - public delegate void PsiSectionTooLong(TsPacket packet, bool justStarted, PsiSection section); - - public event PsiSectionTooLong OnPsiSectionTooLong; - - public delegate void OnSection(PsiSection section, int pid, long available); - public event OnSection OnValidSection; - public event OnSection OnCrcError; - } + public delegate void OnSection(PsiSection section, int pid, long available); + public event OnSection OnValidSection; + public event OnSection OnCrcError; + } }