将树莓派作为一个Web服务器,然后外网可以实现访问。
这个网站的地址为“http://slin1972.vicp.cc:7000/index.html”

实现的网页HTML代码

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=640, initial-scale=1"/>
<title>jsmpeg streaming</title>
<style type="text/css">
body {
background: #333;
text-align: center;
margin-top: 10%;
}

#videoCanvas {
/* Always stretch the canvas to 640x480, regardless of its
internal size. */

width: 640px;
height: 480px;
}

</style>
</head>
<body>
<!-- The Canvas size specified here is the "initial" internal resolution. jsmpeg will
change this internal resolution to whatever the source provides. The size the
canvas is displayed on the website is dictated by the CSS style.
-->

<canvas id="videoCanvas" width="640" height="480">
<p>
Please use a browser that supports the Canvas Element, like
<a href="http://www.google.com/chrome">Chrome</a>,
<a href="http://www.mozilla.com/firefox/">Firefox</a>,
<a href="http://www.apple.com/safari/">Safari</a> or Internet Explorer 10
</p>
</canvas>
<script type="text/javascript" src="jsmpg.js"></script>
<script type="text/javascript">
// Show loading notice
var canvas = document.getElementById('videoCanvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#444';
ctx.fillText('Loading...', canvas.width/2-30, canvas.height/3);

// Setup the WebSocket connection and start the player
var client = new WebSocket('ws://slin1972.vicp.cc:7001/');
var player = new jsmpeg(client, {canvas:canvas});
</script>
</body>
</html>

样式CSS代码

        body {
background: #333;
text-align: center;
margin-top: 10%;
}

#videoCanvas {
/* Always stretch the canvas to 640x480, regardless of its
internal size. */

width: 640px;
height: 480px;
}

后台的JavaScript代码

