199 lines
4.4 KiB
JavaScript
199 lines
4.4 KiB
JavaScript
JSMpeg.BitBuffer = (function(){ "use strict";
|
|
|
|
var BitBuffer = function(bufferOrLength, mode) {
|
|
if (typeof(bufferOrLength) === 'object') {
|
|
this.bytes = (bufferOrLength instanceof Uint8Array)
|
|
? bufferOrLength
|
|
: new Uint8Array(bufferOrLength);
|
|
|
|
this.byteLength = this.bytes.length;
|
|
}
|
|
else {
|
|
this.bytes = new Uint8Array(bufferOrLength || 1024*1024);
|
|
this.byteLength = 0;
|
|
}
|
|
|
|
this.mode = mode || BitBuffer.MODE.EXPAND;
|
|
this.index = 0;
|
|
};
|
|
|
|
BitBuffer.prototype.resize = function(size) {
|
|
var newBytes = new Uint8Array(size);
|
|
if (this.byteLength !== 0) {
|
|
this.byteLength = Math.min(this.byteLength, size);
|
|
newBytes.set(this.bytes, 0, this.byteLength);
|
|
}
|
|
this.bytes = newBytes;
|
|
this.index = Math.min(this.index, this.byteLength << 3);
|
|
};
|
|
|
|
BitBuffer.prototype.evict = function(sizeNeeded) {
|
|
var bytePos = this.index >> 3,
|
|
available = this.bytes.length - this.byteLength;
|
|
|
|
// If the current index is the write position, we can simply reset both
|
|
// to 0. Also reset (and throw away yet unread data) if we won't be able
|
|
// to fit the new data in even after a normal eviction.
|
|
if (
|
|
this.index === this.byteLength << 3 ||
|
|
sizeNeeded > available + bytePos // emergency evac
|
|
) {
|
|
this.byteLength = 0;
|
|
this.index = 0;
|
|
return;
|
|
}
|
|
else if (bytePos === 0) {
|
|
// Nothing read yet - we can't evict anything
|
|
return;
|
|
}
|
|
|
|
// Some browsers don't support copyWithin() yet - we may have to do
|
|
// it manually using set and a subarray
|
|
if (this.bytes.copyWithin) {
|
|
this.bytes.copyWithin(0, bytePos, this.byteLength);
|
|
}
|
|
else {
|
|
this.bytes.set(this.bytes.subarray(bytePos, this.byteLength));
|
|
}
|
|
|
|
this.byteLength = this.byteLength - bytePos;
|
|
this.index -= bytePos << 3;
|
|
return;
|
|
};
|
|
|
|
BitBuffer.prototype.write = function(buffers) {
|
|
var isArrayOfBuffers = (typeof(buffers[0]) === 'object'),
|
|
totalLength = 0,
|
|
available = this.bytes.length - this.byteLength;
|
|
|
|
// Calculate total byte length
|
|
if (isArrayOfBuffers) {
|
|
var totalLength = 0;
|
|
for (var i = 0; i < buffers.length; i++) {
|
|
totalLength += buffers[i].byteLength;
|
|
}
|
|
}
|
|
else {
|
|
totalLength = buffers.byteLength;
|
|
}
|
|
|
|
// Do we need to resize or evict?
|
|
if (totalLength > available) {
|
|
if (this.mode === BitBuffer.MODE.EXPAND) {
|
|
var newSize = Math.max(
|
|
this.bytes.length * 2,
|
|
totalLength - available
|
|
);
|
|
this.resize(newSize)
|
|
}
|
|
else {
|
|
this.evict(totalLength);
|
|
}
|
|
}
|
|
|
|
if (isArrayOfBuffers) {
|
|
for (var i = 0; i < buffers.length; i++) {
|
|
this.appendSingleBuffer(buffers[i]);
|
|
}
|
|
}
|
|
else {
|
|
this.appendSingleBuffer(buffers);
|
|
}
|
|
|
|
return totalLength;
|
|
};
|
|
|
|
BitBuffer.prototype.appendSingleBuffer = function(buffer) {
|
|
buffer = buffer instanceof Uint8Array
|
|
? buffer
|
|
: new Uint8Array(buffer);
|
|
|
|
this.bytes.set(buffer, this.byteLength);
|
|
this.byteLength += buffer.length;
|
|
};
|
|
|
|
BitBuffer.prototype.findNextStartCode = function() {
|
|
for (var i = (this.index+7 >> 3); i < this.byteLength; i++) {
|
|
if(
|
|
this.bytes[i] == 0x00 &&
|
|
this.bytes[i+1] == 0x00 &&
|
|
this.bytes[i+2] == 0x01
|
|
) {
|
|
this.index = (i+4) << 3;
|
|
return this.bytes[i+3];
|
|
}
|
|
}
|
|
this.index = (this.byteLength << 3);
|
|
return -1;
|
|
};
|
|
|
|
BitBuffer.prototype.findStartCode = function(code) {
|
|
var current = 0;
|
|
while (true) {
|
|
current = this.findNextStartCode();
|
|
if (current === code || current === -1) {
|
|
return current;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
BitBuffer.prototype.nextBytesAreStartCode = function() {
|
|
var i = (this.index+7 >> 3);
|
|
return (
|
|
i >= this.byteLength || (
|
|
this.bytes[i] == 0x00 &&
|
|
this.bytes[i+1] == 0x00 &&
|
|
this.bytes[i+2] == 0x01
|
|
)
|
|
);
|
|
};
|
|
|
|
BitBuffer.prototype.peek = function(count) {
|
|
var offset = this.index;
|
|
var value = 0;
|
|
while (count) {
|
|
var currentByte = this.bytes[offset >> 3],
|
|
remaining = 8 - (offset & 7), // remaining bits in byte
|
|
read = remaining < count ? remaining : count, // bits in this run
|
|
shift = remaining - read,
|
|
mask = (0xff >> (8-read));
|
|
|
|
value = (value << read) | ((currentByte & (mask << shift)) >> shift);
|
|
|
|
offset += read;
|
|
count -= read;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
BitBuffer.prototype.read = function(count) {
|
|
var value = this.peek(count);
|
|
this.index += count;
|
|
return value;
|
|
};
|
|
|
|
BitBuffer.prototype.skip = function(count) {
|
|
return (this.index += count);
|
|
};
|
|
|
|
BitBuffer.prototype.rewind = function(count) {
|
|
this.index = Math.max(this.index - count, 0);
|
|
};
|
|
|
|
BitBuffer.prototype.has = function(count) {
|
|
return ((this.byteLength << 3) - this.index) >= count;
|
|
};
|
|
|
|
BitBuffer.MODE = {
|
|
EVICT: 1,
|
|
EXPAND: 2
|
|
};
|
|
|
|
return BitBuffer;
|
|
|
|
})();
|
|
|
|
|