149 lines
4.9 KiB
C#
149 lines
4.9 KiB
C#
using System.IO;
|
|
using skyscraper5.Dvb;
|
|
using skyscraper5.Skyscraper.IO;
|
|
|
|
namespace skyscraper5.Mpeg2
|
|
{
|
|
public class PsiDecoder : ITsPacketProcessor, IPayloadUnitDecoder
|
|
{
|
|
public IPsiProcessor PsiProcessor { get; }
|
|
|
|
public PsiDecoder(int pid, IPsiProcessor psiProcessor)
|
|
{
|
|
PsiProcessor = psiProcessor;
|
|
this.pid = pid;
|
|
}
|
|
|
|
private int pid;
|
|
|
|
public void PushPacket(TsPacket packet)
|
|
{
|
|
if (packet.Payload == null)
|
|
return;
|
|
if (packet.PID != pid)
|
|
return;
|
|
|
|
|
|
bool psiJustStarted = false;
|
|
if (psiSection == null)
|
|
{
|
|
if (packet.PayloadUnitStart)
|
|
{
|
|
psiSection = new PsiSection();
|
|
psiSection.Need = 3;
|
|
psiSection.HeaderComplete = false;
|
|
psiJustStarted = true;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
MemoryStream payload = new MemoryStream(packet.Payload);
|
|
long available = payload.Length - payload.Position;
|
|
if (psiJustStarted)
|
|
{
|
|
long startOffset = packet.PayloadStartOffset;
|
|
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)
|
|
{
|
|
PsiProcessor.GatherPsi(psiSection, pid);
|
|
}
|
|
OnValidSection?.Invoke(psiSection, pid, available);
|
|
}
|
|
else
|
|
{
|
|
if (!DvbCrc32.ValidatePsi(psiSection))
|
|
{
|
|
OnCrcError?.Invoke(psiSection, pid,available);
|
|
}
|
|
}
|
|
|
|
psiSection = null;
|
|
|
|
bool newSection = false;
|
|
if (available > 0)
|
|
{
|
|
byte payloadPos = payload.ReadUInt8();
|
|
payload.Position--;
|
|
if (payloadPos != 0xff)
|
|
newSection = true;
|
|
}
|
|
|
|
if (newSection)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
private PsiSection psiSection;
|
|
public void PacketLoss()
|
|
{
|
|
psiSection = null;
|
|
}
|
|
|
|
public bool HeaderComplete => psiSection.HeaderComplete;
|
|
public int NeededBytes => psiSection.Need;
|
|
|
|
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;
|
|
}
|
|
|
|
}
|