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.
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;
}
}

View File

@ -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<byte> 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<byte> 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;
}
}