Complete Rewrite of the PsiDecoder.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m40s
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m40s
This commit is contained in:
parent
c740e3c6bb
commit
ca9ed0f11a
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user