using System;
namespace Nikse.SubtitleEdit.Core.TransportStream
{
///
/// MPEG transport stream packet
///
public class Packet
{
///
/// ID byte of TS Packet
///
public const byte SynchronizationByte = 0x47; // 74 decimal, or 01000111 binary
///
/// Null packets can ensure that the stream maintains a constant bitrate. Null packets is to be ignored
///
public const int NullPacketId = 0x1FFF;
///
/// Program Association Table: lists all programs available in the transport stream.
///
public const int ProgramAssociationTablePacketId = 0;
///
///
///
public const int ConditionalAccessTablePacketId = 1;
///
///
///
public const int TransportStreamDescriptionTablePacketId = 2;
///
/// Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error
///
public bool TransportErrorIndicator { get; set; }
///
/// Start of PES data or PSI
///
public bool PayloadUnitStartIndicator { get; set; }
///
/// Higher priority than other packets with the same PID
///
public bool TransportPriority { get; set; }
///
/// Program Identifier
///
public int PacketId { get; set; }
///
/// 1 = Reserved for future use, 10 = Scrambled with even key, 11 = Scrambled with odd key
///
public int ScramblingControl { get; set; }
///
/// 1 = no adaptation fields (payload only), 10 = adaptation field only, 11 = adaptation field and payload
///
public int AdaptationFieldControl { get; set; }
///
/// Incremented only when a payload is present (AdaptationFieldExist = 10 or 11). Starts at 0.
///
public int ContinuityCounter { get; set; }
public int AdaptionFieldLength { get; set; }
public AdaptationField AdaptationField { get; private set; }
public bool IsNullPacket { get { return PacketId == NullPacketId; } }
public bool IsProgramAssociationTable { get { return PacketId == ProgramAssociationTablePacketId; } }
public byte[] Payload { get; private set; }
public bool IsPrivateStream1
{
get
{
if (Payload == null || Payload.Length < 4)
return false;
return Payload[0] == 0 &&
Payload[1] == 0 &&
Payload[2] == 1 &&
Payload[3] == 0xbd; // 0xbd == 189 - MPEG-2 Private stream 1 (non MPEG audio, subpictures)
}
}
public bool IsVideoStream
{
get
{
if (Payload == null || Payload.Length < 4)
return false;
return Payload[0] == 0 &&
Payload[1] == 0 &&
Payload[2] == 1 &&
Payload[3] >= 0xE0 &&
Payload[3] < 0xF0;
}
}
public ProgramAssociationTable ProgramAssociationTable { get; private set; }
public Packet(byte[] packetBuffer)
{
if (packetBuffer == null || packetBuffer.Length < 30)
return;
TransportErrorIndicator = 1 == packetBuffer[1] >> 7; // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error
PayloadUnitStartIndicator = 1 == ((packetBuffer[1] & 64) >> 6); // and with 01000000 to get second byte - 1 means start of PES data or PSI otherwise zero
TransportPriority = 1 == ((packetBuffer[1] & 32) >> 5); // and with 00100000 to get third byte - 1 means higher priority than other packets with the same PID
PacketId = (packetBuffer[1] & 31) * 256 + packetBuffer[2];// and with 00011111 to get last 5 bytes
ScramblingControl = packetBuffer[3] >> 6; // '00' = Not scrambled. The following per DVB spec:[12] '01' = Reserved for future use, '10' = Scrambled with even key, '11' = Scrambled with odd key
AdaptationFieldControl = (packetBuffer[3] & 48) >> 4; // and with 00110000, 01 = no adaptation fields (payload only), 10 = adaptation field only, 11 = adaptation field and payload
ContinuityCounter = packetBuffer[3] & 15;
AdaptionFieldLength = AdaptationFieldControl > 1 ? (0xFF & packetBuffer[4]) + 1 : 0;
if (AdaptationFieldControl == Helper.B00000010 || AdaptationFieldControl == Helper.B00000011)
AdaptationField = new AdaptationField(packetBuffer);
if (AdaptationFieldControl == Helper.B00000001 || AdaptationFieldControl == Helper.B00000011) // Payload exists - binary '01' || '11'
{
int payloadStart = 4;
if (AdaptationField != null)
payloadStart += (1 + AdaptationField.Length);
if (PacketId == ProgramAssociationTablePacketId) // PAT = Program Association Table: lists all programs available in the transport stream.
{
ProgramAssociationTable = new ProgramAssociationTable(packetBuffer, payloadStart + 1); // TODO: What index?
}
// Save payload
int payloadLength = packetBuffer.Length - payloadStart;
if (payloadLength < 0)
return;
Payload = new byte[payloadLength];
Buffer.BlockCopy(packetBuffer, payloadStart, Payload, 0, Payload.Length);
}
}
}
}