h264.js 15 KB

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