Complete Rewrite of the PsiDecoder.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m40s

This commit is contained in:
feyris-tan 2026-03-16 00:23:25 +01:00
parent c740e3c6bb
commit ca9ed0f11a
2 changed files with 123 additions and 124 deletions

View File

@ -69,7 +69,7 @@ namespace skyscraper5.Dvb.DataBroadcasting
//Undocumented, manufacterer specific data. //Undocumented, manufacterer specific data.
break; break;
default: 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; return;
} }
} }

View File

@ -1,159 +1,158 @@
using System.IO; using log4net;
using log4net.Repository.Hierarchy;
using skyscraper5.Dvb; using skyscraper5.Dvb;
using skyscraper5.Skyscraper.IO; using skyscraper5.Skyscraper.IO;
using System.IO;
namespace skyscraper5.Mpeg2 namespace skyscraper5.Mpeg2
{ {
public class PsiDecoder : ITsPacketProcessor, IPayloadUnitDecoder public class PsiDecoder : ITsPacketProcessor, IPayloadUnitDecoder
{ {
public IPsiProcessor PsiProcessor { get; } 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; PsiProcessor = psiProcessor;
this.pid = pid; this.pid = pid;
} }
private int pid;
public void PushPacket(TsPacket packet) public void PushPacket(TsPacket packet)
{ {
if (packet.GetPayload() == null) Span<byte> span = packet.GetPayload();
return; int automataIterations = 0;
if (packet.PID != pid)
return;
if (packet.PID == 0x0881)
;
bool psiJustStarted = false; while(span.Length > 0)
if (psiSection == null)
{ {
if (packet.PayloadUnitStart) switch(state)
{ {
psiSection = new PsiSection(); case 0:
psiSection.Need = 3; //Noch keine PSI am zusammenbauen.
psiSection.HeaderComplete = false; if (!packet.PayloadUnitStart)
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)
{ {
lock (this) //im TS Paket beginnt keine neue, also wegwerfen.
{ return;
PsiProcessor.GatherPsi(psiSection, pid);
}
OnValidSection?.Invoke(psiSection, pid, available);
} }
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)) return;
{
OnCrcError?.Invoke(psiSection, pid,available);
}
} }
headerA = span[0];
psiSection = null; span = span.Slice(1);
state = 2;
bool newSection = false; break;
if (available > 0) 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(); if (packet.PayloadUnitStart)
payload.Position--; span = span.Slice(1);
if (payloadPos != 0xff)
newSection = true;
if (payload.Position == 0)
{
payload.Position++;
available--;
}
} }
int copyable = Math.Min(psiPayloadNeeded, span.Length);
if (newSection) Copy(span, 0, psiPayload, psiPayloadOffset, copyable);
psiPayloadNeeded -= copyable;
psiPayloadOffset += copyable;
span = span.Slice(copyable);
if (psiPayloadNeeded == 0)
{ {
psiSection = new PsiSection(); AssemblePsiPayload(headerA, headerB, headerC, psiPayload);
psiSection.Need = 3; headerA = 0;
psiSection.HeaderComplete = false; headerB = 0;
} headerC = 0;
else psiPayload = null;
{ psiPayloadNeeded = 0;
available = 0; psiPayloadOffset = 0;
} copyOps = 0;
} state = 1;
} }
else break;
{ default:
//Nicht mehr genug da throw new NotImplementedException(String.Format("PSI Decoder finite automata state {0}", state));
byte[] filler = payload.ReadBytes(available); }
psiSection.Append(filler); automataIterations++;
psiSection.Need -= (int)available;
available = 0;
}
} }
} }
private PsiSection psiSection; private void AssemblePsiPayload(byte headerA, byte headerB, byte headerC, byte[] psiPayload)
public void PacketLoss() {
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; private void Copy(Span<byte> input, int inputOffset, byte[] output, int outputOffset, int length)
public int NeededBytes => psiSection.Need; {
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 delegate void OnSection(PsiSection section, int pid, long available);
public event OnSection OnValidSection;
public event PsiSectionTooLong OnPsiSectionTooLong; public event OnSection OnCrcError;
}
public delegate void OnSection(PsiSection section, int pid, long available);
public event OnSection OnValidSection;
public event OnSection OnCrcError;
}
} }