/// <summary> /// Digest a PES payload into structured table. /// Does not handle split-payloads -- agregate payloads before calling /// </summary> public PES(byte[] RawPayload) { BitSplitter bs = new BitSplitter(RawPayload); int start_code = (int)bs.GetInteger(24); if (start_code != 1) { throw new DemuxException("Invalid PES: start code prefix missing"); } PTS = DTS = -1; StreamId = (int)bs.GetInteger(8); PacketLength = (int)bs.GetInteger(16); // Both these methods set 'FrameData' if (SpecialStream(StreamId)) { ReadSpecialForm(bs); } else { DecodeElementaryStream(bs); } }
private void ValidateTable(BitSplitter bs) { int pointer = (int)bs.GetInteger(8); if (pointer != 0) { throw new DemuxException("Non-zero pointers are not currently supported"); } int table_id = (int)bs.GetInteger(8); if (table_id != 0x02) { throw new DemuxException("Wrong table ID for PMT"); } SectionSyntax = bs.GetFlag(); if (!SectionSyntax) { throw new DemuxException("Invalid PMT: incorrect section syntax"); } bool zero = bs.GetFlag(); if (zero) { throw new DemuxException("Invalid PMT: zero bit wasn't zero"); } }
private void ReadTransportHeader(BitSplitter bs) { bs.SkipToNextByte(); // Sync byte Error = bs.GetFlag(); StartIndicator = bs.GetFlag(); HighPriority = bs.GetFlag(); PID = (int)bs.GetInteger(13); ScrambleCode = (int)bs.GetInteger(2); HasAdaptionField = bs.GetFlag(); HasPayload = bs.GetFlag(); Counter = (int)bs.GetInteger(4); }
private void CheckAdaptionField(BitSplitter bs) { if (HasAdaptionField) { int adaption_end = (int)bs.GetInteger(8); adaption_end += bs.ByteOffset; Discont = bs.GetFlag(); KeyFrame = bs.GetFlag(); ES_Prio = bs.GetFlag(); HasPCR = bs.GetFlag(); HasOPCR = bs.GetFlag(); HasSplice = bs.GetFlag(); PrivateFlag = bs.GetFlag(); AdapExtFlag = bs.GetFlag(); if (bs.BitOffset != 0) { throw new Exception("bit align problem"); } if (HasPCR) { PCR = (long)bs.GetInteger(33); bs.SkipBits(15); // throw away useless sync stuff. } if (HasOPCR) { bs.SkipBits(48); // throw away useless "old" timecode } if (HasSplice) { bs.SkipBits(8); // throw away splice counter } if (PrivateFlag) { int priv_len = (int)bs.GetInteger(8); bs.SkipBytes(priv_len); // skip private data } // ignore the rest of the adaption field (it's mostly to support stuff we ignore) int skip_len = adaption_end - bs.ByteOffset; bs.SkipBytes(skip_len); } }
/// <summary> /// Decode the bizzare PTS format /// </summary> private void ReadPTS(BitSplitter bs) { int marker = (int)bs.GetInteger(4); if (marker != 2) { throw new DemuxException("Invalid PES: PTS marker incorrect"); } long part_1 = (long)bs.GetInteger(3); bs.SkipBits(1); long part_2 = (long)bs.GetInteger(15); bs.SkipBits(1); long part_3 = (long)bs.GetInteger(15); bs.SkipBits(1); unchecked { // allow overflow so we can catch it later: PTS = (UInt32)(part_3 + (part_2 << 15) + (part_1 << 30)); } }
/// <summary> /// Reads the long list of flags in the default PES header. /// </summary> private void ReadFlags(BitSplitter bs) { ScrambleControl = (int)bs.GetInteger(2); HighPriority = bs.GetFlag(); HasAlignment = bs.GetFlag(); IsCopyright = bs.GetFlag(); IsOriginal = bs.GetFlag(); HasPTS = bs.GetFlag(); HasDTS = bs.GetFlag(); if (HasDTS && !HasPTS) { throw new DemuxException("Invalid PES: DTS without PTS is not allowed"); } HasESCR = bs.GetFlag(); HasEsRate = bs.GetFlag(); UsesTrickMode = bs.GetFlag(); MoreCopyright = bs.GetFlag(); HasPesCRC = bs.GetFlag(); HasPesExtension = bs.GetFlag(); }
private int SectionNumber, LastSection; // should always be zero /// <summary> /// Digest a packet payload into structured table. Payload should be from the pointer field onward. /// Does not yet handle multi-packet tables /// </summary> public PAT(byte[] RawPayload) { Map = new Dictionary <int, int>(); BitSplitter bs = new BitSplitter(RawPayload); ValidateTable(bs); bs.SkipBits(2); // reserved; SectionLength = (int)bs.GetInteger(12); TransportID = (int)bs.GetInteger(16); bs.SkipBits(2); // reserved Version = (int)bs.GetInteger(5); IsCurrent = bs.GetFlag(); SectionNumber = (int)bs.GetInteger(8); LastSection = (int)bs.GetInteger(8); int bits_left = (SectionLength - 5) - 4; // remaining length in bytes, excluding CRC int items = bits_left / 4; for (int i = 0; i < items; i++) { int prog = (int)bs.GetInteger(16); bs.SkipBits(3); int pid = (int)bs.GetInteger(13); if (!Map.ContainsKey(prog)) { Map.Add(prog, pid); } else { throw new DemuxException("Invalid PAT: program number specified more than once (" + prog + ")"); } } // Ignoring CRC. }
private void DecodeElementaryStream(BitSplitter bs) { int marker = (int)bs.GetInteger(2); if (marker != 2) { throw new DemuxException("Invalid PES: first marker missing"); } ReadFlags(bs); if (bs.BitOffset != 0) { throw new DemuxException("Alignment problem in PES (internal)"); } HeaderLength = (int)bs.GetInteger(8); int head_start = bs.ByteOffset; if (HasPTS && HasDTS) { ReadDTS_PTS(bs); } else if (HasPTS) { ReadPTS(bs); } if (HasESCR) { bs.SkipBytes(6); // not currently used. } if (HasEsRate) { bs.SkipBits(24); // not currently used. } if (UsesTrickMode) { bs.SkipBytes(1); // ignored } if (MoreCopyright) { bs.SkipBytes(1); // ignored } if (HasPesCRC) { bs.SkipBytes(2); // ignored } if (HasPesExtension) { ReadExtendedHeader(bs); } // skip anything that's left int head_end = bs.ByteOffset; int to_skip = HeaderLength - (head_end - head_start); if (to_skip < 0) { throw new DemuxException("Invalid PES: declared header length did not match measured length"); } bs.SkipBytes(to_skip); // Now, the remaining bytes are data and padding int data_length = PacketLength - (HeaderLength + to_skip); if (data_length > 3) { data_length -= 3; // no idea where the '3' is coming from... } byte[] data = bs.RemainingBytes(); if (PacketLength == 0) // video is allowed to not specify { data_length = data.Length; } #if DEBUG if (data.Length < data_length) { throw new DemuxException("Invalid PES: packet shorter than described"); } if (data_length < 0) { throw new DemuxException("Invalid PES: Negative packet length"); } #else if (data.Length < data_length || data_length < 0) { data_length = 0; } #endif MemoryStream ms = new MemoryStream(data, 0, data_length); FrameData = ms.ToArray(); }
/// <summary> /// Digest a packet payload into structured table. Payload should be from the pointer field onward. /// Does not yet handle multi-packet tables /// </summary> public PMT(byte[] RawPayload) { BitSplitter bs = new BitSplitter(RawPayload); Map = new Dictionary <int, int>(); ReverseMap = new Dictionary <StreamType, int>(); ValidateTable(bs); bs.SkipBits(2); SectionLength = (int)bs.GetInteger(12); // total length after this, in bytes; includes 4 byte CRC. ProgramNumber = (int)bs.GetInteger(16); bs.SkipBits(2); Version = (int)bs.GetInteger(5); IsCurrent = bs.GetFlag(); SectionNumber = (int)bs.GetInteger(8); LastSection = (int)bs.GetInteger(8); bs.SkipBits(3); PCR_PID = (int)bs.GetInteger(13); // Either the PID of a channel timecode stream, or 0x1FFF for none. bs.SkipBits(4); ProgInfoLength = (int)bs.GetInteger(12); // number of bytes of descriptors. if (bs.BitOffset != 0) { throw new DemuxException("Byte alignment error (internal)"); } bs.SkipBytes(ProgInfoLength); // ignore descriptors. int info_bytes = (SectionLength - ProgInfoLength) - 13; // bytes of descriptor. while (info_bytes > 0) // descriptions can be variable length { int stream_type = (int)bs.GetInteger(8); bs.SkipBits(3); int pid = (int)bs.GetInteger(13); bs.SkipBits(4); if (!Map.ContainsKey(pid)) { Map.Add(pid, stream_type); // more complete map of pid types } else { throw new DemuxException("Invalid PMT: PID specified more than once"); } StreamType st = DecodeStreamType(stream_type); if (!ReverseMap.ContainsKey(st)) { ReverseMap.Add(st, pid); // store first pid of each type } int es_info_length = (int)bs.GetInteger(12); bs.SkipBytes(es_info_length); info_bytes -= 5 + es_info_length; } if (bs.BitOffset != 0) { throw new DemuxException("Invalid PMT: program info length didn't match data"); } // ignoring CRC. }