PesPacket.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Text;
  6. namespace MatrixIO.IO.MpegTs
  7. {
  8. public enum StreamIdentifier : byte
  9. {
  10. ProgramStreamMap = 0xBC,
  11. PrivateStream1 = 0xBD,
  12. PaddingStream = 0xBE,
  13. PrivateStream2 = 0xBF,
  14. #region Numbered Audio Streams
  15. AudioStream0 = 0xC0,
  16. AudioStream1 = 0xC1,
  17. AudioStream2 = 0xC2,
  18. AudioStream3 = 0xC3,
  19. AudioStream4 = 0xC4,
  20. AudioStream5 = 0xC5,
  21. AudioStream6 = 0xC6,
  22. AudioStream7 = 0xC7,
  23. AudioStream8 = 0xC8,
  24. AudioStream9 = 0xC9,
  25. AudioStream10 = 0xCA,
  26. AudioStream11 = 0xCB,
  27. AudioStream12 = 0xCC,
  28. AudioStream13 = 0xCD,
  29. AudioStream14 = 0xCE,
  30. AudioStream15 = 0xCF,
  31. AudioStream16 = 0xD0,
  32. AudioStream17 = 0xD1,
  33. AudioStream18 = 0xD2,
  34. AudioStream19 = 0xD3,
  35. AudioStream20 = 0xD4,
  36. AudioStream21 = 0xD5,
  37. AudioStream22 = 0xD6,
  38. AudioStream23 = 0xD7,
  39. AudioStream24 = 0xD8,
  40. AudioStream25 = 0xD9,
  41. AudioStream26 = 0xDA,
  42. AudioStream27 = 0xDB,
  43. AudioStream28 = 0xDC,
  44. AudioStream29 = 0xDD,
  45. AudioStream30 = 0xDE,
  46. AudioStream31 = 0xDF,
  47. #endregion
  48. #region Numbered Video Streams
  49. VideoStream0 = 0xE0,
  50. VideoStream1 = 0xE1,
  51. VideoStream2 = 0xE2,
  52. VideoStream3 = 0xE3,
  53. VideoStream4 = 0xE4,
  54. VideoStream5 = 0xE5,
  55. VideoStream6 = 0xE6,
  56. VideoStream7 = 0xE7,
  57. VideoStream8 = 0xE8,
  58. VideoStream9 = 0xE9,
  59. VideoStream10 = 0xEA,
  60. VideoStream11 = 0xEB,
  61. VideoStream12 = 0xEC,
  62. VideoStream13 = 0xED,
  63. VideoStream14 = 0xEE,
  64. VideoStream15 = 0xEF,
  65. #endregion
  66. EcmStream = 0xF0,
  67. EmmStream = 0xF1,
  68. DsmCcStream = 0xF2,
  69. Iso13522Stream = 0xF3,
  70. TypeAStream = 0xF4,
  71. TypeBStream = 0xF5,
  72. TypeCStream = 0xF6,
  73. TypeDStream = 0xF7,
  74. TypeEStream = 0xF8,
  75. AncillaryStream = 0xF9,
  76. SlPacketizedStream = 0xFA,
  77. FlexMuxStream = 0xFB,
  78. // 0xFC-0xFE RESERVED
  79. ProgramStreamDirectory = 0xFF,
  80. }
  81. public class PesPacket
  82. {
  83. public StreamIdentifier StreamIdentifier { get; set; }
  84. public int PacketLength { get; private set; }
  85. public PESHeader Header { get; set; }
  86. public byte[] Stuffing { get; set; }
  87. public byte[] Data { get; set; }
  88. private const uint PacketStartCodePrefix = 0x00000100;
  89. private const uint PacketStartCodeMask = 0xFFFFFF00;
  90. private const uint ProgramStreamEndCode = 0x000001B9;
  91. public PesPacket(byte[] buffer, int offset = 0)
  92. {
  93. int position = offset;
  94. if (buffer[position++] != 0 || buffer[position++] != 0 || buffer[position++] != 1)
  95. throw new ArgumentException("PES Packet does not start with 0x000001 Start Code Prefix.");
  96. StreamIdentifier = (StreamIdentifier) buffer[position++];
  97. PacketLength = (buffer[position++] << 8) | buffer[position++];
  98. if (PacketLength == 0) PacketLength = buffer.Length - offset - 6;
  99. if (StreamIdentifier != StreamIdentifier.ProgramStreamMap
  100. && StreamIdentifier != StreamIdentifier.PaddingStream
  101. && StreamIdentifier != StreamIdentifier.PrivateStream2
  102. && StreamIdentifier != StreamIdentifier.EcmStream
  103. && StreamIdentifier != StreamIdentifier.EmmStream
  104. && StreamIdentifier != StreamIdentifier.ProgramStreamDirectory
  105. && StreamIdentifier != StreamIdentifier.DsmCcStream
  106. && StreamIdentifier != StreamIdentifier.TypeEStream)
  107. {
  108. Header = new PESHeader(buffer, position);
  109. position += Header.Length;
  110. Data = new byte[PacketLength - Header.Length];
  111. // TODO: Something may be squirrely here. position+PacketLength should never be > buffer.Length
  112. // Either ffmpeg is producing bad PES packets or we are reassembling units incorrectly.
  113. // Math.Min() here prevents the exception but doesn't fix the underlying problem.
  114. if (PacketLength > 0) Buffer.BlockCopy(buffer, position, Data, 0, Math.Min(Data.Length, buffer.Length - position));
  115. }
  116. else if (StreamIdentifier == StreamIdentifier.ProgramStreamMap
  117. || StreamIdentifier == StreamIdentifier.PrivateStream2
  118. || StreamIdentifier == StreamIdentifier.EcmStream
  119. || StreamIdentifier == StreamIdentifier.EmmStream
  120. || StreamIdentifier == StreamIdentifier.ProgramStreamDirectory
  121. || StreamIdentifier == StreamIdentifier.DsmCcStream
  122. || StreamIdentifier == StreamIdentifier.TypeEStream)
  123. {
  124. Data = new byte[PacketLength];
  125. if (PacketLength > 0) Buffer.BlockCopy(buffer, position, Data, 0, Math.Min(Data.Length, buffer.Length - position));
  126. }
  127. else if (StreamIdentifier == StreamIdentifier.PaddingStream)
  128. {
  129. Stuffing = new byte[PacketLength];
  130. if (PacketLength > 0) Buffer.BlockCopy(buffer, position, Data, 0, Math.Min(Data.Length, buffer.Length - position));
  131. }
  132. #if DEBUG
  133. if(Data!=null && Data.Length > 0)
  134. {
  135. Debug.WriteLine("First 40 bytes of PES Data: " + String.Concat(Data.Take(40).Select((b) => b.ToString("X2")).ToArray()));
  136. }
  137. #endif
  138. }
  139. }
  140. public class PESHeader
  141. {
  142. private byte _header;
  143. private byte _flags;
  144. public int Length
  145. {
  146. get { return 3 + RemainingLength; }
  147. }
  148. // 2 bit Marker = 0x02
  149. // 2 bit ScramblingControl : binary 00 = not scrambled
  150. public int ScramblingControl
  151. {
  152. get { return (_header & 0x30) >> 4; }
  153. set
  154. {
  155. if (value > 4) throw new ArgumentException("ScramblingControl must be a value between 0 and 3.");
  156. _header = (byte) ((_header & 0xCF) | (value << 4));
  157. }
  158. }
  159. // 1 bit Priority
  160. public bool Priority
  161. {
  162. get { return (_header & 0x08) > 0; }
  163. set { _header = (byte)((_header & 0xF7) | (value ? 0x08 : 0)); }
  164. }
  165. // 1 bit DataAlignmentIndicator : 1 indicates packet header is immediately followed by video start code or audio sync word
  166. public bool DataAlignmentIndicator
  167. {
  168. get { return (_header & 0x04) > 0; }
  169. set { _header = (byte)((_header & 0xFB) | (value ? 0x04 : 0)); }
  170. }
  171. // 1 bit IsCopyrighted
  172. public bool IsCopyrighted
  173. {
  174. get { return (_header & 0x02) > 0; }
  175. set { _header = (byte)((_header & 0xFD) | (value ? 0x02 : 0)); }
  176. }
  177. // 1 bit IsOriginal
  178. public bool IsOriginal
  179. {
  180. get { return (_header & 0x01) > 0; }
  181. set { _header = (byte)((_header & 0xFE) | (value ? 0x01 : 0)); }
  182. }
  183. // 1 bit HasPTS
  184. private bool HasPTS
  185. {
  186. get { return (_flags & 0x80) > 0; }
  187. set { _flags = (byte)((_flags & 0x7F) | (value ? 0x80 : 0)); }
  188. }
  189. // 1 bit HasDTS
  190. private bool HasDTS
  191. {
  192. get { return (_flags & 0x40) > 0; }
  193. set { _flags = (byte)((_flags & 0xBF) | (value ? 0x40 : 0)); }
  194. }
  195. // 1 bit ESCRFlag
  196. private bool HasESCR
  197. {
  198. get { return (_flags & 0x20) > 0; }
  199. set { _flags = (byte)((_flags & 0xDF) | (value ? 0x20 : 0)); }
  200. }
  201. // 1 bit ESRateFlag
  202. private bool HasESRate
  203. {
  204. get { return (_flags & 0x10) > 0; }
  205. set { _flags = (byte)((_flags & 0xEF) | (value ? 0x10 : 0)); }
  206. }
  207. // 1 bit DSMTrickModeFlag
  208. private bool HasDSMTrickMode
  209. {
  210. get { return (_flags & 0x08) > 0; }
  211. set { _flags = (byte)((_flags & 0xF7) | (value ? 0x08 : 0)); }
  212. }
  213. // 1 bit AdditionalCopyInfoFlag
  214. private bool HasAdditionalCopyInfo
  215. {
  216. get { return (_flags & 0x04) > 0; }
  217. set { _flags = (byte)((_flags & 0xFB) | (value ? 0x04 : 0)); }
  218. }
  219. // 1 bit CRCFlag
  220. private bool HasCRC
  221. {
  222. get { return (_flags & 0x02) > 0; }
  223. set { _flags = (byte)((_flags & 0xFD) | (value ? 0x02 : 0)); }
  224. }
  225. // 1 bit ExtensionFlag
  226. private bool HasExtension
  227. {
  228. get { return (_flags & 0x01) > 0; }
  229. set { _flags = (byte)((_flags & 0xFE) | (value ? 0x01 : 0)); }
  230. }
  231. // TODO: This should actually be calculated for serialization
  232. private byte RemainingLength { get; set; }
  233. private byte[] _pts;
  234. /// <summary>
  235. /// Presentation Time Stamp
  236. /// </summary>
  237. public TimeSpan? PTS
  238. {
  239. get
  240. {
  241. if (_pts != null)
  242. {
  243. long pts = ((_pts[0] & 0x0E) << 29) | (_pts[1] << 22) | ((_pts[2] & 0xFE) << 14) | (_pts[3] << 7) |
  244. ((_pts[4] & 0xFE) >> 1);
  245. return new TimeSpan((pts * 1111111) / 10000); // Convert 90khz to Ticks (multiply by 111.1111)
  246. }
  247. return null;
  248. }
  249. set
  250. {
  251. if (!value.HasValue) _pts = null;
  252. else
  253. {
  254. var pts = (value.Value.Ticks * 10000) / 1111111;
  255. }
  256. }
  257. }
  258. private byte[] _dts;
  259. public TimeSpan? DTS
  260. {
  261. get
  262. {
  263. if (_dts != null)
  264. {
  265. long dts = ((_dts[0] & 0x0E) << 29) | (_dts[1] << 22) | ((_dts[2] & 0xFE) << 14) | (_dts[3] << 7) |
  266. ((_dts[4] & 0xFE) >> 1);
  267. return new TimeSpan((dts * 1111111) / 10000); // Convert 90khz to Ticks (multiply by 111.1111)
  268. }
  269. return null;
  270. }
  271. }
  272. // Optional Fields
  273. public byte[] Stuffing { get; set; }
  274. public PESHeader(byte[] buffer, int offset = 3)
  275. {
  276. var position = offset;
  277. // TODO: Check for valid marker?
  278. _header = buffer[position++];
  279. _flags = buffer[position++];
  280. RemainingLength = buffer[position++];
  281. var dataOffset = position + RemainingLength;
  282. if(HasPTS)
  283. {
  284. _pts = new byte[5];
  285. Buffer.BlockCopy(buffer, position, _pts, 0, 5);
  286. position += 5;
  287. Debug.WriteLine("Has PTS: " + this.PTS);
  288. }
  289. if(HasDTS)
  290. {
  291. _dts = new byte[5];
  292. Buffer.BlockCopy(buffer, position, _dts, 0, 5);
  293. position += 5;
  294. Debug.WriteLine("Has DTS: " + this.DTS);
  295. }
  296. if(HasESCR)
  297. {
  298. Debug.WriteLine("Has ESCR");
  299. // TODO: Load ESCR
  300. position += 6;
  301. }
  302. if(HasESRate)
  303. {
  304. Debug.WriteLine("Has ES Rate");
  305. // TODO: Load ESRate
  306. position += 3;
  307. }
  308. if(HasDSMTrickMode)
  309. {
  310. Debug.WriteLine("Has DSM Trick Mode");
  311. // TODO: Load DSMTrickMode
  312. position += 1;
  313. }
  314. if(HasAdditionalCopyInfo)
  315. {
  316. Debug.WriteLine("Has Additional Copyright Info");
  317. // TODO: Load AdditionalCopyInfo
  318. position += 1;
  319. }
  320. if(HasCRC)
  321. {
  322. Debug.WriteLine("Has CRC");
  323. // TODO: Load CRC
  324. position += 2;
  325. }
  326. if(HasExtension)
  327. {
  328. Debug.WriteLine("Has Extension");
  329. // TODO: Load PES Extension
  330. byte pesExtensionHeader = buffer[position++];
  331. bool hasPesPrivateData = (pesExtensionHeader & 0x80) > 0;
  332. if(hasPesPrivateData)
  333. {
  334. }
  335. }
  336. }
  337. }
  338. }