exp-golomb.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. 'use strict';
  2. var ExpGolomb;
  3. /**
  4. * Parser for exponential Golomb codes, a variable-bitwidth number encoding
  5. * scheme used by h264.
  6. */
  7. ExpGolomb = function(workingData) {
  8. var
  9. // the number of bytes left to examine in workingData
  10. workingBytesAvailable = workingData.byteLength,
  11. // the current word being examined
  12. workingWord = 0, // :uint
  13. // the number of bits left to examine in the current word
  14. workingBitsAvailable = 0; // :uint;
  15. // ():uint
  16. this.length = function() {
  17. return (8 * workingBytesAvailable);
  18. };
  19. // ():uint
  20. this.bitsAvailable = function() {
  21. return (8 * workingBytesAvailable) + workingBitsAvailable;
  22. };
  23. // ():void
  24. this.loadWord = function() {
  25. var
  26. position = workingData.byteLength - workingBytesAvailable,
  27. workingBytes = new Uint8Array(4),
  28. availableBytes = Math.min(4, workingBytesAvailable);
  29. if (availableBytes === 0) {
  30. throw new Error('no bytes available');
  31. }
  32. workingBytes.set(workingData.subarray(position,
  33. position + availableBytes));
  34. workingWord = new DataView(workingBytes.buffer).getUint32(0);
  35. // track the amount of workingData that has been processed
  36. workingBitsAvailable = availableBytes * 8;
  37. workingBytesAvailable -= availableBytes;
  38. };
  39. // (count:int):void
  40. this.skipBits = function(count) {
  41. var skipBytes; // :int
  42. if (workingBitsAvailable > count) {
  43. workingWord <<= count;
  44. workingBitsAvailable -= count;
  45. } else {
  46. count -= workingBitsAvailable;
  47. skipBytes = Math.floor(count / 8);
  48. count -= (skipBytes * 8);
  49. workingBytesAvailable -= skipBytes;
  50. this.loadWord();
  51. workingWord <<= count;
  52. workingBitsAvailable -= count;
  53. }
  54. };
  55. // (size:int):uint
  56. this.readBits = function(size) {
  57. var
  58. bits = Math.min(workingBitsAvailable, size), // :uint
  59. valu = workingWord >>> (32 - bits); // :uint
  60. // if size > 31, handle error
  61. workingBitsAvailable -= bits;
  62. if (workingBitsAvailable > 0) {
  63. workingWord <<= bits;
  64. } else if (workingBytesAvailable > 0) {
  65. this.loadWord();
  66. }
  67. bits = size - bits;
  68. if (bits > 0) {
  69. return valu << bits | this.readBits(bits);
  70. }
  71. return valu;
  72. };
  73. // ():uint
  74. this.skipLeadingZeros = function() {
  75. var leadingZeroCount; // :uint
  76. for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
  77. if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
  78. // the first bit of working word is 1
  79. workingWord <<= leadingZeroCount;
  80. workingBitsAvailable -= leadingZeroCount;
  81. return leadingZeroCount;
  82. }
  83. }
  84. // we exhausted workingWord and still have not found a 1
  85. this.loadWord();
  86. return leadingZeroCount + this.skipLeadingZeros();
  87. };
  88. // ():void
  89. this.skipUnsignedExpGolomb = function() {
  90. this.skipBits(1 + this.skipLeadingZeros());
  91. };
  92. // ():void
  93. this.skipExpGolomb = function() {
  94. this.skipBits(1 + this.skipLeadingZeros());
  95. };
  96. // ():uint
  97. this.readUnsignedExpGolomb = function() {
  98. var clz = this.skipLeadingZeros(); // :uint
  99. return this.readBits(clz + 1) - 1;
  100. };
  101. // ():int
  102. this.readExpGolomb = function() {
  103. var valu = this.readUnsignedExpGolomb(); // :int
  104. if (0x01 & valu) {
  105. // the number is odd if the low order bit is set
  106. return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
  107. }
  108. return -1 * (valu >>> 1); // divide by two then make it negative
  109. };
  110. // Some convenience functions
  111. // :Boolean
  112. this.readBoolean = function() {
  113. return this.readBits(1) === 1;
  114. };
  115. // ():int
  116. this.readUnsignedByte = function() {
  117. return this.readBits(8);
  118. };
  119. this.loadWord();
  120. };
  121. module.exports = ExpGolomb;