Source: lib/util/exp_golomb.js

  1. /**
  2. * @license
  3. * Copyright Brightcove, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. goog.provide('shaka.util.ExpGolomb');
  18. goog.require('shaka.util.BufferUtils');
  19. goog.require('shaka.util.DataViewReader');
  20. /**
  21. * @summary
  22. * Parser for exponential Golomb codes, a variable-bit width number encoding
  23. * scheme used by h264.
  24. * Based on https://github.com/videojs/mux.js/blob/main/lib/utils/exp-golomb.js
  25. *
  26. * @export
  27. */
  28. shaka.util.ExpGolomb = class {
  29. /**
  30. * @param {!Uint8Array} data
  31. * @param {boolean=} convertEbsp2rbsp
  32. */
  33. constructor(data, convertEbsp2rbsp = false) {
  34. /** @private {!Uint8Array} */
  35. this.data_ = data;
  36. if (convertEbsp2rbsp) {
  37. this.data_ = this.ebsp2rbsp_(data);
  38. }
  39. /** @private {number} */
  40. this.workingBytesAvailable_ = this.data_.byteLength;
  41. // the current word being examined
  42. /** @private {number} */
  43. this.workingWord_ = 0;
  44. // the number of bits left to examine in the current word
  45. /** @private {number} */
  46. this.workingBitsAvailable_ = 0;
  47. }
  48. /**
  49. * @param {!Uint8Array} data
  50. * @return {!Uint8Array}
  51. * @private
  52. */
  53. ebsp2rbsp_(data) {
  54. const ret = new Uint8Array(data.byteLength);
  55. let retIndex = 0;
  56. for (let i = 0; i < data.byteLength; i++) {
  57. if (i >= 2) {
  58. // Unescape: Skip 0x03 after 00 00
  59. if (data[i] == 0x03 && data[i - 1] == 0x00 && data[i - 2] == 0x00) {
  60. continue;
  61. }
  62. }
  63. ret[retIndex] = data[i];
  64. retIndex++;
  65. }
  66. return shaka.util.BufferUtils.toUint8(ret, 0, retIndex);
  67. }
  68. /**
  69. * Load the next word
  70. *
  71. * @private
  72. */
  73. loadWord_() {
  74. const position = this.data_.byteLength - this.workingBytesAvailable_;
  75. const bytes = new Uint8Array(4);
  76. const availableBytes = Math.min(4, this.workingBytesAvailable_);
  77. if (availableBytes === 0) {
  78. return;
  79. }
  80. bytes.set(this.data_.subarray(position, position + availableBytes));
  81. const dataView = new shaka.util.DataViewReader(
  82. bytes, shaka.util.DataViewReader.Endianness.BIG_ENDIAN);
  83. this.workingWord_ = dataView.readUint32();
  84. // track the amount of data that has been processed
  85. this.workingBitsAvailable_ = availableBytes * 8;
  86. this.workingBytesAvailable_ -= availableBytes;
  87. }
  88. /**
  89. * Skip n bits
  90. *
  91. * @param {number} count
  92. */
  93. skipBits(count) {
  94. if (this.workingBitsAvailable_ <= count) {
  95. count -= this.workingBitsAvailable_;
  96. const skipBytes = Math.floor(count / 8);
  97. count -= (skipBytes * 8);
  98. this.workingBitsAvailable_ -= skipBytes;
  99. this.loadWord_();
  100. }
  101. this.workingWord_ <<= count;
  102. this.workingBitsAvailable_ -= count;
  103. }
  104. /**
  105. * Read n bits
  106. *
  107. * @param {number} size
  108. * @return {number}
  109. */
  110. readBits(size) {
  111. let bits = Math.min(this.workingBitsAvailable_, size);
  112. const valu = this.workingWord_ >>> (32 - bits);
  113. this.workingBitsAvailable_ -= bits;
  114. if (this.workingBitsAvailable_ > 0) {
  115. this.workingWord_ <<= bits;
  116. } else if (this.workingBytesAvailable_ > 0) {
  117. this.loadWord_();
  118. }
  119. bits = size - bits;
  120. if (bits > 0) {
  121. return (valu << bits) | this.readBits(bits);
  122. }
  123. return valu;
  124. }
  125. /**
  126. * Return the number of skip leading zeros
  127. *
  128. * @return {number}
  129. * @private
  130. */
  131. skipLeadingZeros_() {
  132. let i;
  133. for (i = 0; i < this.workingBitsAvailable_; ++i) {
  134. if ((this.workingWord_ & (0x80000000 >>> i)) !== 0) {
  135. // the first bit of working word is 1
  136. this.workingWord_ <<= i;
  137. this.workingBitsAvailable_ -= i;
  138. return i;
  139. }
  140. }
  141. // we exhausted workingWord and still have not found a 1
  142. this.loadWord_();
  143. return i + this.skipLeadingZeros_();
  144. }
  145. /**
  146. * Skip exponential Golomb
  147. */
  148. skipExpGolomb() {
  149. this.skipBits(1 + this.skipLeadingZeros_());
  150. }
  151. /**
  152. * Return unsigned exponential Golomb
  153. *
  154. * @return {number}
  155. */
  156. readUnsignedExpGolomb() {
  157. const clz = this.skipLeadingZeros_();
  158. return this.readBits(clz + 1) - 1;
  159. }
  160. /**
  161. * Return exponential Golomb
  162. *
  163. * @return {number}
  164. */
  165. readExpGolomb() {
  166. const valu = this.readUnsignedExpGolomb();
  167. if (0x01 & valu) {
  168. // the number is odd if the low order bit is set
  169. // add 1 to make it even, and divide by 2
  170. return (1 + valu) >>> 1;
  171. }
  172. // divide by two then make it negative
  173. return -1 * (valu >>> 1);
  174. }
  175. /**
  176. * Read 1 bit as boolean
  177. *
  178. * @return {boolean}
  179. */
  180. readBoolean() {
  181. return this.readBits(1) === 1;
  182. }
  183. /**
  184. * Read 8 bits
  185. *
  186. * @return {number}
  187. */
  188. readUnsignedByte() {
  189. return this.readBits(8);
  190. }
  191. /**
  192. * The scaling list is optionally transmitted as part of a Sequence Parameter
  193. * Set (SPS).
  194. *
  195. * @param {number} count the number of entries in this scaling list
  196. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  197. */
  198. skipScalingList(count) {
  199. let lastScale = 8;
  200. let nextScale = 8;
  201. for (let j = 0; j < count; j++) {
  202. if (nextScale !== 0) {
  203. const deltaScale = this.readExpGolomb();
  204. nextScale = (lastScale + deltaScale + 256) % 256;
  205. }
  206. lastScale = (nextScale === 0) ? lastScale : nextScale;
  207. }
  208. }
  209. /**
  210. * Return the slice type
  211. *
  212. * @return {number}
  213. */
  214. readSliceType() {
  215. // skip Nalu type
  216. this.readUnsignedByte();
  217. // discard first_mb_in_slice
  218. this.readUnsignedExpGolomb();
  219. // return slice_type
  220. return this.readUnsignedExpGolomb();
  221. }
  222. };