h264.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /**
  2. * mux.js
  3. *
  4. * Copyright (c) Brightcove
  5. * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
  6. */
  7. 'use strict';
  8. var Stream = require('../utils/stream.js');
  9. var ExpGolomb = require('../utils/exp-golomb.js');
  10. var H264Stream, NalByteStream;
  11. var PROFILES_WITH_OPTIONAL_SPS_DATA;
  12. /**
  13. * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
  14. */
  15. NalByteStream = function() {
  16. var
  17. syncPoint = 0,
  18. i,
  19. buffer;
  20. NalByteStream.prototype.init.call(this);
  21. /*
  22. * Scans a byte stream and triggers a data event with the NAL units found.
  23. * @param {Object} data Event received from H264Stream
  24. * @param {Uint8Array} data.data The h264 byte stream to be scanned
  25. *
  26. * @see H264Stream.push
  27. */
  28. this.push = function(data) {
  29. var swapBuffer;
  30. if (!buffer) {
  31. buffer = data.data;
  32. } else {
  33. swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
  34. swapBuffer.set(buffer);
  35. swapBuffer.set(data.data, buffer.byteLength);
  36. buffer = swapBuffer;
  37. }
  38. var len = buffer.byteLength;
  39. // Rec. ITU-T H.264, Annex B
  40. // scan for NAL unit boundaries
  41. // a match looks like this:
  42. // 0 0 1 .. NAL .. 0 0 1
  43. // ^ sync point ^ i
  44. // or this:
  45. // 0 0 1 .. NAL .. 0 0 0
  46. // ^ sync point ^ i
  47. // advance the sync point to a NAL start, if necessary
  48. for (; syncPoint < len - 3; syncPoint++) {
  49. if (buffer[syncPoint + 2] === 1) {
  50. // the sync point is properly aligned
  51. i = syncPoint + 5;
  52. break;
  53. }
  54. }
  55. while (i < len) {
  56. // look at the current byte to determine if we've hit the end of
  57. // a NAL unit boundary
  58. switch (buffer[i]) {
  59. case 0:
  60. // skip past non-sync sequences
  61. if (buffer[i - 1] !== 0) {
  62. i += 2;
  63. break;
  64. } else if (buffer[i - 2] !== 0) {
  65. i++;
  66. break;
  67. }
  68. // deliver the NAL unit if it isn't empty
  69. if (syncPoint + 3 !== i - 2) {
  70. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  71. }
  72. // drop trailing zeroes
  73. do {
  74. i++;
  75. } while (buffer[i] !== 1 && i < len);
  76. syncPoint = i - 2;
  77. i += 3;
  78. break;
  79. case 1:
  80. // skip past non-sync sequences
  81. if (buffer[i - 1] !== 0 ||
  82. buffer[i - 2] !== 0) {
  83. i += 3;
  84. break;
  85. }
  86. // deliver the NAL unit
  87. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  88. syncPoint = i - 2;
  89. i += 3;
  90. break;
  91. default:
  92. // the current byte isn't a one or zero, so it cannot be part
  93. // of a sync sequence
  94. i += 3;
  95. break;
  96. }
  97. }
  98. // filter out the NAL units that were delivered
  99. buffer = buffer.subarray(syncPoint);
  100. i -= syncPoint;
  101. syncPoint = 0;
  102. };
  103. this.reset = function() {
  104. buffer = null;
  105. syncPoint = 0;
  106. this.trigger('reset');
  107. };
  108. this.flush = function() {
  109. // deliver the last buffered NAL unit
  110. if (buffer && buffer.byteLength > 3) {
  111. this.trigger('data', buffer.subarray(syncPoint + 3));
  112. }
  113. // reset the stream state
  114. buffer = null;
  115. syncPoint = 0;
  116. this.trigger('done');
  117. };
  118. this.endTimeline = function() {
  119. this.flush();
  120. this.trigger('endedtimeline');
  121. };
  122. };
  123. NalByteStream.prototype = new Stream();
  124. // values of profile_idc that indicate additional fields are included in the SPS
  125. // see Recommendation ITU-T H.264 (4/2013),
  126. // 7.3.2.1.1 Sequence parameter set data syntax
  127. PROFILES_WITH_OPTIONAL_SPS_DATA = {
  128. 100: true,
  129. 110: true,
  130. 122: true,
  131. 244: true,
  132. 44: true,
  133. 83: true,
  134. 86: true,
  135. 118: true,
  136. 128: true,
  137. // TODO: the three profiles below don't
  138. // appear to have sps data in the specificiation anymore?
  139. 138: true,
  140. 139: true,
  141. 134: true
  142. };
  143. /**
  144. * Accepts input from a ElementaryStream and produces H.264 NAL unit data
  145. * events.
  146. */
  147. H264Stream = function() {
  148. var
  149. nalByteStream = new NalByteStream(),
  150. self,
  151. trackId,
  152. currentPts,
  153. currentDts,
  154. discardEmulationPreventionBytes,
  155. readSequenceParameterSet,
  156. skipScalingList;
  157. H264Stream.prototype.init.call(this);
  158. self = this;
  159. /*
  160. * Pushes a packet from a stream onto the NalByteStream
  161. *
  162. * @param {Object} packet - A packet received from a stream
  163. * @param {Uint8Array} packet.data - The raw bytes of the packet
  164. * @param {Number} packet.dts - Decode timestamp of the packet
  165. * @param {Number} packet.pts - Presentation timestamp of the packet
  166. * @param {Number} packet.trackId - The id of the h264 track this packet came from
  167. * @param {('video'|'audio')} packet.type - The type of packet
  168. *
  169. */
  170. this.push = function(packet) {
  171. if (packet.type !== 'video') {
  172. return;
  173. }
  174. trackId = packet.trackId;
  175. currentPts = packet.pts;
  176. currentDts = packet.dts;
  177. nalByteStream.push(packet);
  178. };
  179. /*
  180. * Identify NAL unit types and pass on the NALU, trackId, presentation and decode timestamps
  181. * for the NALUs to the next stream component.
  182. * Also, preprocess caption and sequence parameter NALUs.
  183. *
  184. * @param {Uint8Array} data - A NAL unit identified by `NalByteStream.push`
  185. * @see NalByteStream.push
  186. */
  187. nalByteStream.on('data', function(data) {
  188. var
  189. event = {
  190. trackId: trackId,
  191. pts: currentPts,
  192. dts: currentDts,
  193. data: data,
  194. nalUnitTypeCode: data[0] & 0x1f
  195. };
  196. switch (event.nalUnitTypeCode) {
  197. case 0x05:
  198. event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
  199. break;
  200. case 0x06:
  201. event.nalUnitType = 'sei_rbsp';
  202. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  203. break;
  204. case 0x07:
  205. event.nalUnitType = 'seq_parameter_set_rbsp';
  206. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  207. event.config = readSequenceParameterSet(event.escapedRBSP);
  208. break;
  209. case 0x08:
  210. event.nalUnitType = 'pic_parameter_set_rbsp';
  211. break;
  212. case 0x09:
  213. event.nalUnitType = 'access_unit_delimiter_rbsp';
  214. break;
  215. default:
  216. break;
  217. }
  218. // This triggers data on the H264Stream
  219. self.trigger('data', event);
  220. });
  221. nalByteStream.on('done', function() {
  222. self.trigger('done');
  223. });
  224. nalByteStream.on('partialdone', function() {
  225. self.trigger('partialdone');
  226. });
  227. nalByteStream.on('reset', function() {
  228. self.trigger('reset');
  229. });
  230. nalByteStream.on('endedtimeline', function() {
  231. self.trigger('endedtimeline');
  232. });
  233. this.flush = function() {
  234. nalByteStream.flush();
  235. };
  236. this.partialFlush = function() {
  237. nalByteStream.partialFlush();
  238. };
  239. this.reset = function() {
  240. nalByteStream.reset();
  241. };
  242. this.endTimeline = function() {
  243. nalByteStream.endTimeline();
  244. };
  245. /**
  246. * Advance the ExpGolomb decoder past a scaling list. The scaling
  247. * list is optionally transmitted as part of a sequence parameter
  248. * set and is not relevant to transmuxing.
  249. * @param count {number} the number of entries in this scaling list
  250. * @param expGolombDecoder {object} an ExpGolomb pointed to the
  251. * start of a scaling list
  252. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  253. */
  254. skipScalingList = function(count, expGolombDecoder) {
  255. var
  256. lastScale = 8,
  257. nextScale = 8,
  258. j,
  259. deltaScale;
  260. for (j = 0; j < count; j++) {
  261. if (nextScale !== 0) {
  262. deltaScale = expGolombDecoder.readExpGolomb();
  263. nextScale = (lastScale + deltaScale + 256) % 256;
  264. }
  265. lastScale = (nextScale === 0) ? lastScale : nextScale;
  266. }
  267. };
  268. /**
  269. * Expunge any "Emulation Prevention" bytes from a "Raw Byte
  270. * Sequence Payload"
  271. * @param data {Uint8Array} the bytes of a RBSP from a NAL
  272. * unit
  273. * @return {Uint8Array} the RBSP without any Emulation
  274. * Prevention Bytes
  275. */
  276. discardEmulationPreventionBytes = function(data) {
  277. var
  278. length = data.byteLength,
  279. emulationPreventionBytesPositions = [],
  280. i = 1,
  281. newLength, newData;
  282. // Find all `Emulation Prevention Bytes`
  283. while (i < length - 2) {
  284. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  285. emulationPreventionBytesPositions.push(i + 2);
  286. i += 2;
  287. } else {
  288. i++;
  289. }
  290. }
  291. // If no Emulation Prevention Bytes were found just return the original
  292. // array
  293. if (emulationPreventionBytesPositions.length === 0) {
  294. return data;
  295. }
  296. // Create a new array to hold the NAL unit data
  297. newLength = length - emulationPreventionBytesPositions.length;
  298. newData = new Uint8Array(newLength);
  299. var sourceIndex = 0;
  300. for (i = 0; i < newLength; sourceIndex++, i++) {
  301. if (sourceIndex === emulationPreventionBytesPositions[0]) {
  302. // Skip this byte
  303. sourceIndex++;
  304. // Remove this position index
  305. emulationPreventionBytesPositions.shift();
  306. }
  307. newData[i] = data[sourceIndex];
  308. }
  309. return newData;
  310. };
  311. /**
  312. * Read a sequence parameter set and return some interesting video
  313. * properties. A sequence parameter set is the H264 metadata that
  314. * describes the properties of upcoming video frames.
  315. * @param data {Uint8Array} the bytes of a sequence parameter set
  316. * @return {object} an object with configuration parsed from the
  317. * sequence parameter set, including the dimensions of the
  318. * associated video frames.
  319. */
  320. readSequenceParameterSet = function(data) {
  321. var
  322. frameCropLeftOffset = 0,
  323. frameCropRightOffset = 0,
  324. frameCropTopOffset = 0,
  325. frameCropBottomOffset = 0,
  326. sarScale = 1,
  327. expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
  328. chromaFormatIdc, picOrderCntType,
  329. numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
  330. picHeightInMapUnitsMinus1,
  331. frameMbsOnlyFlag,
  332. scalingListCount,
  333. sarRatio = [1, 1],
  334. aspectRatioIdc,
  335. i;
  336. expGolombDecoder = new ExpGolomb(data);
  337. profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
  338. profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
  339. levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
  340. expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
  341. // some profiles have more optional data we don't need
  342. if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
  343. chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
  344. if (chromaFormatIdc === 3) {
  345. expGolombDecoder.skipBits(1); // separate_colour_plane_flag
  346. }
  347. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
  348. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
  349. expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
  350. if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
  351. scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
  352. for (i = 0; i < scalingListCount; i++) {
  353. if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
  354. if (i < 6) {
  355. skipScalingList(16, expGolombDecoder);
  356. } else {
  357. skipScalingList(64, expGolombDecoder);
  358. }
  359. }
  360. }
  361. }
  362. }
  363. expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
  364. picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
  365. if (picOrderCntType === 0) {
  366. expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
  367. } else if (picOrderCntType === 1) {
  368. expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
  369. expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
  370. expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
  371. numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
  372. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  373. expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
  374. }
  375. }
  376. expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
  377. expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
  378. picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  379. picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  380. frameMbsOnlyFlag = expGolombDecoder.readBits(1);
  381. if (frameMbsOnlyFlag === 0) {
  382. expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
  383. }
  384. expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
  385. if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
  386. frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
  387. frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
  388. frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
  389. frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
  390. }
  391. if (expGolombDecoder.readBoolean()) {
  392. // vui_parameters_present_flag
  393. if (expGolombDecoder.readBoolean()) {
  394. // aspect_ratio_info_present_flag
  395. aspectRatioIdc = expGolombDecoder.readUnsignedByte();
  396. switch (aspectRatioIdc) {
  397. case 1: sarRatio = [1, 1]; break;
  398. case 2: sarRatio = [12, 11]; break;
  399. case 3: sarRatio = [10, 11]; break;
  400. case 4: sarRatio = [16, 11]; break;
  401. case 5: sarRatio = [40, 33]; break;
  402. case 6: sarRatio = [24, 11]; break;
  403. case 7: sarRatio = [20, 11]; break;
  404. case 8: sarRatio = [32, 11]; break;
  405. case 9: sarRatio = [80, 33]; break;
  406. case 10: sarRatio = [18, 11]; break;
  407. case 11: sarRatio = [15, 11]; break;
  408. case 12: sarRatio = [64, 33]; break;
  409. case 13: sarRatio = [160, 99]; break;
  410. case 14: sarRatio = [4, 3]; break;
  411. case 15: sarRatio = [3, 2]; break;
  412. case 16: sarRatio = [2, 1]; break;
  413. case 255: {
  414. sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
  415. expGolombDecoder.readUnsignedByte(),
  416. expGolombDecoder.readUnsignedByte() << 8 |
  417. expGolombDecoder.readUnsignedByte() ];
  418. break;
  419. }
  420. }
  421. if (sarRatio) {
  422. sarScale = sarRatio[0] / sarRatio[1];
  423. }
  424. }
  425. }
  426. return {
  427. profileIdc: profileIdc,
  428. levelIdc: levelIdc,
  429. profileCompatibility: profileCompatibility,
  430. width: (((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
  431. height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2),
  432. // sar is sample aspect ratio
  433. sarRatio: sarRatio
  434. };
  435. };
  436. };
  437. H264Stream.prototype = new Stream();
  438. module.exports = {
  439. H264Stream: H264Stream,
  440. NalByteStream: NalByteStream
  441. };