Home Reference Source

src/demux/exp-golomb.ts

  1. /**
  2. * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
  3. */
  4.  
  5. import { logger } from '../utils/logger';
  6.  
  7. class ExpGolomb {
  8. private data: Uint8Array;
  9. public bytesAvailable: number;
  10. private word: number;
  11. private bitsAvailable: number;
  12.  
  13. constructor(data: Uint8Array) {
  14. this.data = data;
  15. // the number of bytes left to examine in this.data
  16. this.bytesAvailable = data.byteLength;
  17. // the current word being examined
  18. this.word = 0; // :uint
  19. // the number of bits left to examine in the current word
  20. this.bitsAvailable = 0; // :uint
  21. }
  22.  
  23. // ():void
  24. loadWord(): void {
  25. const data = this.data;
  26. const bytesAvailable = this.bytesAvailable;
  27. const position = data.byteLength - bytesAvailable;
  28. const workingBytes = new Uint8Array(4);
  29. const availableBytes = Math.min(4, bytesAvailable);
  30. if (availableBytes === 0) {
  31. throw new Error('no bytes available');
  32. }
  33.  
  34. workingBytes.set(data.subarray(position, position + availableBytes));
  35. this.word = new DataView(workingBytes.buffer).getUint32(0);
  36. // track the amount of this.data that has been processed
  37. this.bitsAvailable = availableBytes * 8;
  38. this.bytesAvailable -= availableBytes;
  39. }
  40.  
  41. // (count:int):void
  42. skipBits(count: number): void {
  43. let skipBytes; // :int
  44. count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable);
  45. if (this.bitsAvailable > count) {
  46. this.word <<= count;
  47. this.bitsAvailable -= count;
  48. } else {
  49. count -= this.bitsAvailable;
  50. skipBytes = count >> 3;
  51. count -= skipBytes << 3;
  52. this.bytesAvailable -= skipBytes;
  53. this.loadWord();
  54. this.word <<= count;
  55. this.bitsAvailable -= count;
  56. }
  57. }
  58.  
  59. // (size:int):uint
  60. readBits(size: number): number {
  61. let bits = Math.min(this.bitsAvailable, size); // :uint
  62. const valu = this.word >>> (32 - bits); // :uint
  63. if (size > 32) {
  64. logger.error('Cannot read more than 32 bits at a time');
  65. }
  66.  
  67. this.bitsAvailable -= bits;
  68. if (this.bitsAvailable > 0) {
  69. this.word <<= bits;
  70. } else if (this.bytesAvailable > 0) {
  71. this.loadWord();
  72. } else {
  73. throw new Error('no bits available');
  74. }
  75.  
  76. bits = size - bits;
  77. if (bits > 0 && this.bitsAvailable) {
  78. return (valu << bits) | this.readBits(bits);
  79. } else {
  80. return valu;
  81. }
  82. }
  83.  
  84. // ():uint
  85. skipLZ(): number {
  86. let leadingZeroCount; // :uint
  87. for (
  88. leadingZeroCount = 0;
  89. leadingZeroCount < this.bitsAvailable;
  90. ++leadingZeroCount
  91. ) {
  92. if ((this.word & (0x80000000 >>> leadingZeroCount)) !== 0) {
  93. // the first bit of working word is 1
  94. this.word <<= leadingZeroCount;
  95. this.bitsAvailable -= leadingZeroCount;
  96. return leadingZeroCount;
  97. }
  98. }
  99. // we exhausted word and still have not found a 1
  100. this.loadWord();
  101. return leadingZeroCount + this.skipLZ();
  102. }
  103.  
  104. // ():void
  105. skipUEG(): void {
  106. this.skipBits(1 + this.skipLZ());
  107. }
  108.  
  109. // ():void
  110. skipEG(): void {
  111. this.skipBits(1 + this.skipLZ());
  112. }
  113.  
  114. // ():uint
  115. readUEG(): number {
  116. const clz = this.skipLZ(); // :uint
  117. return this.readBits(clz + 1) - 1;
  118. }
  119.  
  120. // ():int
  121. readEG(): number {
  122. const valu = this.readUEG(); // :int
  123. if (0x01 & valu) {
  124. // the number is odd if the low order bit is set
  125. return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
  126. } else {
  127. return -1 * (valu >>> 1); // divide by two then make it negative
  128. }
  129. }
  130.  
  131. // Some convenience functions
  132. // :Boolean
  133. readBoolean(): boolean {
  134. return this.readBits(1) === 1;
  135. }
  136.  
  137. // ():int
  138. readUByte(): number {
  139. return this.readBits(8);
  140. }
  141.  
  142. // ():int
  143. readUShort(): number {
  144. return this.readBits(16);
  145. }
  146.  
  147. // ():int
  148. readUInt(): number {
  149. return this.readBits(32);
  150. }
  151.  
  152. /**
  153. * Advance the ExpGolomb decoder past a scaling list. The scaling
  154. * list is optionally transmitted as part of a sequence parameter
  155. * set and is not relevant to transmuxing.
  156. * @param count the number of entries in this scaling list
  157. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  158. */
  159. skipScalingList(count: number): void {
  160. let lastScale = 8;
  161. let nextScale = 8;
  162. let deltaScale;
  163. for (let j = 0; j < count; j++) {
  164. if (nextScale !== 0) {
  165. deltaScale = this.readEG();
  166. nextScale = (lastScale + deltaScale + 256) % 256;
  167. }
  168. lastScale = nextScale === 0 ? lastScale : nextScale;
  169. }
  170. }
  171.  
  172. /**
  173. * Read a sequence parameter set and return some interesting video
  174. * properties. A sequence parameter set is the H264 metadata that
  175. * describes the properties of upcoming video frames.
  176. * @param data {Uint8Array} the bytes of a sequence parameter set
  177. * @return {object} an object with configuration parsed from the
  178. * sequence parameter set, including the dimensions of the
  179. * associated video frames.
  180. */
  181. readSPS(): {
  182. width: number;
  183. height: number;
  184. pixelRatio: [number, number];
  185. } {
  186. let frameCropLeftOffset = 0;
  187. let frameCropRightOffset = 0;
  188. let frameCropTopOffset = 0;
  189. let frameCropBottomOffset = 0;
  190. let numRefFramesInPicOrderCntCycle;
  191. let scalingListCount;
  192. let i;
  193. const readUByte = this.readUByte.bind(this);
  194. const readBits = this.readBits.bind(this);
  195. const readUEG = this.readUEG.bind(this);
  196. const readBoolean = this.readBoolean.bind(this);
  197. const skipBits = this.skipBits.bind(this);
  198. const skipEG = this.skipEG.bind(this);
  199. const skipUEG = this.skipUEG.bind(this);
  200. const skipScalingList = this.skipScalingList.bind(this);
  201.  
  202. readUByte();
  203. const profileIdc = readUByte(); // profile_idc
  204. readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
  205. skipBits(3); // reserved_zero_3bits u(3),
  206. readUByte(); // level_idc u(8)
  207. skipUEG(); // seq_parameter_set_id
  208. // some profiles have more optional data we don't need
  209. if (
  210. profileIdc === 100 ||
  211. profileIdc === 110 ||
  212. profileIdc === 122 ||
  213. profileIdc === 244 ||
  214. profileIdc === 44 ||
  215. profileIdc === 83 ||
  216. profileIdc === 86 ||
  217. profileIdc === 118 ||
  218. profileIdc === 128
  219. ) {
  220. const chromaFormatIdc = readUEG();
  221. if (chromaFormatIdc === 3) {
  222. skipBits(1);
  223. } // separate_colour_plane_flag
  224.  
  225. skipUEG(); // bit_depth_luma_minus8
  226. skipUEG(); // bit_depth_chroma_minus8
  227. skipBits(1); // qpprime_y_zero_transform_bypass_flag
  228. if (readBoolean()) {
  229. // seq_scaling_matrix_present_flag
  230. scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
  231. for (i = 0; i < scalingListCount; i++) {
  232. if (readBoolean()) {
  233. // seq_scaling_list_present_flag[ i ]
  234. if (i < 6) {
  235. skipScalingList(16);
  236. } else {
  237. skipScalingList(64);
  238. }
  239. }
  240. }
  241. }
  242. }
  243. skipUEG(); // log2_max_frame_num_minus4
  244. const picOrderCntType = readUEG();
  245. if (picOrderCntType === 0) {
  246. readUEG(); // log2_max_pic_order_cnt_lsb_minus4
  247. } else if (picOrderCntType === 1) {
  248. skipBits(1); // delta_pic_order_always_zero_flag
  249. skipEG(); // offset_for_non_ref_pic
  250. skipEG(); // offset_for_top_to_bottom_field
  251. numRefFramesInPicOrderCntCycle = readUEG();
  252. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  253. skipEG();
  254. } // offset_for_ref_frame[ i ]
  255. }
  256. skipUEG(); // max_num_ref_frames
  257. skipBits(1); // gaps_in_frame_num_value_allowed_flag
  258. const picWidthInMbsMinus1 = readUEG();
  259. const picHeightInMapUnitsMinus1 = readUEG();
  260. const frameMbsOnlyFlag = readBits(1);
  261. if (frameMbsOnlyFlag === 0) {
  262. skipBits(1);
  263. } // mb_adaptive_frame_field_flag
  264.  
  265. skipBits(1); // direct_8x8_inference_flag
  266. if (readBoolean()) {
  267. // frame_cropping_flag
  268. frameCropLeftOffset = readUEG();
  269. frameCropRightOffset = readUEG();
  270. frameCropTopOffset = readUEG();
  271. frameCropBottomOffset = readUEG();
  272. }
  273. let pixelRatio: [number, number] = [1, 1];
  274. if (readBoolean()) {
  275. // vui_parameters_present_flag
  276. if (readBoolean()) {
  277. // aspect_ratio_info_present_flag
  278. const aspectRatioIdc = readUByte();
  279. switch (aspectRatioIdc) {
  280. case 1:
  281. pixelRatio = [1, 1];
  282. break;
  283. case 2:
  284. pixelRatio = [12, 11];
  285. break;
  286. case 3:
  287. pixelRatio = [10, 11];
  288. break;
  289. case 4:
  290. pixelRatio = [16, 11];
  291. break;
  292. case 5:
  293. pixelRatio = [40, 33];
  294. break;
  295. case 6:
  296. pixelRatio = [24, 11];
  297. break;
  298. case 7:
  299. pixelRatio = [20, 11];
  300. break;
  301. case 8:
  302. pixelRatio = [32, 11];
  303. break;
  304. case 9:
  305. pixelRatio = [80, 33];
  306. break;
  307. case 10:
  308. pixelRatio = [18, 11];
  309. break;
  310. case 11:
  311. pixelRatio = [15, 11];
  312. break;
  313. case 12:
  314. pixelRatio = [64, 33];
  315. break;
  316. case 13:
  317. pixelRatio = [160, 99];
  318. break;
  319. case 14:
  320. pixelRatio = [4, 3];
  321. break;
  322. case 15:
  323. pixelRatio = [3, 2];
  324. break;
  325. case 16:
  326. pixelRatio = [2, 1];
  327. break;
  328. case 255: {
  329. pixelRatio = [
  330. (readUByte() << 8) | readUByte(),
  331. (readUByte() << 8) | readUByte(),
  332. ];
  333. break;
  334. }
  335. }
  336. }
  337. }
  338. return {
  339. width: Math.ceil(
  340. (picWidthInMbsMinus1 + 1) * 16 -
  341. frameCropLeftOffset * 2 -
  342. frameCropRightOffset * 2
  343. ),
  344. height:
  345. (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 -
  346. (frameMbsOnlyFlag ? 2 : 4) *
  347. (frameCropTopOffset + frameCropBottomOffset),
  348. pixelRatio: pixelRatio,
  349. };
  350. }
  351.  
  352. readSliceType() {
  353. // skip NALu type
  354. this.readUByte();
  355. // discard first_mb_in_slice
  356. this.readUEG();
  357. // return slice_type
  358. return this.readUEG();
  359. }
  360. }
  361.  
  362. export default ExpGolomb;