(function(window){ "use strict";

// jsmpeg by Dominic Szablewski - phoboslab.org, github.com/phoboslab
//
// Consider this to be under MIT license. It's largely based an an Open Source
// Decoder for Java under GPL, while I looked at another Decoder from Nokia
// (under no particular license?) for certain aspects.
// I'm not sure if this work is "derivative" enough to have a different license
// but then again, who still cares about MPEG1?
//
// Based on "Java MPEG-1 Video Decoder and Player" by Korandi Zoltan:
// http://sourceforge.net/projects/javampeg1video/
//
// Inspired by "MPEG Decoder in Java ME" by Nokia:
// http://www.developer.nokia.com/Community/Wiki/MPEG_decoder_in_Java_ME


var requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();

var jsmpeg = window.jsmpeg = function( url, opts ) {
opts = opts || {};
this.benchmark = !!opts.benchmark;
this.canvas = opts.canvas || document.createElement('canvas');
this.autoplay = !!opts.autoplay;
this.loop = !!opts.loop;
this.externalLoadCallback = opts.onload || null;
this.externalDecodeCallback = opts.ondecodeframe || null;
this.externalFinishedCallback = opts.onfinished || null;
this.bwFilter = opts.bwFilter || false;

this.customIntraQuantMatrix = new Uint8Array(64);
this.customNonIntraQuantMatrix = new Uint8Array(64);
this.blockData = new Int32Array(64);

this.canvasContext = this.canvas.getContext('2d');

if( url instanceof WebSocket ) {
this.client = url;
this.client.onopen = this.initSocketClient.bind(this);
}
else {
this.load(url);
}
};



// ----------------------------------------------------------------------------
// Streaming over WebSockets

jsmpeg.prototype.waitForIntraFrame = true;
jsmpeg.prototype.socketBufferSize = 512 * 1024; // 512kb each
jsmpeg.prototype.onlostconnection = null;

jsmpeg.prototype.initSocketClient = function( client ) {
this.buffer = new BitReader(new ArrayBuffer(this.socketBufferSize));

this.nextPictureBuffer = new BitReader(new ArrayBuffer(this.socketBufferSize));
this.nextPictureBuffer.writePos = 0;
this.nextPictureBuffer.chunkBegin = 0;
this.nextPictureBuffer.lastWriteBeforeWrap = 0;

this.client.binaryType = 'arraybuffer';
this.client.onmessage = this.receiveSocketMessage.bind(this);
};

jsmpeg.prototype.decodeSocketHeader = function( data ) {
// Custom header sent to all newly connected clients when streaming
// over websockets:
// struct { char magic[4] = "jsmp"; unsigned short width, height; };
if(
data[0] == SOCKET_MAGIC_BYTES.charCodeAt(0) &&
data[1] == SOCKET_MAGIC_BYTES.charCodeAt(1) &&
data[2] == SOCKET_MAGIC_BYTES.charCodeAt(2) &&
data[3] == SOCKET_MAGIC_BYTES.charCodeAt(3)
) {
this.width = (data[4] * 256 + data[5]);
this.height = (data[6] * 256 + data[7]);
this.initBuffers();
}
};

jsmpeg.prototype.receiveSocketMessage = function( event ) {
var messageData = new Uint8Array(event.data);

if( !this.sequenceStarted ) {
this.decodeSocketHeader(messageData);
}

var current = this.buffer;
var next = this.nextPictureBuffer;

if( next.writePos + messageData.length > next.length ) {
next.lastWriteBeforeWrap = next.writePos;
next.writePos = 0;
next.index = 0;
}

next.bytes.set( messageData, next.writePos );
next.writePos += messageData.length;

var startCode = 0;
while( true ) {
startCode = next.findNextMPEGStartCode();
if(
startCode == BitReader.NOT_FOUND ||
((next.index >> 3) > next.writePos)
) {
// We reached the end with no picture found yet; move back a few bytes
// in case we are at the beginning of a start code and exit.
next.index = Math.max((next.writePos-3), 0) << 3;
return;
}
else if( startCode == START_PICTURE ) {
break;
}
}

// If we are still here, we found the next picture start code!


// Skip picture decoding until we find the first intra frame?
if( this.waitForIntraFrame ) {
next.advance(10); // skip temporalReference
if( next.getBits(3) == PICTURE_TYPE_I ) {
this.waitForIntraFrame = false;
next.chunkBegin = (next.index-13) >> 3;
}
return;
}

// Last picture hasn't been decoded yet? Decode now but skip output
// before scheduling the next one
if( !this.currentPictureDecoded ) {
this.decodePicture(DECODE_SKIP_OUTPUT);
}


// Copy the picture chunk over to 'this.buffer' and schedule decoding.
var chunkEnd = ((next.index) >> 3);

if( chunkEnd > next.chunkBegin ) {
// Just copy the current picture chunk
current.bytes.set( next.bytes.subarray(next.chunkBegin, chunkEnd) );
current.writePos = chunkEnd - next.chunkBegin;
}
else {
// We wrapped the nextPictureBuffer around, so we have to copy the last part
// till the end, as well as from 0 to the current writePos
current.bytes.set( next.bytes.subarray(next.chunkBegin, next.lastWriteBeforeWrap) );
var written = next.lastWriteBeforeWrap - next.chunkBegin;
current.bytes.set( next.bytes.subarray(0, chunkEnd), written );
current.writePos = chunkEnd + written;
}

current.index = 0;
next.chunkBegin = chunkEnd;

// Decode!
this.currentPictureDecoded = false;
requestAnimFrame( this.scheduleDecoding.bind(this), this.canvas );
};

jsmpeg.prototype.scheduleDecoding = function() {
this.decodePicture();
this.currentPictureDecoded = true;
};



// ----------------------------------------------------------------------------
// Recording from WebSockets

jsmpeg.prototype.isRecording = false;
jsmpeg.prototype.recorderWaitForIntraFrame = false;
jsmpeg.prototype.recordedFrames = 0;
jsmpeg.prototype.recordedSize = 0;
jsmpeg.prototype.didStartRecordingCallback = null;

jsmpeg.prototype.recordBuffers = [];

jsmpeg.prototype.canRecord = function(){
return (this.client && this.client.readyState == this.client.OPEN);
};

jsmpeg.prototype.startRecording = function(callback) {
if( !this.canRecord() ) {
return;
}

// Discard old buffers and set for recording
this.discardRecordBuffers();
this.isRecording = true;
this.recorderWaitForIntraFrame = true;
this.didStartRecordingCallback = callback || null;

this.recordedFrames = 0;
this.recordedSize = 0;

// Fudge a simple Sequence Header for the MPEG file

// 3 bytes width & height, 12 bits each
var wh1 = (this.width >> 4),
wh2 = ((this.width & 0xf) << 4) | (this.height >> 8),
wh3 = (this.height & 0xff);

this.recordBuffers.push(new Uint8Array([
0x00, 0x00, 0x01, 0xb3, // Sequence Start Code
wh1, wh2, wh3, // Width & height
0x13, // aspect ratio & framerate
0xff, 0xff, 0xe1, 0x58, // Meh. Bitrate and other boring stuff
0x00, 0x00, 0x01, 0xb8, 0x00, 0x08, 0x00, // GOP
0x00, 0x00, 0x00, 0x01, 0x00 // First Picture Start Code
]));
};

jsmpeg.prototype.recordFrameFromCurrentBuffer = function() {
if( !this.isRecording ) { return; }

if( this.recorderWaitForIntraFrame ) {
// Not an intra frame? Exit.
if( this.pictureCodingType != PICTURE_TYPE_I ) { return; }

// Start recording!
this.recorderWaitForIntraFrame = false;
if( this.didStartRecordingCallback ) {
this.didStartRecordingCallback( this );
}
}

this.recordedFrames++;
this.recordedSize += this.buffer.writePos;

// Copy the actual subrange for the current picture into a new Buffer
this.recordBuffers.push(new Uint8Array(this.buffer.bytes.subarray(0, this.buffer.writePos)));
};

jsmpeg.prototype.discardRecordBuffers = function() {
this.recordBuffers = [];
this.recordedFrames = 0;
};

jsmpeg.prototype.stopRecording = function() {
var blob = new Blob(this.recordBuffers, {type: 'video/mpeg'});
this.discardRecordBuffers();
this.isRecording = false;
return blob;
};



// ----------------------------------------------------------------------------
// Loading via Ajax

jsmpeg.prototype.load = function( url ) {
this.url = url;

var request = new XMLHttpRequest();
var that = this;
request.onreadystatechange = function() {
if( request.readyState == request.DONE && request.status == 200 ) {
that.loadCallback(request.response);
}
};
request.onprogress = this.updateLoader.bind(this);

request.open('GET', url);
request.responseType = "arraybuffer";
request.send();
};

jsmpeg.prototype.updateLoader = function( ev ) {
var
p = ev.loaded / ev.total,
w = this.canvas.width,
h = this.canvas.height,
ctx = this.canvasContext;

ctx.fillStyle = '#222';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = '#fff';
ctx.fillRect(0, h - h*p, w, h*p);
};

jsmpeg.prototype.loadCallback = function(file) {
var time = Date.now();
this.buffer = new BitReader(file);

this.findStartCode(START_SEQUENCE);
this.firstSequenceHeader = this.buffer.index;
this.decodeSequenceHeader();

// Load the first frame
this.nextFrame();

if( this.autoplay ) {
this.play();
}

if( this.externalLoadCallback ) {
this.externalLoadCallback(this);
}
};

jsmpeg.prototype.play = function(file) {
if( this.playing ) { return; }
this.targetTime = Date.now();
this.playing = true;
this.scheduleNextFrame();
};

jsmpeg.prototype.pause = function(file) {
this.playing = false;
};

jsmpeg.prototype.stop = function(file) {
if( this.buffer ) {
this.buffer.index = this.firstSequenceHeader;
}
this.playing = false;
if( this.client ) {
this.client.close();
this.client = null;
}
};



// ----------------------------------------------------------------------------
// Utilities

jsmpeg.prototype.readCode = function(codeTable) {
var state = 0;
do {
state = codeTable[state + this.buffer.getBits(1)];
} while( state >= 0 && codeTable[state] != 0 );
return codeTable[state+2];
};

jsmpeg.prototype.findStartCode = function( code ) {
var current = 0;
while( true ) {
current = this.buffer.findNextMPEGStartCode();
if( current == code || current == BitReader.NOT_FOUND ) {
return current;
}
}
return BitReader.NOT_FOUND;
};

jsmpeg.prototype.fillArray = function(a, value) {
for( var i = 0, length = a.length; i < length; i++ ) {
a[i] = value;
}
};



// ----------------------------------------------------------------------------
// Sequence Layer

jsmpeg.prototype.pictureRate = 30;
jsmpeg.prototype.lateTime = 0;
jsmpeg.prototype.firstSequenceHeader = 0;
jsmpeg.prototype.targetTime = 0;

jsmpeg.prototype.nextFrame = function() {
if( !this.buffer ) { return; }
while(true) {
var code = this.buffer.findNextMPEGStartCode();

if( code == START_SEQUENCE ) {
this.decodeSequenceHeader();
}
else if( code == START_PICTURE ) {
if( this.playing ) {
this.scheduleNextFrame();
}
this.decodePicture();
return this.canvas;
}
else if( code == BitReader.NOT_FOUND ) {
this.stop(); // Jump back to the beginning

if( this.externalFinishedCallback ) {
this.externalFinishedCallback(this);
}

// Only loop if we found a sequence header
if( this.loop && this.sequenceStarted ) {
this.play();
}
return null;
}
else {
// ignore (GROUP, USER_DATA, EXTENSION, SLICES...)
}
}
};

jsmpeg.prototype.scheduleNextFrame = function() {
this.lateTime = Date.now() - this.targetTime;
var wait = Math.max(0, (1000/this.pictureRate) - this.lateTime);
this.targetTime = Date.now() + wait;

if( this.benchmark ) {
var now = Date.now();
if(!this.benchframe) {
this.benchstart = now;
this.benchframe = 0;
}
this.benchframe++;
var timepassed = now - this.benchstart;
if( this.benchframe >= 100 ) {
this.benchfps = (this.benchframe / timepassed) * 1000;
if( console ) {
console.log("frames per second: " + this.benchfps);
}
this.benchframe = null;
}
setTimeout( this.nextFrame.bind(this), 0);
}
else if( wait < 18) {
this.scheduleAnimation();
}
else {
setTimeout( this.scheduleAnimation.bind(this), wait );
}
};

jsmpeg.prototype.scheduleAnimation = function() {
requestAnimFrame( this.nextFrame.bind(this), this.canvas );
};

jsmpeg.prototype.decodeSequenceHeader = function() {
this.width = this.buffer.getBits(12);
this.height = this.buffer.getBits(12);
this.buffer.advance(4); // skip pixel aspect ratio
this.pictureRate = PICTURE_RATE[this.buffer.getBits(4)];
this.buffer.advance(18 + 1 + 10 + 1); // skip bitRate, marker, bufferSize and constrained bit

this.initBuffers();

if( this.buffer.getBits(1) ) { // load custom intra quant matrix?
for( var i = 0; i < 64; i++ ) {
this.customIntraQuantMatrix[ZIG_ZAG[i]] = this.buffer.getBits(8);
}
this.intraQuantMatrix = this.customIntraQuantMatrix;
}

if( this.buffer.getBits(1) ) { // load custom non intra quant matrix?
for( var i = 0; i < 64; i++ ) {
this.customNonIntraQuantMatrix[ZIG_ZAG[i]] = this.buffer.getBits(8);
}
this.nonIntraQuantMatrix = this.customNonIntraQuantMatrix;
}
};

jsmpeg.prototype.initBuffers = function() {
this.intraQuantMatrix = DEFAULT_INTRA_QUANT_MATRIX;
this.nonIntraQuantMatrix = DEFAULT_NON_INTRA_QUANT_MATRIX;

this.mbWidth = (this.width + 15) >> 4;
this.mbHeight = (this.height + 15) >> 4;
this.mbSize = this.mbWidth * this.mbHeight;

this.codedWidth = this.mbWidth << 4;
this.codedHeight = this.mbHeight << 4;
this.codedSize = this.codedWidth * this.codedHeight;

this.halfWidth = this.mbWidth << 3;
this.halfHeight = this.mbHeight << 3;
this.quarterSize = this.codedSize >> 2;

// Sequence already started? Don't allocate buffers again
if( this.sequenceStarted ) { return; }
this.sequenceStarted = true;


// Manually clamp values when writing macroblocks for shitty browsers
// that don't support Uint8ClampedArray
var MaybeClampedUint8Array = window.Uint8ClampedArray || window.Uint8Array;
if( !window.Uint8ClampedArray ) {
this.copyBlockToDestination = this.copyBlockToDestinationClamp;
this.addBlockToDestination = this.addBlockToDestinationClamp;
}

// Allocated buffers and resize the canvas
this.currentY = new MaybeClampedUint8Array(this.codedSize);
this.currentY32 = new Uint32Array(this.currentY.buffer);

this.currentCr = new MaybeClampedUint8Array(this.codedSize >> 2);
this.currentCr32 = new Uint32Array(this.currentCr.buffer);

this.currentCb = new MaybeClampedUint8Array(this.codedSize >> 2);
this.currentCb32 = new Uint32Array(this.currentCb.buffer);


this.forwardY = new MaybeClampedUint8Array(this.codedSize);
this.forwardY32 = new Uint32Array(this.forwardY.buffer);

this.forwardCr = new MaybeClampedUint8Array(this.codedSize >> 2);
this.forwardCr32 = new Uint32Array(this.forwardCr.buffer);

this.forwardCb = new MaybeClampedUint8Array(this.codedSize >> 2);
this.forwardCb32 = new Uint32Array(this.forwardCb.buffer);

this.canvas.width = this.width;
this.canvas.height = this.height;

this.currentRGBA = this.canvasContext.getImageData(0, 0, this.width, this.height);

if( this.bwFilter ) {
// This fails in IE10; don't use the bwFilter if you need to support it.
this.currentRGBA32 = new Uint32Array( this.currentRGBA.data.buffer );
}
this.fillArray(this.currentRGBA.data, 255);
};




// ----------------------------------------------------------------------------
// Picture Layer

jsmpeg.prototype.currentY = null;
jsmpeg.prototype.currentCr = null;
jsmpeg.prototype.currentCb = null;

jsmpeg.prototype.currentRGBA = null;

jsmpeg.prototype.pictureCodingType = 0;

// Buffers for motion compensation
jsmpeg.prototype.forwardY = null;
jsmpeg.prototype.forwardCr = null;
jsmpeg.prototype.forwardCb = null;

jsmpeg.prototype.fullPelForward = false;
jsmpeg.prototype.forwardFCode = 0;
jsmpeg.prototype.forwardRSize = 0;
jsmpeg.prototype.forwardF = 0;


jsmpeg.prototype.decodePicture = function(skipOutput) {
this.buffer.advance(10); // skip temporalReference
this.pictureCodingType = this.buffer.getBits(3);
this.buffer.advance(16); // skip vbv_delay

// Skip B and D frames or unknown coding type
if( this.pictureCodingType <= 0 || this.pictureCodingType >= PICTURE_TYPE_B ) {
return;
}

// full_pel_forward, forward_f_code
if( this.pictureCodingType == PICTURE_TYPE_P ) {
this.fullPelForward = this.buffer.getBits(1);
this.forwardFCode = this.buffer.getBits(3);
if( this.forwardFCode == 0 ) {
// Ignore picture with zero forward_f_code
return;
}
this.forwardRSize = this.forwardFCode - 1;
this.forwardF = 1 << this.forwardRSize;
}

var code = 0;
do {
code = this.buffer.findNextMPEGStartCode();
} while( code == START_EXTENSION || code == START_USER_DATA );


while( code >= START_SLICE_FIRST && code <= START_SLICE_LAST ) {
this.decodeSlice( (code & 0x000000FF) );
code = this.buffer.findNextMPEGStartCode();
}

// We found the next start code; rewind 32bits and let the main loop handle it.
this.buffer.rewind(32);

// Record this frame, if the recorder wants it
this.recordFrameFromCurrentBuffer();


if( skipOutput != DECODE_SKIP_OUTPUT ) {
if( this.bwFilter ) {
this.YToRGBA();
}
else {
this.YCbCrToRGBA();
}
this.canvasContext.putImageData(this.currentRGBA, 0, 0);

if(this.externalDecodeCallback) {
this.externalDecodeCallback(this, this.canvas);
}
}

// If this is a reference picutre then rotate the prediction pointers
if( this.pictureCodingType == PICTURE_TYPE_I || this.pictureCodingType == PICTURE_TYPE_P ) {
var
tmpY = this.forwardY,
tmpY32 = this.forwardY32,
tmpCr = this.forwardCr,
tmpCr32 = this.forwardCr32,
tmpCb = this.forwardCb,
tmpCb32 = this.forwardCb32;

this.forwardY = this.currentY;
this.forwardY32 = this.currentY32;
this.forwardCr = this.currentCr;
this.forwardCr32 = this.currentCr32;
this.forwardCb = this.currentCb;
this.forwardCb32 = this.currentCb32;

this.currentY = tmpY;
this.currentY32 = tmpY32;
this.currentCr = tmpCr;
this.currentCr32 = tmpCr32;
this.currentCb = tmpCb;
this.currentCb32 = tmpCb32;
}
};

jsmpeg.prototype.YCbCrToRGBA = function() {
var pY = this.currentY;
var pCb = this.currentCb;
var pCr = this.currentCr;
var pRGBA = this.currentRGBA.data;

// Chroma values are the same for each block of 4 pixels, so we proccess
// 2 lines at a time, 2 neighboring pixels each.
// I wish we could use 32bit writes to the RGBA buffer instead of writing
// each byte separately, but we need the automatic clamping of the RGBA
// buffer.

var yIndex1 = 0;
var yIndex2 = this.codedWidth;
var yNext2Lines = this.codedWidth + (this.codedWidth - this.width);

var cIndex = 0;
var cNextLine = this.halfWidth - (this.width >> 1);

var rgbaIndex1 = 0;
var rgbaIndex2 = this.width * 4;
var rgbaNext2Lines = this.width * 4;

var cols = this.width >> 1;
var rows = this.height >> 1;

var y, cb, cr, r, g, b;

for( var row = 0; row < rows; row++ ) {
for( var col = 0; col < cols; col++ ) {
cb = pCb[cIndex];
cr = pCr[cIndex];
cIndex++;

r = (cr + ((cr * 103) >> 8)) - 179;
g = ((cb * 88) >> 8) - 44 + ((cr * 183) >> 8) - 91;
b = (cb + ((cb * 198) >> 8)) - 227;

// Line 1
y = pY[yIndex1++];
pRGBA[rgbaIndex1] = y + r;
pRGBA[rgbaIndex1+1] = y - g;
pRGBA[rgbaIndex1+2] = y + b;
rgbaIndex1 += 4;

y = pY[yIndex1++];
pRGBA[rgbaIndex1] = y + r;
pRGBA[rgbaIndex1+1] = y - g;
pRGBA[rgbaIndex1+2] = y + b;
rgbaIndex1 += 4;

// Line 2
y = pY[yIndex2++];
pRGBA[rgbaIndex2] = y + r;
pRGBA[rgbaIndex2+1] = y - g;
pRGBA[rgbaIndex2+2] = y + b;
rgbaIndex2 += 4;

y = pY[yIndex2++];
pRGBA[rgbaIndex2] = y + r;
pRGBA[rgbaIndex2+1] = y - g;
pRGBA[rgbaIndex2+2] = y + b;
rgbaIndex2 += 4;
}

yIndex1 += yNext2Lines;
yIndex2 += yNext2Lines;
rgbaIndex1 += rgbaNext2Lines;
rgbaIndex2 += rgbaNext2Lines;
cIndex += cNextLine;
}
};

jsmpeg.prototype.YToRGBA = function() {
// Luma only
var pY = this.currentY;
var pRGBA = this.currentRGBA32;

var yIndex = 0;
var yNext2Lines = (this.codedWidth - this.width);

var rgbaIndex = 0;
var cols = this.width;
var rows = this.height;

var y;

for( var row = 0; row < rows; row++ ) {
for( var col = 0; col < cols; col++ ) {
y = pY[yIndex++];
pRGBA[rgbaIndex++] = 0xff000000 | y << 16 | y << 8 | y;
}

yIndex += yNext2Lines;
}
};




// ----------------------------------------------------------------------------
// Slice Layer

jsmpeg.prototype.quantizerScale = 0;
jsmpeg.prototype.sliceBegin = false;

jsmpeg.prototype.decodeSlice = function(slice) {
this.sliceBegin = true;
this.macroblockAddress = (slice - 1) * this.mbWidth - 1;

// Reset motion vectors and DC predictors
this.motionFwH = this.motionFwHPrev = 0;
this.motionFwV = this.motionFwVPrev = 0;
this.dcPredictorY = 128;
this.dcPredictorCr = 128;
this.dcPredictorCb = 128;

this.quantizerScale = this.buffer.getBits(5);

// skip extra bits
while( this.buffer.getBits(1) ) {
this.buffer.advance(8);
}

do {
this.decodeMacroblock();
// We may have to ignore Video Stream Start Codes here (0xE0)!?
} while( !this.buffer.nextBytesAreStartCode() );
}


// ----------------------------------------------------------------------------
// Macroblock Layer

jsmpeg.prototype.macroblockAddress = 0;
jsmpeg.prototype.mbRow = 0;
jsmpeg.prototype.mbCol = 0;

jsmpeg.prototype.macroblockType = 0;
jsmpeg.prototype.macroblockIntra = false;
jsmpeg.prototype.macroblockMotFw = false;

jsmpeg.prototype.motionFwH = 0;
jsmpeg.prototype.motionFwV = 0;
jsmpeg.prototype.motionFwHPrev = 0;
jsmpeg.prototype.motionFwVPrev = 0;

jsmpeg.prototype.decodeMacroblock = function() {
// Decode macroblock_address_increment
var
increment = 0,
t = this.readCode(MACROBLOCK_ADDRESS_INCREMENT);

while( t == 34 ) {
// macroblock_stuffing
t = this.readCode(MACROBLOCK_ADDRESS_INCREMENT);
}
while( t == 35 ) {
// macroblock_escape
increment += 33;
t = this.readCode(MACROBLOCK_ADDRESS_INCREMENT);
}
increment += t;

// Process any skipped macroblocks
if( this.sliceBegin ) {
// The first macroblock_address_increment of each slice is relative
// to beginning of the preverious row, not the preverious macroblock
this.sliceBegin = false;
this.macroblockAddress += increment;
}
else {
if( this.macroblockAddress + increment >= this.mbSize ) {
// Illegal (too large) macroblock_address_increment
return;
}
if( increment > 1 ) {
// Skipped macroblocks reset DC predictors
this.dcPredictorY = 128;
this.dcPredictorCr = 128;
this.dcPredictorCb = 128;

// Skipped macroblocks in P-pictures reset motion vectors
if( this.pictureCodingType == PICTURE_TYPE_P ) {
this.motionFwH = this.motionFwHPrev = 0;
this.motionFwV = this.motionFwVPrev = 0;
}
}

// Predict skipped macroblocks
while( increment > 1) {
this.macroblockAddress++;
this.mbRow = (this.macroblockAddress / this.mbWidth)|0;
this.mbCol = this.macroblockAddress % this.mbWidth;
this.copyMacroblock(this.motionFwH, this.motionFwV, this.forwardY, this.forwardCr, this.forwardCb);
increment--;
}
this.macroblockAddress++;
}
this.mbRow = (this.macroblockAddress / this.mbWidth)|0;
this.mbCol = this.macroblockAddress % this.mbWidth;

// Process the current macroblock
this.macroblockType = this.readCode(MACROBLOCK_TYPE_TABLES[this.pictureCodingType]);
this.macroblockIntra = (this.macroblockType & 0x01);
this.macroblockMotFw = (this.macroblockType & 0x08);

// Quantizer scale
if( (this.macroblockType & 0x10) != 0 ) {
this.quantizerScale = this.buffer.getBits(5);
}

if( this.macroblockIntra ) {
// Intra-coded macroblocks reset motion vectors
this.motionFwH = this.motionFwHPrev = 0;
this.motionFwV = this.motionFwVPrev = 0;
}
else {
// Non-intra macroblocks reset DC predictors
this.dcPredictorY = 128;
this.dcPredictorCr = 128;
this.dcPredictorCb = 128;

this.decodeMotionVectors();
this.copyMacroblock(this.motionFwH, this.motionFwV, this.forwardY, this.forwardCr, this.forwardCb);
}

// Decode blocks
var cbp = ((this.macroblockType & 0x02) != 0)
? this.readCode(CODE_BLOCK_PATTERN)
: (this.macroblockIntra ? 0x3f : 0);

for( var block = 0, mask = 0x20; block < 6; block++ ) {
if( (cbp & mask) != 0 ) {
this.decodeBlock(block);
}
mask >>= 1;
}
};


jsmpeg.prototype.decodeMotionVectors = function() {
var code, d, r = 0;

// Forward
if( this.macroblockMotFw ) {
// Horizontal forward
code = this.readCode(MOTION);
if( (code != 0) && (this.forwardF != 1) ) {
r = this.buffer.getBits(this.forwardRSize);
d = ((Math.abs(code) - 1) << this.forwardRSize) + r + 1;
if( code < 0 ) {
d = -d;
}
}
else {
d = code;
}

this.motionFwHPrev += d;
if( this.motionFwHPrev > (this.forwardF << 4) - 1 ) {
this.motionFwHPrev -= this.forwardF << 5;
}
else if( this.motionFwHPrev < ((-this.forwardF) << 4) ) {
this.motionFwHPrev += this.forwardF << 5;
}

this.motionFwH = this.motionFwHPrev;
if( this.fullPelForward ) {
this.motionFwH <<= 1;
}

// Vertical forward
code = this.readCode(MOTION);
if( (code != 0) && (this.forwardF != 1) ) {
r = this.buffer.getBits(this.forwardRSize);
d = ((Math.abs(code) - 1) << this.forwardRSize) + r + 1;
if( code < 0 ) {
d = -d;
}
}
else {
d = code;
}

this.motionFwVPrev += d;
if( this.motionFwVPrev > (this.forwardF << 4) - 1 ) {
this.motionFwVPrev -= this.forwardF << 5;
}
else if( this.motionFwVPrev < ((-this.forwardF) << 4) ) {
this.motionFwVPrev += this.forwardF << 5;
}

this.motionFwV = this.motionFwVPrev;
if( this.fullPelForward ) {
this.motionFwV <<= 1;
}
}
else if( this.pictureCodingType == PICTURE_TYPE_P ) {
// No motion information in P-picture, reset vectors
this.motionFwH = this.motionFwHPrev = 0;
this.motionFwV = this.motionFwVPrev = 0;
}
};

jsmpeg.prototype.copyMacroblock = function(motionH, motionV, sY, sCr, sCb ) {
var
width, scan,
H, V, oddH, oddV,
src, dest, last;

// We use 32bit writes here
var dY = this.currentY32;
var dCb = this.currentCb32;
var dCr = this.currentCr32;

// Luminance
width = this.codedWidth;
scan = width - 16;

H = motionH >> 1;
V = motionV >> 1;
oddH = (motionH & 1) == 1;
oddV = (motionV & 1) == 1;

src = ((this.mbRow << 4) + V) * width + (this.mbCol << 4) + H;
dest = (this.mbRow * width + this.mbCol) << 2;
last = dest + (width << 2);

var y1, y2, y;
if( oddH ) {
if( oddV ) {
while( dest < last ) {
y1 = sY[src] + sY[src+width]; src++;
for( var x = 0; x < 4; x++ ) {
y2 = sY[src] + sY[src+width]; src++;
y = (((y1 + y2 + 2) >> 2) & 0xff);

y1 = sY[src] + sY[src+width]; src++;
y |= (((y1 + y2 + 2) << 6) & 0xff00);

y2 = sY[src] + sY[src+width]; src++;
y |= (((y1 + y2 + 2) << 14) & 0xff0000);

y1 = sY[src] + sY[src+width]; src++;
y |= (((y1 + y2 + 2) << 22) & 0xff000000);

dY[dest++] = y;
}
dest += scan >> 2; src += scan-1;
}
}
else {
while( dest < last ) {
y1 = sY[src++];
for( var x = 0; x < 4; x++ ) {
y2 = sY[src++];
y = (((y1 + y2 + 1) >> 1) & 0xff);

y1 = sY[src++];
y |= (((y1 + y2 + 1) << 7) & 0xff00);

y2 = sY[src++];
y |= (((y1 + y2 + 1) << 15) & 0xff0000);

y1 = sY[src++];
y |= (((y1 + y2 + 1) << 23) & 0xff000000);

dY[dest++] = y;
}
dest += scan >> 2; src += scan-1;
}
}
}
else {
if( oddV ) {
while( dest < last ) {
for( var x = 0; x < 4; x++ ) {
y = (((sY[src] + sY[src+width] + 1) >> 1) & 0xff); src++;
y |= (((sY[src] + sY[src+width] + 1) << 7) & 0xff00); src++;
y |= (((sY[src] + sY[src+width] + 1) << 15) & 0xff0000); src++;
y |= (((sY[src] + sY[src+width] + 1) << 23) & 0xff000000); src++;

dY[dest++] = y;
}
dest += scan >> 2; src += scan;
}
}
else {
while( dest < last ) {
for( var x = 0; x < 4; x++ ) {
y = sY[src]; src++;
y |= sY[src] << 8; src++;
y |= sY[src] << 16; src++;
y |= sY[src] << 24; src++;

dY[dest++] = y;
}
dest += scan >> 2; src += scan;
}
}
}

if( this.bwFilter ) {
// No need to copy chrominance when black&white filter is active
return;
}


// Chrominance

width = this.halfWidth;
scan = width - 8;

H = (motionH/2) >> 1;
V = (motionV/2) >> 1;
oddH = ((motionH/2) & 1) == 1;
oddV = ((motionV/2) & 1) == 1;

src = ((this.mbRow << 3) + V) * width + (this.mbCol << 3) + H;
dest = (this.mbRow * width + this.mbCol) << 1;
last = dest + (width << 1);

var cr1, cr2, cr;
var cb1, cb2, cb;
if( oddH ) {
if( oddV ) {
while( dest < last ) {
cr1 = sCr[src] + sCr[src+width];
cb1 = sCb[src] + sCb[src+width];
src++;
for( var x = 0; x < 2; x++ ) {
cr2 = sCr[src] + sCr[src+width];
cb2 = sCb[src] + sCb[src+width]; src++;
cr = (((cr1 + cr2 + 2) >> 2) & 0xff);
cb = (((cb1 + cb2 + 2) >> 2) & 0xff);

cr1 = sCr[src] + sCr[src+width];
cb1 = sCb[src] + sCb[src+width]; src++;
cr |= (((cr1 + cr2 + 2) << 6) & 0xff00);
cb |= (((cb1 + cb2 + 2) << 6) & 0xff00);

cr2 = sCr[src] + sCr[src+width];
cb2 = sCb[src] + sCb[src+width]; src++;
cr |= (((cr1 + cr2 + 2) << 14) & 0xff0000);
cb |= (((cb1 + cb2 + 2) << 14) & 0xff0000);

cr1 = sCr[src] + sCr[src+width];
cb1 = sCb[src] + sCb[src+width]; src++;
cr |= (((cr1 + cr2 + 2) << 22) & 0xff000000);
cb |= (((cb1 + cb2 + 2) << 22) & 0xff000000);

dCr[dest] = cr;
dCb[dest] = cb;
dest++;
}
dest += scan >> 2; src += scan-1;
}
}
else {
while( dest < last ) {
cr1 = sCr[src];
cb1 = sCb[src];
src++;
for( var x = 0; x < 2; x++ ) {
cr2 = sCr[src];
cb2 = sCb[src++];
cr = (((cr1 + cr2 + 1) >> 1) & 0xff);
cb = (((cb1 + cb2 + 1) >> 1) & 0xff);

cr1 = sCr[src];
cb1 = sCb[src++];
cr |= (((cr1 + cr2 + 1) << 7) & 0xff00);
cb |= (((cb1 + cb2 + 1) << 7) & 0xff00);

cr2 = sCr[src];
cb2 = sCb[src++];
cr |= (((cr1 + cr2 + 1) << 15) & 0xff0000);
cb |= (((cb1 + cb2 + 1) << 15) & 0xff0000);

cr1 = sCr[src];
cb1 = sCb[src++];
cr |= (((cr1 + cr2 + 1) << 23) & 0xff000000);
cb |= (((cb1 + cb2 + 1) << 23) & 0xff000000);

dCr[dest] = cr;
dCb[dest] = cb;
dest++;
}
dest += scan >> 2; src += scan-1;
}
}
}
else {
if( oddV ) {
while( dest < last ) {
for( var x = 0; x < 2; x++ ) {
cr = (((sCr[src] + sCr[src+width] + 1) >> 1) & 0xff);
cb = (((sCb[src] + sCb[src+width] + 1) >> 1) & 0xff); src++;

cr |= (((sCr[src] + sCr[src+width] + 1) << 7) & 0xff00);
cb |= (((sCb[src] + sCb[src+width] + 1) << 7) & 0xff00); src++;

cr |= (((sCr[src] + sCr[src+width] + 1) << 15) & 0xff0000);
cb |= (((sCb[src] + sCb[src+width] + 1) << 15) & 0xff0000); src++;

cr |= (((sCr[src] + sCr[src+width] + 1) << 23) & 0xff000000);
cb |= (((sCb[src] + sCb[src+width] + 1) << 23) & 0xff000000); src++;

dCr[dest] = cr;
dCb[dest] = cb;
dest++;
}
dest += scan >> 2; src += scan;
}
}
else {
while( dest < last ) {
for( var x = 0; x < 2; x++ ) {
cr = sCr[src];
cb = sCb[src]; src++;

cr |= sCr[src] << 8;
cb |= sCb[src] << 8; src++;

cr |= sCr[src] << 16;
cb |= sCb[src] << 16; src++;

cr |= sCr[src] << 24;
cb |= sCb[src] << 24; src++;

dCr[dest] = cr;
dCb[dest] = cb;
dest++;
}
dest += scan >> 2; src += scan;
}
}
}
};


// ----------------------------------------------------------------------------
// Block layer

jsmpeg.prototype.dcPredictorY;
jsmpeg.prototype.dcPredictorCr;
jsmpeg.prototype.dcPredictorCb;

jsmpeg.prototype.blockData = null;
jsmpeg.prototype.decodeBlock = function(block) {

var
n = 0,
quantMatrix;

// Clear preverious data
this.fillArray(this.blockData, 0);

// Decode DC coefficient of intra-coded blocks
if( this.macroblockIntra ) {
var
predictor,
dctSize;

// DC prediction

if( block < 4 ) {
predictor = this.dcPredictorY;
dctSize = this.readCode(DCT_DC_SIZE_LUMINANCE);
}
else {
predictor = (block == 4 ? this.dcPredictorCr : this.dcPredictorCb);
dctSize = this.readCode(DCT_DC_SIZE_CHROMINANCE);
}

// Read DC coeff
if( dctSize > 0 ) {
var differential = this.buffer.getBits(dctSize);
if( (differential & (1 << (dctSize - 1))) != 0 ) {
this.blockData[0] = predictor + differential;
}
else {
this.blockData[0] = predictor + ((-1 << dctSize)|(differential+1));
}
}
else {
this.blockData[0] = predictor;
}

// Save predictor value
if( block < 4 ) {
this.dcPredictorY = this.blockData[0];
}
else if( block == 4 ) {
this.dcPredictorCr = this.blockData[0];
}
else {
this.dcPredictorCb = this.blockData[0];
}

// Dequantize + premultiply
this.blockData[0] <<= (3 + 5);

quantMatrix = this.intraQuantMatrix;
n = 1;
}
else {
quantMatrix = this.nonIntraQuantMatrix;
}

// Decode AC coefficients (+DC for non-intra)
var level = 0;
while( true ) {
var
run = 0,
coeff = this.readCode(DCT_COEFF);

if( (coeff == 0x0001) && (n > 0) && (this.buffer.getBits(1) == 0) ) {
// end_of_block
break;
}
if( coeff == 0xffff ) {
// escape
run = this.buffer.getBits(6);
level = this.buffer.getBits(8);
if( level == 0 ) {
level = this.buffer.getBits(8);
}
else if( level == 128 ) {
level = this.buffer.getBits(8) - 256;
}
else if( level > 128 ) {
level = level - 256;
}
}
else {
run = coeff >> 8;
level = coeff & 0xff;
if( this.buffer.getBits(1) ) {
level = -level;
}
}

n += run;
var dezigZagged = ZIG_ZAG[n];
n++;

// Dequantize, oddify, clip
level <<= 1;
if( !this.macroblockIntra ) {
level += (level < 0 ? -1 : 1);
}
level = (level * this.quantizerScale * quantMatrix[dezigZagged]) >> 4;
if( (level & 1) == 0 ) {
level -= level > 0 ? 1 : -1;
}
if( level > 2047 ) {
level = 2047;
}
else if( level < -2048 ) {
level = -2048;
}

// Save premultiplied coefficient
this.blockData[dezigZagged] = level * PREMULTIPLIER_MATRIX[dezigZagged];
};

// Transform block data to the spatial domain
if( n == 1 ) {
// Only DC coeff., no IDCT needed
this.fillArray(this.blockData, (this.blockData[0] + 128) >> 8);
}
else {
this.IDCT();
}

// Move block to its place
var
destArray,
destIndex,
scan;

if( block < 4 ) {
destArray = this.currentY;
scan = this.codedWidth - 8;
destIndex = (this.mbRow * this.codedWidth + this.mbCol) << 4;
if( (block & 1) != 0 ) {
destIndex += 8;
}
if( (block & 2) != 0 ) {
destIndex += this.codedWidth << 3;
}
}
else {
destArray = (block == 4) ? this.currentCb : this.currentCr;
scan = (this.codedWidth >> 1) - 8;
destIndex = ((this.mbRow * this.codedWidth) << 2) + (this.mbCol << 3);
}

n = 0;

var blockData = this.blockData;
if( this.macroblockIntra ) {
// Overwrite (no prediction)
this.copyBlockToDestination(this.blockData, destArray, destIndex, scan);
}
else {
// Add data to the predicted macroblock
this.addBlockToDestination(this.blockData, destArray, destIndex, scan);
}
};


jsmpeg.prototype.copyBlockToDestination = function(blockData, destArray, destIndex, scan) {
var n = 0;
for( var i = 0; i < 8; i++ ) {
for( var j = 0; j < 8; j++ ) {
destArray[destIndex++] = blockData[n++];
}
destIndex += scan;
}
};

jsmpeg.prototype.addBlockToDestination = function(blockData, destArray, destIndex, scan) {
var n = 0;
for( var i = 0; i < 8; i++ ) {
for( var j = 0; j < 8; j++ ) {
destArray[destIndex++] += blockData[n++];
}
destIndex += scan;
}
};

// Clamping version for shitty browsers (IE) that don't support Uint8ClampedArray
jsmpeg.prototype.copyBlockToDestinationClamp = function(blockData, destArray, destIndex, scan) {
var n = 0;
for( var i = 0; i < 8; i++ ) {
for( var j = 0; j < 8; j++ ) {
var p = blockData[n++];
destArray[destIndex++] = p > 255 ? 255 : (p < 0 ? 0 : p);
}
destIndex += scan;
}
};

jsmpeg.prototype.addBlockToDestinationClamp = function(blockData, destArray, destIndex, scan) {
var n = 0;
for( var i = 0; i < 8; i++ ) {
for( var j = 0; j < 8; j++ ) {
var p = blockData[n++] + destArray[destIndex];
destArray[destIndex++] = p > 255 ? 255 : (p < 0 ? 0 : p);
}
destIndex += scan;
}
};

jsmpeg.prototype.IDCT = function() {
// See http://vsr.informatik.tu-chemnitz.de/~jan/MPEG/HTML/IDCT.html
// for more info.

var
b1, b3, b4, b6, b7, tmp1, tmp2, m0,
x0, x1, x2, x3, x4, y3, y4, y5, y6, y7,
i,
blockData = this.blockData;

// Transform columns
for( i = 0; i < 8; ++i ) {
b1 = blockData[4*8+i];
b3 = blockData[2*8+i] + blockData[6*8+i];
b4 = blockData[5*8+i] - blockData[3*8+i];
tmp1 = blockData[1*8+i] + blockData[7*8+i];
tmp2 = blockData[3*8+i] + blockData[5*8+i];
b6 = blockData[1*8+i] - blockData[7*8+i];
b7 = tmp1 + tmp2;
m0 = blockData[0*8+i];
x4 = ((b6*473 - b4*196 + 128) >> 8) - b7;
x0 = x4 - (((tmp1 - tmp2)*362 + 128) >> 8);
x1 = m0 - b1;
x2 = (((blockData[2*8+i] - blockData[6*8+i])*362 + 128) >> 8) - b3;
x3 = m0 + b1;
y3 = x1 + x2;
y4 = x3 + b3;
y5 = x1 - x2;
y6 = x3 - b3;
y7 = -x0 - ((b4*473 + b6*196 + 128) >> 8);
blockData[0*8+i] = b7 + y4;
blockData[1*8+i] = x4 + y3;
blockData[2*8+i] = y5 - x0;
blockData[3*8+i] = y6 - y7;
blockData[4*8+i] = y6 + y7;
blockData[5*8+i] = x0 + y5;
blockData[6*8+i] = y3 - x4;
blockData[7*8+i] = y4 - b7;
}

// Transform rows
for( i = 0; i < 64; i += 8 ) {
b1 = blockData[4+i];
b3 = blockData[2+i] + blockData[6+i];
b4 = blockData[5+i] - blockData[3+i];
tmp1 = blockData[1+i] + blockData[7+i];
tmp2 = blockData[3+i] + blockData[5+i];
b6 = blockData[1+i] - blockData[7+i];
b7 = tmp1 + tmp2;
m0 = blockData[0+i];
x4 = ((b6*473 - b4*196 + 128) >> 8) - b7;
x0 = x4 - (((tmp1 - tmp2)*362 + 128) >> 8);
x1 = m0 - b1;
x2 = (((blockData[2+i] - blockData[6+i])*362 + 128) >> 8) - b3;
x3 = m0 + b1;
y3 = x1 + x2;
y4 = x3 + b3;
y5 = x1 - x2;
y6 = x3 - b3;
y7 = -x0 - ((b4*473 + b6*196 + 128) >> 8);
blockData[0+i] = (b7 + y4 + 128) >> 8;
blockData[1+i] = (x4 + y3 + 128) >> 8;
blockData[2+i] = (y5 - x0 + 128) >> 8;
blockData[3+i] = (y6 - y7 + 128) >> 8;
blockData[4+i] = (y6 + y7 + 128) >> 8;
blockData[5+i] = (x0 + y5 + 128) >> 8;
blockData[6+i] = (y3 - x4 + 128) >> 8;
blockData[7+i] = (y4 - b7 + 128) >> 8;
}
};


// ----------------------------------------------------------------------------
// VLC Tables and Constants

var
SOCKET_MAGIC_BYTES = 'jsmp',
DECODE_SKIP_OUTPUT = 1,
PICTURE_RATE = [
0.000, 23.976, 24.000, 25.000, 29.970, 30.000, 50.000, 59.940,
60.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
],
ZIG_ZAG = new Uint8Array([
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63
]),
DEFAULT_INTRA_QUANT_MATRIX = new Uint8Array([
8, 16, 19, 22, 26, 27, 29, 34,
16, 16, 22, 24, 27, 29, 34, 37,
19, 22, 26, 27, 29, 34, 34, 38,
22, 22, 26, 27, 29, 34, 37, 40,
22, 26, 27, 29, 32, 35, 40, 48,
26, 27, 29, 32, 35, 40, 48, 58,
26, 27, 29, 34, 38, 46, 56, 69,
27, 29, 35, 38, 46, 56, 69, 83
]),
DEFAULT_NON_INTRA_QUANT_MATRIX = new Uint8Array([
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16
]),

PREMULTIPLIER_MATRIX = new Uint8Array([
32, 44, 42, 38, 32, 25, 17, 9,
44, 62, 58, 52, 44, 35, 24, 12,
42, 58, 55, 49, 42, 33, 23, 12,
38, 52, 49, 44, 38, 30, 20, 10,
32, 44, 42, 38, 32, 25, 17, 9,
25, 35, 33, 30, 25, 20, 14, 7,
17, 24, 23, 20, 17, 14, 9, 5,
9, 12, 12, 10, 9, 7, 5, 2
]),

// MPEG-1 VLC

// macroblock_stuffing decodes as 34.
// macroblock_escape decodes as 35.

MACROBLOCK_ADDRESS_INCREMENT = new Int16Array([
1*3, 2*3, 0, // 0
3*3, 4*3, 0, // 1 0
0, 0, 1, // 2 1.
5*3, 6*3, 0, // 3 00
7*3, 8*3, 0, // 4 01
9*3, 10*3, 0, // 5 000
11*3, 12*3, 0, // 6 001
0, 0, 3, // 7 010.
0, 0, 2, // 8 011.
13*3, 14*3, 0, // 9 0000
15*3, 16*3, 0, // 10 0001
0, 0, 5, // 11 0010.
0, 0, 4, // 12 0011.
17*3, 18*3, 0, // 13 0000 0
19*3, 20*3, 0, // 14 0000 1
0, 0, 7, // 15 0001 0.
0, 0, 6, // 16 0001 1.
21*3, 22*3, 0, // 17 0000 00
23*3, 24*3, 0, // 18 0000 01
25*3, 26*3, 0, // 19 0000 10
27*3, 28*3, 0, // 20 0000 11
-1, 29*3, 0, // 21 0000 000
-1, 30*3, 0, // 22 0000 001
31*3, 32*3, 0, // 23 0000 010
33*3, 34*3, 0, // 24 0000 011
35*3, 36*3, 0, // 25 0000 100
37*3, 38*3, 0, // 26 0000 101
0, 0, 9, // 27 0000 110.
0, 0, 8, // 28 0000 111.
39*3, 40*3, 0, // 29 0000 0001
41*3, 42*3, 0, // 30 0000 0011
43*3, 44*3, 0, // 31 0000 0100
45*3, 46*3, 0, // 32 0000 0101
0, 0, 15, // 33 0000 0110.
0, 0, 14, // 34 0000 0111.
0, 0, 13, // 35 0000 1000.
0, 0, 12, // 36 0000 1001.
0, 0, 11, // 37 0000 1010.
0, 0, 10, // 38 0000 1011.
47*3, -1, 0, // 39 0000 0001 0
-1, 48*3, 0, // 40 0000 0001 1
49*3, 50*3, 0, // 41 0000 0011 0
51*3, 52*3, 0, // 42 0000 0011 1
53*3, 54*3, 0, // 43 0000 0100 0
55*3, 56*3, 0, // 44 0000 0100 1
57*3, 58*3, 0, // 45 0000 0101 0
59*3, 60*3, 0, // 46 0000 0101 1
61*3, -1, 0, // 47 0000 0001 00
-1, 62*3, 0, // 48 0000 0001 11
63*3, 64*3, 0, // 49 0000 0011 00
65*3, 66*3, 0, // 50 0000 0011 01
67*3, 68*3, 0, // 51 0000 0011 10
69*3, 70*3, 0, // 52 0000 0011 11
71*3, 72*3, 0, // 53 0000 0100 00
73*3, 74*3, 0, // 54 0000 0100 01
0, 0, 21, // 55 0000 0100 10.
0, 0, 20, // 56 0000 0100 11.
0, 0, 19, // 57 0000 0101 00.
0, 0, 18, // 58 0000 0101 01.
0, 0, 17, // 59 0000 0101 10.
0, 0, 16, // 60 0000 0101 11.
0, 0, 35, // 61 0000 0001 000. -- macroblock_escape
0, 0, 34, // 62 0000 0001 111. -- macroblock_stuffing
0, 0, 33, // 63 0000 0011 000.
0, 0, 32, // 64 0000 0011 001.
0, 0, 31, // 65 0000 0011 010.
0, 0, 30, // 66 0000 0011 011.
0, 0, 29, // 67 0000 0011 100.
0, 0, 28, // 68 0000 0011 101.
0, 0, 27, // 69 0000 0011 110.
0, 0, 26, // 70 0000 0011 111.
0, 0, 25, // 71 0000 0100 000.
0, 0, 24, // 72 0000 0100 001.
0, 0, 23, // 73 0000 0100 010.
0, 0, 22 // 74 0000 0100 011.
]),

// macroblock_type bitmap:
// 0x10 macroblock_quant
// 0x08 macroblock_motion_forward
// 0x04 macroblock_motion_backward
// 0x02 macrobkock_pattern
// 0x01 macroblock_intra
//

MACROBLOCK_TYPE_I = new Int8Array([
1*3, 2*3, 0, // 0
-1, 3*3, 0, // 1 0
0, 0, 0x01, // 2 1.
0, 0, 0x11 // 3 01.
]),

MACROBLOCK_TYPE_P = new Int8Array([
1*3, 2*3, 0, // 0
3*3, 4*3, 0, // 1 0
0, 0, 0x0a, // 2 1.
5*3, 6*3, 0, // 3 00
0, 0, 0x02, // 4 01.
7*3, 8*3, 0, // 5 000
0, 0, 0x08, // 6 001.
9*3, 10*3, 0, // 7 0000
11*3, 12*3, 0, // 8 0001
-1, 13*3, 0, // 9 00000
0, 0, 0x12, // 10 00001.
0, 0, 0x1a, // 11 00010.
0, 0, 0x01, // 12 00011.
0, 0, 0x11 // 13 000001.
]),

MACROBLOCK_TYPE_B = new Int8Array([
1*3, 2*3, 0, // 0
3*3, 5*3, 0, // 1 0
4*3, 6*3, 0, // 2 1
8*3, 7*3, 0, // 3 00
0, 0, 0x0c, // 4 10.
9*3, 10*3, 0, // 5 01
0, 0, 0x0e, // 6 11.
13*3, 14*3, 0, // 7 001
12*3, 11*3, 0, // 8 000
0, 0, 0x04, // 9 010.
0, 0, 0x06, // 10 011.
18*3, 16*3, 0, // 11 0001
15*3, 17*3, 0, // 12 0000
0, 0, 0x08, // 13 0010.
0, 0, 0x0a, // 14 0011.
-1, 19*3, 0, // 15 00000
0, 0, 0x01, // 16 00011.
20*3, 21*3, 0, // 17 00001
0, 0, 0x1e, // 18 00010.
0, 0, 0x11, // 19 000001.
0, 0, 0x16, // 20 000010.
0, 0, 0x1a // 21 000011.
]),

CODE_BLOCK_PATTERN = new Int16Array([
2*3, 1*3, 0, // 0
3*3, 6*3, 0, // 1 1
4*3, 5*3, 0, // 2 0
8*3, 11*3, 0, // 3 10
12*3, 13*3, 0, // 4 00
9*3, 7*3, 0, // 5 01
10*3, 14*3, 0, // 6 11
20*3, 19*3, 0, // 7 011
18*3, 16*3, 0, // 8 100
23*3, 17*3, 0, // 9 010
27*3, 25*3, 0, // 10 110
21*3, 28*3, 0, // 11 101
15*3, 22*3, 0, // 12 000
24*3, 26*3, 0, // 13 001
0, 0, 60, // 14 111.
35*3, 40*3, 0, // 15 0000
44*3, 48*3, 0, // 16 1001
38*3, 36*3, 0, // 17 0101
42*3, 47*3, 0, // 18 1000
29*3, 31*3, 0, // 19 0111
39*3, 32*3, 0, // 20 0110
0, 0, 32, // 21 1010.
45*3, 46*3, 0, // 22 0001
33*3, 41*3, 0, // 23 0100
43*3, 34*3, 0, // 24 0010
0, 0, 4, // 25 1101.
30*3, 37*3, 0, // 26 0011
0, 0, 8, // 27 1100.
0, 0, 16, // 28 1011.
0, 0, 44, // 29 0111 0.
50*3, 56*3, 0, // 30 0011 0
0, 0, 28, // 31 0111 1.
0, 0, 52, // 32 0110 1.
0, 0, 62, // 33 0100 0.
61*3, 59*3, 0, // 34 0010 1
52*3, 60*3, 0, // 35 0000 0
0, 0, 1, // 36 0101 1.
55*3, 54*3, 0, // 37 0011 1
0, 0, 61, // 38 0101 0.
0, 0, 56, // 39 0110 0.
57*3, 58*3, 0, // 40 0000 1
0, 0, 2, // 41 0100 1.
0, 0, 40, // 42 1000 0.
51*3, 62*3, 0, // 43 0010 0
0, 0, 48, // 44 1001 0.
64*3, 63*3, 0, // 45 0001 0
49*3, 53*3, 0, // 46 0001 1
0, 0, 20, // 47 1000 1.
0, 0, 12, // 48 1001 1.
80*3, 83*3, 0, // 49 0001 10
0, 0, 63, // 50 0011 00.
77*3, 75*3, 0, // 51 0010 00
65*3, 73*3, 0, // 52 0000 00
84*3, 66*3, 0, // 53 0001 11
0, 0, 24, // 54 0011 11.
0, 0, 36, // 55 0011 10.
0, 0, 3, // 56 0011 01.
69*3, 87*3, 0, // 57 0000 10
81*3, 79*3, 0, // 58 0000 11
68*3, 71*3, 0, // 59 0010 11
70*3, 78*3, 0, // 60 0000 01
67*3, 76*3, 0, // 61 0010 10
72*3, 74*3, 0, // 62 0010 01
86*3, 85*3, 0, // 63 0001 01
88*3, 82*3, 0, // 64 0001 00
-1, 94*3, 0, // 65 0000 000
95*3, 97*3, 0, // 66 0001 111
0, 0, 33, // 67 0010 100.
0, 0, 9, // 68 0010 110.
106*3, 110*3, 0, // 69 0000 100
102*3, 116*3, 0, // 70 0000 010
0, 0, 5, // 71 0010 111.
0, 0, 10, // 72 0010 010.
93*3, 89*3, 0, // 73 0000 001
0, 0, 6, // 74 0010 011.
0, 0, 18, // 75 0010 001.
0, 0, 17, // 76 0010 101.
0, 0, 34, // 77 0010 000.
113*3, 119*3, 0, // 78 0000 011
103*3, 104*3, 0, // 79 0000 111
90*3, 92*3, 0, // 80 0001 100
109*3, 107*3, 0, // 81 0000 110
117*3, 118*3, 0, // 82 0001 001
101*3, 99*3, 0, // 83 0001 101
98*3, 96*3, 0, // 84 0001 110
100*3, 91*3, 0, // 85 0001 011
114*3, 115*3, 0, // 86 0001 010
105*3, 108*3, 0, // 87 0000 101
112*3, 111*3, 0, // 88 0001 000
121*3, 125*3, 0, // 89 0000 0011
0, 0, 41, // 90 0001 1000.
0, 0, 14, // 91 0001 0111.
0, 0, 21, // 92 0001 1001.
124*3, 122*3, 0, // 93 0000 0010
120*3, 123*3, 0, // 94 0000 0001
0, 0, 11, // 95 0001 1110.
0, 0, 19, // 96 0001 1101.
0, 0, 7, // 97 0001 1111.
0, 0, 35, // 98 0001 1100.
0, 0, 13, // 99 0001 1011.
0, 0, 50, // 100 0001 0110.
0, 0, 49, // 101 0001 1010.
0, 0, 58, // 102 0000 0100.
0, 0, 37, // 103 0000 1110.
0, 0, 25, // 104 0000 1111.
0, 0, 45, // 105 0000 1010.
0, 0, 57, // 106 0000 1000.
0, 0, 26, // 107 0000 1101.
0, 0, 29, // 108 0000 1011.
0, 0, 38, // 109 0000 1100.
0, 0, 53, // 110 0000 1001.
0, 0, 23, // 111 0001 0001.
0, 0, 43, // 112 0001 0000.
0, 0, 46, // 113 0000 0110.
0, 0, 42, // 114 0001 0100.
0, 0, 22, // 115 0001 0101.
0, 0, 54, // 116 0000 0101.
0, 0, 51, // 117 0001 0010.
0, 0, 15, // 118 0001 0011.
0, 0, 30, // 119 0000 0111.
0, 0, 39, // 120 0000 0001 0.
0, 0, 47, // 121 0000 0011 0.
0, 0, 55, // 122 0000 0010 1.
0, 0, 27, // 123 0000 0001 1.
0, 0, 59, // 124 0000 0010 0.
0, 0, 31 // 125 0000 0011 1.
]),

MOTION = new Int16Array([
1*3, 2*3, 0, // 0
4*3, 3*3, 0, // 1 0
0, 0, 0, // 2 1.
6*3, 5*3, 0, // 3 01
8*3, 7*3, 0, // 4 00
0, 0, -1, // 5 011.
0, 0, 1, // 6 010.
9*3, 10*3, 0, // 7 001
12*3, 11*3, 0, // 8 000
0, 0, 2, // 9 0010.
0, 0, -2, // 10 0011.
14*3, 15*3, 0, // 11 0001
16*3, 13*3, 0, // 12 0000
20*3, 18*3, 0, // 13 0000 1
0, 0, 3, // 14 0001 0.
0, 0, -3, // 15 0001 1.
17*3, 19*3, 0, // 16 0000 0
-1, 23*3, 0, // 17 0000 00
27*3, 25*3, 0, // 18 0000 11
26*3, 21*3, 0, // 19 0000 01
24*3, 22*3, 0, // 20 0000 10
32*3, 28*3, 0, // 21 0000 011
29*3, 31*3, 0, // 22 0000 101
-1, 33*3, 0, // 23 0000 001
36*3, 35*3, 0, // 24 0000 100
0, 0, -4, // 25 0000 111.
30*3, 34*3, 0, // 26 0000 010
0, 0, 4, // 27 0000 110.
0, 0, -7, // 28 0000 0111.
0, 0, 5, // 29 0000 1010.
37*3, 41*3, 0, // 30 0000 0100
0, 0, -5, // 31 0000 1011.
0, 0, 7, // 32 0000 0110.
38*3, 40*3, 0, // 33 0000 0011
42*3, 39*3, 0, // 34 0000 0101
0, 0, -6, // 35 0000 1001.
0, 0, 6, // 36 0000 1000.
51*3, 54*3, 0, // 37 0000 0100 0
50*3, 49*3, 0, // 38 0000 0011 0
45*3, 46*3, 0, // 39 0000 0101 1
52*3, 47*3, 0, // 40 0000 0011 1
43*3, 53*3, 0, // 41 0000 0100 1
44*3, 48*3, 0, // 42 0000 0101 0
0, 0, 10, // 43 0000 0100 10.
0, 0, 9, // 44 0000 0101 00.
0, 0, 8, // 45 0000 0101 10.
0, 0, -8, // 46 0000 0101 11.
57*3, 66*3, 0, // 47 0000 0011 11
0, 0, -9, // 48 0000 0101 01.
60*3, 64*3, 0, // 49 0000 0011 01
56*3, 61*3, 0, // 50 0000 0011 00
55*3, 62*3, 0, // 51 0000 0100 00
58*3, 63*3, 0, // 52 0000 0011 10
0, 0, -10, // 53 0000 0100 11.
59*3, 65*3, 0, // 54 0000 0100 01
0, 0, 12, // 55 0000 0100 000.
0, 0, 16, // 56 0000 0011 000.
0, 0, 13, // 57 0000 0011 110.
0, 0, 14, // 58 0000 0011 100.
0, 0, 11, // 59 0000 0100 010.
0, 0, 15, // 60 0000 0011 010.
0, 0, -16, // 61 0000 0011 001.
0, 0, -12, // 62 0000 0100 001.
0, 0, -14, // 63 0000 0011 101.
0, 0, -15, // 64 0000 0011 011.
0, 0, -11, // 65 0000 0100 011.
0, 0, -13 // 66 0000 0011 111.
]),

DCT_DC_SIZE_LUMINANCE = new Int8Array([
2*3, 1*3, 0, // 0
6*3, 5*3, 0, // 1 1
3*3, 4*3, 0, // 2 0
0, 0, 1, // 3 00.
0, 0, 2, // 4 01.
9*3, 8*3, 0, // 5 11
7*3, 10*3, 0, // 6 10
0, 0, 0, // 7 100.
12*3, 11*3, 0, // 8 111
0, 0, 4, // 9 110.
0, 0, 3, // 10 101.
13*3, 14*3, 0, // 11 1111
0, 0, 5, // 12 1110.
0, 0, 6, // 13 1111 0.
16*3, 15*3, 0, // 14 1111 1
17*3, -1, 0, // 15 1111 11
0, 0, 7, // 16 1111 10.
0, 0, 8 // 17 1111 110.
]),

DCT_DC_SIZE_CHROMINANCE = new Int8Array([
2*3, 1*3, 0, // 0
4*3, 3*3, 0, // 1 1
6*3, 5*3, 0, // 2 0
8*3, 7*3, 0, // 3 11
0, 0, 2, // 4 10.
0, 0, 1, // 5 01.
0, 0, 0, // 6 00.
10*3, 9*3, 0, // 7 111
0, 0, 3, // 8 110.
12*3, 11*3, 0, // 9 1111
0, 0, 4, // 10 1110.
14*3, 13*3, 0, // 11 1111 1
0, 0, 5, // 12 1111 0.
16*3, 15*3, 0, // 13 1111 11
0, 0, 6, // 14 1111 10.
17*3, -1, 0, // 15 1111 111
0, 0, 7, // 16 1111 110.
0, 0, 8 // 17 1111 1110.
]),

// dct_coeff bitmap:
// 0xff00 run
// 0x00ff level

// Decoded values are unsigned. Sign bit follows in the stream.

// Interpretation of the value 0x0001
// for dc_coeff_first: run=0, level=1
// for dc_coeff_next: If the next bit is 1: run=0, level=1
// If the next bit is 0: end_of_block

// escape decodes as 0xffff.

DCT_COEFF = new Int32Array([
1*3, 2*3, 0, // 0
4*3, 3*3, 0, // 1 0
0, 0, 0x0001, // 2 1.
7*3, 8*3, 0, // 3 01
6*3, 5*3, 0, // 4 00
13*3, 9*3, 0, // 5 001
11*3, 10*3, 0, // 6 000
14*3, 12*3, 0, // 7 010
0, 0, 0x0101, // 8 011.
20*3, 22*3, 0, // 9 0011
18*3, 21*3, 0, // 10 0001
16*3, 19*3, 0, // 11 0000
0, 0, 0x0201, // 12 0101.
17*3, 15*3, 0, // 13 0010
0, 0, 0x0002, // 14 0100.
0, 0, 0x0003, // 15 0010 1.
27*3, 25*3, 0, // 16 0000 0
29*3, 31*3, 0, // 17 0010 0
24*3, 26*3, 0, // 18 0001 0
32*3, 30*3, 0, // 19 0000 1
0, 0, 0x0401, // 20 0011 0.
23*3, 28*3, 0, // 21 0001 1
0, 0, 0x0301, // 22 0011 1.
0, 0, 0x0102, // 23 0001 10.
0, 0, 0x0701, // 24 0001 00.
0, 0, 0xffff, // 25 0000 01. -- escape
0, 0, 0x0601, // 26 0001 01.
37*3, 36*3, 0, // 27 0000 00
0, 0, 0x0501, // 28 0001 11.
35*3, 34*3, 0, // 29 0010 00
39*3, 38*3, 0, // 30 0000 11
33*3, 42*3, 0, // 31 0010 01
40*3, 41*3, 0, // 32 0000 10
52*3, 50*3, 0, // 33 0010 010
54*3, 53*3, 0, // 34 0010 001
48*3, 49*3, 0, // 35 0010 000
43*3, 45*3, 0, // 36 0000 001
46*3, 44*3, 0, // 37 0000 000
0, 0, 0x0801, // 38 0000 111.
0, 0, 0x0004, // 39 0000 110.
0, 0, 0x0202, // 40 0000 100.
0, 0, 0x0901, // 41 0000 101.
51*3, 47*3, 0, // 42 0010 011
55*3, 57*3, 0, // 43 0000 0010
60*3, 56*3, 0, // 44 0000 0001
59*3, 58*3, 0, // 45 0000 0011
61*3, 62*3, 0, // 46 0000 0000
0, 0, 0x0a01, // 47 0010 0111.
0, 0, 0x0d01, // 48 0010 0000.
0, 0, 0x0006, // 49 0010 0001.
0, 0, 0x0103, // 50 0010 0101.
0, 0, 0x0005, // 51 0010 0110.
0, 0, 0x0302, // 52 0010 0100.
0, 0, 0x0b01, // 53 0010 0011.
0, 0, 0x0c01, // 54 0010 0010.
76*3, 75*3, 0, // 55 0000 0010 0
67*3, 70*3, 0, // 56 0000 0001 1
73*3, 71*3, 0, // 57 0000 0010 1
78*3, 74*3, 0, // 58 0000 0011 1
72*3, 77*3, 0, // 59 0000 0011 0
69*3, 64*3, 0, // 60 0000 0001 0
68*3, 63*3, 0, // 61 0000 0000 0
66*3, 65*3, 0, // 62 0000 0000 1
81*3, 87*3, 0, // 63 0000 0000 01
91*3, 80*3, 0, // 64 0000 0001 01
82*3, 79*3, 0, // 65 0000 0000 11
83*3, 86*3, 0, // 66 0000 0000 10
93*3, 92*3, 0, // 67 0000 0001 10
84*3, 85*3, 0, // 68 0000 0000 00
90*3, 94*3, 0, // 69 0000 0001 00
88*3, 89*3, 0, // 70 0000 0001 11
0, 0, 0x0203, // 71 0000 0010 11.
0, 0, 0x0104, // 72 0000 0011 00.
0, 0, 0x0007, // 73 0000 0010 10.
0, 0, 0x0402, // 74 0000 0011 11.
0, 0, 0x0502, // 75 0000 0010 01.
0, 0, 0x1001, // 76 0000 0010 00.
0, 0, 0x0f01, // 77 0000 0011 01.
0, 0, 0x0e01, // 78 0000 0011 10.
105*3, 107*3, 0, // 79 0000 0000 111
111*3, 114*3, 0, // 80 0000 0001 011
104*3, 97*3, 0, // 81 0000 0000 010
125*3, 119*3, 0, // 82 0000 0000 110
96*3, 98*3, 0, // 83 0000 0000 100
-1, 123*3, 0, // 84 0000 0000 000
95*3, 101*3, 0, // 85 0000 0000 001
106*3, 121*3, 0, // 86 0000 0000 101
99*3, 102*3, 0, // 87 0000 0000 011
113*3, 103*3, 0, // 88 0000 0001 110
112*3, 116*3, 0, // 89 0000 0001 111
110*3, 100*3, 0, // 90 0000 0001 000
124*3, 115*3, 0, // 91 0000 0001 010
117*3, 122*3, 0, // 92 0000 0001 101
109*3, 118*3, 0, // 93 0000 0001 100
120*3, 108*3, 0, // 94 0000 0001 001
127*3, 136*3, 0, // 95 0000 0000 0010
139*3, 140*3, 0, // 96 0000 0000 1000
130*3, 126*3, 0, // 97 0000 0000 0101
145*3, 146*3, 0, // 98 0000 0000 1001
128*3, 129*3, 0, // 99 0000 0000 0110
0, 0, 0x0802, // 100 0000 0001 0001.
132*3, 134*3, 0, // 101 0000 0000 0011
155*3, 154*3, 0, // 102 0000 0000 0111
0, 0, 0x0008, // 103 0000 0001 1101.
137*3, 133*3, 0, // 104 0000 0000 0100
143*3, 144*3, 0, // 105 0000 0000 1110
151*3, 138*3, 0, // 106 0000 0000 1010
142*3, 141*3, 0, // 107 0000 0000 1111
0, 0, 0x000a, // 108 0000 0001 0011.
0, 0, 0x0009, // 109 0000 0001 1000.
0, 0, 0x000b, // 110 0000 0001 0000.
0, 0, 0x1501, // 111 0000 0001 0110.
0, 0, 0x0602, // 112 0000 0001 1110.
0, 0, 0x0303, // 113 0000 0001 1100.
0, 0, 0x1401, // 114 0000 0001 0111.
0, 0, 0x0702, // 115 0000 0001 0101.
0, 0, 0x1101, // 116 0000 0001 1111.
0, 0, 0x1201, // 117 0000 0001 1010.
0, 0, 0x1301, // 118 0000 0001 1001.
148*3, 152*3, 0, // 119 0000 0000 1101
0, 0, 0x0403, // 120 0000 0001 0010.
153*3, 150*3, 0, // 121 0000 0000 1011
0, 0, 0x0105, // 122 0000 0001 1011.
131*3, 135*3, 0, // 123 0000 0000 0001
0, 0, 0x0204, // 124 0000 0001 0100.
149*3, 147*3, 0, // 125 0000 0000 1100
172*3, 173*3, 0, // 126 0000 0000 0101 1
162*3, 158*3, 0, // 127 0000 0000 0010 0
170*3, 161*3, 0, // 128 0000 0000 0110 0
168*3, 166*3, 0, // 129 0000 0000 0110 1
157*3, 179*3, 0, // 130 0000 0000 0101 0
169*3, 167*3, 0, // 131 0000 0000 0001 0
174*3, 171*3, 0, // 132 0000 0000 0011 0
178*3, 177*3, 0, // 133 0000 0000 0100 1
156*3, 159*3, 0, // 134 0000 0000 0011 1
164*3, 165*3, 0, // 135 0000 0000 0001 1
183*3, 182*3, 0, // 136 0000 0000 0010 1
175*3, 176*3, 0, // 137 0000 0000 0100 0
0, 0, 0x0107, // 138 0000 0000 1010 1.
0, 0, 0x0a02, // 139 0000 0000 1000 0.
0, 0, 0x0902, // 140 0000 0000 1000 1.
0, 0, 0x1601, // 141 0000 0000 1111 1.
0, 0, 0x1701, // 142 0000 0000 1111 0.
0, 0, 0x1901, // 143 0000 0000 1110 0.
0, 0, 0x1801, // 144 0000 0000 1110 1.
0, 0, 0x0503, // 145 0000 0000 1001 0.
0, 0, 0x0304, // 146 0000 0000 1001 1.
0, 0, 0x000d, // 147 0000 0000 1100 1.
0, 0, 0x000c, // 148 0000 0000 1101 0.
0, 0, 0x000e, // 149 0000 0000 1100 0.
0, 0, 0x000f, // 150 0000 0000 1011 1.
0, 0, 0x0205, // 151 0000 0000 1010 0.
0, 0, 0x1a01, // 152 0000 0000 1101 1.
0, 0, 0x0106, // 153 0000 0000 1011 0.
180*3, 181*3, 0, // 154 0000 0000 0111 1
160*3, 163*3, 0, // 155 0000 0000 0111 0
196*3, 199*3, 0, // 156 0000 0000 0011 10
0, 0, 0x001b, // 157 0000 0000 0101 00.
203*3, 185*3, 0, // 158 0000 0000 0010 01
202*3, 201*3, 0, // 159 0000 0000 0011 11
0, 0, 0x0013, // 160 0000 0000 0111 00.
0, 0, 0x0016, // 161 0000 0000 0110 01.
197*3, 207*3, 0, // 162 0000 0000 0010 00
0, 0, 0x0012, // 163 0000 0000 0111 01.
191*3, 192*3, 0, // 164 0000 0000 0001 10
188*3, 190*3, 0, // 165 0000 0000 0001 11
0, 0, 0x0014, // 166 0000 0000 0110 11.
184*3, 194*3, 0, // 167 0000 0000 0001 01
0, 0, 0x0015, // 168 0000 0000 0110 10.
186*3, 193*3, 0, // 169 0000 0000 0001 00
0, 0, 0x0017, // 170 0000 0000 0110 00.
204*3, 198*3, 0, // 171 0000 0000 0011 01
0, 0, 0x0019, // 172 0000 0000 0101 10.
0, 0, 0x0018, // 173 0000 0000 0101 11.
200*3, 205*3, 0, // 174 0000 0000 0011 00
0, 0, 0x001f, // 175 0000 0000 0100 00.
0, 0, 0x001e, // 176 0000 0000 0100 01.
0, 0, 0x001c, // 177 0000 0000 0100 11.
0, 0, 0x001d, // 178 0000 0000 0100 10.
0, 0, 0x001a, // 179 0000 0000 0101 01.
0, 0, 0x0011, // 180 0000 0000 0111 10.
0, 0, 0x0010, // 181 0000 0000 0111 11.
189*3, 206*3, 0, // 182 0000 0000 0010 11
187*3, 195*3, 0, // 183 0000 0000 0010 10
218*3, 211*3, 0, // 184 0000 0000 0001 010
0, 0, 0x0025, // 185 0000 0000 0010 011.
215*3, 216*3, 0, // 186 0000 0000 0001 000
0, 0, 0x0024, // 187 0000 0000 0010 100.
210*3, 212*3, 0, // 188 0000 0000 0001 110
0, 0, 0x0022, // 189 0000 0000 0010 110.
213*3, 209*3, 0, // 190 0000 0000 0001 111
221*3, 222*3, 0, // 191 0000 0000 0001 100
219*3, 208*3, 0, // 192 0000 0000 0001 101
217*3, 214*3, 0, // 193 0000 0000 0001 001
223*3, 220*3, 0, // 194 0000 0000 0001 011
0, 0, 0x0023, // 195 0000 0000 0010 101.
0, 0, 0x010b, // 196 0000 0000 0011 100.
0, 0, 0x0028, // 197 0000 0000 0010 000.
0, 0, 0x010c, // 198 0000 0000 0011 011.
0, 0, 0x010a, // 199 0000 0000 0011 101.
0, 0, 0x0020, // 200 0000 0000 0011 000.
0, 0, 0x0108, // 201 0000 0000 0011 111.
0, 0, 0x0109, // 202 0000 0000 0011 110.
0, 0, 0x0026, // 203 0000 0000 0010 010.
0, 0, 0x010d, // 204 0000 0000 0011 010.
0, 0, 0x010e, // 205 0000 0000 0011 001.
0, 0, 0x0021, // 206 0000 0000 0010 111.
0, 0, 0x0027, // 207 0000 0000 0010 001.
0, 0, 0x1f01, // 208 0000 0000 0001 1011.
0, 0, 0x1b01, // 209 0000 0000 0001 1111.
0, 0, 0x1e01, // 210 0000 0000 0001 1100.
0, 0, 0x1002, // 211 0000 0000 0001 0101.
0, 0, 0x1d01, // 212 0000 0000 0001 1101.
0, 0, 0x1c01, // 213 0000 0000 0001 1110.
0, 0, 0x010f, // 214 0000 0000 0001 0011.
0, 0, 0x0112, // 215 0000 0000 0001 0000.
0, 0, 0x0111, // 216 0000 0000 0001 0001.
0, 0, 0x0110, // 217 0000 0000 0001 0010.
0, 0, 0x0603, // 218 0000 0000 0001 0100.
0, 0, 0x0b02, // 219 0000 0000 0001 1010.
0, 0, 0x0e02, // 220 0000 0000 0001 0111.
0, 0, 0x0d02, // 221 0000 0000 0001 1000.
0, 0, 0x0c02, // 222 0000 0000 0001 1001.
0, 0, 0x0f02 // 223 0000 0000 0001 0110.
]),

PICTURE_TYPE_I = 1,
PICTURE_TYPE_P = 2,
PICTURE_TYPE_B = 3,
PICTURE_TYPE_D = 4,

START_SEQUENCE = 0xB3,
START_SLICE_FIRST = 0x01,
START_SLICE_LAST = 0xAF,
START_PICTURE = 0x00,
START_EXTENSION = 0xB5,
START_USER_DATA = 0xB2;

var MACROBLOCK_TYPE_TABLES = [
null,
MACROBLOCK_TYPE_I,
MACROBLOCK_TYPE_P,
MACROBLOCK_TYPE_B
];



// ----------------------------------------------------------------------------
// Bit Reader

var BitReader = function(arrayBuffer) {
this.bytes = new Uint8Array(arrayBuffer);
this.length = this.bytes.length;
this.writePos = this.bytes.length;
this.index = 0;
};

BitReader.NOT_FOUND = -1;

BitReader.prototype.findNextMPEGStartCode = function() {
for( var i = (this.index+7 >> 3); i < this.writePos; 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.writePos << 3);
return BitReader.NOT_FOUND;
};

BitReader.prototype.nextBytesAreStartCode = function() {
var i = (this.index+7 >> 3);
return (
i >= this.writePos || (
this.bytes[i] == 0x00 &&
this.bytes[i+1] == 0x00 &&
this.bytes[i+2] == 0x01
)
);
};

BitReader.prototype.nextBits = function(count) {
var
byteOffset = this.index >> 3,
room = (8 - this.index % 8);

if( room >= count ) {
return (this.bytes[byteOffset] >> (room - count)) & (0xff >> (8-count));
}

var
leftover = (this.index + count) % 8, // Leftover bits in last byte
end = (this.index + count -1) >> 3,
value = this.bytes[byteOffset] & (0xff >> (8-room)); // Fill out first byte

for( byteOffset++; byteOffset < end; byteOffset++ ) {
value <<= 8; // Shift and
value |= this.bytes[byteOffset]; // Put next byte
}

if (leftover > 0) {
value <<= leftover; // Make room for remaining bits
value |= (this.bytes[byteOffset] >> (8 - leftover));
}
else {
value <<= 8;
value |= this.bytes[byteOffset];
}

return value;
};

BitReader.prototype.getBits = function(count) {
var value = this.nextBits(count);
this.index += count;
return value;
};

BitReader.prototype.advance = function(count) {
return (this.index += count);
};

BitReader.prototype.rewind = function(count) {
return (this.index -= count);
};

})(window);



更多相关文章

  1. TextView显示html样式的文字
  2. 样式通常以相同的组合出现:创建单个类还是组合元素?
  3. HTML5用户身份认证源代码:注册、登录、会话保持的解决方案
  4. 将PHP代码添加到.html文件
  5. 如何让你的前端代码更像HTML5(用语义元素构造html5)
  6. 如何在CSS中单独定位此HTML代码?
  7. QQ、MSN、淘包旺旺、Skype常设对话的html链接代码
  8. 单选按钮和多选按钮的样式设置
  9. 10段实用的HTML5代码

随机推荐

  1. 浅谈easy-mock 最好的备胎没有之一
  2. 谁要是再敢用Map传参,我过去就是一JIO
  3. 不瞒你说,我最近跟Java源码杠上了
  4. 听说程序猿不会撩妹,我笑了
  5. immutability-helper因React官方出镜,而被
  6. 万圣节带你一起去鬼混!
  7. webpack4配置详解之常用插件分享
  8. 全民抵制“辱华”品牌秀,D&G神回复:呵呵~ 那
  9. 面对裁员,你还敢说老了真学不动了吗?
  10. 我的本科回忆录:从迷茫自卑到保送华科