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();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool timePresent;
|
||||||
public void OnTdtTime(DateTime utcTime)
|
public void OnTdtTime(DateTime utcTime)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
@ -220,7 +221,7 @@ public class Matenro : IPatEventHandler, ICatEventHandler, IBatEventHandler, ISd
|
|||||||
continue;
|
continue;
|
||||||
case StreamType.IsdbSubtitles:
|
case StreamType.IsdbSubtitles:
|
||||||
case StreamType.IsdbTeletext:
|
case StreamType.IsdbTeletext:
|
||||||
DvbContext.RegisterPacketProcessor(pmtStream.ElementaryPid, new PesDecoder(new IsdbSubtitlePesParser(this)));
|
DvbContext.RegisterPacketProcessor(pmtStream.ElementaryPid, new PesDecoder(new IsdbSubtitlePesParser(this, pmtStream.ElementaryPid)));
|
||||||
continue;
|
continue;
|
||||||
case StreamType.TryAutodetect:
|
case StreamType.TryAutodetect:
|
||||||
DvbContext.RegisterPacketProcessor(pmtStream.ElementaryPid, new StreamTypeAutodetection(pmtStream.ElementaryPid, this));
|
DvbContext.RegisterPacketProcessor(pmtStream.ElementaryPid, new StreamTypeAutodetection(pmtStream.ElementaryPid, this));
|
||||||
@ -305,4 +306,12 @@ public class Matenro : IPatEventHandler, ICatEventHandler, IBatEventHandler, ISd
|
|||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
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
|
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 log4net;
|
||||||
using skyscraper5.Mpeg2;
|
using skyscraper5.Mpeg2;
|
||||||
|
using skyscraper5.Skyscraper.IO;
|
||||||
|
|
||||||
namespace skyscraper8.Isdb.Subtitles;
|
namespace skyscraper8.Isdb.Subtitles;
|
||||||
|
|
||||||
@ -9,13 +10,122 @@ class IsdbSubtitlePesParser : IPesProcessor
|
|||||||
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
|
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;
|
Handler = handler;
|
||||||
|
Pid = pmtStreamElementaryPid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Pid { get; private set; }
|
||||||
|
|
||||||
public void ProcessPesPacket(PesPacket packet)
|
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