├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── package.json
└── src
├── index.html
├── index.js
└── jsmpeg.min.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
11 | build.
12 | 2. Update the README.md with details of changes to the interface, this includes new environment
13 | variables, exposed ports, useful file locations and container parameters.
14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this
15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
17 | do not have permission to do that, you may request the second reviewer to merge it for you.
18 |
19 | ## Code of Conduct
20 |
21 | ### Our Pledge
22 |
23 | In the interest of fostering an open and welcoming environment, we as
24 | contributors and maintainers pledge to making participation in our project and
25 | our community a harassment-free experience for everyone, regardless of age, body
26 | size, disability, ethnicity, gender identity and expression, level of experience,
27 | nationality, personal appearance, race, religion, or sexual identity and
28 | orientation.
29 |
30 | ### Our Standards
31 |
32 | Examples of behavior that contributes to creating a positive environment
33 | include:
34 |
35 | * Using welcoming and inclusive language
36 | * Being respectful of differing viewpoints and experiences
37 | * Gracefully accepting constructive criticism
38 | * Focusing on what is best for the community
39 | * Showing empathy towards other community members
40 |
41 | Examples of unacceptable behavior by participants include:
42 |
43 | * The use of sexualized language or imagery and unwelcome sexual attention or
44 | advances
45 | * Trolling, insulting/derogatory comments, and personal or political attacks
46 | * Public or private harassment
47 | * Publishing others' private information, such as a physical or electronic
48 | address, without explicit permission
49 | * Other conduct which could reasonably be considered inappropriate in a
50 | professional setting
51 |
52 | ### Our Responsibilities
53 |
54 | Project maintainers are responsible for clarifying the standards of acceptable
55 | behavior and are expected to take appropriate and fair corrective action in
56 | response to any instances of unacceptable behavior.
57 |
58 | Project maintainers have the right and responsibility to remove, edit, or
59 | reject comments, commits, code, wiki edits, issues, and other contributions
60 | that are not aligned to this Code of Conduct, or to ban temporarily or
61 | permanently any contributor for other behaviors that they deem inappropriate,
62 | threatening, offensive, or harmful.
63 |
64 | ### Scope
65 |
66 | This Code of Conduct applies both within project spaces and in public spaces
67 | when an individual is representing the project or its community. Examples of
68 | representing a project or community include using an official project e-mail
69 | address, posting via an official social media account, or acting as an appointed
70 | representative at an online or offline event. Representation of a project may be
71 | further defined and clarified by project maintainers.
72 |
73 | ### Enforcement
74 |
75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
76 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
77 | complaints will be reviewed and investigated and will result in a response that
78 | is deemed necessary and appropriate to the circumstances. The project team is
79 | obligated to maintain confidentiality with regard to the reporter of an incident.
80 | Further details of specific enforcement policies may be posted separately.
81 |
82 | Project maintainers who do not follow or enforce the Code of Conduct in good
83 | faith may face temporary or permanent repercussions as determined by other
84 | members of the project's leadership.
85 |
86 | ### Attribution
87 |
88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
89 | available at [http://contributor-covenant.org/version/1/4][version]
90 |
91 | [homepage]: http://contributor-covenant.org
92 | [version]: http://contributor-covenant.org/version/1/4/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Phok Chanrithisak
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Steaming IP Camera Nodejs
2 | ### Donate me (if it helpful)
3 | [](https://github.com/QingWei-Li/donate)
4 | Bitcoin (BTC): bc1qtmvj9670pxrrdhqg98zge6qkw3kr590r96kq5p
5 | Ethereum (ETH) or USDT (ETH Network) or USDC (ETH Network): 0x16f2b8e63859f5665023D33d65DBcba189e4A9d4
6 |
7 | Open source project of real time streaming (~30 fps) IP/Network security camera on web browser using NodeJS
8 |
9 | 
10 |
11 | ## Getting Started
12 |
13 | These instructions will get you a copy of the project to make it up and running on your local machine for development and testing purposes.
14 |
15 | ### Prerequisites
16 |
17 | What things you need to install the software and how to install them
18 |
19 | * [Git](https://git-scm.com/downloads) - free and open source distributed version control system
20 | * [Node.js](https://nodejs.org/en/) - Node.js >= 10.15.0
21 | * [FFmpeg](https://ffmpeg.zeranoe.com/builds/) - Multimedia framework to decode, encode, transcode, mux, demux, stream, filter and play
22 |
23 | ### Installing
24 |
25 | A step by step series of examples that tell you how to get a development up and running
26 |
27 | 1. Download Git
28 | * [Git](https://git-scm.com/downloads)
29 | 2. Open command prompt/terminal, Clone this repository to your local machine
30 | ```
31 | git clone https://github.com/xpcrts/Steaming-IP-Camera-Nodejs
32 | ```
33 | 3. Download and install Node.js on your local machine
34 | * [Node.js](https://nodejs.org/en/) - Node.js >= 10.15.0
35 | 4. Download and install pre-build FFMPEG Builds on your local machine (Download Build)
36 | * [FFmpeg](https://ffmpeg.zeranoe.com/builds/) - Multimedia framework to decode, encode, transcode, mux, demux, stream, filter and play
37 | * Copy the FFMPEG Zip folder you have just downloaded, paste it into C: drive for simplicity and unzip it.
38 | * Rename the file to ffmpeg for simpicity
39 | * After unzipped the file, navigate ffmpeg/bin
40 | #### On Microsoft Windows
41 | You need to add ffmpeg to system variables (For all users) or User variables (For specific user)
42 | For research and test, I recommend to add the ffmpeg path to the system variables to do that just navigate to:
43 | a. Control Panel
44 | b. System and Security
45 | c. System
46 | d. Advanced system settings
47 | e. Environment Variables...
48 | f. System variables
49 | g. Path (Double-click on it)
50 | h. New
51 | i. Paste this
52 | ```C:\ffmpeg\bin```
53 | j. OK (3 times)
54 | 5. NPM install node-onvif
55 | ```
56 | npm install node-onvif -s
57 | ```
58 | 6.NPM install node-rtsp-stream
59 | ```
60 | npm i node-rtsp-stream -s
61 | ```
62 | 7.NPM install http-server
63 | ```
64 | npm install http-server -g
65 | ```
66 |
67 | ## Running the tests
68 |
69 | 1. In the repository, open app.js file
70 | * Change IP address to your camera IP address
71 | * Username of your network camera
72 | * Password of your network camera
73 | 2. Open one command prompt/terminal, navigate to Streaming-IP-Camera-Nodejs/src directory and type:
74 | ```
75 | http-server
76 | ```
77 | hit enter to run
78 | 3. Open another command prompt/terminal, on the same directory path and type:
79 | ```
80 | npm start
81 | ```
82 | hit enter to run
83 | Now keep those two terminal up and running
84 | Preview Streaming Camera on web browser by go to this URL:
85 | > [127.0.0.1:8000](http://127.0.0.1:8080/)
86 |
87 | You are ready to go.
88 |
89 | ## Contributing
90 |
91 | Please read [CONTRIBUTING.md](https://github.com/xpcrts/Steaming-IP-Camera-Nodejs/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
92 |
93 | ## To-dos
94 |
95 | * Streaming Multiple camera channels at once, using 4x4 grid or more
96 | * Customize width and height of canvas
97 | * Improve streaming resolution quality
98 | * Decrease streaming latency
99 |
100 | ## Versioning
101 |
102 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/xpcrts/Steaming-IP-Camera-Nodejs/tags).
103 |
104 | ## Authors
105 |
106 | * **Phok Chanrithisak** - [xpcrts](https://github.com/xpcrts)
107 |
108 | See also the list of [contributors](https://github.com/xpcrts/Steaming-IP-Camera-Nodejs/graphs/contributors) who participated in this project.
109 |
110 | ## License
111 |
112 | This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/xpcrts/Steaming-IP-Camera-Nodejs/blob/master/LICENSE) file for details
113 |
114 | ## Acknowledgments
115 |
116 | * Credit to: Celalettin Erbulut
117 |
118 |
119 | # Donate me (if it helpful)
120 | [](https://github.com/QingWei-Li/donate)
121 | Bitcoin (BTC): bc1qtmvj9670pxrrdhqg98zge6qkw3kr590r96kq5p
122 | Ethereum (ETH) or USDT (ETH Network) or USDC (ETH Network): 0x16f2b8e63859f5665023D33d65DBcba189e4A9d4
123 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "steaming-ip-camera-nodejs",
3 | "version": "1.0.0",
4 | "description": "Open source project of real time streaming IP/Network security camera on web application using NodeJS",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node src/index.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/xpcrts/Steaming-IP-Camera-Nodejs.git"
13 | },
14 | "keywords": [
15 | "nodejs",
16 | "ip-camera",
17 | "real-time",
18 | "webapp"
19 | ],
20 | "author": "Phok Chanrithisak",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/xpcrts/Steaming-IP-Camera-Nodejs/issues"
24 | },
25 | "homepage": "https://github.com/xpcrts/Steaming-IP-Camera-Nodejs#readme",
26 | "dependencies": {
27 | "node-onvif": "^0.1.7",
28 | "node-rtsp-stream": "0.0.4"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Streaming IP Camera Nodejs
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const rtspStream = require('node-rtsp-stream');
2 |
3 | //@desc Camera Authentication
4 | var ip_address = "10.0.17.11" //NOTE: replace it with your camera IP address
5 |
6 | //@desc Camera username and password
7 | var username = "admin";
8 | var password="admin";
9 |
10 | //@desc A channel of camera stream
11 | stream = new rtspStream({
12 | streamUrl: 'rtsp://' + username + ':' + password + '@' + ip_address +':554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif',
13 | wsPort: 9999
14 | });
--------------------------------------------------------------------------------
/src/jsmpeg.min.js:
--------------------------------------------------------------------------------
1 | (function(window){ 'use strict';
2 |
3 | var jsmpeg = window.jsmpeg = function(url, opts) {
4 | opts = opts || {};
5 | this.benchmark = !!opts.benchmark;
6 | this.canvas = opts.canvas || document.createElement('canvas');
7 | this.autoplay = !!opts.autoplay;
8 | this.wantsToPlay = this.autoplay;
9 | this.loop = !!opts.loop;
10 | this.seekable = !!opts.seekable;
11 | this.externalLoadCallback = opts.onload || null;
12 | this.externalDecodeCallback = opts.ondecodeframe || null;
13 | this.externalFinishedCallback = opts.onfinished || null;
14 |
15 | this.progressive = !!opts.progressive;
16 | this.progressiveThrottled = !!opts.progressiveThrottled;
17 | this.progressiveChunkSize = opts.progressiveChunkSize || 256 * 1024;
18 | this.progressiveChunkSizeMax = 4 * 1024 * 1024;
19 |
20 | this.customIntraQuantMatrix = new Uint8Array(64);
21 | this.customNonIntraQuantMatrix = new Uint8Array(64);
22 | this.blockData = new Int32Array(64);
23 | this.zeroBlockData = new Int32Array(64);
24 | this.fillArray(this.zeroBlockData, 0);
25 |
26 | // Use WebGL for YCbCrToRGBA conversion if possible (much faster)
27 | if (!opts.forceCanvas2D && this.initWebGL()) {
28 | this.renderFrame = this.renderFrameGL;
29 | this.updateLoader = this.updateLoaderGL;
30 | }
31 | else {
32 | this.canvasContext = this.canvas.getContext('2d');
33 | this.renderFrame = this.renderFrame2D;
34 | this.updateLoader = this.updateLoader2D;
35 | }
36 |
37 | if (url instanceof WebSocket) {
38 | this.client = url;
39 | this.client.onopen = this.initSocketClient.bind(this);
40 | }
41 | else if (this.progressive) {
42 | this.beginProgressiveLoad(url);
43 | }
44 | else {
45 | this.load(url);
46 | }
47 | };
48 |
49 |
50 |
51 | // ----------------------------------------------------------------------------
52 | // Streaming over WebSockets
53 |
54 | jsmpeg.prototype.waitForIntraFrame = true;
55 | jsmpeg.prototype.socketBufferSize = 512 * 1024; // 512kb each
56 |
57 | jsmpeg.prototype.initSocketClient = function() {
58 | this.buffer = new BitReader(new ArrayBuffer(this.socketBufferSize));
59 |
60 | this.nextPictureBuffer = new BitReader(new ArrayBuffer(this.socketBufferSize));
61 | this.nextPictureBuffer.writePos = 0;
62 | this.nextPictureBuffer.chunkBegin = 0;
63 | this.nextPictureBuffer.lastWriteBeforeWrap = 0;
64 |
65 | this.client.binaryType = 'arraybuffer';
66 | this.client.onmessage = this.receiveSocketMessage.bind(this);
67 | };
68 |
69 | jsmpeg.prototype.decodeSocketHeader = function(data) {
70 | // Custom header sent to all newly connected clients when streaming
71 | // over websockets:
72 | // struct { char magic[4] = 'jsmp'; unsigned short width, height; };
73 | if (
74 | data[0] === SOCKET_MAGIC_BYTES.charCodeAt(0) &&
75 | data[1] === SOCKET_MAGIC_BYTES.charCodeAt(1) &&
76 | data[2] === SOCKET_MAGIC_BYTES.charCodeAt(2) &&
77 | data[3] === SOCKET_MAGIC_BYTES.charCodeAt(3)
78 | ) {
79 | this.width = (data[4] * 256 + data[5]);
80 | this.height = (data[6] * 256 + data[7]);
81 | this.initBuffers();
82 | }
83 | };
84 |
85 | jsmpeg.prototype.receiveSocketMessage = function(event) {
86 | var messageData = new Uint8Array(event.data);
87 |
88 | if (!this.sequenceStarted) {
89 | this.decodeSocketHeader(messageData);
90 | }
91 |
92 | var current = this.buffer;
93 | var next = this.nextPictureBuffer;
94 |
95 | if (next.writePos + messageData.length > next.length) {
96 | next.lastWriteBeforeWrap = next.writePos;
97 | next.writePos = 0;
98 | next.index = 0;
99 | }
100 |
101 | next.bytes.set( messageData, next.writePos );
102 | next.writePos += messageData.length;
103 |
104 | var startCode = 0;
105 | while (true) {
106 | startCode = next.findNextMPEGStartCode();
107 | if (
108 | startCode === BitReader.NOT_FOUND ||
109 | ((next.index >> 3) > next.writePos)
110 | ) {
111 | // We reached the end with no picture found yet; move back a few bytes
112 | // in case we are at the beginning of a start code and exit.
113 | next.index = Math.max((next.writePos-3), 0) << 3;
114 | return;
115 | }
116 | else if (startCode === START_PICTURE) {
117 | break;
118 | }
119 | }
120 |
121 | // If we are still here, we found the next picture start code!
122 |
123 |
124 | // Skip picture decoding until we find the first intra frame?
125 | if (this.waitForIntraFrame) {
126 | next.advance(10); // skip temporalReference
127 | if (next.getBits(3) === PICTURE_TYPE_I) {
128 | this.waitForIntraFrame = false;
129 | next.chunkBegin = (next.index-13) >> 3;
130 | }
131 | return;
132 | }
133 |
134 | // Last picture hasn't been decoded yet? Decode now but skip output
135 | // before scheduling the next one
136 | if (!this.currentPictureDecoded) {
137 | this.decodePicture(DECODE_SKIP_OUTPUT);
138 | }
139 |
140 |
141 | // Copy the picture chunk over to 'this.buffer' and schedule decoding.
142 | var chunkEnd = ((next.index) >> 3);
143 |
144 | if (chunkEnd > next.chunkBegin) {
145 | // Just copy the current picture chunk
146 | current.bytes.set( next.bytes.subarray(next.chunkBegin, chunkEnd) );
147 | current.writePos = chunkEnd - next.chunkBegin;
148 | }
149 | else {
150 | // We wrapped the nextPictureBuffer around, so we have to copy the last part
151 | // till the end, as well as from 0 to the current writePos
152 | current.bytes.set( next.bytes.subarray(next.chunkBegin, next.lastWriteBeforeWrap) );
153 | var written = next.lastWriteBeforeWrap - next.chunkBegin;
154 | current.bytes.set( next.bytes.subarray(0, chunkEnd), written );
155 | current.writePos = chunkEnd + written;
156 | }
157 |
158 | current.index = 0;
159 | next.chunkBegin = chunkEnd;
160 |
161 | // Decode!
162 | this.currentPictureDecoded = false;
163 | setTimeout(function(){
164 | this.decodePicture();
165 | this.currentPictureDecoded = true;
166 | }.bind(this));
167 | };
168 |
169 |
170 | // ----------------------------------------------------------------------------
171 | // Recording from WebSockets
172 |
173 | jsmpeg.prototype.isRecording = false;
174 | jsmpeg.prototype.recorderWaitForIntraFrame = false;
175 | jsmpeg.prototype.recordedFrames = 0;
176 | jsmpeg.prototype.recordedSize = 0;
177 | jsmpeg.prototype.didStartRecordingCallback = null;
178 |
179 | jsmpeg.prototype.recordBuffers = [];
180 |
181 | jsmpeg.prototype.canRecord = function(){
182 | return (this.client && this.client.readyState === this.client.OPEN);
183 | };
184 |
185 | jsmpeg.prototype.startRecording = function(callback) {
186 | if (!this.canRecord()) {
187 | return;
188 | }
189 |
190 | // Discard old buffers and set for recording
191 | this.discardRecordBuffers();
192 | this.isRecording = true;
193 | this.recorderWaitForIntraFrame = true;
194 | this.didStartRecordingCallback = callback || null;
195 |
196 | this.recordedFrames = 0;
197 | this.recordedSize = 0;
198 |
199 | // Fudge a simple Sequence Header for the MPEG file
200 |
201 | // 3 bytes width & height, 12 bits each
202 | var wh1 = (this.width >> 4),
203 | wh2 = ((this.width & 0xf) << 4) | (this.height >> 8),
204 | wh3 = (this.height & 0xff);
205 |
206 | this.recordBuffers.push(new Uint8Array([
207 | 0x00, 0x00, 0x01, 0xb3, // Sequence Start Code
208 | wh1, wh2, wh3, // Width & height
209 | 0x13, // aspect ratio & framerate
210 | 0xff, 0xff, 0xe1, 0x58, // Meh. Bitrate and other boring stuff
211 | 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08, 0x00, // GOP
212 | 0x00, 0x00, 0x00, 0x01, 0x00 // First Picture Start Code
213 | ]));
214 | };
215 |
216 | jsmpeg.prototype.recordFrameFromCurrentBuffer = function() {
217 | if (!this.isRecording) { return; }
218 |
219 | if (this.recorderWaitForIntraFrame) {
220 | // Not an intra frame? Exit.
221 | if (this.pictureCodingType !== PICTURE_TYPE_I) { return; }
222 |
223 | // Start recording!
224 | this.recorderWaitForIntraFrame = false;
225 | if (this.didStartRecordingCallback) {
226 | this.didStartRecordingCallback( this );
227 | }
228 | }
229 |
230 | this.recordedFrames++;
231 | this.recordedSize += this.buffer.writePos;
232 |
233 | // Copy the actual subrange for the current picture into a new Buffer
234 | this.recordBuffers.push(new Uint8Array(this.buffer.bytes.subarray(0, this.buffer.writePos)));
235 | };
236 |
237 | jsmpeg.prototype.discardRecordBuffers = function() {
238 | this.recordBuffers = [];
239 | this.recordedFrames = 0;
240 | };
241 |
242 | jsmpeg.prototype.stopRecording = function() {
243 | var blob = new Blob(this.recordBuffers, {type: 'video/mpeg'});
244 | this.discardRecordBuffers();
245 | this.isRecording = false;
246 | return blob;
247 | };
248 |
249 |
250 |
251 | // ----------------------------------------------------------------------------
252 | // Loading via Ajax
253 |
254 | jsmpeg.prototype.intraFrames = [];
255 | jsmpeg.prototype.currentFrame = -1;
256 | jsmpeg.prototype.currentTime = 0;
257 | jsmpeg.prototype.frameCount = 0;
258 | jsmpeg.prototype.duration = 0;
259 |
260 | jsmpeg.prototype.load = function(url) {
261 | this.url = url;
262 |
263 | var request = new XMLHttpRequest();
264 | var that = this;
265 | request.onreadystatechange = function() {
266 | if (request.readyState == request.DONE && request.status == 200) {
267 | that.loadCallback(request.response);
268 | }
269 | };
270 |
271 | request.onprogress = this.updateLoader.bind(this);
272 | request.open('GET', url);
273 | request.responseType = "arraybuffer";
274 | request.send();
275 | };
276 |
277 | jsmpeg.prototype.updateLoader2D = function(ev) {
278 | var
279 | p = ev.loaded / ev.total,
280 | w = this.canvas.width,
281 | h = this.canvas.height,
282 | ctx = this.canvasContext;
283 |
284 | ctx.fillStyle = '#222';
285 | ctx.fillRect(0, 0, w, h);
286 | ctx.fillStyle = '#fff';
287 | ctx.fillRect(0, h - h*p, w, h*p);
288 | };
289 |
290 | jsmpeg.prototype.updateLoaderGL = function(ev) {
291 | var gl = this.gl;
292 | gl.uniform1f(gl.getUniformLocation(this.loadingProgram, 'loaded'), (ev.loaded / ev.total));
293 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
294 | };
295 |
296 | jsmpeg.prototype.loadCallback = function(file) {
297 | this.buffer = new BitReader(file);
298 |
299 | if (this.seekable) {
300 | this.collectIntraFrames();
301 | this.buffer.index = 0;
302 | }
303 |
304 | this.findStartCode(START_SEQUENCE);
305 | this.firstSequenceHeader = this.buffer.index;
306 | this.decodeSequenceHeader();
307 |
308 | // Calculate the duration. This only works if the video is seekable and we have a frame count
309 | this.duration = this.frameCount / this.pictureRate;
310 |
311 | // Load the first frame
312 | this.nextFrame();
313 |
314 | if (this.autoplay) {
315 | this.play();
316 | }
317 |
318 | if (this.externalLoadCallback) {
319 | this.externalLoadCallback(this);
320 | }
321 | };
322 |
323 | jsmpeg.prototype.collectIntraFrames = function() {
324 | // Loop through the whole buffer and collect all intraFrames to build our seek index.
325 | // We also keep track of total frame count here
326 | var frame;
327 | for (frame = 0; this.findStartCode(START_PICTURE) !== BitReader.NOT_FOUND; frame++) {
328 |
329 | // Check if the found picture is an intra frame and remember the position
330 | this.buffer.advance(10); // skip temporalReference
331 | if (this.buffer.getBits(3) === PICTURE_TYPE_I) {
332 | // Remember index 13 bits back, before temporalReference and picture type
333 | this.intraFrames.push({frame: frame, index: this.buffer.index - 13});
334 | }
335 | }
336 |
337 | this.frameCount = frame;
338 | };
339 |
340 | jsmpeg.prototype.seekToFrame = function(seekFrame, seekExact) {
341 | if (seekFrame < 0 || seekFrame >= this.frameCount || !this.intraFrames.length) {
342 | return false;
343 | }
344 |
345 | // Find the last intra frame before or equal to seek frame
346 | var target = null;
347 | for (var i = 0; i < this.intraFrames.length && this.intraFrames[i].frame <= seekFrame; i++) {
348 | target = this.intraFrames[i];
349 | }
350 |
351 | this.buffer.index = target.index;
352 | this.currentFrame = target.frame-1;
353 |
354 | // If we're seeking to the exact frame, we may have to decode some more frames before
355 | // the one we want
356 | if (seekExact) {
357 | for (var frame = target.frame; frame < seekFrame; frame++) {
358 | this.decodePicture(DECODE_SKIP_OUTPUT);
359 | this.findStartCode(START_PICTURE);
360 | }
361 | this.currentFrame = seekFrame-1;
362 | }
363 |
364 | // Decode and display the picture we have seeked to
365 | this.decodePicture();
366 | return true;
367 | };
368 |
369 | jsmpeg.prototype.seekToTime = function(time, seekExact) {
370 | this.seekToFrame( (time * this.pictureRate)|0, seekExact );
371 | };
372 |
373 | jsmpeg.prototype.play = function() {
374 | if (this.playing) { return; }
375 | if (this.progressive) {
376 | this.wantsToPlay = true;
377 | this.attemptToPlay();
378 | }
379 | else {
380 | this._playNow();
381 | }
382 | };
383 |
384 | jsmpeg.prototype._playNow = function() {
385 | this.targetTime = this.now();
386 | this.playing = true;
387 | this.scheduleNextFrame();
388 | };
389 |
390 | jsmpeg.prototype.pause = function() {
391 | this.playing = false;
392 | this.wantsToPlay = false;
393 | };
394 |
395 | jsmpeg.prototype.stop = function() {
396 | this.currentFrame = -1;
397 | this.currentTime = 0;
398 | this.wantsToPlay = false;
399 | if (this.buffer) {
400 | this.buffer.index = this.firstSequenceHeader;
401 | }
402 | this.playing = false;
403 | if (this.client) {
404 | this.client.close();
405 | this.client = null;
406 | }
407 | };
408 |
409 |
410 |
411 | // ----------------------------------------------------------------------------
412 | // Progressive loading via AJAX
413 |
414 | jsmpeg.prototype.beginProgressiveLoad = function(url) {
415 | this.url = url;
416 |
417 | this.progressiveLoadPositon = 0;
418 | this.fileSize = 0;
419 |
420 | var request = new XMLHttpRequest();
421 | var that = this;
422 | request.onreadystatechange = function() {
423 | if (request.readyState === request.DONE) {
424 | that.fileSize = parseInt(request.getResponseHeader("Content-Length"));
425 | that.buffer = new BitReader(new ArrayBuffer(that.fileSize));
426 | that.buffer.writePos = 0;
427 | that.loadNextChunk();
428 | }
429 | };
430 | request.open('HEAD', url);
431 | request.send();
432 | };
433 |
434 | jsmpeg.prototype.maybeLoadNextChunk = function() {
435 | if (
436 | !this.chunkIsLoading &&
437 | (this.buffer.index >> 3) > this.nextChunkLoadAt &&
438 | this.progressiveLoadFails < 5
439 | ) {
440 | this.loadNextChunk();
441 | }
442 | };
443 |
444 | jsmpeg.prototype.loadNextChunk = function() {
445 | var that = this;
446 | var start = this.buffer.writePos,
447 | end = Math.min(this.buffer.writePos + that.progressiveChunkSize-1, this.fileSize-1);
448 |
449 | if (start >= this.fileSize) {
450 | return;
451 | }
452 |
453 | this.chunkIsLoading = true;
454 | this.chunkLoadStart = Date.now();
455 | var request = new XMLHttpRequest();
456 |
457 | request.onreadystatechange = function() {
458 | if (
459 | request.readyState === request.DONE &&
460 | request.status > 200 && request.status < 300
461 | ) {
462 | that.progressiveLoadFails = 0;
463 | that.progressiveLoadCallback(request.response);
464 | }
465 | else if (request.readyState === request.DONE) {
466 | // retry
467 | that.chunkIsLoading = false;
468 | that.progressiveLoadFails++;
469 | that.maybeLoadNextChunk();
470 | }
471 | };
472 |
473 | if (start === 0) {
474 | request.onprogress = this.updateLoader.bind(this);
475 | }
476 |
477 | request.open('GET', this.url+'?'+start+"-"+end);
478 | request.setRequestHeader("Range", "bytes="+start+"-"+end);
479 | request.responseType = "arraybuffer";
480 | request.send();
481 | };
482 |
483 | jsmpeg.prototype.canPlayThrough = false;
484 |
485 | jsmpeg.prototype.progressiveLoadCallback = function(data) {
486 | this.chunkIsLoading = false;
487 | var isFirstChunk = (this.buffer.writePos === 0);
488 |
489 | var bytes = new Uint8Array(data);
490 | this.buffer.bytes.set(bytes, this.buffer.writePos);
491 | this.buffer.writePos += bytes.length;
492 |
493 | if (isFirstChunk) {
494 | this.findStartCode(START_SEQUENCE);
495 | this.firstSequenceHeader = this.buffer.index;
496 | this.decodeSequenceHeader();
497 | }
498 |
499 | var loadTime = (Date.now() - this.chunkLoadStart)/1000;
500 |
501 | // Throttled loading and playback did start already? Calculate when
502 | // the next chunk needs to be loaded
503 | var playIndex = (this.buffer.index >> 3);
504 | var bytesPerSecondLoaded = bytes.length / loadTime;
505 | var bytesPerSecondPlayed = 0;
506 |
507 | if (this.currentTime > 0) {
508 | bytesPerSecondPlayed = playIndex / this.currentTime;
509 | }
510 | else {
511 | // Playback didn't start - we need to count the frames we got and estimate the bytes per second
512 | var currentIndex = this.buffer.index;
513 | this.buffer.index = (this.buffer.writePos - bytes.length) << 3;
514 | var frames;
515 | for (frames = 0; this.findStartCode(START_PICTURE) !== BitReader.NOT_FOUND; frames++) {}
516 | this.buffer.index = currentIndex;
517 | bytesPerSecondPlayed = bytes.length/(frames / this.pictureRate);
518 | }
519 |
520 |
521 | var remainingTimeToPlay = (this.buffer.writePos - playIndex) / bytesPerSecondPlayed;
522 | var remainingTimeToLoad = (this.fileSize - this.buffer.writePos) / bytesPerSecondLoaded;
523 | var totalRemainingPlayTime = (this.fileSize-playIndex) / bytesPerSecondPlayed;
524 | this.canPlayThrough = totalRemainingPlayTime > remainingTimeToLoad;
525 |
526 | // Do we have a lot more remaining play time than we typically need for loading?
527 | // -> Increase the chunk size for the next load
528 | if (remainingTimeToPlay > loadTime * 8) {
529 | this.progressiveChunkSize = Math.min(this.progressiveChunkSize * 2, this.progressiveChunkSizeMax);
530 | }
531 |
532 | // Start loading at the latest when only one chunk is left to play, but subtract twice as
533 | // much bytes as the next chunk will take to load than it will to play
534 | if (this.progressiveThrottled && this.canPlayThrough) {
535 | this.nextChunkLoadAt = this.buffer.writePos
536 | - this.progressiveChunkSize * 2
537 | - this.progressiveChunkSize * (bytesPerSecondPlayed / bytesPerSecondLoaded) * 4;
538 | }
539 | else {
540 | this.nextChunkLoadAt = 0;
541 | }
542 |
543 | if (this.buffer.writePos >= this.fileSize) {
544 | // All loaded. We don't need to schedule another load
545 | this.lastFrameIndex = this.buffer.writePos << 3;
546 | this.canPlayThrough = true;
547 |
548 | if (this.seekable) {
549 | var currentBufferPos = this.buffer.index;
550 | this.buffer.index = 0;
551 | this.collectIntraFrames();
552 | this.buffer.index = currentBufferPos;
553 | }
554 | if (this.externalLoadCallback) {
555 | this.externalLoadCallback(this);
556 | }
557 | }
558 | else {
559 | this.lastFrameIndex = this.findLastPictureStartCode();
560 | this.maybeLoadNextChunk();
561 | }
562 |
563 | this.attemptToPlay();
564 | };
565 |
566 | jsmpeg.prototype.findLastPictureStartCode = function() {
567 | var bufferBytes = this.buffer.bytes;
568 | for (var i = this.buffer.writePos; i > 3; i--) {
569 | if (
570 | bufferBytes[i] == START_PICTURE &&
571 | bufferBytes[i-1] == 0x01 &&
572 | bufferBytes[i-2] == 0x00 &&
573 | bufferBytes[i-3] == 0x00
574 | ) {
575 | return (i-3) << 3;
576 | }
577 | }
578 | return 0;
579 | };
580 |
581 | jsmpeg.prototype.attemptToPlay = function() {
582 | if (this.playing || !this.wantsToPlay || !this.canPlayThrough) {
583 | return;
584 | }
585 | this._playNow();
586 | };
587 |
588 |
589 |
590 | // ----------------------------------------------------------------------------
591 | // Utilities
592 |
593 | jsmpeg.prototype.readCode = function(codeTable) {
594 | var state = 0;
595 | do {
596 | state = codeTable[state + this.buffer.getBits(1)];
597 | } while (state >= 0 && codeTable[state] !== 0 );
598 | return codeTable[state+2];
599 | };
600 |
601 | jsmpeg.prototype.findStartCode = function(code) {
602 | var current = 0;
603 | while (true) {
604 | current = this.buffer.findNextMPEGStartCode();
605 | if (current === code || current === BitReader.NOT_FOUND) {
606 | return current;
607 | }
608 | }
609 | return BitReader.NOT_FOUND;
610 | };
611 |
612 | jsmpeg.prototype.fillArray = function(a, value) {
613 | for (var i = 0, length = a.length; i < length; i++) {
614 | a[i] = value;
615 | }
616 | };
617 |
618 |
619 |
620 | // ----------------------------------------------------------------------------
621 | // Sequence Layer
622 |
623 | jsmpeg.prototype.pictureRate = 30;
624 | jsmpeg.prototype.lateTime = 0;
625 | jsmpeg.prototype.firstSequenceHeader = 0;
626 | jsmpeg.prototype.targetTime = 0;
627 |
628 | jsmpeg.prototype.benchmark = false;
629 | jsmpeg.prototype.benchFrame = 0;
630 | jsmpeg.prototype.benchDecodeTimes = 0;
631 | jsmpeg.prototype.benchAvgFrameTime = 0;
632 |
633 | jsmpeg.prototype.now = function() {
634 | return window.performance
635 | ? window.performance.now()
636 | : Date.now();
637 | };
638 |
639 | jsmpeg.prototype.nextFrame = function() {
640 | if (!this.buffer) { return; }
641 |
642 | var frameStart = this.now();
643 | while(true) {
644 | var code = this.buffer.findNextMPEGStartCode();
645 |
646 | if (code === START_SEQUENCE) {
647 | this.decodeSequenceHeader();
648 | }
649 | else if (code === START_PICTURE) {
650 | if (this.progressive && this.buffer.index >= this.lastFrameIndex) {
651 | // Starved
652 | this.playing = false;
653 | this.maybeLoadNextChunk();
654 | return;
655 | }
656 |
657 | if (this.playing) {
658 | this.scheduleNextFrame();
659 | }
660 | this.decodePicture();
661 | this.benchDecodeTimes += this.now() - frameStart;
662 | return this.canvas;
663 | }
664 | else if (code === BitReader.NOT_FOUND) {
665 | this.stop(); // Jump back to the beginning
666 |
667 | if (this.externalFinishedCallback) {
668 | this.externalFinishedCallback(this);
669 | }
670 |
671 | // Only loop if we found a sequence header
672 | if (this.loop && this.sequenceStarted) {
673 | this.play();
674 | }
675 | return null;
676 | }
677 | else {
678 | // ignore (GROUP, USER_DATA, EXTENSION, SLICES...)
679 | }
680 | }
681 | };
682 |
683 | jsmpeg.prototype.scheduleNextFrame = function() {
684 | this.lateTime = this.now() - this.targetTime;
685 | var wait = Math.max(0, (1000/this.pictureRate) - this.lateTime);
686 | this.targetTime = this.now() + wait;
687 |
688 | if (this.benchmark) {
689 | this.benchFrame++;
690 | if (this.benchFrame >= 120) {
691 | this.benchAvgFrameTime = this.benchDecodeTimes / this.benchFrame;
692 | this.benchFrame = 0;
693 | this.benchDecodeTimes = 0;
694 | if (window.console) { console.log('Average time per frame:', this.benchAvgFrameTime, 'ms'); }
695 | }
696 | setTimeout( this.nextFrame.bind(this), 0);
697 | }
698 | setTimeout(this.nextFrame.bind(this), Math.max(wait,1));
699 | };
700 |
701 | jsmpeg.prototype.decodeSequenceHeader = function() {
702 | this.width = this.buffer.getBits(12);
703 | this.height = this.buffer.getBits(12);
704 | this.buffer.advance(4); // skip pixel aspect ratio
705 | this.pictureRate = PICTURE_RATE[this.buffer.getBits(4)];
706 | this.buffer.advance(18 + 1 + 10 + 1); // skip bitRate, marker, bufferSize and constrained bit
707 |
708 | this.initBuffers();
709 |
710 | var i;
711 |
712 | if (this.buffer.getBits(1)) { // load custom intra quant matrix?
713 | for (i = 0; i < 64; i++) {
714 | this.customIntraQuantMatrix[ZIG_ZAG[i]] = this.buffer.getBits(8);
715 | }
716 | this.intraQuantMatrix = this.customIntraQuantMatrix;
717 | }
718 |
719 | if (this.buffer.getBits(1)) { // load custom non intra quant matrix?
720 | for (i = 0; i < 64; i++) {
721 | this.customNonIntraQuantMatrix[ZIG_ZAG[i]] = this.buffer.getBits(8);
722 | }
723 | this.nonIntraQuantMatrix = this.customNonIntraQuantMatrix;
724 | }
725 | };
726 |
727 | jsmpeg.prototype.initBuffers = function() {
728 | this.intraQuantMatrix = DEFAULT_INTRA_QUANT_MATRIX;
729 | this.nonIntraQuantMatrix = DEFAULT_NON_INTRA_QUANT_MATRIX;
730 |
731 | this.mbWidth = (this.width + 15) >> 4;
732 | this.mbHeight = (this.height + 15) >> 4;
733 | this.mbSize = this.mbWidth * this.mbHeight;
734 |
735 | this.codedWidth = this.mbWidth << 4;
736 | this.codedHeight = this.mbHeight << 4;
737 | this.codedSize = this.codedWidth * this.codedHeight;
738 |
739 | this.halfWidth = this.mbWidth << 3;
740 | this.halfHeight = this.mbHeight << 3;
741 | this.quarterSize = this.codedSize >> 2;
742 |
743 | // Sequence already started? Don't allocate buffers again
744 | if (this.sequenceStarted) { return; }
745 | this.sequenceStarted = true;
746 |
747 |
748 | // Manually clamp values when writing macroblocks for shitty browsers
749 | // that don't support Uint8ClampedArray
750 | var MaybeClampedUint8Array = window.Uint8ClampedArray || window.Uint8Array;
751 | if (!window.Uint8ClampedArray) {
752 | this.copyBlockToDestination = this.copyBlockToDestinationClamp;
753 | this.addBlockToDestination = this.addBlockToDestinationClamp;
754 | }
755 |
756 | // Allocated buffers and resize the canvas
757 | this.currentY = new MaybeClampedUint8Array(this.codedSize);
758 | this.currentY32 = new Uint32Array(this.currentY.buffer);
759 |
760 | this.currentCr = new MaybeClampedUint8Array(this.codedSize >> 2);
761 | this.currentCr32 = new Uint32Array(this.currentCr.buffer);
762 |
763 | this.currentCb = new MaybeClampedUint8Array(this.codedSize >> 2);
764 | this.currentCb32 = new Uint32Array(this.currentCb.buffer);
765 |
766 |
767 | this.forwardY = new MaybeClampedUint8Array(this.codedSize);
768 | this.forwardY32 = new Uint32Array(this.forwardY.buffer);
769 |
770 | this.forwardCr = new MaybeClampedUint8Array(this.codedSize >> 2);
771 | this.forwardCr32 = new Uint32Array(this.forwardCr.buffer);
772 |
773 | this.forwardCb = new MaybeClampedUint8Array(this.codedSize >> 2);
774 | this.forwardCb32 = new Uint32Array(this.forwardCb.buffer);
775 |
776 | this.canvas.width = this.width;
777 | this.canvas.height = this.height;
778 |
779 | if (this.gl) {
780 | this.gl.useProgram(this.program);
781 | this.gl.viewport(0, 0, this.width, this.height);
782 | }
783 | else {
784 | this.currentRGBA = this.canvasContext.getImageData(0, 0, this.width, this.height);
785 | this.fillArray(this.currentRGBA.data, 255);
786 | }
787 | };
788 |
789 |
790 |
791 |
792 | // ----------------------------------------------------------------------------
793 | // Picture Layer
794 |
795 | jsmpeg.prototype.currentY = null;
796 | jsmpeg.prototype.currentCr = null;
797 | jsmpeg.prototype.currentCb = null;
798 |
799 | jsmpeg.prototype.currentRGBA = null;
800 |
801 | jsmpeg.prototype.pictureCodingType = 0;
802 |
803 | // Buffers for motion compensation
804 | jsmpeg.prototype.forwardY = null;
805 | jsmpeg.prototype.forwardCr = null;
806 | jsmpeg.prototype.forwardCb = null;
807 |
808 | jsmpeg.prototype.fullPelForward = false;
809 | jsmpeg.prototype.forwardFCode = 0;
810 | jsmpeg.prototype.forwardRSize = 0;
811 | jsmpeg.prototype.forwardF = 0;
812 |
813 |
814 | jsmpeg.prototype.decodePicture = function(skipOutput) {
815 | this.currentFrame++;
816 | this.currentTime = this.currentFrame / this.pictureRate;
817 |
818 | if (this.progressive) {
819 | this.maybeLoadNextChunk();
820 | }
821 |
822 | this.buffer.advance(10); // skip temporalReference
823 | this.pictureCodingType = this.buffer.getBits(3);
824 | this.buffer.advance(16); // skip vbv_delay
825 |
826 | // Skip B and D frames or unknown coding type
827 | if (this.pictureCodingType <= 0 || this.pictureCodingType >= PICTURE_TYPE_B) {
828 | return;
829 | }
830 |
831 | // full_pel_forward, forward_f_code
832 | if (this.pictureCodingType === PICTURE_TYPE_P) {
833 | this.fullPelForward = this.buffer.getBits(1);
834 | this.forwardFCode = this.buffer.getBits(3);
835 | if (this.forwardFCode === 0) {
836 | // Ignore picture with zero forward_f_code
837 | return;
838 | }
839 | this.forwardRSize = this.forwardFCode - 1;
840 | this.forwardF = 1 << this.forwardRSize;
841 | }
842 |
843 | var code = 0;
844 | do {
845 | code = this.buffer.findNextMPEGStartCode();
846 | } while (code === START_EXTENSION || code === START_USER_DATA );
847 |
848 |
849 | while (code >= START_SLICE_FIRST && code <= START_SLICE_LAST) {
850 | this.decodeSlice( (code & 0x000000FF) );
851 | code = this.buffer.findNextMPEGStartCode();
852 | }
853 |
854 | // We found the next start code; rewind 32bits and let the main loop handle it.
855 | this.buffer.rewind(32);
856 |
857 | // Record this frame, if the recorder wants it
858 | this.recordFrameFromCurrentBuffer();
859 |
860 |
861 | if (skipOutput !== DECODE_SKIP_OUTPUT) {
862 | this.renderFrame();
863 |
864 | if (this.externalDecodeCallback) {
865 | this.externalDecodeCallback(this, this.canvas);
866 | }
867 | }
868 |
869 | // If this is a reference picutre then rotate the prediction pointers
870 | if (this.pictureCodingType === PICTURE_TYPE_I || this.pictureCodingType === PICTURE_TYPE_P) {
871 | var
872 | tmpY = this.forwardY,
873 | tmpY32 = this.forwardY32,
874 | tmpCr = this.forwardCr,
875 | tmpCr32 = this.forwardCr32,
876 | tmpCb = this.forwardCb,
877 | tmpCb32 = this.forwardCb32;
878 |
879 | this.forwardY = this.currentY;
880 | this.forwardY32 = this.currentY32;
881 | this.forwardCr = this.currentCr;
882 | this.forwardCr32 = this.currentCr32;
883 | this.forwardCb = this.currentCb;
884 | this.forwardCb32 = this.currentCb32;
885 |
886 | this.currentY = tmpY;
887 | this.currentY32 = tmpY32;
888 | this.currentCr = tmpCr;
889 | this.currentCr32 = tmpCr32;
890 | this.currentCb = tmpCb;
891 | this.currentCb32 = tmpCb32;
892 | }
893 | };
894 |
895 | jsmpeg.prototype.YCbCrToRGBA = function() {
896 | var pY = this.currentY;
897 | var pCb = this.currentCb;
898 | var pCr = this.currentCr;
899 | var pRGBA = this.currentRGBA.data;
900 |
901 | // Chroma values are the same for each block of 4 pixels, so we proccess
902 | // 2 lines at a time, 2 neighboring pixels each.
903 | // I wish we could use 32bit writes to the RGBA buffer instead of writing
904 | // each byte separately, but we need the automatic clamping of the RGBA
905 | // buffer.
906 |
907 | var yIndex1 = 0;
908 | var yIndex2 = this.codedWidth;
909 | var yNext2Lines = this.codedWidth + (this.codedWidth - this.width);
910 |
911 | var cIndex = 0;
912 | var cNextLine = this.halfWidth - (this.width >> 1);
913 |
914 | var rgbaIndex1 = 0;
915 | var rgbaIndex2 = this.width * 4;
916 | var rgbaNext2Lines = this.width * 4;
917 |
918 | var cols = this.width >> 1;
919 | var rows = this.height >> 1;
920 |
921 | var cb, cr, r, g, b;
922 |
923 | for (var row = 0; row < rows; row++) {
924 | for (var col = 0; col < cols; col++) {
925 | cb = pCb[cIndex];
926 | cr = pCr[cIndex];
927 | cIndex++;
928 |
929 | r = (cr + ((cr * 103) >> 8)) - 179;
930 | g = ((cb * 88) >> 8) - 44 + ((cr * 183) >> 8) - 91;
931 | b = (cb + ((cb * 198) >> 8)) - 227;
932 |
933 | // Line 1
934 | var y1 = pY[yIndex1++];
935 | var y2 = pY[yIndex1++];
936 | pRGBA[rgbaIndex1] = y1 + r;
937 | pRGBA[rgbaIndex1+1] = y1 - g;
938 | pRGBA[rgbaIndex1+2] = y1 + b;
939 | pRGBA[rgbaIndex1+4] = y2 + r;
940 | pRGBA[rgbaIndex1+5] = y2 - g;
941 | pRGBA[rgbaIndex1+6] = y2 + b;
942 | rgbaIndex1 += 8;
943 |
944 | // Line 2
945 | var y3 = pY[yIndex2++];
946 | var y4 = pY[yIndex2++];
947 | pRGBA[rgbaIndex2] = y3 + r;
948 | pRGBA[rgbaIndex2+1] = y3 - g;
949 | pRGBA[rgbaIndex2+2] = y3 + b;
950 | pRGBA[rgbaIndex2+4] = y4 + r;
951 | pRGBA[rgbaIndex2+5] = y4 - g;
952 | pRGBA[rgbaIndex2+6] = y4 + b;
953 | rgbaIndex2 += 8;
954 | }
955 |
956 | yIndex1 += yNext2Lines;
957 | yIndex2 += yNext2Lines;
958 | rgbaIndex1 += rgbaNext2Lines;
959 | rgbaIndex2 += rgbaNext2Lines;
960 | cIndex += cNextLine;
961 | }
962 | };
963 |
964 | jsmpeg.prototype.renderFrame2D = function() {
965 | this.YCbCrToRGBA();
966 | this.canvasContext.putImageData(this.currentRGBA, 0, 0);
967 | };
968 |
969 |
970 | // ----------------------------------------------------------------------------
971 | // Accelerated WebGL YCbCrToRGBA conversion
972 |
973 | jsmpeg.prototype.gl = null;
974 | jsmpeg.prototype.program = null;
975 | jsmpeg.prototype.YTexture = null;
976 | jsmpeg.prototype.CBTexture = null;
977 | jsmpeg.prototype.CRTexture = null;
978 |
979 | jsmpeg.prototype.createTexture = function(index, name) {
980 | var gl = this.gl;
981 | var texture = gl.createTexture();
982 |
983 | gl.bindTexture(gl.TEXTURE_2D, texture);
984 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
985 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
986 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
987 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
988 | gl.uniform1i(gl.getUniformLocation(this.program, name), index);
989 |
990 | return texture;
991 | };
992 |
993 | jsmpeg.prototype.compileShader = function(type, source) {
994 | var gl = this.gl;
995 | var shader = gl.createShader(type);
996 | gl.shaderSource(shader, source);
997 | gl.compileShader(shader);
998 |
999 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
1000 | throw new Error(gl.getShaderInfoLog(shader));
1001 | }
1002 |
1003 | return shader;
1004 | };
1005 |
1006 | jsmpeg.prototype.initWebGL = function() {
1007 | var gl;
1008 |
1009 | // attempt to get a webgl context
1010 | try {
1011 | gl = this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl');
1012 | } catch (e) {
1013 | return false;
1014 | }
1015 |
1016 | if (!gl) {
1017 | return false;
1018 | }
1019 |
1020 | // init buffers
1021 | this.buffer = gl.createBuffer();
1022 | gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
1023 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]), gl.STATIC_DRAW);
1024 |
1025 | // The main YCbCrToRGBA Shader
1026 | this.program = gl.createProgram();
1027 | gl.attachShader(this.program, this.compileShader(gl.VERTEX_SHADER, SHADER_VERTEX_IDENTITY));
1028 | gl.attachShader(this.program, this.compileShader(gl.FRAGMENT_SHADER, SHADER_FRAGMENT_YCBCRTORGBA));
1029 | gl.linkProgram(this.program);
1030 |
1031 | if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
1032 | throw new Error(gl.getProgramInfoLog(this.program));
1033 | }
1034 |
1035 | gl.useProgram(this.program);
1036 |
1037 | // setup textures
1038 | this.YTexture = this.createTexture(0, 'YTexture');
1039 | this.CBTexture = this.createTexture(1, 'CBTexture');
1040 | this.CRTexture = this.createTexture(2, 'CRTexture');
1041 |
1042 | var vertexAttr = gl.getAttribLocation(this.program, 'vertex');
1043 | gl.enableVertexAttribArray(vertexAttr);
1044 | gl.vertexAttribPointer(vertexAttr, 2, gl.FLOAT, false, 0, 0);
1045 |
1046 |
1047 | // Shader for the loading screen
1048 | this.loadingProgram = gl.createProgram();
1049 | gl.attachShader(this.loadingProgram, this.compileShader(gl.VERTEX_SHADER, SHADER_VERTEX_IDENTITY));
1050 | gl.attachShader(this.loadingProgram, this.compileShader(gl.FRAGMENT_SHADER, SHADER_FRAGMENT_LOADING));
1051 | gl.linkProgram(this.loadingProgram);
1052 |
1053 | gl.useProgram(this.loadingProgram);
1054 |
1055 | vertexAttr = gl.getAttribLocation(this.loadingProgram, 'vertex');
1056 | gl.enableVertexAttribArray(vertexAttr);
1057 | gl.vertexAttribPointer(vertexAttr, 2, gl.FLOAT, false, 0, 0);
1058 |
1059 | return true;
1060 | };
1061 |
1062 | jsmpeg.prototype.renderFrameGL = function() {
1063 | var gl = this.gl;
1064 |
1065 | // WebGL doesn't like Uint8ClampedArrays, so we have to create a Uint8Array view for
1066 | // each plane
1067 | var uint8Y = new Uint8Array(this.currentY.buffer),
1068 | uint8Cr = new Uint8Array(this.currentCr.buffer),
1069 | uint8Cb = new Uint8Array(this.currentCb.buffer);
1070 |
1071 | gl.activeTexture(gl.TEXTURE0);
1072 | gl.bindTexture(gl.TEXTURE_2D, this.YTexture);
1073 |
1074 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, this.codedWidth, this.height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, uint8Y);
1075 |
1076 | gl.activeTexture(gl.TEXTURE1);
1077 | gl.bindTexture(gl.TEXTURE_2D, this.CBTexture);
1078 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, this.halfWidth, this.height/2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, uint8Cr);
1079 |
1080 | gl.activeTexture(gl.TEXTURE2);
1081 | gl.bindTexture(gl.TEXTURE_2D, this.CRTexture);
1082 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, this.halfWidth, this.height/2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, uint8Cb);
1083 |
1084 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
1085 | };
1086 |
1087 |
1088 | // ----------------------------------------------------------------------------
1089 | // Slice Layer
1090 |
1091 | jsmpeg.prototype.quantizerScale = 0;
1092 | jsmpeg.prototype.sliceBegin = false;
1093 |
1094 | jsmpeg.prototype.decodeSlice = function(slice) {
1095 | this.sliceBegin = true;
1096 | this.macroblockAddress = (slice - 1) * this.mbWidth - 1;
1097 |
1098 | // Reset motion vectors and DC predictors
1099 | this.motionFwH = this.motionFwHPrev = 0;
1100 | this.motionFwV = this.motionFwVPrev = 0;
1101 | this.dcPredictorY = 128;
1102 | this.dcPredictorCr = 128;
1103 | this.dcPredictorCb = 128;
1104 |
1105 | this.quantizerScale = this.buffer.getBits(5);
1106 |
1107 | // skip extra bits
1108 | while (this.buffer.getBits(1)) {
1109 | this.buffer.advance(8);
1110 | }
1111 |
1112 | do {
1113 | this.decodeMacroblock();
1114 | // We may have to ignore Video Stream Start Codes here (0xE0)!?
1115 | } while (!this.buffer.nextBytesAreStartCode() );
1116 | };
1117 |
1118 |
1119 | // ----------------------------------------------------------------------------
1120 | // Macroblock Layer
1121 |
1122 | jsmpeg.prototype.macroblockAddress = 0;
1123 | jsmpeg.prototype.mbRow = 0;
1124 | jsmpeg.prototype.mbCol = 0;
1125 |
1126 | jsmpeg.prototype.macroblockType = 0;
1127 | jsmpeg.prototype.macroblockIntra = false;
1128 | jsmpeg.prototype.macroblockMotFw = false;
1129 |
1130 | jsmpeg.prototype.motionFwH = 0;
1131 | jsmpeg.prototype.motionFwV = 0;
1132 | jsmpeg.prototype.motionFwHPrev = 0;
1133 | jsmpeg.prototype.motionFwVPrev = 0;
1134 |
1135 | jsmpeg.prototype.decodeMacroblock = function() {
1136 | // Decode macroblock_address_increment
1137 | var
1138 | increment = 0,
1139 | t = this.readCode(MACROBLOCK_ADDRESS_INCREMENT);
1140 |
1141 | while (t === 34) {
1142 | // macroblock_stuffing
1143 | t = this.readCode(MACROBLOCK_ADDRESS_INCREMENT);
1144 | }
1145 | while (t === 35) {
1146 | // macroblock_escape
1147 | increment += 33;
1148 | t = this.readCode(MACROBLOCK_ADDRESS_INCREMENT);
1149 | }
1150 | increment += t;
1151 |
1152 | // Process any skipped macroblocks
1153 | if (this.sliceBegin) {
1154 | // The first macroblock_address_increment of each slice is relative
1155 | // to beginning of the preverious row, not the preverious macroblock
1156 | this.sliceBegin = false;
1157 | this.macroblockAddress += increment;
1158 | }
1159 | else {
1160 | if (this.macroblockAddress + increment >= this.mbSize) {
1161 | // Illegal (too large) macroblock_address_increment
1162 | return;
1163 | }
1164 | if (increment > 1) {
1165 | // Skipped macroblocks reset DC predictors
1166 | this.dcPredictorY = 128;
1167 | this.dcPredictorCr = 128;
1168 | this.dcPredictorCb = 128;
1169 |
1170 | // Skipped macroblocks in P-pictures reset motion vectors
1171 | if (this.pictureCodingType === PICTURE_TYPE_P) {
1172 | this.motionFwH = this.motionFwHPrev = 0;
1173 | this.motionFwV = this.motionFwVPrev = 0;
1174 | }
1175 | }
1176 |
1177 | // Predict skipped macroblocks
1178 | while (increment > 1) {
1179 | this.macroblockAddress++;
1180 | this.mbRow = (this.macroblockAddress / this.mbWidth)|0;
1181 | this.mbCol = this.macroblockAddress % this.mbWidth;
1182 | this.copyMacroblock(this.motionFwH, this.motionFwV, this.forwardY, this.forwardCr, this.forwardCb);
1183 | increment--;
1184 | }
1185 | this.macroblockAddress++;
1186 | }
1187 | this.mbRow = (this.macroblockAddress / this.mbWidth)|0;
1188 | this.mbCol = this.macroblockAddress % this.mbWidth;
1189 |
1190 | // Process the current macroblock
1191 | this.macroblockType = this.readCode(MACROBLOCK_TYPE_TABLES[this.pictureCodingType]);
1192 | this.macroblockIntra = (this.macroblockType & 0x01);
1193 | this.macroblockMotFw = (this.macroblockType & 0x08);
1194 |
1195 | // Quantizer scale
1196 | if ((this.macroblockType & 0x10) !== 0) {
1197 | this.quantizerScale = this.buffer.getBits(5);
1198 | }
1199 |
1200 | if (this.macroblockIntra) {
1201 | // Intra-coded macroblocks reset motion vectors
1202 | this.motionFwH = this.motionFwHPrev = 0;
1203 | this.motionFwV = this.motionFwVPrev = 0;
1204 | }
1205 | else {
1206 | // Non-intra macroblocks reset DC predictors
1207 | this.dcPredictorY = 128;
1208 | this.dcPredictorCr = 128;
1209 | this.dcPredictorCb = 128;
1210 |
1211 | this.decodeMotionVectors();
1212 | this.copyMacroblock(this.motionFwH, this.motionFwV, this.forwardY, this.forwardCr, this.forwardCb);
1213 | }
1214 |
1215 | // Decode blocks
1216 | var cbp = ((this.macroblockType & 0x02) !== 0)
1217 | ? this.readCode(CODE_BLOCK_PATTERN)
1218 | : (this.macroblockIntra ? 0x3f : 0);
1219 |
1220 | for (var block = 0, mask = 0x20; block < 6; block++) {
1221 | if ((cbp & mask) !== 0) {
1222 | this.decodeBlock(block);
1223 | }
1224 | mask >>= 1;
1225 | }
1226 | };
1227 |
1228 |
1229 | jsmpeg.prototype.decodeMotionVectors = function() {
1230 | var code, d, r = 0;
1231 |
1232 | // Forward
1233 | if (this.macroblockMotFw) {
1234 | // Horizontal forward
1235 | code = this.readCode(MOTION);
1236 | if ((code !== 0) && (this.forwardF !== 1)) {
1237 | r = this.buffer.getBits(this.forwardRSize);
1238 | d = ((Math.abs(code) - 1) << this.forwardRSize) + r + 1;
1239 | if (code < 0) {
1240 | d = -d;
1241 | }
1242 | }
1243 | else {
1244 | d = code;
1245 | }
1246 |
1247 | this.motionFwHPrev += d;
1248 | if (this.motionFwHPrev > (this.forwardF << 4) - 1) {
1249 | this.motionFwHPrev -= this.forwardF << 5;
1250 | }
1251 | else if (this.motionFwHPrev < ((-this.forwardF) << 4)) {
1252 | this.motionFwHPrev += this.forwardF << 5;
1253 | }
1254 |
1255 | this.motionFwH = this.motionFwHPrev;
1256 | if (this.fullPelForward) {
1257 | this.motionFwH <<= 1;
1258 | }
1259 |
1260 | // Vertical forward
1261 | code = this.readCode(MOTION);
1262 | if ((code !== 0) && (this.forwardF !== 1)) {
1263 | r = this.buffer.getBits(this.forwardRSize);
1264 | d = ((Math.abs(code) - 1) << this.forwardRSize) + r + 1;
1265 | if (code < 0) {
1266 | d = -d;
1267 | }
1268 | }
1269 | else {
1270 | d = code;
1271 | }
1272 |
1273 | this.motionFwVPrev += d;
1274 | if (this.motionFwVPrev > (this.forwardF << 4) - 1) {
1275 | this.motionFwVPrev -= this.forwardF << 5;
1276 | }
1277 | else if (this.motionFwVPrev < ((-this.forwardF) << 4)) {
1278 | this.motionFwVPrev += this.forwardF << 5;
1279 | }
1280 |
1281 | this.motionFwV = this.motionFwVPrev;
1282 | if (this.fullPelForward) {
1283 | this.motionFwV <<= 1;
1284 | }
1285 | }
1286 | else if (this.pictureCodingType === PICTURE_TYPE_P) {
1287 | // No motion information in P-picture, reset vectors
1288 | this.motionFwH = this.motionFwHPrev = 0;
1289 | this.motionFwV = this.motionFwVPrev = 0;
1290 | }
1291 | };
1292 |
1293 | jsmpeg.prototype.copyMacroblock = function(motionH, motionV, sY, sCr, sCb) {
1294 | var
1295 | width, scan,
1296 | H, V, oddH, oddV,
1297 | src, dest, last;
1298 |
1299 | // We use 32bit writes here
1300 | var dY = this.currentY32;
1301 | var dCb = this.currentCb32;
1302 | var dCr = this.currentCr32;
1303 |
1304 | // Luminance
1305 | width = this.codedWidth;
1306 | scan = width - 16;
1307 |
1308 | H = motionH >> 1;
1309 | V = motionV >> 1;
1310 | oddH = (motionH & 1) === 1;
1311 | oddV = (motionV & 1) === 1;
1312 |
1313 | src = ((this.mbRow << 4) + V) * width + (this.mbCol << 4) + H;
1314 | dest = (this.mbRow * width + this.mbCol) << 2;
1315 | last = dest + (width << 2);
1316 |
1317 | var x;
1318 | var y1, y2, y;
1319 | if (oddH) {
1320 | if (oddV) {
1321 | while (dest < last) {
1322 | y1 = sY[src] + sY[src+width]; src++;
1323 | for (x = 0; x < 4; x++) {
1324 | y2 = sY[src] + sY[src+width]; src++;
1325 | y = (((y1 + y2 + 2) >> 2) & 0xff);
1326 |
1327 | y1 = sY[src] + sY[src+width]; src++;
1328 | y |= (((y1 + y2 + 2) << 6) & 0xff00);
1329 |
1330 | y2 = sY[src] + sY[src+width]; src++;
1331 | y |= (((y1 + y2 + 2) << 14) & 0xff0000);
1332 |
1333 | y1 = sY[src] + sY[src+width]; src++;
1334 | y |= (((y1 + y2 + 2) << 22) & 0xff000000);
1335 |
1336 | dY[dest++] = y;
1337 | }
1338 | dest += scan >> 2; src += scan-1;
1339 | }
1340 | }
1341 | else {
1342 | while (dest < last) {
1343 | y1 = sY[src++];
1344 | for (x = 0; x < 4; x++) {
1345 | y2 = sY[src++];
1346 | y = (((y1 + y2 + 1) >> 1) & 0xff);
1347 |
1348 | y1 = sY[src++];
1349 | y |= (((y1 + y2 + 1) << 7) & 0xff00);
1350 |
1351 | y2 = sY[src++];
1352 | y |= (((y1 + y2 + 1) << 15) & 0xff0000);
1353 |
1354 | y1 = sY[src++];
1355 | y |= (((y1 + y2 + 1) << 23) & 0xff000000);
1356 |
1357 | dY[dest++] = y;
1358 | }
1359 | dest += scan >> 2; src += scan-1;
1360 | }
1361 | }
1362 | }
1363 | else {
1364 | if (oddV) {
1365 | while (dest < last) {
1366 | for (x = 0; x < 4; x++) {
1367 | y = (((sY[src] + sY[src+width] + 1) >> 1) & 0xff); src++;
1368 | y |= (((sY[src] + sY[src+width] + 1) << 7) & 0xff00); src++;
1369 | y |= (((sY[src] + sY[src+width] + 1) << 15) & 0xff0000); src++;
1370 | y |= (((sY[src] + sY[src+width] + 1) << 23) & 0xff000000); src++;
1371 |
1372 | dY[dest++] = y;
1373 | }
1374 | dest += scan >> 2; src += scan;
1375 | }
1376 | }
1377 | else {
1378 | while (dest < last) {
1379 | for (x = 0; x < 4; x++) {
1380 | y = sY[src]; src++;
1381 | y |= sY[src] << 8; src++;
1382 | y |= sY[src] << 16; src++;
1383 | y |= sY[src] << 24; src++;
1384 |
1385 | dY[dest++] = y;
1386 | }
1387 | dest += scan >> 2; src += scan;
1388 | }
1389 | }
1390 | }
1391 |
1392 | // Chrominance
1393 |
1394 | width = this.halfWidth;
1395 | scan = width - 8;
1396 |
1397 | H = (motionH/2) >> 1;
1398 | V = (motionV/2) >> 1;
1399 | oddH = ((motionH/2) & 1) === 1;
1400 | oddV = ((motionV/2) & 1) === 1;
1401 |
1402 | src = ((this.mbRow << 3) + V) * width + (this.mbCol << 3) + H;
1403 | dest = (this.mbRow * width + this.mbCol) << 1;
1404 | last = dest + (width << 1);
1405 |
1406 | var cr1, cr2, cr;
1407 | var cb1, cb2, cb;
1408 | if (oddH) {
1409 | if (oddV) {
1410 | while (dest < last) {
1411 | cr1 = sCr[src] + sCr[src+width];
1412 | cb1 = sCb[src] + sCb[src+width];
1413 | src++;
1414 | for (x = 0; x < 2; x++) {
1415 | cr2 = sCr[src] + sCr[src+width];
1416 | cb2 = sCb[src] + sCb[src+width]; src++;
1417 | cr = (((cr1 + cr2 + 2) >> 2) & 0xff);
1418 | cb = (((cb1 + cb2 + 2) >> 2) & 0xff);
1419 |
1420 | cr1 = sCr[src] + sCr[src+width];
1421 | cb1 = sCb[src] + sCb[src+width]; src++;
1422 | cr |= (((cr1 + cr2 + 2) << 6) & 0xff00);
1423 | cb |= (((cb1 + cb2 + 2) << 6) & 0xff00);
1424 |
1425 | cr2 = sCr[src] + sCr[src+width];
1426 | cb2 = sCb[src] + sCb[src+width]; src++;
1427 | cr |= (((cr1 + cr2 + 2) << 14) & 0xff0000);
1428 | cb |= (((cb1 + cb2 + 2) << 14) & 0xff0000);
1429 |
1430 | cr1 = sCr[src] + sCr[src+width];
1431 | cb1 = sCb[src] + sCb[src+width]; src++;
1432 | cr |= (((cr1 + cr2 + 2) << 22) & 0xff000000);
1433 | cb |= (((cb1 + cb2 + 2) << 22) & 0xff000000);
1434 |
1435 | dCr[dest] = cr;
1436 | dCb[dest] = cb;
1437 | dest++;
1438 | }
1439 | dest += scan >> 2; src += scan-1;
1440 | }
1441 | }
1442 | else {
1443 | while (dest < last) {
1444 | cr1 = sCr[src];
1445 | cb1 = sCb[src];
1446 | src++;
1447 | for (x = 0; x < 2; x++) {
1448 | cr2 = sCr[src];
1449 | cb2 = sCb[src++];
1450 | cr = (((cr1 + cr2 + 1) >> 1) & 0xff);
1451 | cb = (((cb1 + cb2 + 1) >> 1) & 0xff);
1452 |
1453 | cr1 = sCr[src];
1454 | cb1 = sCb[src++];
1455 | cr |= (((cr1 + cr2 + 1) << 7) & 0xff00);
1456 | cb |= (((cb1 + cb2 + 1) << 7) & 0xff00);
1457 |
1458 | cr2 = sCr[src];
1459 | cb2 = sCb[src++];
1460 | cr |= (((cr1 + cr2 + 1) << 15) & 0xff0000);
1461 | cb |= (((cb1 + cb2 + 1) << 15) & 0xff0000);
1462 |
1463 | cr1 = sCr[src];
1464 | cb1 = sCb[src++];
1465 | cr |= (((cr1 + cr2 + 1) << 23) & 0xff000000);
1466 | cb |= (((cb1 + cb2 + 1) << 23) & 0xff000000);
1467 |
1468 | dCr[dest] = cr;
1469 | dCb[dest] = cb;
1470 | dest++;
1471 | }
1472 | dest += scan >> 2; src += scan-1;
1473 | }
1474 | }
1475 | }
1476 | else {
1477 | if (oddV) {
1478 | while (dest < last) {
1479 | for (x = 0; x < 2; x++) {
1480 | cr = (((sCr[src] + sCr[src+width] + 1) >> 1) & 0xff);
1481 | cb = (((sCb[src] + sCb[src+width] + 1) >> 1) & 0xff); src++;
1482 |
1483 | cr |= (((sCr[src] + sCr[src+width] + 1) << 7) & 0xff00);
1484 | cb |= (((sCb[src] + sCb[src+width] + 1) << 7) & 0xff00); src++;
1485 |
1486 | cr |= (((sCr[src] + sCr[src+width] + 1) << 15) & 0xff0000);
1487 | cb |= (((sCb[src] + sCb[src+width] + 1) << 15) & 0xff0000); src++;
1488 |
1489 | cr |= (((sCr[src] + sCr[src+width] + 1) << 23) & 0xff000000);
1490 | cb |= (((sCb[src] + sCb[src+width] + 1) << 23) & 0xff000000); src++;
1491 |
1492 | dCr[dest] = cr;
1493 | dCb[dest] = cb;
1494 | dest++;
1495 | }
1496 | dest += scan >> 2; src += scan;
1497 | }
1498 | }
1499 | else {
1500 | while (dest < last) {
1501 | for (x = 0; x < 2; x++) {
1502 | cr = sCr[src];
1503 | cb = sCb[src]; src++;
1504 |
1505 | cr |= sCr[src] << 8;
1506 | cb |= sCb[src] << 8; src++;
1507 |
1508 | cr |= sCr[src] << 16;
1509 | cb |= sCb[src] << 16; src++;
1510 |
1511 | cr |= sCr[src] << 24;
1512 | cb |= sCb[src] << 24; src++;
1513 |
1514 | dCr[dest] = cr;
1515 | dCb[dest] = cb;
1516 | dest++;
1517 | }
1518 | dest += scan >> 2; src += scan;
1519 | }
1520 | }
1521 | }
1522 | };
1523 |
1524 |
1525 | // ----------------------------------------------------------------------------
1526 | // Block layer
1527 |
1528 | //jsmpeg.prototype.dcPredictorY;
1529 | //jsmpeg.prototype.dcPredictorCr;
1530 | //jsmpeg.prototype.dcPredictorCb;
1531 |
1532 | jsmpeg.prototype.blockData = null;
1533 | jsmpeg.prototype.decodeBlock = function(block) {
1534 |
1535 | var
1536 | n = 0,
1537 | quantMatrix;
1538 |
1539 | // Decode DC coefficient of intra-coded blocks
1540 | if (this.macroblockIntra) {
1541 | var
1542 | predictor,
1543 | dctSize;
1544 |
1545 | // DC prediction
1546 |
1547 | if (block < 4) {
1548 | predictor = this.dcPredictorY;
1549 | dctSize = this.readCode(DCT_DC_SIZE_LUMINANCE);
1550 | }
1551 | else {
1552 | predictor = (block === 4 ? this.dcPredictorCr : this.dcPredictorCb);
1553 | dctSize = this.readCode(DCT_DC_SIZE_CHROMINANCE);
1554 | }
1555 |
1556 | // Read DC coeff
1557 | if (dctSize > 0) {
1558 | var differential = this.buffer.getBits(dctSize);
1559 | if ((differential & (1 << (dctSize - 1))) !== 0) {
1560 | this.blockData[0] = predictor + differential;
1561 | }
1562 | else {
1563 | this.blockData[0] = predictor + ((-1 << dctSize)|(differential+1));
1564 | }
1565 | }
1566 | else {
1567 | this.blockData[0] = predictor;
1568 | }
1569 |
1570 | // Save predictor value
1571 | if (block < 4) {
1572 | this.dcPredictorY = this.blockData[0];
1573 | }
1574 | else if (block === 4) {
1575 | this.dcPredictorCr = this.blockData[0];
1576 | }
1577 | else {
1578 | this.dcPredictorCb = this.blockData[0];
1579 | }
1580 |
1581 | // Dequantize + premultiply
1582 | this.blockData[0] <<= (3 + 5);
1583 |
1584 | quantMatrix = this.intraQuantMatrix;
1585 | n = 1;
1586 | }
1587 | else {
1588 | quantMatrix = this.nonIntraQuantMatrix;
1589 | }
1590 |
1591 | // Decode AC coefficients (+DC for non-intra)
1592 | var level = 0;
1593 | while (true) {
1594 | var
1595 | run = 0,
1596 | coeff = this.readCode(DCT_COEFF);
1597 |
1598 | if ((coeff === 0x0001) && (n > 0) && (this.buffer.getBits(1) === 0)) {
1599 | // end_of_block
1600 | break;
1601 | }
1602 | if (coeff === 0xffff) {
1603 | // escape
1604 | run = this.buffer.getBits(6);
1605 | level = this.buffer.getBits(8);
1606 | if (level === 0) {
1607 | level = this.buffer.getBits(8);
1608 | }
1609 | else if (level === 128) {
1610 | level = this.buffer.getBits(8) - 256;
1611 | }
1612 | else if (level > 128) {
1613 | level = level - 256;
1614 | }
1615 | }
1616 | else {
1617 | run = coeff >> 8;
1618 | level = coeff & 0xff;
1619 | if (this.buffer.getBits(1)) {
1620 | level = -level;
1621 | }
1622 | }
1623 |
1624 | n += run;
1625 | var dezigZagged = ZIG_ZAG[n];
1626 | n++;
1627 |
1628 | // Dequantize, oddify, clip
1629 | level <<= 1;
1630 | if (!this.macroblockIntra) {
1631 | level += (level < 0 ? -1 : 1);
1632 | }
1633 | level = (level * this.quantizerScale * quantMatrix[dezigZagged]) >> 4;
1634 | if ((level & 1) === 0) {
1635 | level -= level > 0 ? 1 : -1;
1636 | }
1637 | if (level > 2047) {
1638 | level = 2047;
1639 | }
1640 | else if (level < -2048) {
1641 | level = -2048;
1642 | }
1643 |
1644 | // Save premultiplied coefficient
1645 | this.blockData[dezigZagged] = level * PREMULTIPLIER_MATRIX[dezigZagged];
1646 | }
1647 |
1648 | // Move block to its place
1649 | var
1650 | destArray,
1651 | destIndex,
1652 | scan;
1653 |
1654 | if (block < 4) {
1655 | destArray = this.currentY;
1656 | scan = this.codedWidth - 8;
1657 | destIndex = (this.mbRow * this.codedWidth + this.mbCol) << 4;
1658 | if ((block & 1) !== 0) {
1659 | destIndex += 8;
1660 | }
1661 | if ((block & 2) !== 0) {
1662 | destIndex += this.codedWidth << 3;
1663 | }
1664 | }
1665 | else {
1666 | destArray = (block === 4) ? this.currentCb : this.currentCr;
1667 | scan = (this.codedWidth >> 1) - 8;
1668 | destIndex = ((this.mbRow * this.codedWidth) << 2) + (this.mbCol << 3);
1669 | }
1670 |
1671 | if (this.macroblockIntra) {
1672 | // Overwrite (no prediction)
1673 | if (n === 1) {
1674 | this.copyValueToDestination((this.blockData[0] + 128) >> 8, destArray, destIndex, scan);
1675 | this.blockData[0] = 0;
1676 | } else {
1677 | this.IDCT();
1678 | this.copyBlockToDestination(this.blockData, destArray, destIndex, scan);
1679 | this.blockData.set(this.zeroBlockData);
1680 | }
1681 | }
1682 | else {
1683 | // Add data to the predicted macroblock
1684 | if (n === 1) {
1685 | this.addValueToDestination((this.blockData[0] + 128) >> 8, destArray, destIndex, scan);
1686 | this.blockData[0] = 0;
1687 | } else {
1688 | this.IDCT();
1689 | this.addBlockToDestination(this.blockData, destArray, destIndex, scan);
1690 | this.blockData.set(this.zeroBlockData);
1691 | }
1692 | }
1693 |
1694 | n = 0;
1695 | };
1696 |
1697 | jsmpeg.prototype.copyBlockToDestination = function(blockData, destArray, destIndex, scan) {
1698 | for (var n = 0; n < 64; n += 8, destIndex += scan+8) {
1699 | destArray[destIndex+0] = blockData[n+0];
1700 | destArray[destIndex+1] = blockData[n+1];
1701 | destArray[destIndex+2] = blockData[n+2];
1702 | destArray[destIndex+3] = blockData[n+3];
1703 | destArray[destIndex+4] = blockData[n+4];
1704 | destArray[destIndex+5] = blockData[n+5];
1705 | destArray[destIndex+6] = blockData[n+6];
1706 | destArray[destIndex+7] = blockData[n+7];
1707 | }
1708 | };
1709 |
1710 | jsmpeg.prototype.addBlockToDestination = function(blockData, destArray, destIndex, scan) {
1711 | for (var n = 0; n < 64; n += 8, destIndex += scan+8) {
1712 | destArray[destIndex+0] += blockData[n+0];
1713 | destArray[destIndex+1] += blockData[n+1];
1714 | destArray[destIndex+2] += blockData[n+2];
1715 | destArray[destIndex+3] += blockData[n+3];
1716 | destArray[destIndex+4] += blockData[n+4];
1717 | destArray[destIndex+5] += blockData[n+5];
1718 | destArray[destIndex+6] += blockData[n+6];
1719 | destArray[destIndex+7] += blockData[n+7];
1720 | }
1721 | };
1722 |
1723 | jsmpeg.prototype.copyValueToDestination = function(value, destArray, destIndex, scan) {
1724 | for (var n = 0; n < 64; n += 8, destIndex += scan+8) {
1725 | destArray[destIndex+0] = value;
1726 | destArray[destIndex+1] = value;
1727 | destArray[destIndex+2] = value;
1728 | destArray[destIndex+3] = value;
1729 | destArray[destIndex+4] = value;
1730 | destArray[destIndex+5] = value;
1731 | destArray[destIndex+6] = value;
1732 | destArray[destIndex+7] = value;
1733 | }
1734 | };
1735 |
1736 | jsmpeg.prototype.addValueToDestination = function(value, destArray, destIndex, scan) {
1737 | for (var n = 0; n < 64; n += 8, destIndex += scan+8) {
1738 | destArray[destIndex+0] += value;
1739 | destArray[destIndex+1] += value;
1740 | destArray[destIndex+2] += value;
1741 | destArray[destIndex+3] += value;
1742 | destArray[destIndex+4] += value;
1743 | destArray[destIndex+5] += value;
1744 | destArray[destIndex+6] += value;
1745 | destArray[destIndex+7] += value;
1746 | }
1747 | };
1748 |
1749 | // Clamping version for shitty browsers (IE) that don't support Uint8ClampedArray
1750 | jsmpeg.prototype.copyBlockToDestinationClamp = function(blockData, destArray, destIndex, scan) {
1751 | var n = 0;
1752 | for (var i = 0; i < 8; i++) {
1753 | for (var j = 0; j < 8; j++) {
1754 | var p = blockData[n++];
1755 | destArray[destIndex++] = p > 255 ? 255 : (p < 0 ? 0 : p);
1756 | }
1757 | destIndex += scan;
1758 | }
1759 | };
1760 |
1761 | jsmpeg.prototype.addBlockToDestinationClamp = function(blockData, destArray, destIndex, scan) {
1762 | var n = 0;
1763 | for (var i = 0; i < 8; i++) {
1764 | for (var j = 0; j < 8; j++) {
1765 | var p = blockData[n++] + destArray[destIndex];
1766 | destArray[destIndex++] = p > 255 ? 255 : (p < 0 ? 0 : p);
1767 | }
1768 | destIndex += scan;
1769 | }
1770 | };
1771 |
1772 | jsmpeg.prototype.IDCT = function() {
1773 | // See http://vsr.informatik.tu-chemnitz.de/~jan/MPEG/HTML/IDCT.html
1774 | // for more info.
1775 |
1776 | var
1777 | b1, b3, b4, b6, b7, tmp1, tmp2, m0,
1778 | x0, x1, x2, x3, x4, y3, y4, y5, y6, y7,
1779 | i,
1780 | blockData = this.blockData;
1781 |
1782 | // Transform columns
1783 | for (i = 0; i < 8; ++i) {
1784 | b1 = blockData[4*8+i];
1785 | b3 = blockData[2*8+i] + blockData[6*8+i];
1786 | b4 = blockData[5*8+i] - blockData[3*8+i];
1787 | tmp1 = blockData[1*8+i] + blockData[7*8+i];
1788 | tmp2 = blockData[3*8+i] + blockData[5*8+i];
1789 | b6 = blockData[1*8+i] - blockData[7*8+i];
1790 | b7 = tmp1 + tmp2;
1791 | m0 = blockData[0*8+i];
1792 | x4 = ((b6*473 - b4*196 + 128) >> 8) - b7;
1793 | x0 = x4 - (((tmp1 - tmp2)*362 + 128) >> 8);
1794 | x1 = m0 - b1;
1795 | x2 = (((blockData[2*8+i] - blockData[6*8+i])*362 + 128) >> 8) - b3;
1796 | x3 = m0 + b1;
1797 | y3 = x1 + x2;
1798 | y4 = x3 + b3;
1799 | y5 = x1 - x2;
1800 | y6 = x3 - b3;
1801 | y7 = -x0 - ((b4*473 + b6*196 + 128) >> 8);
1802 | blockData[0*8+i] = b7 + y4;
1803 | blockData[1*8+i] = x4 + y3;
1804 | blockData[2*8+i] = y5 - x0;
1805 | blockData[3*8+i] = y6 - y7;
1806 | blockData[4*8+i] = y6 + y7;
1807 | blockData[5*8+i] = x0 + y5;
1808 | blockData[6*8+i] = y3 - x4;
1809 | blockData[7*8+i] = y4 - b7;
1810 | }
1811 |
1812 | // Transform rows
1813 | for (i = 0; i < 64; i += 8) {
1814 | b1 = blockData[4+i];
1815 | b3 = blockData[2+i] + blockData[6+i];
1816 | b4 = blockData[5+i] - blockData[3+i];
1817 | tmp1 = blockData[1+i] + blockData[7+i];
1818 | tmp2 = blockData[3+i] + blockData[5+i];
1819 | b6 = blockData[1+i] - blockData[7+i];
1820 | b7 = tmp1 + tmp2;
1821 | m0 = blockData[0+i];
1822 | x4 = ((b6*473 - b4*196 + 128) >> 8) - b7;
1823 | x0 = x4 - (((tmp1 - tmp2)*362 + 128) >> 8);
1824 | x1 = m0 - b1;
1825 | x2 = (((blockData[2+i] - blockData[6+i])*362 + 128) >> 8) - b3;
1826 | x3 = m0 + b1;
1827 | y3 = x1 + x2;
1828 | y4 = x3 + b3;
1829 | y5 = x1 - x2;
1830 | y6 = x3 - b3;
1831 | y7 = -x0 - ((b4*473 + b6*196 + 128) >> 8);
1832 | blockData[0+i] = (b7 + y4 + 128) >> 8;
1833 | blockData[1+i] = (x4 + y3 + 128) >> 8;
1834 | blockData[2+i] = (y5 - x0 + 128) >> 8;
1835 | blockData[3+i] = (y6 - y7 + 128) >> 8;
1836 | blockData[4+i] = (y6 + y7 + 128) >> 8;
1837 | blockData[5+i] = (x0 + y5 + 128) >> 8;
1838 | blockData[6+i] = (y3 - x4 + 128) >> 8;
1839 | blockData[7+i] = (y4 - b7 + 128) >> 8;
1840 | }
1841 | };
1842 |
1843 |
1844 | // ----------------------------------------------------------------------------
1845 | // VLC Tables and Constants
1846 |
1847 | var
1848 | SOCKET_MAGIC_BYTES = 'jsmp',
1849 | DECODE_SKIP_OUTPUT = 1,
1850 | PICTURE_RATE = [
1851 | 0.000, 23.976, 24.000, 25.000, 29.970, 30.000, 50.000, 59.940,
1852 | 60.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
1853 | ],
1854 | ZIG_ZAG = new Uint8Array([
1855 | 0, 1, 8, 16, 9, 2, 3, 10,
1856 | 17, 24, 32, 25, 18, 11, 4, 5,
1857 | 12, 19, 26, 33, 40, 48, 41, 34,
1858 | 27, 20, 13, 6, 7, 14, 21, 28,
1859 | 35, 42, 49, 56, 57, 50, 43, 36,
1860 | 29, 22, 15, 23, 30, 37, 44, 51,
1861 | 58, 59, 52, 45, 38, 31, 39, 46,
1862 | 53, 60, 61, 54, 47, 55, 62, 63
1863 | ]),
1864 | DEFAULT_INTRA_QUANT_MATRIX = new Uint8Array([
1865 | 8, 16, 19, 22, 26, 27, 29, 34,
1866 | 16, 16, 22, 24, 27, 29, 34, 37,
1867 | 19, 22, 26, 27, 29, 34, 34, 38,
1868 | 22, 22, 26, 27, 29, 34, 37, 40,
1869 | 22, 26, 27, 29, 32, 35, 40, 48,
1870 | 26, 27, 29, 32, 35, 40, 48, 58,
1871 | 26, 27, 29, 34, 38, 46, 56, 69,
1872 | 27, 29, 35, 38, 46, 56, 69, 83
1873 | ]),
1874 | DEFAULT_NON_INTRA_QUANT_MATRIX = new Uint8Array([
1875 | 16, 16, 16, 16, 16, 16, 16, 16,
1876 | 16, 16, 16, 16, 16, 16, 16, 16,
1877 | 16, 16, 16, 16, 16, 16, 16, 16,
1878 | 16, 16, 16, 16, 16, 16, 16, 16,
1879 | 16, 16, 16, 16, 16, 16, 16, 16,
1880 | 16, 16, 16, 16, 16, 16, 16, 16,
1881 | 16, 16, 16, 16, 16, 16, 16, 16,
1882 | 16, 16, 16, 16, 16, 16, 16, 16
1883 | ]),
1884 |
1885 | PREMULTIPLIER_MATRIX = new Uint8Array([
1886 | 32, 44, 42, 38, 32, 25, 17, 9,
1887 | 44, 62, 58, 52, 44, 35, 24, 12,
1888 | 42, 58, 55, 49, 42, 33, 23, 12,
1889 | 38, 52, 49, 44, 38, 30, 20, 10,
1890 | 32, 44, 42, 38, 32, 25, 17, 9,
1891 | 25, 35, 33, 30, 25, 20, 14, 7,
1892 | 17, 24, 23, 20, 17, 14, 9, 5,
1893 | 9, 12, 12, 10, 9, 7, 5, 2
1894 | ]),
1895 |
1896 | // MPEG-1 VLC
1897 |
1898 | // macroblock_stuffing decodes as 34.
1899 | // macroblock_escape decodes as 35.
1900 |
1901 | MACROBLOCK_ADDRESS_INCREMENT = new Int16Array([
1902 | 1*3, 2*3, 0, // 0
1903 | 3*3, 4*3, 0, // 1 0
1904 | 0, 0, 1, // 2 1.
1905 | 5*3, 6*3, 0, // 3 00
1906 | 7*3, 8*3, 0, // 4 01
1907 | 9*3, 10*3, 0, // 5 000
1908 | 11*3, 12*3, 0, // 6 001
1909 | 0, 0, 3, // 7 010.
1910 | 0, 0, 2, // 8 011.
1911 | 13*3, 14*3, 0, // 9 0000
1912 | 15*3, 16*3, 0, // 10 0001
1913 | 0, 0, 5, // 11 0010.
1914 | 0, 0, 4, // 12 0011.
1915 | 17*3, 18*3, 0, // 13 0000 0
1916 | 19*3, 20*3, 0, // 14 0000 1
1917 | 0, 0, 7, // 15 0001 0.
1918 | 0, 0, 6, // 16 0001 1.
1919 | 21*3, 22*3, 0, // 17 0000 00
1920 | 23*3, 24*3, 0, // 18 0000 01
1921 | 25*3, 26*3, 0, // 19 0000 10
1922 | 27*3, 28*3, 0, // 20 0000 11
1923 | -1, 29*3, 0, // 21 0000 000
1924 | -1, 30*3, 0, // 22 0000 001
1925 | 31*3, 32*3, 0, // 23 0000 010
1926 | 33*3, 34*3, 0, // 24 0000 011
1927 | 35*3, 36*3, 0, // 25 0000 100
1928 | 37*3, 38*3, 0, // 26 0000 101
1929 | 0, 0, 9, // 27 0000 110.
1930 | 0, 0, 8, // 28 0000 111.
1931 | 39*3, 40*3, 0, // 29 0000 0001
1932 | 41*3, 42*3, 0, // 30 0000 0011
1933 | 43*3, 44*3, 0, // 31 0000 0100
1934 | 45*3, 46*3, 0, // 32 0000 0101
1935 | 0, 0, 15, // 33 0000 0110.
1936 | 0, 0, 14, // 34 0000 0111.
1937 | 0, 0, 13, // 35 0000 1000.
1938 | 0, 0, 12, // 36 0000 1001.
1939 | 0, 0, 11, // 37 0000 1010.
1940 | 0, 0, 10, // 38 0000 1011.
1941 | 47*3, -1, 0, // 39 0000 0001 0
1942 | -1, 48*3, 0, // 40 0000 0001 1
1943 | 49*3, 50*3, 0, // 41 0000 0011 0
1944 | 51*3, 52*3, 0, // 42 0000 0011 1
1945 | 53*3, 54*3, 0, // 43 0000 0100 0
1946 | 55*3, 56*3, 0, // 44 0000 0100 1
1947 | 57*3, 58*3, 0, // 45 0000 0101 0
1948 | 59*3, 60*3, 0, // 46 0000 0101 1
1949 | 61*3, -1, 0, // 47 0000 0001 00
1950 | -1, 62*3, 0, // 48 0000 0001 11
1951 | 63*3, 64*3, 0, // 49 0000 0011 00
1952 | 65*3, 66*3, 0, // 50 0000 0011 01
1953 | 67*3, 68*3, 0, // 51 0000 0011 10
1954 | 69*3, 70*3, 0, // 52 0000 0011 11
1955 | 71*3, 72*3, 0, // 53 0000 0100 00
1956 | 73*3, 74*3, 0, // 54 0000 0100 01
1957 | 0, 0, 21, // 55 0000 0100 10.
1958 | 0, 0, 20, // 56 0000 0100 11.
1959 | 0, 0, 19, // 57 0000 0101 00.
1960 | 0, 0, 18, // 58 0000 0101 01.
1961 | 0, 0, 17, // 59 0000 0101 10.
1962 | 0, 0, 16, // 60 0000 0101 11.
1963 | 0, 0, 35, // 61 0000 0001 000. -- macroblock_escape
1964 | 0, 0, 34, // 62 0000 0001 111. -- macroblock_stuffing
1965 | 0, 0, 33, // 63 0000 0011 000.
1966 | 0, 0, 32, // 64 0000 0011 001.
1967 | 0, 0, 31, // 65 0000 0011 010.
1968 | 0, 0, 30, // 66 0000 0011 011.
1969 | 0, 0, 29, // 67 0000 0011 100.
1970 | 0, 0, 28, // 68 0000 0011 101.
1971 | 0, 0, 27, // 69 0000 0011 110.
1972 | 0, 0, 26, // 70 0000 0011 111.
1973 | 0, 0, 25, // 71 0000 0100 000.
1974 | 0, 0, 24, // 72 0000 0100 001.
1975 | 0, 0, 23, // 73 0000 0100 010.
1976 | 0, 0, 22 // 74 0000 0100 011.
1977 | ]),
1978 |
1979 | // macroblock_type bitmap:
1980 | // 0x10 macroblock_quant
1981 | // 0x08 macroblock_motion_forward
1982 | // 0x04 macroblock_motion_backward
1983 | // 0x02 macrobkock_pattern
1984 | // 0x01 macroblock_intra
1985 | //
1986 |
1987 | MACROBLOCK_TYPE_I = new Int8Array([
1988 | 1*3, 2*3, 0, // 0
1989 | -1, 3*3, 0, // 1 0
1990 | 0, 0, 0x01, // 2 1.
1991 | 0, 0, 0x11 // 3 01.
1992 | ]),
1993 |
1994 | MACROBLOCK_TYPE_P = new Int8Array([
1995 | 1*3, 2*3, 0, // 0
1996 | 3*3, 4*3, 0, // 1 0
1997 | 0, 0, 0x0a, // 2 1.
1998 | 5*3, 6*3, 0, // 3 00
1999 | 0, 0, 0x02, // 4 01.
2000 | 7*3, 8*3, 0, // 5 000
2001 | 0, 0, 0x08, // 6 001.
2002 | 9*3, 10*3, 0, // 7 0000
2003 | 11*3, 12*3, 0, // 8 0001
2004 | -1, 13*3, 0, // 9 00000
2005 | 0, 0, 0x12, // 10 00001.
2006 | 0, 0, 0x1a, // 11 00010.
2007 | 0, 0, 0x01, // 12 00011.
2008 | 0, 0, 0x11 // 13 000001.
2009 | ]),
2010 |
2011 | MACROBLOCK_TYPE_B = new Int8Array([
2012 | 1*3, 2*3, 0, // 0
2013 | 3*3, 5*3, 0, // 1 0
2014 | 4*3, 6*3, 0, // 2 1
2015 | 8*3, 7*3, 0, // 3 00
2016 | 0, 0, 0x0c, // 4 10.
2017 | 9*3, 10*3, 0, // 5 01
2018 | 0, 0, 0x0e, // 6 11.
2019 | 13*3, 14*3, 0, // 7 001
2020 | 12*3, 11*3, 0, // 8 000
2021 | 0, 0, 0x04, // 9 010.
2022 | 0, 0, 0x06, // 10 011.
2023 | 18*3, 16*3, 0, // 11 0001
2024 | 15*3, 17*3, 0, // 12 0000
2025 | 0, 0, 0x08, // 13 0010.
2026 | 0, 0, 0x0a, // 14 0011.
2027 | -1, 19*3, 0, // 15 00000
2028 | 0, 0, 0x01, // 16 00011.
2029 | 20*3, 21*3, 0, // 17 00001
2030 | 0, 0, 0x1e, // 18 00010.
2031 | 0, 0, 0x11, // 19 000001.
2032 | 0, 0, 0x16, // 20 000010.
2033 | 0, 0, 0x1a // 21 000011.
2034 | ]),
2035 |
2036 | CODE_BLOCK_PATTERN = new Int16Array([
2037 | 2*3, 1*3, 0, // 0
2038 | 3*3, 6*3, 0, // 1 1
2039 | 4*3, 5*3, 0, // 2 0
2040 | 8*3, 11*3, 0, // 3 10
2041 | 12*3, 13*3, 0, // 4 00
2042 | 9*3, 7*3, 0, // 5 01
2043 | 10*3, 14*3, 0, // 6 11
2044 | 20*3, 19*3, 0, // 7 011
2045 | 18*3, 16*3, 0, // 8 100
2046 | 23*3, 17*3, 0, // 9 010
2047 | 27*3, 25*3, 0, // 10 110
2048 | 21*3, 28*3, 0, // 11 101
2049 | 15*3, 22*3, 0, // 12 000
2050 | 24*3, 26*3, 0, // 13 001
2051 | 0, 0, 60, // 14 111.
2052 | 35*3, 40*3, 0, // 15 0000
2053 | 44*3, 48*3, 0, // 16 1001
2054 | 38*3, 36*3, 0, // 17 0101
2055 | 42*3, 47*3, 0, // 18 1000
2056 | 29*3, 31*3, 0, // 19 0111
2057 | 39*3, 32*3, 0, // 20 0110
2058 | 0, 0, 32, // 21 1010.
2059 | 45*3, 46*3, 0, // 22 0001
2060 | 33*3, 41*3, 0, // 23 0100
2061 | 43*3, 34*3, 0, // 24 0010
2062 | 0, 0, 4, // 25 1101.
2063 | 30*3, 37*3, 0, // 26 0011
2064 | 0, 0, 8, // 27 1100.
2065 | 0, 0, 16, // 28 1011.
2066 | 0, 0, 44, // 29 0111 0.
2067 | 50*3, 56*3, 0, // 30 0011 0
2068 | 0, 0, 28, // 31 0111 1.
2069 | 0, 0, 52, // 32 0110 1.
2070 | 0, 0, 62, // 33 0100 0.
2071 | 61*3, 59*3, 0, // 34 0010 1
2072 | 52*3, 60*3, 0, // 35 0000 0
2073 | 0, 0, 1, // 36 0101 1.
2074 | 55*3, 54*3, 0, // 37 0011 1
2075 | 0, 0, 61, // 38 0101 0.
2076 | 0, 0, 56, // 39 0110 0.
2077 | 57*3, 58*3, 0, // 40 0000 1
2078 | 0, 0, 2, // 41 0100 1.
2079 | 0, 0, 40, // 42 1000 0.
2080 | 51*3, 62*3, 0, // 43 0010 0
2081 | 0, 0, 48, // 44 1001 0.
2082 | 64*3, 63*3, 0, // 45 0001 0
2083 | 49*3, 53*3, 0, // 46 0001 1
2084 | 0, 0, 20, // 47 1000 1.
2085 | 0, 0, 12, // 48 1001 1.
2086 | 80*3, 83*3, 0, // 49 0001 10
2087 | 0, 0, 63, // 50 0011 00.
2088 | 77*3, 75*3, 0, // 51 0010 00
2089 | 65*3, 73*3, 0, // 52 0000 00
2090 | 84*3, 66*3, 0, // 53 0001 11
2091 | 0, 0, 24, // 54 0011 11.
2092 | 0, 0, 36, // 55 0011 10.
2093 | 0, 0, 3, // 56 0011 01.
2094 | 69*3, 87*3, 0, // 57 0000 10
2095 | 81*3, 79*3, 0, // 58 0000 11
2096 | 68*3, 71*3, 0, // 59 0010 11
2097 | 70*3, 78*3, 0, // 60 0000 01
2098 | 67*3, 76*3, 0, // 61 0010 10
2099 | 72*3, 74*3, 0, // 62 0010 01
2100 | 86*3, 85*3, 0, // 63 0001 01
2101 | 88*3, 82*3, 0, // 64 0001 00
2102 | -1, 94*3, 0, // 65 0000 000
2103 | 95*3, 97*3, 0, // 66 0001 111
2104 | 0, 0, 33, // 67 0010 100.
2105 | 0, 0, 9, // 68 0010 110.
2106 | 106*3, 110*3, 0, // 69 0000 100
2107 | 102*3, 116*3, 0, // 70 0000 010
2108 | 0, 0, 5, // 71 0010 111.
2109 | 0, 0, 10, // 72 0010 010.
2110 | 93*3, 89*3, 0, // 73 0000 001
2111 | 0, 0, 6, // 74 0010 011.
2112 | 0, 0, 18, // 75 0010 001.
2113 | 0, 0, 17, // 76 0010 101.
2114 | 0, 0, 34, // 77 0010 000.
2115 | 113*3, 119*3, 0, // 78 0000 011
2116 | 103*3, 104*3, 0, // 79 0000 111
2117 | 90*3, 92*3, 0, // 80 0001 100
2118 | 109*3, 107*3, 0, // 81 0000 110
2119 | 117*3, 118*3, 0, // 82 0001 001
2120 | 101*3, 99*3, 0, // 83 0001 101
2121 | 98*3, 96*3, 0, // 84 0001 110
2122 | 100*3, 91*3, 0, // 85 0001 011
2123 | 114*3, 115*3, 0, // 86 0001 010
2124 | 105*3, 108*3, 0, // 87 0000 101
2125 | 112*3, 111*3, 0, // 88 0001 000
2126 | 121*3, 125*3, 0, // 89 0000 0011
2127 | 0, 0, 41, // 90 0001 1000.
2128 | 0, 0, 14, // 91 0001 0111.
2129 | 0, 0, 21, // 92 0001 1001.
2130 | 124*3, 122*3, 0, // 93 0000 0010
2131 | 120*3, 123*3, 0, // 94 0000 0001
2132 | 0, 0, 11, // 95 0001 1110.
2133 | 0, 0, 19, // 96 0001 1101.
2134 | 0, 0, 7, // 97 0001 1111.
2135 | 0, 0, 35, // 98 0001 1100.
2136 | 0, 0, 13, // 99 0001 1011.
2137 | 0, 0, 50, // 100 0001 0110.
2138 | 0, 0, 49, // 101 0001 1010.
2139 | 0, 0, 58, // 102 0000 0100.
2140 | 0, 0, 37, // 103 0000 1110.
2141 | 0, 0, 25, // 104 0000 1111.
2142 | 0, 0, 45, // 105 0000 1010.
2143 | 0, 0, 57, // 106 0000 1000.
2144 | 0, 0, 26, // 107 0000 1101.
2145 | 0, 0, 29, // 108 0000 1011.
2146 | 0, 0, 38, // 109 0000 1100.
2147 | 0, 0, 53, // 110 0000 1001.
2148 | 0, 0, 23, // 111 0001 0001.
2149 | 0, 0, 43, // 112 0001 0000.
2150 | 0, 0, 46, // 113 0000 0110.
2151 | 0, 0, 42, // 114 0001 0100.
2152 | 0, 0, 22, // 115 0001 0101.
2153 | 0, 0, 54, // 116 0000 0101.
2154 | 0, 0, 51, // 117 0001 0010.
2155 | 0, 0, 15, // 118 0001 0011.
2156 | 0, 0, 30, // 119 0000 0111.
2157 | 0, 0, 39, // 120 0000 0001 0.
2158 | 0, 0, 47, // 121 0000 0011 0.
2159 | 0, 0, 55, // 122 0000 0010 1.
2160 | 0, 0, 27, // 123 0000 0001 1.
2161 | 0, 0, 59, // 124 0000 0010 0.
2162 | 0, 0, 31 // 125 0000 0011 1.
2163 | ]),
2164 |
2165 | MOTION = new Int16Array([
2166 | 1*3, 2*3, 0, // 0
2167 | 4*3, 3*3, 0, // 1 0
2168 | 0, 0, 0, // 2 1.
2169 | 6*3, 5*3, 0, // 3 01
2170 | 8*3, 7*3, 0, // 4 00
2171 | 0, 0, -1, // 5 011.
2172 | 0, 0, 1, // 6 010.
2173 | 9*3, 10*3, 0, // 7 001
2174 | 12*3, 11*3, 0, // 8 000
2175 | 0, 0, 2, // 9 0010.
2176 | 0, 0, -2, // 10 0011.
2177 | 14*3, 15*3, 0, // 11 0001
2178 | 16*3, 13*3, 0, // 12 0000
2179 | 20*3, 18*3, 0, // 13 0000 1
2180 | 0, 0, 3, // 14 0001 0.
2181 | 0, 0, -3, // 15 0001 1.
2182 | 17*3, 19*3, 0, // 16 0000 0
2183 | -1, 23*3, 0, // 17 0000 00
2184 | 27*3, 25*3, 0, // 18 0000 11
2185 | 26*3, 21*3, 0, // 19 0000 01
2186 | 24*3, 22*3, 0, // 20 0000 10
2187 | 32*3, 28*3, 0, // 21 0000 011
2188 | 29*3, 31*3, 0, // 22 0000 101
2189 | -1, 33*3, 0, // 23 0000 001
2190 | 36*3, 35*3, 0, // 24 0000 100
2191 | 0, 0, -4, // 25 0000 111.
2192 | 30*3, 34*3, 0, // 26 0000 010
2193 | 0, 0, 4, // 27 0000 110.
2194 | 0, 0, -7, // 28 0000 0111.
2195 | 0, 0, 5, // 29 0000 1010.
2196 | 37*3, 41*3, 0, // 30 0000 0100
2197 | 0, 0, -5, // 31 0000 1011.
2198 | 0, 0, 7, // 32 0000 0110.
2199 | 38*3, 40*3, 0, // 33 0000 0011
2200 | 42*3, 39*3, 0, // 34 0000 0101
2201 | 0, 0, -6, // 35 0000 1001.
2202 | 0, 0, 6, // 36 0000 1000.
2203 | 51*3, 54*3, 0, // 37 0000 0100 0
2204 | 50*3, 49*3, 0, // 38 0000 0011 0
2205 | 45*3, 46*3, 0, // 39 0000 0101 1
2206 | 52*3, 47*3, 0, // 40 0000 0011 1
2207 | 43*3, 53*3, 0, // 41 0000 0100 1
2208 | 44*3, 48*3, 0, // 42 0000 0101 0
2209 | 0, 0, 10, // 43 0000 0100 10.
2210 | 0, 0, 9, // 44 0000 0101 00.
2211 | 0, 0, 8, // 45 0000 0101 10.
2212 | 0, 0, -8, // 46 0000 0101 11.
2213 | 57*3, 66*3, 0, // 47 0000 0011 11
2214 | 0, 0, -9, // 48 0000 0101 01.
2215 | 60*3, 64*3, 0, // 49 0000 0011 01
2216 | 56*3, 61*3, 0, // 50 0000 0011 00
2217 | 55*3, 62*3, 0, // 51 0000 0100 00
2218 | 58*3, 63*3, 0, // 52 0000 0011 10
2219 | 0, 0, -10, // 53 0000 0100 11.
2220 | 59*3, 65*3, 0, // 54 0000 0100 01
2221 | 0, 0, 12, // 55 0000 0100 000.
2222 | 0, 0, 16, // 56 0000 0011 000.
2223 | 0, 0, 13, // 57 0000 0011 110.
2224 | 0, 0, 14, // 58 0000 0011 100.
2225 | 0, 0, 11, // 59 0000 0100 010.
2226 | 0, 0, 15, // 60 0000 0011 010.
2227 | 0, 0, -16, // 61 0000 0011 001.
2228 | 0, 0, -12, // 62 0000 0100 001.
2229 | 0, 0, -14, // 63 0000 0011 101.
2230 | 0, 0, -15, // 64 0000 0011 011.
2231 | 0, 0, -11, // 65 0000 0100 011.
2232 | 0, 0, -13 // 66 0000 0011 111.
2233 | ]),
2234 |
2235 | DCT_DC_SIZE_LUMINANCE = new Int8Array([
2236 | 2*3, 1*3, 0, // 0
2237 | 6*3, 5*3, 0, // 1 1
2238 | 3*3, 4*3, 0, // 2 0
2239 | 0, 0, 1, // 3 00.
2240 | 0, 0, 2, // 4 01.
2241 | 9*3, 8*3, 0, // 5 11
2242 | 7*3, 10*3, 0, // 6 10
2243 | 0, 0, 0, // 7 100.
2244 | 12*3, 11*3, 0, // 8 111
2245 | 0, 0, 4, // 9 110.
2246 | 0, 0, 3, // 10 101.
2247 | 13*3, 14*3, 0, // 11 1111
2248 | 0, 0, 5, // 12 1110.
2249 | 0, 0, 6, // 13 1111 0.
2250 | 16*3, 15*3, 0, // 14 1111 1
2251 | 17*3, -1, 0, // 15 1111 11
2252 | 0, 0, 7, // 16 1111 10.
2253 | 0, 0, 8 // 17 1111 110.
2254 | ]),
2255 |
2256 | DCT_DC_SIZE_CHROMINANCE = new Int8Array([
2257 | 2*3, 1*3, 0, // 0
2258 | 4*3, 3*3, 0, // 1 1
2259 | 6*3, 5*3, 0, // 2 0
2260 | 8*3, 7*3, 0, // 3 11
2261 | 0, 0, 2, // 4 10.
2262 | 0, 0, 1, // 5 01.
2263 | 0, 0, 0, // 6 00.
2264 | 10*3, 9*3, 0, // 7 111
2265 | 0, 0, 3, // 8 110.
2266 | 12*3, 11*3, 0, // 9 1111
2267 | 0, 0, 4, // 10 1110.
2268 | 14*3, 13*3, 0, // 11 1111 1
2269 | 0, 0, 5, // 12 1111 0.
2270 | 16*3, 15*3, 0, // 13 1111 11
2271 | 0, 0, 6, // 14 1111 10.
2272 | 17*3, -1, 0, // 15 1111 111
2273 | 0, 0, 7, // 16 1111 110.
2274 | 0, 0, 8 // 17 1111 1110.
2275 | ]),
2276 |
2277 | // dct_coeff bitmap:
2278 | // 0xff00 run
2279 | // 0x00ff level
2280 |
2281 | // Decoded values are unsigned. Sign bit follows in the stream.
2282 |
2283 | // Interpretation of the value 0x0001
2284 | // for dc_coeff_first: run=0, level=1
2285 | // for dc_coeff_next: If the next bit is 1: run=0, level=1
2286 | // If the next bit is 0: end_of_block
2287 |
2288 | // escape decodes as 0xffff.
2289 |
2290 | DCT_COEFF = new Int32Array([
2291 | 1*3, 2*3, 0, // 0
2292 | 4*3, 3*3, 0, // 1 0
2293 | 0, 0, 0x0001, // 2 1.
2294 | 7*3, 8*3, 0, // 3 01
2295 | 6*3, 5*3, 0, // 4 00
2296 | 13*3, 9*3, 0, // 5 001
2297 | 11*3, 10*3, 0, // 6 000
2298 | 14*3, 12*3, 0, // 7 010
2299 | 0, 0, 0x0101, // 8 011.
2300 | 20*3, 22*3, 0, // 9 0011
2301 | 18*3, 21*3, 0, // 10 0001
2302 | 16*3, 19*3, 0, // 11 0000
2303 | 0, 0, 0x0201, // 12 0101.
2304 | 17*3, 15*3, 0, // 13 0010
2305 | 0, 0, 0x0002, // 14 0100.
2306 | 0, 0, 0x0003, // 15 0010 1.
2307 | 27*3, 25*3, 0, // 16 0000 0
2308 | 29*3, 31*3, 0, // 17 0010 0
2309 | 24*3, 26*3, 0, // 18 0001 0
2310 | 32*3, 30*3, 0, // 19 0000 1
2311 | 0, 0, 0x0401, // 20 0011 0.
2312 | 23*3, 28*3, 0, // 21 0001 1
2313 | 0, 0, 0x0301, // 22 0011 1.
2314 | 0, 0, 0x0102, // 23 0001 10.
2315 | 0, 0, 0x0701, // 24 0001 00.
2316 | 0, 0, 0xffff, // 25 0000 01. -- escape
2317 | 0, 0, 0x0601, // 26 0001 01.
2318 | 37*3, 36*3, 0, // 27 0000 00
2319 | 0, 0, 0x0501, // 28 0001 11.
2320 | 35*3, 34*3, 0, // 29 0010 00
2321 | 39*3, 38*3, 0, // 30 0000 11
2322 | 33*3, 42*3, 0, // 31 0010 01
2323 | 40*3, 41*3, 0, // 32 0000 10
2324 | 52*3, 50*3, 0, // 33 0010 010
2325 | 54*3, 53*3, 0, // 34 0010 001
2326 | 48*3, 49*3, 0, // 35 0010 000
2327 | 43*3, 45*3, 0, // 36 0000 001
2328 | 46*3, 44*3, 0, // 37 0000 000
2329 | 0, 0, 0x0801, // 38 0000 111.
2330 | 0, 0, 0x0004, // 39 0000 110.
2331 | 0, 0, 0x0202, // 40 0000 100.
2332 | 0, 0, 0x0901, // 41 0000 101.
2333 | 51*3, 47*3, 0, // 42 0010 011
2334 | 55*3, 57*3, 0, // 43 0000 0010
2335 | 60*3, 56*3, 0, // 44 0000 0001
2336 | 59*3, 58*3, 0, // 45 0000 0011
2337 | 61*3, 62*3, 0, // 46 0000 0000
2338 | 0, 0, 0x0a01, // 47 0010 0111.
2339 | 0, 0, 0x0d01, // 48 0010 0000.
2340 | 0, 0, 0x0006, // 49 0010 0001.
2341 | 0, 0, 0x0103, // 50 0010 0101.
2342 | 0, 0, 0x0005, // 51 0010 0110.
2343 | 0, 0, 0x0302, // 52 0010 0100.
2344 | 0, 0, 0x0b01, // 53 0010 0011.
2345 | 0, 0, 0x0c01, // 54 0010 0010.
2346 | 76*3, 75*3, 0, // 55 0000 0010 0
2347 | 67*3, 70*3, 0, // 56 0000 0001 1
2348 | 73*3, 71*3, 0, // 57 0000 0010 1
2349 | 78*3, 74*3, 0, // 58 0000 0011 1
2350 | 72*3, 77*3, 0, // 59 0000 0011 0
2351 | 69*3, 64*3, 0, // 60 0000 0001 0
2352 | 68*3, 63*3, 0, // 61 0000 0000 0
2353 | 66*3, 65*3, 0, // 62 0000 0000 1
2354 | 81*3, 87*3, 0, // 63 0000 0000 01
2355 | 91*3, 80*3, 0, // 64 0000 0001 01
2356 | 82*3, 79*3, 0, // 65 0000 0000 11
2357 | 83*3, 86*3, 0, // 66 0000 0000 10
2358 | 93*3, 92*3, 0, // 67 0000 0001 10
2359 | 84*3, 85*3, 0, // 68 0000 0000 00
2360 | 90*3, 94*3, 0, // 69 0000 0001 00
2361 | 88*3, 89*3, 0, // 70 0000 0001 11
2362 | 0, 0, 0x0203, // 71 0000 0010 11.
2363 | 0, 0, 0x0104, // 72 0000 0011 00.
2364 | 0, 0, 0x0007, // 73 0000 0010 10.
2365 | 0, 0, 0x0402, // 74 0000 0011 11.
2366 | 0, 0, 0x0502, // 75 0000 0010 01.
2367 | 0, 0, 0x1001, // 76 0000 0010 00.
2368 | 0, 0, 0x0f01, // 77 0000 0011 01.
2369 | 0, 0, 0x0e01, // 78 0000 0011 10.
2370 | 105*3, 107*3, 0, // 79 0000 0000 111
2371 | 111*3, 114*3, 0, // 80 0000 0001 011
2372 | 104*3, 97*3, 0, // 81 0000 0000 010
2373 | 125*3, 119*3, 0, // 82 0000 0000 110
2374 | 96*3, 98*3, 0, // 83 0000 0000 100
2375 | -1, 123*3, 0, // 84 0000 0000 000
2376 | 95*3, 101*3, 0, // 85 0000 0000 001
2377 | 106*3, 121*3, 0, // 86 0000 0000 101
2378 | 99*3, 102*3, 0, // 87 0000 0000 011
2379 | 113*3, 103*3, 0, // 88 0000 0001 110
2380 | 112*3, 116*3, 0, // 89 0000 0001 111
2381 | 110*3, 100*3, 0, // 90 0000 0001 000
2382 | 124*3, 115*3, 0, // 91 0000 0001 010
2383 | 117*3, 122*3, 0, // 92 0000 0001 101
2384 | 109*3, 118*3, 0, // 93 0000 0001 100
2385 | 120*3, 108*3, 0, // 94 0000 0001 001
2386 | 127*3, 136*3, 0, // 95 0000 0000 0010
2387 | 139*3, 140*3, 0, // 96 0000 0000 1000
2388 | 130*3, 126*3, 0, // 97 0000 0000 0101
2389 | 145*3, 146*3, 0, // 98 0000 0000 1001
2390 | 128*3, 129*3, 0, // 99 0000 0000 0110
2391 | 0, 0, 0x0802, // 100 0000 0001 0001.
2392 | 132*3, 134*3, 0, // 101 0000 0000 0011
2393 | 155*3, 154*3, 0, // 102 0000 0000 0111
2394 | 0, 0, 0x0008, // 103 0000 0001 1101.
2395 | 137*3, 133*3, 0, // 104 0000 0000 0100
2396 | 143*3, 144*3, 0, // 105 0000 0000 1110
2397 | 151*3, 138*3, 0, // 106 0000 0000 1010
2398 | 142*3, 141*3, 0, // 107 0000 0000 1111
2399 | 0, 0, 0x000a, // 108 0000 0001 0011.
2400 | 0, 0, 0x0009, // 109 0000 0001 1000.
2401 | 0, 0, 0x000b, // 110 0000 0001 0000.
2402 | 0, 0, 0x1501, // 111 0000 0001 0110.
2403 | 0, 0, 0x0602, // 112 0000 0001 1110.
2404 | 0, 0, 0x0303, // 113 0000 0001 1100.
2405 | 0, 0, 0x1401, // 114 0000 0001 0111.
2406 | 0, 0, 0x0702, // 115 0000 0001 0101.
2407 | 0, 0, 0x1101, // 116 0000 0001 1111.
2408 | 0, 0, 0x1201, // 117 0000 0001 1010.
2409 | 0, 0, 0x1301, // 118 0000 0001 1001.
2410 | 148*3, 152*3, 0, // 119 0000 0000 1101
2411 | 0, 0, 0x0403, // 120 0000 0001 0010.
2412 | 153*3, 150*3, 0, // 121 0000 0000 1011
2413 | 0, 0, 0x0105, // 122 0000 0001 1011.
2414 | 131*3, 135*3, 0, // 123 0000 0000 0001
2415 | 0, 0, 0x0204, // 124 0000 0001 0100.
2416 | 149*3, 147*3, 0, // 125 0000 0000 1100
2417 | 172*3, 173*3, 0, // 126 0000 0000 0101 1
2418 | 162*3, 158*3, 0, // 127 0000 0000 0010 0
2419 | 170*3, 161*3, 0, // 128 0000 0000 0110 0
2420 | 168*3, 166*3, 0, // 129 0000 0000 0110 1
2421 | 157*3, 179*3, 0, // 130 0000 0000 0101 0
2422 | 169*3, 167*3, 0, // 131 0000 0000 0001 0
2423 | 174*3, 171*3, 0, // 132 0000 0000 0011 0
2424 | 178*3, 177*3, 0, // 133 0000 0000 0100 1
2425 | 156*3, 159*3, 0, // 134 0000 0000 0011 1
2426 | 164*3, 165*3, 0, // 135 0000 0000 0001 1
2427 | 183*3, 182*3, 0, // 136 0000 0000 0010 1
2428 | 175*3, 176*3, 0, // 137 0000 0000 0100 0
2429 | 0, 0, 0x0107, // 138 0000 0000 1010 1.
2430 | 0, 0, 0x0a02, // 139 0000 0000 1000 0.
2431 | 0, 0, 0x0902, // 140 0000 0000 1000 1.
2432 | 0, 0, 0x1601, // 141 0000 0000 1111 1.
2433 | 0, 0, 0x1701, // 142 0000 0000 1111 0.
2434 | 0, 0, 0x1901, // 143 0000 0000 1110 0.
2435 | 0, 0, 0x1801, // 144 0000 0000 1110 1.
2436 | 0, 0, 0x0503, // 145 0000 0000 1001 0.
2437 | 0, 0, 0x0304, // 146 0000 0000 1001 1.
2438 | 0, 0, 0x000d, // 147 0000 0000 1100 1.
2439 | 0, 0, 0x000c, // 148 0000 0000 1101 0.
2440 | 0, 0, 0x000e, // 149 0000 0000 1100 0.
2441 | 0, 0, 0x000f, // 150 0000 0000 1011 1.
2442 | 0, 0, 0x0205, // 151 0000 0000 1010 0.
2443 | 0, 0, 0x1a01, // 152 0000 0000 1101 1.
2444 | 0, 0, 0x0106, // 153 0000 0000 1011 0.
2445 | 180*3, 181*3, 0, // 154 0000 0000 0111 1
2446 | 160*3, 163*3, 0, // 155 0000 0000 0111 0
2447 | 196*3, 199*3, 0, // 156 0000 0000 0011 10
2448 | 0, 0, 0x001b, // 157 0000 0000 0101 00.
2449 | 203*3, 185*3, 0, // 158 0000 0000 0010 01
2450 | 202*3, 201*3, 0, // 159 0000 0000 0011 11
2451 | 0, 0, 0x0013, // 160 0000 0000 0111 00.
2452 | 0, 0, 0x0016, // 161 0000 0000 0110 01.
2453 | 197*3, 207*3, 0, // 162 0000 0000 0010 00
2454 | 0, 0, 0x0012, // 163 0000 0000 0111 01.
2455 | 191*3, 192*3, 0, // 164 0000 0000 0001 10
2456 | 188*3, 190*3, 0, // 165 0000 0000 0001 11
2457 | 0, 0, 0x0014, // 166 0000 0000 0110 11.
2458 | 184*3, 194*3, 0, // 167 0000 0000 0001 01
2459 | 0, 0, 0x0015, // 168 0000 0000 0110 10.
2460 | 186*3, 193*3, 0, // 169 0000 0000 0001 00
2461 | 0, 0, 0x0017, // 170 0000 0000 0110 00.
2462 | 204*3, 198*3, 0, // 171 0000 0000 0011 01
2463 | 0, 0, 0x0019, // 172 0000 0000 0101 10.
2464 | 0, 0, 0x0018, // 173 0000 0000 0101 11.
2465 | 200*3, 205*3, 0, // 174 0000 0000 0011 00
2466 | 0, 0, 0x001f, // 175 0000 0000 0100 00.
2467 | 0, 0, 0x001e, // 176 0000 0000 0100 01.
2468 | 0, 0, 0x001c, // 177 0000 0000 0100 11.
2469 | 0, 0, 0x001d, // 178 0000 0000 0100 10.
2470 | 0, 0, 0x001a, // 179 0000 0000 0101 01.
2471 | 0, 0, 0x0011, // 180 0000 0000 0111 10.
2472 | 0, 0, 0x0010, // 181 0000 0000 0111 11.
2473 | 189*3, 206*3, 0, // 182 0000 0000 0010 11
2474 | 187*3, 195*3, 0, // 183 0000 0000 0010 10
2475 | 218*3, 211*3, 0, // 184 0000 0000 0001 010
2476 | 0, 0, 0x0025, // 185 0000 0000 0010 011.
2477 | 215*3, 216*3, 0, // 186 0000 0000 0001 000
2478 | 0, 0, 0x0024, // 187 0000 0000 0010 100.
2479 | 210*3, 212*3, 0, // 188 0000 0000 0001 110
2480 | 0, 0, 0x0022, // 189 0000 0000 0010 110.
2481 | 213*3, 209*3, 0, // 190 0000 0000 0001 111
2482 | 221*3, 222*3, 0, // 191 0000 0000 0001 100
2483 | 219*3, 208*3, 0, // 192 0000 0000 0001 101
2484 | 217*3, 214*3, 0, // 193 0000 0000 0001 001
2485 | 223*3, 220*3, 0, // 194 0000 0000 0001 011
2486 | 0, 0, 0x0023, // 195 0000 0000 0010 101.
2487 | 0, 0, 0x010b, // 196 0000 0000 0011 100.
2488 | 0, 0, 0x0028, // 197 0000 0000 0010 000.
2489 | 0, 0, 0x010c, // 198 0000 0000 0011 011.
2490 | 0, 0, 0x010a, // 199 0000 0000 0011 101.
2491 | 0, 0, 0x0020, // 200 0000 0000 0011 000.
2492 | 0, 0, 0x0108, // 201 0000 0000 0011 111.
2493 | 0, 0, 0x0109, // 202 0000 0000 0011 110.
2494 | 0, 0, 0x0026, // 203 0000 0000 0010 010.
2495 | 0, 0, 0x010d, // 204 0000 0000 0011 010.
2496 | 0, 0, 0x010e, // 205 0000 0000 0011 001.
2497 | 0, 0, 0x0021, // 206 0000 0000 0010 111.
2498 | 0, 0, 0x0027, // 207 0000 0000 0010 001.
2499 | 0, 0, 0x1f01, // 208 0000 0000 0001 1011.
2500 | 0, 0, 0x1b01, // 209 0000 0000 0001 1111.
2501 | 0, 0, 0x1e01, // 210 0000 0000 0001 1100.
2502 | 0, 0, 0x1002, // 211 0000 0000 0001 0101.
2503 | 0, 0, 0x1d01, // 212 0000 0000 0001 1101.
2504 | 0, 0, 0x1c01, // 213 0000 0000 0001 1110.
2505 | 0, 0, 0x010f, // 214 0000 0000 0001 0011.
2506 | 0, 0, 0x0112, // 215 0000 0000 0001 0000.
2507 | 0, 0, 0x0111, // 216 0000 0000 0001 0001.
2508 | 0, 0, 0x0110, // 217 0000 0000 0001 0010.
2509 | 0, 0, 0x0603, // 218 0000 0000 0001 0100.
2510 | 0, 0, 0x0b02, // 219 0000 0000 0001 1010.
2511 | 0, 0, 0x0e02, // 220 0000 0000 0001 0111.
2512 | 0, 0, 0x0d02, // 221 0000 0000 0001 1000.
2513 | 0, 0, 0x0c02, // 222 0000 0000 0001 1001.
2514 | 0, 0, 0x0f02 // 223 0000 0000 0001 0110.
2515 | ]),
2516 |
2517 | PICTURE_TYPE_I = 1,
2518 | PICTURE_TYPE_P = 2,
2519 | PICTURE_TYPE_B = 3,
2520 | //PICTURE_TYPE_D = 4,
2521 |
2522 | START_SEQUENCE = 0xB3,
2523 | START_SLICE_FIRST = 0x01,
2524 | START_SLICE_LAST = 0xAF,
2525 | START_PICTURE = 0x00,
2526 | START_EXTENSION = 0xB5,
2527 | START_USER_DATA = 0xB2,
2528 |
2529 | // Shaders for accelerated WebGL YCbCrToRGBA conversion
2530 | SHADER_FRAGMENT_YCBCRTORGBA = [
2531 | 'precision mediump float;',
2532 | 'uniform sampler2D YTexture;',
2533 | 'uniform sampler2D CBTexture;',
2534 | 'uniform sampler2D CRTexture;',
2535 | 'varying vec2 texCoord;',
2536 |
2537 | 'void main() {',
2538 | 'float y = texture2D(YTexture, texCoord).r;',
2539 | 'float cr = texture2D(CBTexture, texCoord).r - 0.5;',
2540 | 'float cb = texture2D(CRTexture, texCoord).r - 0.5;',
2541 |
2542 | 'gl_FragColor = vec4(',
2543 | 'y + 1.4 * cr,',
2544 | 'y + -0.343 * cb - 0.711 * cr,',
2545 | 'y + 1.765 * cb,',
2546 | '1.0',
2547 | ');',
2548 | '}'
2549 | ].join('\n'),
2550 |
2551 | SHADER_FRAGMENT_LOADING = [
2552 | 'precision mediump float;',
2553 | 'uniform float loaded;',
2554 | 'varying vec2 texCoord;',
2555 |
2556 | 'void main() {',
2557 | 'float c = ceil(loaded-(1.0-texCoord.y));',
2558 | 'gl_FragColor = vec4(c,c,c,1);',
2559 | '}'
2560 | ].join('\n'),
2561 |
2562 | SHADER_VERTEX_IDENTITY = [
2563 | 'attribute vec2 vertex;',
2564 | 'varying vec2 texCoord;',
2565 |
2566 | 'void main() {',
2567 | 'texCoord = vertex;',
2568 | 'gl_Position = vec4((vertex * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);',
2569 | '}'
2570 | ].join('\n');
2571 |
2572 | var MACROBLOCK_TYPE_TABLES = [
2573 | null,
2574 | MACROBLOCK_TYPE_I,
2575 | MACROBLOCK_TYPE_P,
2576 | MACROBLOCK_TYPE_B
2577 | ];
2578 |
2579 |
2580 |
2581 | // ----------------------------------------------------------------------------
2582 | // Bit Reader
2583 |
2584 | var BitReader = function(arrayBuffer) {
2585 | this.bytes = new Uint8Array(arrayBuffer);
2586 | this.length = this.bytes.length;
2587 | this.writePos = this.bytes.length;
2588 | this.index = 0;
2589 | };
2590 |
2591 | BitReader.NOT_FOUND = -1;
2592 |
2593 | BitReader.prototype.findNextMPEGStartCode = function() {
2594 | for (var i = (this.index+7 >> 3); i < this.writePos; i++) {
2595 | if (
2596 | this.bytes[i] === 0x00 &&
2597 | this.bytes[i+1] === 0x00 &&
2598 | this.bytes[i+2] === 0x01
2599 | ) {
2600 | this.index = (i+4) << 3;
2601 | return this.bytes[i+3];
2602 | }
2603 | }
2604 | this.index = (this.writePos << 3);
2605 | return BitReader.NOT_FOUND;
2606 | };
2607 |
2608 | BitReader.prototype.nextBytesAreStartCode = function() {
2609 | var i = (this.index+7 >> 3);
2610 | return (
2611 | i >= this.writePos || (
2612 | this.bytes[i] === 0x00 &&
2613 | this.bytes[i+1] === 0x00 &&
2614 | this.bytes[i+2] === 0x01
2615 | )
2616 | );
2617 | };
2618 |
2619 | BitReader.prototype.nextBits = function(count) {
2620 | var
2621 | byteOffset = this.index >> 3,
2622 | room = (8 - this.index % 8);
2623 |
2624 | if (room >= count) {
2625 | return (this.bytes[byteOffset] >> (room - count)) & (0xff >> (8-count));
2626 | }
2627 |
2628 | var
2629 | leftover = (this.index + count) % 8, // Leftover bits in last byte
2630 | end = (this.index + count -1) >> 3,
2631 | value = this.bytes[byteOffset] & (0xff >> (8-room)); // Fill out first byte
2632 |
2633 | for (byteOffset++; byteOffset < end; byteOffset++) {
2634 | value <<= 8; // Shift and
2635 | value |= this.bytes[byteOffset]; // Put next byte
2636 | }
2637 |
2638 | if (leftover > 0) {
2639 | value <<= leftover; // Make room for remaining bits
2640 | value |= (this.bytes[byteOffset] >> (8 - leftover));
2641 | }
2642 | else {
2643 | value <<= 8;
2644 | value |= this.bytes[byteOffset];
2645 | }
2646 |
2647 | return value;
2648 | };
2649 |
2650 | BitReader.prototype.getBits = function(count) {
2651 | var value = this.nextBits(count);
2652 | this.index += count;
2653 | return value;
2654 | };
2655 |
2656 | BitReader.prototype.advance = function(count) {
2657 | return (this.index += count);
2658 | };
2659 |
2660 | BitReader.prototype.rewind = function(count) {
2661 | return (this.index -= count);
2662 | };
2663 |
2664 | })(window);
2665 |
--------------------------------------------------------------------------------