h264.js 13 KB

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