├── .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://img.shields.io/badge/%24-donate-green.svg)](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 | ![Content of web page analyzation by Google](https://lh3.googleusercontent.com/71kkGWwhwsM8tGXdrM1_wdpKlJS0Lqfta_f7pT0UBwaqRHdNgac_LkoEmMjBHsIpXBYaRThhNatL712anNZsEbTVJ1UEfiQMzJLdSvxquYOmH9ilDvQ_1J-XIR-4kjreB7ctyfIp_Z5oriIaAqTK8DvIFbadIHqyzaKjVBrCgQpnjeUZ_gDxBR5gw2H5l6TjNwvhG3dV1j3i_PZP5abj4lgDgJmNbB_Za9Gjejr4Ba1l4CEop4bqOkiGqS5OFjhp_XWCpngg5hhOZq7WLsLVd6w865-Cb1AFfuwoOiRTjUnoG9Nrpheuf-VxsOXaPuCgWvnwDi3BmgUujUXLvokfxcqQYY1SbAxxAXlyp5n22LyStthiolF9X2_1GTF1Lc_qNWJySv2BUAepMp_hQiyq1-ALMAfwrxCb4ONhVLBcypoZ9WfjbRHC-tRyr4rcLZ2KfPZyI4twAD11HVg2zvBWjJ5OccBPA3OFyxTwU9dA61WQvygJJFRugEm6fw194U87L46V5zgZ_R_Fb0ttjbFFLsDWRPJCU5VOdpjC27qTexpH6WHxgwx84qPbDc5v_TAopuWEHuj88zv3_8BXeU6NLr3miEkGz1_wjy6UXKOr9HJO8xUUHMuTuvYMmsR9QhSanx0jibE1l3PPpFmPZcIcQy2GbqncrT6j4jWNQeZAiyZlIpwv7oUmMMvxXrdY11MnnvlVFN4Ov3JxX3jf_bg=w1187-h667-no) 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://img.shields.io/badge/%24-donate-green.svg)](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 | --------------------------------------------------------------------------------