Added preliminary subtitle handling.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 50s
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 50s
This commit is contained in:
parent
c6551dbd52
commit
ddd4181dc3
@ -163,6 +163,7 @@ public class Matenro : IPatEventHandler, ICatEventHandler, IBatEventHandler, ISd
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private bool timePresent;
|
||||
public void OnTdtTime(DateTime utcTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@ -220,7 +221,7 @@ public class Matenro : IPatEventHandler, ICatEventHandler, IBatEventHandler, ISd
|
||||
continue;
|
||||
case StreamType.IsdbSubtitles:
|
||||
case StreamType.IsdbTeletext:
|
||||
DvbContext.RegisterPacketProcessor(pmtStream.ElementaryPid, new PesDecoder(new IsdbSubtitlePesParser(this)));
|
||||
DvbContext.RegisterPacketProcessor(pmtStream.ElementaryPid, new PesDecoder(new IsdbSubtitlePesParser(this, pmtStream.ElementaryPid)));
|
||||
continue;
|
||||
case StreamType.TryAutodetect:
|
||||
DvbContext.RegisterPacketProcessor(pmtStream.ElementaryPid, new StreamTypeAutodetection(pmtStream.ElementaryPid, this));
|
||||
@ -305,4 +306,12 @@ public class Matenro : IPatEventHandler, ICatEventHandler, IBatEventHandler, ISd
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnCaption(int pid, IsdbCaption plaintextCaption)
|
||||
{
|
||||
if (!timePresent)
|
||||
return;
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@ -2,5 +2,5 @@ namespace skyscraper8.Isdb.Subtitles;
|
||||
|
||||
public interface IIsdbSubtitleHandler
|
||||
{
|
||||
|
||||
void OnCaption(int pid, IsdbCaption plaintextCaption);
|
||||
}
|
||||
261
skyscraper8/Isdb/Subtitles/IsdbCaption.cs
Normal file
261
skyscraper8/Isdb/Subtitles/IsdbCaption.cs
Normal file
@ -0,0 +1,261 @@
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using log4net;
|
||||
using skyscraper5.Dvb.DataBroadcasting.IntModel;
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
|
||||
namespace skyscraper8.Isdb.Subtitles;
|
||||
|
||||
public class IsdbCaption
|
||||
{
|
||||
private static readonly ILog Logger = LogManager.GetLogger(typeof(IsdbCaption));
|
||||
|
||||
public IsdbCaption(byte[] buffer)
|
||||
{
|
||||
byte[] outBuffer = new byte[buffer.Length];
|
||||
int inPointer = 0;
|
||||
int outPointer = 0;
|
||||
|
||||
while (inPointer < buffer.Length)
|
||||
{
|
||||
byte b = buffer[inPointer++];
|
||||
switch (b)
|
||||
{
|
||||
case 0x00: //NUL
|
||||
break;
|
||||
case 0x07: //BEL
|
||||
throw new NotImplementedException("BEL");
|
||||
case 0x08: //APB
|
||||
throw new NotImplementedException("APB");
|
||||
case 0x09: //APF
|
||||
throw new NotImplementedException("APF");
|
||||
case 0x0A: //APD
|
||||
throw new NotImplementedException("APD");
|
||||
case 0x0B: //APU
|
||||
throw new NotImplementedException("APU");
|
||||
case 0x0C: //CS
|
||||
this.ClearScreen = true;
|
||||
break;
|
||||
case 0x0D: //APR
|
||||
throw new NotImplementedException("APR");
|
||||
case 0x0E: //LS1
|
||||
throw new NotImplementedException("LS1");
|
||||
case 0x0F: //LS0
|
||||
throw new NotImplementedException("LS0");
|
||||
case 0x16: //PAPF
|
||||
throw new NotImplementedException("PAPF");
|
||||
case 0x18: //CAN
|
||||
throw new NotImplementedException("CAN");
|
||||
case 0x19: //SS2
|
||||
throw new NotImplementedException("SS2");
|
||||
case 0x1B: //ESC
|
||||
throw new NotImplementedException("ESC");
|
||||
case 0x1C: //APS
|
||||
int y = buffer[inPointer++];
|
||||
int x = buffer[inPointer++];
|
||||
x -= 0x40;
|
||||
y -= 0x40;
|
||||
ActivePositionSet = new Point(x, y);
|
||||
break;
|
||||
case 0x1D:
|
||||
throw new NotImplementedException("SS3");
|
||||
case 0x1E:
|
||||
throw new NotImplementedException("RS");
|
||||
case 0x1F:
|
||||
throw new NotImplementedException("US");
|
||||
case 0x20: //SP (Space)
|
||||
outBuffer[outPointer++] = 0x20;
|
||||
break;
|
||||
case 0x7F: //DEL
|
||||
throw new NotImplementedException("DEL");
|
||||
case 0x80: //BKF
|
||||
throw new NotImplementedException("BKF");
|
||||
case 0x81: //RDF
|
||||
throw new NotImplementedException("RDF");
|
||||
case 0x82: //GRF
|
||||
throw new NotImplementedException("GRF");
|
||||
case 0x83: //YLF
|
||||
throw new NotImplementedException("YLF");
|
||||
case 0x84: //BLF
|
||||
throw new NotImplementedException("BLF");
|
||||
case 0x85: //MGF
|
||||
throw new NotImplementedException("MGF");
|
||||
case 0x86: //CNF
|
||||
throw new NotImplementedException("CNF");
|
||||
case 0x87: //WHF
|
||||
ForegroundColor = Color.White;
|
||||
break;
|
||||
case 0x88: //SSZ
|
||||
CharacterSize = Subtitles.CharacterSize.Small;
|
||||
break;
|
||||
case 0x89: //MSZ
|
||||
throw new NotImplementedException("MSZ");
|
||||
case 0x8A: //NSZ
|
||||
throw new NotImplementedException("NSZ");
|
||||
case 0x8B:
|
||||
throw new NotImplementedException("SZX");
|
||||
case 0x90: //COL
|
||||
byte colP1 = buffer[inPointer++];
|
||||
switch (colP1)
|
||||
{
|
||||
case 0x50: BackgroundColor = Color.Black; break;
|
||||
case 0x58: BackgroundColor = Color.Transparent; break;
|
||||
default:
|
||||
//see 6-STD-B24v6_4-1p3-E1.pdf, page 108
|
||||
throw new NotImplementedException(String.Format("colP1 0x{0:X2}", colP1));
|
||||
}
|
||||
break;
|
||||
case 0x91: //FLC
|
||||
throw new NotImplementedException("FLC");
|
||||
case 0x92: //CDC
|
||||
throw new NotImplementedException("CDC");
|
||||
case 0x93: //POL
|
||||
throw new NotImplementedException("POL");
|
||||
case 0x94: //WLF
|
||||
throw new NotImplementedException("WLF");
|
||||
case 0x95: //MACRO
|
||||
throw new NotImplementedException("MACRO");
|
||||
case 0x97: //HLC
|
||||
throw new NotImplementedException("HLC");
|
||||
case 0x98: //RPC
|
||||
throw new NotImplementedException("RPC");
|
||||
case 0x99: //SPL
|
||||
throw new NotImplementedException("SPL");
|
||||
case 0x9A: //STL
|
||||
throw new NotImplementedException("STL");
|
||||
case 0x9B: //CSI
|
||||
ControlSequenceIntroduced = true;
|
||||
string csiArguments = "";
|
||||
while (inPointer < buffer.Length)
|
||||
{
|
||||
byte csiByte = buffer[inPointer++];
|
||||
if (csiByte >= 0x20 && csiByte <= 0x3F)
|
||||
{
|
||||
csiArguments += (char)csiByte;
|
||||
}
|
||||
else if (csiByte >= 0x40 && csiByte <= 0x7E)
|
||||
{
|
||||
char command = (char)csiByte;
|
||||
HandleCsiCommand(command, csiArguments);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x9D: //TIME
|
||||
throw new NotImplementedException("TIME");
|
||||
case 0xA0: //10/0
|
||||
throw new NotImplementedException("10/0");
|
||||
case 0xFF:
|
||||
throw new NotImplementedException("15/15");
|
||||
default:
|
||||
outBuffer[outPointer++] = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Text = Encoding.UTF8.GetString(outBuffer, 0, outPointer);
|
||||
}
|
||||
|
||||
private void HandleCsiCommand(char command, string csiArguments)
|
||||
{
|
||||
//Um den passenden CMD zu finden, die 6-STD-B24v6_4-1p3-E1.pdf durchsuchen und Table 7-17 (ab Seite 113) nach Final character durchsuchen.
|
||||
//Wenn cmd zum Beispiel 0x53 ist, dann nach 05/3 suchen.
|
||||
byte cmd = (byte)command;
|
||||
csiArguments = csiArguments.TrimEnd();
|
||||
switch (cmd)
|
||||
{
|
||||
case 0x53:
|
||||
WritingFormat = (WritingFormat)int.Parse(csiArguments);
|
||||
break;
|
||||
case 0x56:
|
||||
string[] sdfArgs = csiArguments.Split(';');
|
||||
DisplayFormat = new Size(int.Parse(sdfArgs[0]), int.Parse(sdfArgs[1]));
|
||||
break;
|
||||
case 0x5f:
|
||||
string[] sdpArgs = csiArguments.Split(';');
|
||||
DisplayPosition = new Point(int.Parse(sdpArgs[0]), int.Parse(sdpArgs[1]));
|
||||
break;
|
||||
case 0x57:
|
||||
string[] ssmArgs = csiArguments.Split(';');
|
||||
CharacterCompositionDots = new Size(int.Parse(ssmArgs[0]), int.Parse(ssmArgs[1]));
|
||||
break;
|
||||
case 0x58:
|
||||
HorizontalSpacing = int.Parse(csiArguments);
|
||||
break;
|
||||
case 0x59:
|
||||
VerticalSpace = int.Parse(csiArguments);
|
||||
break;
|
||||
case 0x6e:
|
||||
int rcsColor = int.Parse(csiArguments);
|
||||
bool isTransparent = rcsColor > 8;
|
||||
if (isTransparent)
|
||||
rcsColor -= 8;
|
||||
switch (rcsColor)
|
||||
{
|
||||
case 0: RasterColour = Color.Black; break;
|
||||
case 1: RasterColour = Color.Red; break;
|
||||
case 2: RasterColour = Color.Green; break;
|
||||
case 3: RasterColour = Color.Yellow; break;
|
||||
case 4: RasterColour = Color.Blue; break;
|
||||
case 5: RasterColour = Color.Magenta; break;
|
||||
case 6: RasterColour = Color.Cyan; break;
|
||||
case 7: RasterColour = Color.White; break;
|
||||
case 8: RasterColour = Color.Transparent; break;
|
||||
}
|
||||
if (isTransparent)
|
||||
RasterColour = Color.FromArgb(RasterColour.Value.A,RasterColour.Value.R / 2,RasterColour.Value.G / 2,RasterColour.Value.B / 2);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("cmd 0x{0:X2}", cmd));
|
||||
}
|
||||
}
|
||||
|
||||
public Color? RasterColour { get; set; }
|
||||
|
||||
public int? VerticalSpace { get; set; }
|
||||
|
||||
public int? HorizontalSpacing { get; set; }
|
||||
|
||||
public Size? CharacterCompositionDots { get; set; }
|
||||
|
||||
public Point? DisplayPosition { get; set; }
|
||||
|
||||
public Size? DisplayFormat { get; set; }
|
||||
|
||||
public WritingFormat? WritingFormat { get; set; }
|
||||
|
||||
public string Text { get; private set; }
|
||||
|
||||
public bool ControlSequenceIntroduced { get; private set; }
|
||||
|
||||
public bool ClearScreen { get; private set; }
|
||||
|
||||
public CharacterSize? CharacterSize { get; private set; }
|
||||
|
||||
public Color? ForegroundColor { get; private set; }
|
||||
public Color? BackgroundColor { get; private set; }
|
||||
public Point? ActivePositionSet { get; private set; }
|
||||
}
|
||||
|
||||
public enum CharacterSize
|
||||
{
|
||||
Small,
|
||||
Medium,
|
||||
Large
|
||||
}
|
||||
|
||||
public enum WritingFormat
|
||||
{
|
||||
HorizontalStandardDensity = 0,
|
||||
VerticalStandardDensity = 1,
|
||||
HorizontalHighDensity = 2,
|
||||
VerticalHighDensity = 3,
|
||||
HorizontalWritingWestern = 4,
|
||||
HorizontalWriting1920_1080 = 5,
|
||||
VerticalWriting1920_1080 = 6,
|
||||
HorizontalWriting960_540 = 7,
|
||||
VerticalWriting960_540 = 8,
|
||||
HorizontalWriting720_480 = 9,
|
||||
VerticalWriting720_480 = 10,
|
||||
HorizontalWriting1280_720 = 11,
|
||||
VerticalWriting1280_720 = 12,
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
using log4net;
|
||||
using skyscraper5.Mpeg2;
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
|
||||
namespace skyscraper8.Isdb.Subtitles;
|
||||
|
||||
@ -9,13 +10,122 @@ class IsdbSubtitlePesParser : IPesProcessor
|
||||
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
|
||||
|
||||
|
||||
public IsdbSubtitlePesParser(IIsdbSubtitleHandler handler)
|
||||
public IsdbSubtitlePesParser(IIsdbSubtitleHandler handler, int pmtStreamElementaryPid)
|
||||
{
|
||||
Handler = handler;
|
||||
Pid = pmtStreamElementaryPid;
|
||||
}
|
||||
|
||||
|
||||
public int Pid { get; private set; }
|
||||
|
||||
public void ProcessPesPacket(PesPacket packet)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//Peel back the outer layer, as stated by 6-STD-B37v2_4-E1.pdf, clause 2.2.3.6, Table 2-25
|
||||
byte[] payload = packet.Payload;
|
||||
MemoryStream ms = new MemoryStream(payload, false);
|
||||
|
||||
byte dataIdentifier = ms.ReadUInt8();
|
||||
byte privateStreamId = ms.ReadUInt8();
|
||||
|
||||
byte byte3 = ms.ReadUInt8();
|
||||
int reserved = (byte3 & 0xf0) >> 4;
|
||||
int pesDataPacketHeaderLength = (byte3 & 0x0f);
|
||||
byte[] pesDataPacketHeader = ms.ReadBytes(pesDataPacketHeaderLength);
|
||||
|
||||
if (pesDataPacketHeaderLength > 0)
|
||||
{
|
||||
throw new NotImplementedException("PES packet header length not implemented");
|
||||
}
|
||||
|
||||
//Finally, we can start parsing the data group 6-STD-B24v6_4-1p3-E1.pdf, clause 9.2
|
||||
byte byte4 = ms.ReadUInt8();
|
||||
int dataGroupId = (byte4 & 0xfc) >> 2;
|
||||
int dataGroupVersion = (byte4 & 0x03);
|
||||
|
||||
byte dataGroupLinkNumber = ms.ReadUInt8();
|
||||
byte lastDataGroupLinkNumber = ms.ReadUInt8();
|
||||
ushort dataGroupSize = ms.ReadUInt16BE();
|
||||
long expectedRemaining = ms.GetAvailableBytes() - 2;
|
||||
if (dataGroupSize != expectedRemaining)
|
||||
{
|
||||
throw new IsdbException("Unexpected data group size: " + dataGroupSize);
|
||||
}
|
||||
|
||||
byte[] dataGroupSizeBytes = ms.ReadBytes(dataGroupSize);
|
||||
switch (dataGroupId)
|
||||
{
|
||||
case 0:
|
||||
ParseCaptionManagement(dataGroupSizeBytes);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
ParseCaptionStatement(dataGroupSizeBytes);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
ushort crc16 = ms.ReadUInt16BE();
|
||||
|
||||
}
|
||||
|
||||
private void ParseCaptionManagement(byte[] dataGroupSizeBytes)
|
||||
{
|
||||
throw new NotImplementedException("Caption management not implemented");
|
||||
}
|
||||
|
||||
private void ParseCaptionStatement(byte[] dataGroupSizeBytes)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(dataGroupSizeBytes, false);
|
||||
byte byteA = ms.ReadUInt8();
|
||||
int tmd = (byteA & 0xc0) >> 6;
|
||||
int reserved = (byteA & 0x3f);
|
||||
if (tmd == 1 || tmd == 2)
|
||||
{
|
||||
byte[] stm = ms.ReadBytes(5);
|
||||
}
|
||||
|
||||
uint dataUnitLoopLength = ms.ReadUInt24BE();
|
||||
long expectedDataUnitLoopLength = ms.GetAvailableBytes();
|
||||
if (dataUnitLoopLength != expectedDataUnitLoopLength)
|
||||
{
|
||||
throw new IsdbException("Unexpected data unit-loop length: " + dataUnitLoopLength);
|
||||
}
|
||||
|
||||
byte[] dataUnit = ms.ReadBytes(dataUnitLoopLength);
|
||||
ParseDataUnit(dataUnit);
|
||||
}
|
||||
|
||||
private void ParseDataUnit(byte[] dataUnit)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(dataUnit, false);
|
||||
byte unitSeperator = ms.ReadUInt8();
|
||||
if (unitSeperator != 0x1f)
|
||||
{
|
||||
throw new IsdbException("Unexpected data unit-seperator: " + unitSeperator);
|
||||
}
|
||||
byte dataUnitParameter = ms.ReadUInt8();
|
||||
uint dataUnitSize = ms.ReadUInt24BE();
|
||||
long expectedDataUnitSize = ms.GetAvailableBytes();
|
||||
if (dataUnitSize != expectedDataUnitSize)
|
||||
{
|
||||
throw new IsdbException("Unexpected data unit-size: " + dataUnitSize);
|
||||
}
|
||||
byte[] dataUnitData = ms.ReadBytes(dataUnitSize);
|
||||
switch (dataUnitParameter)
|
||||
{
|
||||
case 0x20:
|
||||
IsdbCaption plaintextCaption = new IsdbCaption(dataUnitData);
|
||||
logger.InfoFormat("Caption on PID 0x{0:X4} - {1}", Pid, plaintextCaption.Text);
|
||||
Handler.OnCaption(Pid, plaintextCaption);
|
||||
break;
|
||||
default:
|
||||
throw new IsdbException("Unexpected data unit-parameter: " + dataUnitParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user