utils.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. var
  2. mp2t = require('../lib/m2ts'),
  3. id3Generator = require('./utils/id3-generator'),
  4. MP2T_PACKET_LENGTH = mp2t.MP2T_PACKET_LENGTH,
  5. PMT,
  6. PAT,
  7. generatePMT,
  8. pesHeader,
  9. packetize,
  10. transportPacket,
  11. videoPes,
  12. adtsFrame,
  13. audioPes,
  14. timedMetadataPes,
  15. binaryStringToArrayOfBytes,
  16. leftPad;
  17. PMT = [
  18. 0x47, // sync byte
  19. // tei:0 pusi:1 tp:0 pid:0 0000 0010 0000
  20. 0x40, 0x10,
  21. // tsc:01 afc:01 cc:0000 pointer_field:0000 0000
  22. 0x50, 0x00,
  23. // tid:0000 0010 ssi:0 0:0 r:00 sl:0000 0001 1100
  24. 0x02, 0x00, 0x1c,
  25. // pn:0000 0000 0000 0001
  26. 0x00, 0x01,
  27. // r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
  28. 0x01, 0x00, 0x00,
  29. // r:000 ppid:0 0011 1111 1111
  30. 0x03, 0xff,
  31. // r:0000 pil:0000 0000 0000
  32. 0x00, 0x00,
  33. // h264
  34. // st:0001 1010 r:000 epid:0 0000 0001 0001
  35. 0x1b, 0x00, 0x11,
  36. // r:0000 esil:0000 0000 0000
  37. 0x00, 0x00,
  38. // adts
  39. // st:0000 1111 r:000 epid:0 0000 0001 0010
  40. 0x0f, 0x00, 0x12,
  41. // r:0000 esil:0000 0000 0000
  42. 0x00, 0x00,
  43. // timed metadata
  44. // st:0001 0111 r:000 epid:0 0000 0001 0011
  45. 0x15, 0x00, 0x13,
  46. // r:0000 esil:0000 0000 0000
  47. 0x00, 0x00,
  48. // crc
  49. 0x00, 0x00, 0x00, 0x00
  50. ];
  51. /*
  52. Packet Header:
  53. | sb | tei pusi tp pid:5 | pid | tsc afc cc |
  54. with af:
  55. | afl | ... | <data> |
  56. without af:
  57. | <data> |
  58. PAT:
  59. | pf? | ... |
  60. | tid | ssi '0' r sl:4 | sl | tsi:8 |
  61. | tsi | r vn cni | sn | lsn |
  62. with program_number == '0':
  63. | pn | pn | r np:5 | np |
  64. otherwise:
  65. | pn | pn | r pmp:5 | pmp |
  66. */
  67. PAT = [
  68. 0x47, // sync byte
  69. // tei:0 pusi:1 tp:0 pid:0 0000 0000 0000
  70. 0x40, 0x00,
  71. // tsc:01 afc:01 cc:0000 pointer_field:0000 0000
  72. 0x50, 0x00,
  73. // tid:0000 0000 ssi:0 0:0 r:00 sl:0000 0000 0000
  74. 0x00, 0x00, 0x00,
  75. // tsi:0000 0000 0000 0000
  76. 0x00, 0x00,
  77. // r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
  78. 0x01, 0x00, 0x00,
  79. // pn:0000 0000 0000 0001
  80. 0x00, 0x01,
  81. // r:000 pmp:0 0000 0010 0000
  82. 0x00, 0x10,
  83. // crc32:0000 0000 0000 0000 0000 0000 0000 0000
  84. 0x00, 0x00, 0x00, 0x00
  85. ];
  86. generatePMT = function(options) {
  87. var PMT = [
  88. 0x47, // sync byte
  89. // tei:0 pusi:1 tp:0 pid:0 0000 0010 0000
  90. 0x40, 0x10,
  91. // tsc:01 afc:01 cc:0000 pointer_field:0000 0000
  92. 0x50, 0x00,
  93. // tid:0000 0010 ssi:0 0:0 r:00 sl:0000 0001 1100
  94. 0x02, 0x00, 0x1c,
  95. // pn:0000 0000 0000 0001
  96. 0x00, 0x01,
  97. // r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
  98. 0x01, 0x00, 0x00,
  99. // r:000 ppid:0 0011 1111 1111
  100. 0x03, 0xff,
  101. // r:0000 pil:0000 0000 0000
  102. 0x00, 0x00];
  103. if (options.hasVideo) {
  104. // h264
  105. PMT = PMT.concat([
  106. // st:0001 1010 r:000 epid:0 0000 0001 0001
  107. 0x1b, 0x00, 0x11,
  108. // r:0000 esil:0000 0000 0000
  109. 0x00, 0x00
  110. ]);
  111. }
  112. if (options.hasAudio) {
  113. // adts
  114. PMT = PMT.concat([
  115. // st:0000 1111 r:000 epid:0 0000 0001 0010
  116. 0x0f, 0x00, 0x12,
  117. // r:0000 esil:0000 0000 0000
  118. 0x00, 0x00
  119. ]);
  120. }
  121. if (options.hasMetadata) {
  122. // timed metadata
  123. PMT = PMT.concat([
  124. // st:0001 0111 r:000 epid:0 0000 0001 0011
  125. 0x15, 0x00, 0x13,
  126. // r:0000 esil:0000 0000 0000
  127. 0x00, 0x00
  128. ]);
  129. }
  130. // crc
  131. return PMT.concat([0x00, 0x00, 0x00, 0x00]);
  132. };
  133. pesHeader = function(first, pts, dataLength) {
  134. if (!dataLength) {
  135. dataLength = 0;
  136. } else {
  137. // Add the pes header length (only the portion after the
  138. // pes_packet_length field)
  139. dataLength += 3;
  140. }
  141. // PES_packet(), Rec. ITU-T H.222.0, Table 2-21
  142. var result = [
  143. // pscp:0000 0000 0000 0000 0000 0001
  144. 0x00, 0x00, 0x01,
  145. // sid:0000 0000 ppl:0000 0000 0000 0000
  146. 0x00, 0x00, 0x00,
  147. // 10 psc:00 pp:0 dai:1 c:0 ooc:0
  148. 0x84,
  149. // pdf:?0 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0
  150. 0x20 | (pts ? 0x80 : 0x00),
  151. // phdl:0000 0000
  152. (first ? 0x01 : 0x00) + (pts ? 0x05 : 0x00)
  153. ];
  154. // Only store 15 bits of the PTS for QUnit.testing purposes
  155. if (pts) {
  156. var
  157. pts32 = Math.floor(pts / 2), // right shift by 1
  158. leftMostBit = ((pts32 & 0x80000000) >>> 31) & 0x01,
  159. firstThree;
  160. pts = pts & 0xffffffff; // remove left most bit
  161. firstThree = (leftMostBit << 3) | (((pts & 0xc0000000) >>> 29) & 0x06) | 0x01;
  162. result.push((0x2 << 4) | firstThree);
  163. result.push((pts >>> 22) & 0xff);
  164. result.push(((pts >>> 14) | 0x01) & 0xff);
  165. result.push((pts >>> 7) & 0xff);
  166. result.push(((pts << 1) | 0x01) & 0xff);
  167. // Add the bytes spent on the pts info
  168. dataLength += 5;
  169. }
  170. if (first) {
  171. result.push(0x00);
  172. dataLength += 1;
  173. }
  174. // Finally set the pes_packet_length field
  175. result[4] = (dataLength & 0x0000FF00) >> 8;
  176. result[5] = dataLength & 0x000000FF;
  177. return result;
  178. };
  179. packetize = function(data) {
  180. var packet = new Uint8Array(MP2T_PACKET_LENGTH);
  181. packet.set(data);
  182. return packet;
  183. };
  184. /**
  185. * Helper function to create transport stream PES packets
  186. * @param pid {uint8} - the program identifier (PID)
  187. * @param data {arraylike} - the payload bytes
  188. * @payload first {boolean} - true if this PES should be a payload
  189. * unit start
  190. */
  191. transportPacket = function(pid, data, first, pts, isVideoData) {
  192. var
  193. adaptationFieldLength = 188 - data.length - 14 - (first ? 1 : 0) - (pts ? 5 : 0),
  194. // transport_packet(), Rec. ITU-T H.222.0, Table 2-2
  195. result = [
  196. // sync byte
  197. 0x47,
  198. // tei:0 pusi:1 tp:0 pid:0 0000 0001 0001
  199. 0x40, pid,
  200. // tsc:01 afc:11 cc:0000
  201. 0x70
  202. ].concat([
  203. // afl
  204. adaptationFieldLength & 0xff,
  205. // di:0 rai:0 espi:0 pf:0 of:0 spf:0 tpdf:0 afef:0
  206. 0x00
  207. ]),
  208. i;
  209. i = adaptationFieldLength - 1;
  210. while (i--) {
  211. // stuffing_bytes
  212. result.push(0xff);
  213. }
  214. // PES_packet(), Rec. ITU-T H.222.0, Table 2-21
  215. result = result.concat(pesHeader(first, pts, isVideoData ? 0 : data.length));
  216. return result.concat(data);
  217. };
  218. /**
  219. * Helper function to create video PES packets
  220. * @param data {arraylike} - the payload bytes
  221. * @payload first {boolean} - true if this PES should be a payload
  222. * unit start
  223. */
  224. videoPes = function(data, first, pts) {
  225. return transportPacket(0x11, [
  226. // NAL unit start code
  227. 0x00, 0x00, 0x01
  228. ].concat(data), first, pts, true);
  229. };
  230. /**
  231. * Helper function to create audio ADTS frame header
  232. * @param dataLength {number} - the payload byte count
  233. */
  234. adtsFrame = function(dataLength) {
  235. var frameLength = dataLength + 7;
  236. return [
  237. 0xff, 0xf1, // no CRC
  238. 0x10, // AAC Main, 44.1KHz
  239. 0xb0 | ((frameLength & 0x1800) >> 11), // 2 channels
  240. (frameLength & 0x7f8) >> 3,
  241. ((frameLength & 0x07) << 5) + 7, // frame length in bytes
  242. 0x00 // one AAC per ADTS frame
  243. ];
  244. };
  245. /**
  246. * Helper function to create audio PES packets
  247. * @param data {arraylike} - the payload bytes
  248. * @payload first {boolean} - true if this PES should be a payload
  249. * unit start
  250. */
  251. audioPes = function(data, first, pts) {
  252. return transportPacket(0x12,
  253. adtsFrame(data.length).concat(data),
  254. first, pts);
  255. };
  256. timedMetadataPes = function(data) {
  257. var id3 = id3Generator;
  258. return transportPacket(0x13, id3.id3Tag(id3.id3Frame('PRIV', 0x00, 0x01)));
  259. };
  260. binaryStringToArrayOfBytes = function(string) {
  261. var
  262. array = [],
  263. arrayIndex = 0,
  264. stringIndex = 0;
  265. while (stringIndex < string.length) {
  266. array[arrayIndex] = parseInt(string.slice(stringIndex, stringIndex + 8), 2);
  267. arrayIndex++;
  268. // next byte
  269. stringIndex += 8;
  270. }
  271. return array;
  272. };
  273. leftPad = function(string, targetLength) {
  274. if (string.length >= targetLength) {
  275. return string;
  276. }
  277. return new Array(targetLength - string.length + 1).join('0') + string;
  278. };
  279. module.exports = {
  280. PMT: PMT,
  281. PAT: PAT,
  282. generatePMT: generatePMT,
  283. pesHeader: pesHeader,
  284. packetize: packetize,
  285. transportPacket: transportPacket,
  286. videoPes: videoPes,
  287. adtsFrame: adtsFrame,
  288. audioPes: audioPes,
  289. timedMetadataPes: timedMetadataPes,
  290. binaryStringToArrayOfBytes: binaryStringToArrayOfBytes,
  291. leftPad: leftPad
  292. };