소스 검색

项目设置

wy 1 년 전
부모
커밋
b55bda0014
100개의 변경된 파일9287개의 추가작업 그리고 0개의 파일을 삭제
  1. 47 0
      .drone.yml
  2. 14 0
      .editorconfig
  3. 7 0
      .env.development
  4. 7 0
      .env.production
  5. 2 0
      .gitignore
  6. 5 0
      .vscode/extensions.json
  7. 3 0
      .vscode/settings.json
  8. 4 0
      Dockerfile
  9. 10 0
      babel.config.js
  10. 37 0
      default.conf
  11. 9 0
      jsconfig.json
  12. 8 0
      jsmpeg-master/LICENSE
  13. 253 0
      jsmpeg-master/README.md
  14. 115 0
      jsmpeg-master/build.sh
  15. 0 0
      jsmpeg-master/jsmpeg.min.js
  16. 137 0
      jsmpeg-master/src/ajax-progressive.js
  17. 72 0
      jsmpeg-master/src/ajax.js
  18. 198 0
      jsmpeg-master/src/buffer.js
  19. 119 0
      jsmpeg-master/src/canvas2d.js
  20. 112 0
      jsmpeg-master/src/decoder.js
  21. 74 0
      jsmpeg-master/src/fetch.js
  22. 122 0
      jsmpeg-master/src/jsmpeg.js
  23. 124 0
      jsmpeg-master/src/mp2-wasm.js
  24. 690 0
      jsmpeg-master/src/mp2.js
  25. 133 0
      jsmpeg-master/src/mpeg1-wasm.js
  26. 1683 0
      jsmpeg-master/src/mpeg1.js
  27. 324 0
      jsmpeg-master/src/player.js
  28. 228 0
      jsmpeg-master/src/ts.js
  29. 162 0
      jsmpeg-master/src/video-element.js
  30. 157 0
      jsmpeg-master/src/wasm-module.js
  31. 190 0
      jsmpeg-master/src/wasm/buffer.c
  32. 31 0
      jsmpeg-master/src/wasm/buffer.h
  33. 704 0
      jsmpeg-master/src/wasm/mp2.c
  34. 22 0
      jsmpeg-master/src/wasm/mp2.h
  35. 1748 0
      jsmpeg-master/src/wasm/mpeg1.c
  36. 27 0
      jsmpeg-master/src/wasm/mpeg1.h
  37. 145 0
      jsmpeg-master/src/webaudio.js
  38. 277 0
      jsmpeg-master/src/webgl.js
  39. 83 0
      jsmpeg-master/src/websocket.js
  40. 22 0
      jsmpeg-master/view-stream.html
  41. 92 0
      jsmpeg-master/websocket-relay.js
  42. 106 0
      package.json
  43. 5 0
      postcss.config.js
  44. 12 0
      src/App.vue
  45. 65 0
      src/api/car.js
  46. 24 0
      src/api/downfile.js
  47. 46 0
      src/api/equipment.js
  48. 100 0
      src/api/log.js
  49. 160 0
      src/api/powerCharge.js
  50. 130 0
      src/api/records.js
  51. 84 0
      src/api/statistics.js
  52. 65 0
      src/api/user.js
  53. BIN
      src/assets/401_images/401.gif
  54. BIN
      src/assets/404_images/404.png
  55. BIN
      src/assets/404_images/404_cloud.png
  56. BIN
      src/assets/car1x.png
  57. BIN
      src/assets/cartype.png
  58. BIN
      src/assets/custom-theme/fonts/element-icons.ttf
  59. BIN
      src/assets/custom-theme/fonts/element-icons.woff
  60. 0 0
      src/assets/custom-theme/index.css
  61. BIN
      src/assets/default.png
  62. BIN
      src/assets/machine.png
  63. BIN
      src/assets/messageb.png
  64. BIN
      src/assets/messaget.png
  65. BIN
      src/assets/onlineb.png
  66. BIN
      src/assets/onlinet.png
  67. BIN
      src/assets/record.png
  68. BIN
      src/assets/roleb.png
  69. BIN
      src/assets/roletop.png
  70. 62 0
      src/components/SvgIcon/index.vue
  71. 13 0
      src/directive/permission/index.js
  72. 31 0
      src/directive/permission/permission.js
  73. 113 0
      src/filters/index.js
  74. 9 0
      src/icons/index.js
  75. 1 0
      src/icons/svg/404.svg
  76. 1 0
      src/icons/svg/bug.svg
  77. 1 0
      src/icons/svg/chart.svg
  78. 1 0
      src/icons/svg/clipboard.svg
  79. 1 0
      src/icons/svg/component.svg
  80. 0 0
      src/icons/svg/dashboard.svg
  81. 1 0
      src/icons/svg/documentation.svg
  82. 1 0
      src/icons/svg/drag.svg
  83. 1 0
      src/icons/svg/edit.svg
  84. 1 0
      src/icons/svg/education.svg
  85. 1 0
      src/icons/svg/email.svg
  86. 1 0
      src/icons/svg/example.svg
  87. 1 0
      src/icons/svg/excel.svg
  88. 1 0
      src/icons/svg/exit-fullscreen.svg
  89. 1 0
      src/icons/svg/eye-open.svg
  90. 1 0
      src/icons/svg/eye.svg
  91. 0 0
      src/icons/svg/form.svg
  92. 1 0
      src/icons/svg/fullscreen.svg
  93. 1 0
      src/icons/svg/guide.svg
  94. 6 0
      src/icons/svg/hdjk.svg
  95. 13 0
      src/icons/svg/hdjk_active.svg
  96. 3 0
      src/icons/svg/ico.svg
  97. 1 0
      src/icons/svg/icon.svg
  98. 1 0
      src/icons/svg/international.svg
  99. 12 0
      src/icons/svg/kzpz.svg
  100. 12 0
      src/icons/svg/kzpz_active.svg

+ 47 - 0
.drone.yml

@@ -0,0 +1,47 @@
+kind: pipeline
+name: default
+
+steps:
+  - name: restore-cache
+    image: drillster/drone-volume-cache
+    volumes:
+    - name: cache
+      path: /cache
+    settings:
+      restore: true
+      mount:
+        - ./node_modules
+
+  - name: build
+    image: node:16.15
+    commands:
+      - npm config set registry http://nexus.fast-fun.cn:92/repository/npm-public/
+      - git config --global http.sslverify "false"
+      - git config --global url."https://".insteadOf ssh://git
+      - npm install
+      - npm run build:prod
+  
+  - name: rebuild-cache
+    image: drillster/drone-volume-cache
+    volumes:
+    - name: cache
+      path: /cache
+    settings:
+      rebuild: true
+      mount:
+        - ./node_modules
+
+  - name: docker-web
+    image: plugins/docker
+    settings:
+      tags:
+        - latest
+        - 2.0.${DRONE_BUILD_NUMBER}
+      registry: nas.fast-fun.cn:5000
+      insecure: true
+      repo: nas.fast-fun.cn:5000/zk/station-control-frontend-v2
+
+volumes:
+  - name: cache
+    host:
+      path: /home/drone/cache

+ 14 - 0
.editorconfig

@@ -0,0 +1,14 @@
+# https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 7 - 0
.env.development

@@ -0,0 +1,7 @@
+# just a flag
+ENV = 'development'
+
+# base api
+VUE_APP_BASE_API = 'http://192.168.3.177:8080/api'
+VUE_APP_WS = 'ws://192.168.3.177:8080/ws'
+# VUE_APP_VIDEO = 'ws://192.168.3.110:8082/ws'

+ 7 - 0
.env.production

@@ -0,0 +1,7 @@
+# just a flag
+ENV = 'production'
+
+# base api
+VUE_APP_BASE_API = 'http://192.168.3.177:8080/api'
+VUE_APP_WS = 'ws://192.168.3.177:8080/ws'
+VUE_APP_VIDEO = 'ws://192.168.3.177:8082/ws'

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/node_modules
+/dist

+ 5 - 0
.vscode/extensions.json

@@ -0,0 +1,5 @@
+{
+    "recommendations": [
+        "ms-ceintl.vscode-language-pack-zh-hans"
+    ]
+}

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    "git.ignoreLimitWarning": true
+}

+ 4 - 0
Dockerfile

@@ -0,0 +1,4 @@
+FROM nas.fast-fun.cn:5000/nginx:alpine
+
+ADD default.conf /etc/nginx/conf.d/
+ADD dist /usr/share/nginx/html

+ 10 - 0
babel.config.js

@@ -0,0 +1,10 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ],
+  'env': {
+    'development': {
+      'plugins': ['dynamic-import-node']
+    }
+  }
+}

+ 37 - 0
default.conf

@@ -0,0 +1,37 @@
+server {
+    listen       80;
+    server_name  localhost;
+
+    #charset koi8-r;
+    access_log  /var/log/nginx/host.access.log  main;
+
+    gzip on;
+    gzip_min_length 1k;
+    gzip_buffers 4 16k;
+    gzip_comp_level 8;
+    gzip_types text/plain application/javascript application/x-javascript text/css application/json text/javascript application/x-httpd-php image/jpeg image/gif image/png;
+    gzip_vary off;
+    gzip_disable "MSIE [1-6]\.";
+
+    location / {
+        root   /usr/share/nginx/html;
+        try_files $uri $uri/ /index.html;
+        index  start.html index.html;
+    }
+    
+    # location /api {
+    #     proxy_read_timeout 300;
+    #     proxy_pass   http://192.168.3.177:8080;
+    # }
+
+    # location /ws {
+    #     proxy_read_timeout 300;
+    #     proxy_pass   http://192.168.3.177:8080/ws;
+    # }
+    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
+    
+    # location /api {
+    #     proxy_pass   http://api-server:8082;
+    # }
+
+}

+ 9 - 0
jsconfig.json

@@ -0,0 +1,9 @@
+{ 
+  "compilerOptions": {
+    "baseUrl": "./",
+    "paths": {
+        "@/*": ["src/*"]
+    }
+  },
+  "exclude": ["node_modules", "dist"]
+}

+ 8 - 0
jsmpeg-master/LICENSE

@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright (c) 2017 Dominic Szablewski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 253 - 0
jsmpeg-master/README.md

@@ -0,0 +1,253 @@
+# JSMpeg – MPEG1 Video & MP2 Audio Decoder in JavaScript
+
+JSMpeg is a Video Player written in JavaScript. It consists of an MPEG-TS demuxer, MPEG1 video & MP2 audio decoders, WebGL & Canvas2D renderers and WebAudio sound output. JSMpeg can load static videos via Ajax and allows low latency streaming (~50ms) via WebSockets.
+
+JSMpeg can decode 720p Video at 30fps on an iPhone 5S, works in any modern browser (Chrome, Firefox, Safari, Edge) and comes in at just 20kb gzipped.
+
+Using it can be as simple as this:
+```html
+<script src="jsmpeg.min.js"></script>
+<div class="jsmpeg" data-url="video.ts"></div>
+```
+
+Some more info and demos: [jsmpeg.com](http://jsmpeg.com/)
+
+
+## Usage
+
+A JSMpeg video player can either be created in HTML using the CSS class `jsmpeg` for the container:
+
+```html
+<div class="jsmpeg" data-url="<url>"></div>
+```
+
+or by directly calling the `JSMpeg.Player()` constructor in JavaScript:
+
+```javascript
+var player = new JSMpeg.Player(url [, options]);
+```
+
+Note that using the HTML Element (internally `JSMpeg.VideoElement`) provides some features on top of `JSMpeg.Player`. Namely a SVG pause/play button and the ability to "unlock" audio on iOS devices.
+
+The `url` argument accepts a URL to an MPEG .ts file or a WebSocket server (ws://...).
+
+The `options` argument supports the following properties:
+
+- `canvas` – the HTML Canvas elment to use for video rendering. If none is given, the renderer will create its own Canvas element.
+- `loop` – whether to loop the video (static files only). Default `true`.
+- `autoplay` - whether to start playing immediately (static files only). Default `false`.
+- `audio` - whether to decode audio. Default `true`.
+- `video` - whether to decode video. Default `true`.
+- `poster` – URL to an image to use as the poster to show before the video plays.
+- `pauseWhenHidden` – whether to pause playback when the tab is inactive. Default `true`. Note that browsers usually throttle JS in inactive tabs anyway.
+- `disableGl` - whether to disable WebGL and always use the Canvas2D renderer. Default `false`.
+- `disableWebAssembly` - whether to disable WebAssembly and always use JavaScript decoders. Default `false`.
+- `preserveDrawingBuffer` – whether the WebGL context is created with `preserveDrawingBuffer` - necessary for "screenshots" via `canvas.toDataURL()`. Default `false`.
+- `progressive` - whether to load data in chunks (static files only). When enabled, playback can begin before the whole source has been completely loaded. Default `true`.
+- `throttled` - when using `progressive`, whether to defer loading chunks when they're not needed for playback yet. Default `true`.
+- `chunkSize` - when using `progressive`, the chunk size in bytes to load at a time. Default `1024*1024` (1mb).
+- `decodeFirstFrame` - whether to decode and display the first frame of the video. Useful to set up the Canvas size and use the frame as the "poster" image. This has no effect when using `autoplay` or streaming sources. Default `true`.
+- `maxAudioLag` – when streaming, the maximum enqueued audio length in seconds.
+- `videoBufferSize` – when streaming, size in bytes for the video decode buffer. Default 512*1024 (512kb). You may have to increase this for very high bitrates.
+- `audioBufferSize` – when streaming, size in bytes for the audio decode buffer. Default 128*1024 (128kb). You may have to increase this for very high bitrates.
+- `onVideoDecode(decoder, time)` – A callback that is called after each decoded and rendered video frame
+- `onAudioDecode(decoder, time)` – A callback that is called after each decoded audio frame
+- `onPlay(player)` – A callback that is called whenever playback starts
+- `onPause(player)` – A callback that is called whenever playback paused (e.g. when .pause() is called or the source has ended)
+- `onEnded(player)` – A callback that is called when playback has reached the end of the source (only called when `loop` is `false`).
+- `onStalled(player)` – A callback that is called whenever there's not enough data for playback
+- `onSourceEstablished(source)` – A callback that is called when source has first received data
+- `onSourceCompleted(source)` – A callback that is called when the source has received all data
+
+
+All options except from `canvas` can also be used with the HTML Element through `data-` attributes. E.g. to specify looping and autoplay in JavaScript:
+
+```javascript
+var player = new JSMpeg.Player('video.ts' {loop: true, autoplay: true});
+```
+
+or HTML
+```html
+<div class="jsmpeg" data-url="video.ts" 
+	data-loop="true" data-autoplay="true"></div>
+```
+
+Note that `camelCased` options have to be hyphenated when used as data attributes. E.g. `decodeFirstFrame: true` becomes `data-decode-first-frame="true"` for the HTML element.
+
+
+## JSMpeg.Player API
+
+A `JSMpeg.Player` instance supports the following methods and properties:
+
+- `.play()` – start playback
+- `.pause()` – pause playback
+- `.stop()` – stop playback and seek to the beginning
+- `.nextFrame()` – advance playback by one video frame. This does not decode audio. Returns `true` on success, `false` when there's not enough data.
+- `.destroy()` – stops playback, disconnects the source and cleans up WebGL and WebAudio state. The player can not be used afterwards.
+- `.volume` – get or set the audio volume (0-1)
+- `.currentTime` – get or set the current playback position in seconds
+- `.paused` – read only, wether playback is paused
+
+
+## Encoding Video/Audio for JSMpeg
+
+JSMpeg only supports playback of MPEG-TS containers with the MPEG1 Video Codec and the MP2 Audio Codec. The Video Decoder does not handle B-Frames correctly (though no modern encoder seems to use these by default anyway) and the width of the video has to be a multiple of 2.
+
+You can encode a suitable video using [ffmpeg](https://ffmpeg.org/) like this:
+
+```sh
+ffmpeg -i in.mp4 -f mpegts -codec:v mpeg1video -codec:a mp2 -b 0 out.ts
+```
+
+You can also control the video size (`-s`), framerate (`-r`), video bitrate (`-b:v`), audio bitrate (`-b:a`), number of audio channels (`-ac`), sampling rate (`-ar`) and much more. Please refer to the ffmpeg documentation for the details.
+
+Comprehensive example:
+```sh
+ffmpeg -i in.mp4 -f mpegts \
+	-codec:v mpeg1video -s 960x540 -b:v 1500k -r 30 -bf 0 \
+	-codec:a mp2 -ar 44100 -ac 1 -b:a 128k \
+	out.ts
+```
+
+
+## Performance Considerations
+
+While JSMpeg can handle 720p video at 30fps even on an iPhone 5S, keep in mind that MPEG1 is not as efficient as modern codecs. MPEG1 needs quite a bit of bandwidth for HD video. 720p begins to look okay-ish at 2 Mbits/s (that's 250kb/s). Also, the higher the bitrate, the more work JavaScript has to do to decode it.
+
+This should not be a problem for static files, or if you're only streaming within your local WiFi. If you don't need to support mobile devices, 1080p at 10mbit/s works just fine (if your encoder can keep up). For everything else I would advise you to use 540p (960x540) at 2Mbit/s max.
+
+Here is a performance comparison with multiple resolutions and features en-/disabled. Test this on your target devices to get a feel for what you can get away with.
+
+https://jsmpeg.com/perf.html
+
+
+## Streaming via WebSockets
+
+JSMpeg can connect to a WebSocket server that sends out binary MPEG-TS data. When streaming, JSMpeg tries to keep latency as low as possible - it immediately decodes everything it has, ignoring video and audio timestamps altogether. To keep everything in sync (and latency low), audio data should be interleaved between video frames very frequently (`-muxdelay` in ffmpeg).
+
+A separate, buffered streaming mode, where JSMpeg pre-loads a few seconds of data and presents everything with exact timing and audio/video sync is conceivable, but currently not implemented.
+
+The internal buffers for video and audio are fairly small (512kb and 128kb respectively) and JSMpeg will discard old (even unplayed) data to make room for newly arriving data without much fuzz. This could introduce decoding artifacts when there's a network congestion, but ensures that latency is kept at a minimum. If necessary You can increase the `videoBufferSize` and `audioBufferSize` through the options.
+
+JSMpeg comes with a tiny WebSocket "relay", written in Node.js. This server accepts an MPEG-TS source over HTTP and serves it via WebSocket to all connecting Browsers. The incoming HTTP stream can be generated using [ffmpeg](https://ffmpeg.org/), [gstreamer](https://gstreamer.freedesktop.org/) or by other means.
+
+The split between the source and the WebSocket relay is necessary, because ffmpeg doesn't speak the WebSocket protocol. However, this split also allows you to install the WebSocket relay on a public server and share your stream on the Internet (typically NAT in your router prevents the public Internet from connecting _into_ your local network).
+
+In short, it works like this:
+
+1. run the websocket-relay.js
+2. run ffmpeg, send output to the relay's HTTP port
+3. connect JSMpeg in the browser to the relay's Websocket port
+
+
+## Example Setup for Streaming: Raspberry Pi Live Webcam
+
+For this example, ffmpeg and the WebSocket relay run on the same system. This allows you to view the stream in your local network, but not on the public internet.
+
+This example assumes that your webcam is compatible with Video4Linux2 and appears as `/dev/video0` in the filesystem. Most USB webcams support the UVC standard and should work just fine. The onboard Raspberry Camera can be made available as V4L2 device by loading a kernel module: `sudo modprobe bcm2835-v4l2`.
+
+
+1) Install ffmpeg (See [How to install ffmpeg on Debian / Raspbian](http://superuser.com/questions/286675/how-to-install-ffmpeg-on-debian)). Using ffmpeg, we can capture the webcam video & audio and encode it into MPEG1/MP2.
+
+2) Install Node.js and npm (See [Installing Node.js on Debian and Ubuntu based Linux distributions](https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions) for newer versions). The Websocket relay is written in Node.js
+
+3) Install http-server. We will use this to serve the static files (view-stream.html, jsmpeg.min.js), so that we can view the website with the video in our browser. Any other webserver would work as well (nginx, apache, etc.):
+`sudo npm -g install http-server`
+
+4) Install git and clone this repository (or just download it as ZIP and unpack)
+```
+sudo apt-get install git
+git clone https://github.com/phoboslab/jsmpeg.git
+```
+
+5) Change into the jsmpeg/ directory
+`cd jsmpeg/`
+
+6) Install the Node.js Websocket Library:
+`npm install ws`
+
+7) Start the Websocket relay. Provide a password and a port for the incomming HTTP video stream and a Websocket port that we can connect to in the browser:
+`node websocket-relay.js supersecret 8081 8082`
+
+8) In a new terminal window (still in the `jsmpeg/` directory, start the `http-server` so we can serve the view-stream.html to the browser:
+`http-server`
+
+9) Open the streaming website in your browser. The `http-server` will tell you the ip (usually `192.168.[...]`) and port (usually `8080`) where it's running on:
+`http://192.168.[...]:8080/view-stream.html`
+
+10) In a third terminal window, start ffmpeg to capture the webcam video and send it to the Websocket relay. Provide the password and port (from step 7) in the destination URL:
+```
+ffmpeg \
+	-f v4l2 \
+		-framerate 25 -video_size 640x480 -i /dev/video0 \
+	-f mpegts \
+		-codec:v mpeg1video -s 640x480 -b:v 1000k -bf 0 \
+	http://localhost:8081/supersecret
+```
+
+You should now see a live webcam image in your browser. 
+
+If ffmpeg failed to open the input video, it's likely that your webcam does not support the given resolution, format or framerate. To get a list of compatible modes run:
+
+`ffmpeg -f v4l2 -list_formats all -i /dev/video0`
+
+
+To add the webcam audio, just call ffmpeg with two separate inputs.
+
+```
+ffmpeg \
+	-f v4l2 \
+		-framerate 25 -video_size 640x480 -i /dev/video0 \
+	-f alsa \
+		-ar 44100 -c 2 -i hw:0 \
+	-f mpegts \
+		-codec:v mpeg1video -s 640x480 -b:v 1000k -bf 0 \
+		-codec:a mp2 -b:a 128k \
+		-muxdelay 0.001 \
+	http://localhost:8081/supersecret
+```
+
+Note the `muxdelay` argument. This should reduce lag, but doesn't always work when streaming video and audio - see remarks below.
+
+
+## Some remarks about ffmpeg muxing and latency
+
+Adding an audio stream to the MPEG-TS can sometimes introduce considerable latency. I especially found this to be a problem on linux using ALSA and V4L2 (using AVFoundation on macOS worked just fine). However, there is a simple workaround: just run two instances of ffmpeg in parallel. One for audio, one for video. Send both outputs to the same Websocket relay. Thanks to the simplicity of the MPEG-TS format, proper "muxing" of the two streams happens automatically in the relay.
+
+```
+ffmpeg \
+	-f v4l2 \
+		-framerate 25 -video_size 640x480 -i /dev/video0 \
+	-f mpegts \
+		-codec:v mpeg1video -s 640x480 -b:v 1000k -bf 0 \
+		-muxdelay 0.001 \
+	http://localhost:8081/supersecret
+
+# In a second terminal
+ffmpeg \
+	-f alsa \
+		-ar 44100 -c 2 -i hw:0 \
+	-f mpegts \
+		-codec:a mp2 -b:a 128k \
+		-muxdelay 0.001 \
+	http://localhost:8081/supersecret
+```
+In my tests, USB Webcams introduce about ~180ms of latency and there seems to be nothing we can do about it. The Raspberry Pi however has a [camera module](https://www.raspberrypi.org/products/camera-module-v2/) that provides lower latency video capture.
+
+To capture webcam input on Windows or macOS using ffmpeg, see the [ffmpeg Capture/Webcam Wiki](https://trac.ffmpeg.org/wiki/Capture/Webcam).
+
+
+## JSMpeg Architecture and Internals
+
+This library was built in a fairly modular fashion while keeping overhead at a minimum. Implementing new Demuxers, Decoders, Outputs (Renderers, Audio Devices) or Sources should be possible without changing any other parts. However, you would still need to subclass the `JSMpeg.Player` in order to use any new modules.
+
+Have a look a the [jsmpeg.js source](https://github.com/phoboslab/jsmpeg/blob/master/src/jsmpeg.js) for an overview of how the modules interconnect and what APIs they should provide. I also wrote a blog post about some of JSMpeg's internals: [Decode It Like It's 1999](http://phoboslab.org/log/2017/02/decode-it-like-its-1999).
+
+Using parts of the library without creating a full player should also be fairly straightforward. E.g. you can create a stand-alone instance of the `JSMpeg.Decoder.MPEG1Video` class, `.connect()` a renderer, `.write()` some data to it and `.decode()` a frame, without touching JSMpeg's other parts.
+
+
+## Previous Version
+
+The JSMpeg version currently living in this repo is a complete rewrite of the original jsmpeg library that was just able to decode raw mpeg1video. If you're looking for the old version, see the [v0.2 tag](https://github.com/phoboslab/jsmpeg/releases/tag/v0.2).
+
+

+ 115 - 0
jsmpeg-master/build.sh

@@ -0,0 +1,115 @@
+#!/bin/sh
+
+
+# Build the .wasm Module first
+
+# Since we're compiling a side module here, so that we can load it without the
+# runtime cruft, we have to explicitly compile in support for malloc and
+# friends.
+# Note memcpy, memmove and memset are explicitly exported, otherwise they will
+# be eliminated by the SIDE_MODULE=2 setting - not sure why that happens.
+
+# This NEEDS to be compiled with emscripten 1.38.47. Newer versions mess with
+# malloc and friends and need some more glue code for side modules that I 
+# haven't quite worked out yet. If you have any idea how to build a SIDE_MODULE
+# (or STANDALONE_WASM - as seems to be the new deal) with support for malloc,
+# please let me know or file a PR.
+
+# To install the correct version, issue the following in your emsdk directory:
+# ./emsdk install 1.38.47
+# ./emsdk activate 1.38.47
+# source ./emsdk_env.sh
+
+# The $EMSCRIPTEN_LIB var needs to point to the correct directory within the sdk
+# that has emmalloc.cpp. This is usually $EMSDK/fastcomp/emscripten/system/lib
+# but it might differ per system. I don't know.
+# There used to be an $EMSCRIPTEN var set by the emsdk_env script that pointed
+# to the correct directory, but this seems to have gone now.
+
+# In conclusion, emscripten encapsulates everything that I hate about native 
+# development :/
+
+EMSCRIPTEN_LIB=$EMSDK/fastcomp/emscripten/system/lib
+
+emcc \
+	src/wasm/mpeg1.c \
+	src/wasm/mp2.c \
+	src/wasm/buffer.c \
+	$EMSCRIPTEN_LIB/emmalloc.cpp \
+	$EMSCRIPTEN_LIB/libc/musl/src/string/memcpy.c \
+	$EMSCRIPTEN_LIB/libc/musl/src/string/memmove.c \
+	$EMSCRIPTEN_LIB/libc/musl/src/string/memset.c \
+	-s WASM=1 \
+	-s SIDE_MODULE=2 \
+	-s TOTAL_STACK=5242880\
+	-s USE_PTHREADS=0 \
+	-s LEGALIZE_JS_FFI=0\
+	-s NO_FILESYSTEM=1 \
+	-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE="[]" \
+	-s "EXPORTED_FUNCTIONS=[
+		'_memcpy',
+		'_memmove',
+		'_memset',
+		'_mpeg1_decoder_create',
+		'_mpeg1_decoder_destroy',
+		'_mpeg1_decoder_get_write_ptr',
+		'_mpeg1_decoder_get_index',
+		'_mpeg1_decoder_set_index',
+		'_mpeg1_decoder_did_write',
+		'_mpeg1_decoder_has_sequence_header',
+		'_mpeg1_decoder_get_frame_rate',
+		'_mpeg1_decoder_get_coded_size',
+		'_mpeg1_decoder_get_width',
+		'_mpeg1_decoder_get_height',
+		'_mpeg1_decoder_get_y_ptr',
+		'_mpeg1_decoder_get_cr_ptr',
+		'_mpeg1_decoder_get_cb_ptr',
+		'_mpeg1_decoder_decode',
+		'_mp2_decoder_create',
+		'_mp2_decoder_destroy',
+		'_mp2_decoder_get_write_ptr',
+		'_mp2_decoder_get_index',
+		'_mp2_decoder_set_index',
+		'_mp2_decoder_did_write',
+		'_mp2_decoder_get_left_channel_ptr',
+		'_mp2_decoder_get_right_channel_ptr',
+		'_mp2_decoder_get_sample_rate',
+		'_mp2_decoder_decode']" \
+	-O3 \
+	-o jsmpeg.wasm
+
+
+# Concat all .js sources
+cat \
+	src/jsmpeg.js \
+	src/video-element.js \
+	src/player.js \
+	src/buffer.js \
+	src/ajax.js \
+	src/fetch.js \
+	src/ajax-progressive.js \
+	src/websocket.js \
+	src/ts.js \
+	src/decoder.js \
+	src/mpeg1.js \
+	src/mpeg1-wasm.js \
+	src/mp2.js \
+	src/mp2-wasm.js \
+	src/webgl.js \
+	src/canvas2d.js \
+	src/webaudio.js \
+	src/wasm-module.js \
+	> jsmpeg.js
+
+# Append the .wasm module to the .js source as base64 string
+echo "JSMpeg.WASM_BINARY_INLINED='$(base64 -w 0 jsmpeg.wasm)';" \
+	>> jsmpeg.js
+
+
+# Minify
+uglifyjs jsmpeg.js -o jsmpeg.min.js
+
+# Cleanup
+rm jsmpeg.js
+rm jsmpeg.wasm
+

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
jsmpeg-master/jsmpeg.min.js


+ 137 - 0
jsmpeg-master/src/ajax-progressive.js

@@ -0,0 +1,137 @@
+JSMpeg.Source.AjaxProgressive = (function(){ "use strict";
+
+var AjaxProgressiveSource = function(url, options) {
+	this.url = url;
+	this.destination = null;
+	this.request = null;
+	this.streaming = false;
+
+	this.completed = false;
+	this.established = false;
+	this.progress = 0;
+
+	this.fileSize = 0;
+	this.loadedSize = 0;
+	this.chunkSize = options.chunkSize || 1024*1024;
+
+	this.isLoading = false;
+	this.loadStartTime = 0;
+	this.throttled = options.throttled !== false;
+	this.aborted = false;
+
+	this.onEstablishedCallback = options.onSourceEstablished;
+	this.onCompletedCallback = options.onSourceCompleted;
+};
+
+AjaxProgressiveSource.prototype.connect = function(destination) {
+	this.destination = destination;
+};
+
+AjaxProgressiveSource.prototype.start = function() {
+	this.request = new XMLHttpRequest();
+
+	this.request.onreadystatechange = function() {
+		if (this.request.readyState === this.request.DONE) {
+			this.fileSize = parseInt(
+				this.request.getResponseHeader("Content-Length")
+			);
+			this.loadNextChunk();
+		}
+	}.bind(this);
+
+	this.request.onprogress = this.onProgress.bind(this);
+	this.request.open('HEAD', this.url);
+	this.request.send();
+};
+
+AjaxProgressiveSource.prototype.resume = function(secondsHeadroom) {
+	if (this.isLoading || !this.throttled) {
+		return;
+	}
+
+	// Guess the worst case loading time with lots of safety margin. This is
+	// somewhat arbitrary...
+	var worstCaseLoadingTime = this.loadTime * 8 + 2;
+	if (worstCaseLoadingTime > secondsHeadroom) {
+		this.loadNextChunk();
+	}
+};
+
+AjaxProgressiveSource.prototype.destroy = function() {
+	this.request.abort();
+	this.aborted = true;
+};
+
+AjaxProgressiveSource.prototype.loadNextChunk = function() {
+	var start = this.loadedSize,
+		end = Math.min(this.loadedSize + this.chunkSize-1, this.fileSize-1);
+	
+	if (start >= this.fileSize || this.aborted) {
+		this.completed = true;
+		if (this.onCompletedCallback) {
+			this.onCompletedCallback(this);
+		}
+		return;
+	}
+	
+	this.isLoading = true;
+	this.loadStartTime = JSMpeg.Now();
+	this.request = new XMLHttpRequest();
+
+	this.request.onreadystatechange = function() {		
+		if (
+			this.request.readyState === this.request.DONE && 
+			this.request.status >= 200 && this.request.status < 300
+		) {
+			this.onChunkLoad(this.request.response);
+		}
+		else if (this.request.readyState === this.request.DONE) {
+			// Retry?
+			if (this.loadFails++ < 3) {
+				this.loadNextChunk();
+			}
+		}
+	}.bind(this);
+	
+	if (start === 0) {
+		this.request.onprogress = this.onProgress.bind(this);
+	}
+
+	this.request.open('GET', this.url+'?'+start+"-"+end);
+	this.request.setRequestHeader("Range", "bytes="+start+"-"+end);
+	this.request.responseType = "arraybuffer";
+	this.request.send();
+};
+
+AjaxProgressiveSource.prototype.onProgress = function(ev) {
+	this.progress = (ev.loaded / ev.total);
+};
+
+AjaxProgressiveSource.prototype.onChunkLoad = function(data) {
+	var isFirstChunk = !this.established;
+	this.established = true;
+	this.progress = 1;
+	
+	this.loadedSize += data.byteLength;
+	this.loadFails = 0;
+	this.isLoading = false;
+
+	if (isFirstChunk && this.onEstablishedCallback) {
+		this.onEstablishedCallback(this);
+	}
+
+	if (this.destination) {
+		this.destination.write(data);
+	}
+
+	this.loadTime = JSMpeg.Now() - this.loadStartTime;
+	if (!this.throttled) {
+		this.loadNextChunk();
+	}
+};
+
+return AjaxProgressiveSource;
+
+})();
+
+

+ 72 - 0
jsmpeg-master/src/ajax.js

@@ -0,0 +1,72 @@
+JSMpeg.Source.Ajax = (function(){ "use strict";
+
+var AjaxSource = function(url, options) {
+	this.url = url;
+	this.destination = null;
+	this.request = null;
+	this.streaming = false;
+
+	this.completed = false;
+	this.established = false;
+	this.progress = 0;
+
+	this.onEstablishedCallback = options.onSourceEstablished;
+	this.onCompletedCallback = options.onSourceCompleted;
+};
+
+AjaxSource.prototype.connect = function(destination) {
+	this.destination = destination;
+};
+
+AjaxSource.prototype.start = function() {
+	this.request = new XMLHttpRequest();
+
+	this.request.onreadystatechange = function() {
+		if (
+			this.request.readyState === this.request.DONE && 
+			this.request.status === 200
+		) {
+			this.onLoad(this.request.response);
+		}
+	}.bind(this);
+
+	this.request.onprogress = this.onProgress.bind(this);
+	this.request.open('GET', this.url);
+	this.request.responseType = "arraybuffer";
+	this.request.send();
+};
+
+AjaxSource.prototype.resume = function(secondsHeadroom) {
+	// Nothing to do here
+};
+
+AjaxSource.prototype.destroy = function() {
+	this.request.abort();
+};
+
+AjaxSource.prototype.onProgress = function(ev) {
+	this.progress = (ev.loaded / ev.total);
+};
+
+AjaxSource.prototype.onLoad = function(data) {
+	this.established = true;
+	this.completed = true;
+	this.progress = 1;
+
+	if (this.onEstablishedCallback) {
+		this.onEstablishedCallback(this);
+	}
+	if (this.onCompletedCallback) {
+		this.onCompletedCallback(this);
+	}
+
+	if (this.destination) {
+		this.destination.write(data);
+	}
+};
+
+return AjaxSource;
+
+})();
+
+

+ 198 - 0
jsmpeg-master/src/buffer.js

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

+ 119 - 0
jsmpeg-master/src/canvas2d.js

@@ -0,0 +1,119 @@
+JSMpeg.Renderer.Canvas2D = (function(){ "use strict";
+
+var CanvasRenderer = function(options) {
+	this.canvas = options.canvas || document.createElement('canvas');
+	this.width = this.canvas.width;
+	this.height = this.canvas.height;
+	this.enabled = true;
+
+	this.context = this.canvas.getContext('2d');
+};
+
+CanvasRenderer.prototype.destroy = function() {
+	// Nothing to do here
+};
+
+CanvasRenderer.prototype.resize = function(width, height) {
+	this.width = width|0;
+	this.height = height|0;
+
+	this.canvas.width = this.width;
+	this.canvas.height = this.height;
+
+	this.imageData = this.context.getImageData(0, 0, this.width, this.height);
+	JSMpeg.Fill(this.imageData.data, 255);
+};
+
+CanvasRenderer.prototype.renderProgress = function(progress) {
+	var 
+		w = this.canvas.width,
+		h = this.canvas.height,
+		ctx = this.context;
+
+	ctx.fillStyle = '#222';
+	ctx.fillRect(0, 0, w, h);
+	ctx.fillStyle = '#fff';
+	ctx.fillRect(0, h - h * progress, w, h * progress);
+};
+
+CanvasRenderer.prototype.render = function(y, cb, cr) {
+	this.YCbCrToRGBA(y, cb, cr, this.imageData.data);
+	this.context.putImageData(this.imageData, 0, 0);
+};
+
+CanvasRenderer.prototype.YCbCrToRGBA = function(y, cb, cr, rgba) {
+	if (!this.enabled) {
+		return;
+	}
+
+	// Chroma values are the same for each block of 4 pixels, so we proccess
+	// 2 lines at a time, 2 neighboring pixels each.
+	// I wish we could use 32bit writes to the RGBA buffer instead of writing
+	// each byte separately, but we need the automatic clamping of the RGBA
+	// buffer.
+	
+	var w = ((this.width + 15) >> 4) << 4,
+		w2 = w >> 1;
+
+	var yIndex1 = 0,
+		yIndex2 = w,
+		yNext2Lines = w + (w - this.width);
+
+	var cIndex = 0,
+		cNextLine = w2 - (this.width >> 1);
+
+	var rgbaIndex1 = 0,
+		rgbaIndex2 = this.width * 4,
+		rgbaNext2Lines = this.width * 4;
+
+	var cols = this.width >> 1,
+		rows = this.height >> 1;
+
+	var ccb, ccr, r, g, b;
+
+	for (var row = 0; row < rows; row++) {
+		for (var col = 0; col < cols; col++) {
+			ccb = cb[cIndex];
+			ccr = cr[cIndex];
+			cIndex++;
+
+			r = (ccb + ((ccb * 103) >> 8)) - 179;
+			g = ((ccr * 88) >> 8) - 44 + ((ccb * 183) >> 8) - 91;
+			b = (ccr + ((ccr * 198) >> 8)) - 227;
+
+			// Line 1
+			var y1 = y[yIndex1++];
+			var y2 = y[yIndex1++];
+			rgba[rgbaIndex1]   = y1 + r;
+			rgba[rgbaIndex1+1] = y1 - g;
+			rgba[rgbaIndex1+2] = y1 + b;
+			rgba[rgbaIndex1+4] = y2 + r;
+			rgba[rgbaIndex1+5] = y2 - g;
+			rgba[rgbaIndex1+6] = y2 + b;
+			rgbaIndex1 += 8;
+
+			// Line 2
+			var y3 = y[yIndex2++];
+			var y4 = y[yIndex2++];
+			rgba[rgbaIndex2]   = y3 + r;
+			rgba[rgbaIndex2+1] = y3 - g;
+			rgba[rgbaIndex2+2] = y3 + b;
+			rgba[rgbaIndex2+4] = y4 + r;
+			rgba[rgbaIndex2+5] = y4 - g;
+			rgba[rgbaIndex2+6] = y4 + b;
+			rgbaIndex2 += 8;
+		}
+
+		yIndex1 += yNext2Lines;
+		yIndex2 += yNext2Lines;
+		rgbaIndex1 += rgbaNext2Lines;
+		rgbaIndex2 += rgbaNext2Lines;
+		cIndex += cNextLine;
+	}
+};
+
+return CanvasRenderer;
+
+})();
+
+

+ 112 - 0
jsmpeg-master/src/decoder.js

@@ -0,0 +1,112 @@
+JSMpeg.Decoder.Base = (function(){ "use strict";
+
+var BaseDecoder = function(options) {
+	this.destination = null;
+	this.canPlay = false;
+
+	this.collectTimestamps = !options.streaming;
+	this.bytesWritten = 0;
+	this.timestamps = [];
+	this.timestampIndex = 0;
+
+	this.startTime = 0;
+	this.decodedTime = 0;
+
+	Object.defineProperty(this, 'currentTime', {get: this.getCurrentTime});
+};
+
+BaseDecoder.prototype.destroy = function() {};
+
+BaseDecoder.prototype.connect = function(destination) {
+	this.destination = destination;
+};
+
+BaseDecoder.prototype.bufferGetIndex = function() {
+	return this.bits.index;
+};
+
+BaseDecoder.prototype.bufferSetIndex = function(index) {
+	this.bits.index = index;
+};
+
+BaseDecoder.prototype.bufferWrite = function(buffers) {
+	return this.bits.write(buffers);
+};
+
+BaseDecoder.prototype.write = function(pts, buffers) {
+	if (this.collectTimestamps) {
+		if (this.timestamps.length === 0) {
+			this.startTime = pts;
+			this.decodedTime = pts;
+		}
+		this.timestamps.push({index: this.bytesWritten << 3, time: pts});
+	}
+
+	this.bytesWritten += this.bufferWrite(buffers);
+	this.canPlay = true;
+};
+
+BaseDecoder.prototype.seek = function(time) {
+	if (!this.collectTimestamps) {
+		return;
+	}
+
+	this.timestampIndex = 0;
+	for (var i = 0; i < this.timestamps.length; i++) {
+		if (this.timestamps[i].time > time) {
+			break;
+		}
+		this.timestampIndex = i;
+	}
+
+	var ts = this.timestamps[this.timestampIndex];
+	if (ts) {
+		this.bufferSetIndex(ts.index);
+		this.decodedTime = ts.time;
+	}
+	else {
+		this.bufferSetIndex(0);
+		this.decodedTime = this.startTime;
+	}
+};
+
+BaseDecoder.prototype.decode = function() {
+	this.advanceDecodedTime(0);
+};
+
+BaseDecoder.prototype.advanceDecodedTime = function(seconds) {
+	if (this.collectTimestamps) {
+		var newTimestampIndex = -1;
+		var currentIndex = this.bufferGetIndex();
+		for (var i = this.timestampIndex; i < this.timestamps.length; i++) {
+			if (this.timestamps[i].index > currentIndex) {
+				break;
+			}
+			newTimestampIndex = i;
+		}
+
+		// Did we find a new PTS, different from the last? If so, we don't have
+		// to advance the decoded time manually and can instead sync it exactly
+		// to the PTS.
+		if (
+			newTimestampIndex !== -1 && 
+			newTimestampIndex !== this.timestampIndex
+		) {
+			this.timestampIndex = newTimestampIndex;
+			this.decodedTime = this.timestamps[this.timestampIndex].time;
+			return;
+		}
+	}
+
+	this.decodedTime += seconds;
+};
+
+BaseDecoder.prototype.getCurrentTime = function() {
+	return this.decodedTime;
+};
+
+return BaseDecoder;
+
+})();
+
+

+ 74 - 0
jsmpeg-master/src/fetch.js

@@ -0,0 +1,74 @@
+JSMpeg.Source.Fetch = (function(){ "use strict";
+
+var FetchSource = function(url, options) {
+	this.url = url;
+	this.destination = null;
+	this.request = null;
+	this.streaming = true;
+
+	this.completed = false;
+	this.established = false;
+	this.progress = 0;
+	this.aborted = false;
+
+	this.onEstablishedCallback = options.onSourceEstablished;
+	this.onCompletedCallback = options.onSourceCompleted;
+};
+
+FetchSource.prototype.connect = function(destination) {
+	this.destination = destination;
+};
+
+FetchSource.prototype.start = function() {
+	var params = {
+		method: 'GET',
+		headers: new Headers(),
+		cache: 'default'
+	};
+	
+	self.fetch(this.url, params).then(function(res) {
+		if (res.ok && (res.status >= 200 && res.status <= 299)) {
+			this.progress = 1;
+			this.established = true;
+			return this.pump(res.body.getReader());
+		}
+		else {
+			//error
+		}
+	}.bind(this)).catch(function(err) {
+		throw(err);
+	});
+};
+
+FetchSource.prototype.pump = function(reader) {
+	return reader.read().then(function(result) {
+		if (result.done) {
+			this.completed = true;
+		}
+		else {
+			if (this.aborted) {
+				return reader.cancel();
+			}
+			
+			if (this.destination) {
+				this.destination.write(result.value.buffer);
+			}
+
+			return this.pump(reader);
+		}
+	}.bind(this)).catch(function(err) {
+		throw(err);
+	});
+};
+
+FetchSource.prototype.resume = function(secondsHeadroom) {
+	// Nothing to do here
+};
+
+FetchSource.prototype.abort = function() {
+	this.aborted = true;
+};
+
+return FetchSource;
+
+})();

+ 122 - 0
jsmpeg-master/src/jsmpeg.js

@@ -0,0 +1,122 @@
+/*! jsmpeg v1.0 | (c) Dominic Szablewski | MIT license */
+
+
+// This sets up the JSMpeg "Namespace". The object is empty apart from the Now()
+// utility function and the automatic CreateVideoElements() after DOMReady.
+var JSMpeg = {
+
+	// The Player sets up the connections between source, demuxer, decoders,
+	// renderer and audio output. It ties everything together, is responsible
+	// of scheduling decoding and provides some convenience methods for
+	// external users.
+	Player: null,
+
+	// A Video Element wraps the Player, shows HTML controls to start/pause
+	// the video and handles Audio unlocking on iOS. VideoElements can be
+	// created directly in HTML using the <div class="jsmpeg"/> tag.
+	VideoElement: null,
+	
+	// The BitBuffer wraps a Uint8Array and allows reading an arbitrary number
+	// of bits at a time. On writing, the BitBuffer either expands its
+	// internal buffer (for static files) or deletes old data (for streaming).
+	BitBuffer: null,
+
+	// A Source provides raw data from HTTP, a WebSocket connection or any
+	// other mean. Sources must support the following API:
+	//   .connect(destinationNode)
+	//   .write(buffer)
+	//   .start() - start reading
+	//   .resume(headroom) - continue reading; headroom to play pos in seconds
+	//   .established - boolean, true after connection is established
+	//   .completed - boolean, true if the source is completely loaded
+	//   .progress - float 0-1
+	Source: {}, 
+
+	// A Demuxer may sit between a Source and a Decoder. It separates the
+	// incoming raw data into Video, Audio and other Streams. API:
+	//   .connect(streamId, destinationNode)
+	//   .write(buffer)
+	//   .currentTime – float, in seconds
+	//   .startTime - float, in seconds
+	Demuxer: {},
+
+	// A Decoder accepts an incoming Stream of raw Audio or Video data, buffers
+	// it and upon `.decode()` decodes a single frame of data. Video decoders
+	// call `destinationNode.render(Y, Cr, CB)` with the decoded pixel data;
+	// Audio decoders call `destinationNode.play(left, right)` with the decoded
+	// PCM data. API:
+	//   .connect(destinationNode)
+	//   .write(pts, buffer)
+	//   .decode()
+	//   .seek(time)
+	//   .currentTime - float, in seconds
+	//   .startTime - float, in seconds
+	Decoder: {},
+
+	// A Renderer accepts raw YCrCb data in 3 separate buffers via the render()
+	// method. Renderers typically convert the data into the RGBA color space
+	// and draw it on a Canvas, but other output - such as writing PNGs - would
+	// be conceivable. API:
+	//   .render(y, cr, cb) - pixel data as Uint8Arrays
+	//   .enabled - wether the renderer does anything upon receiving data
+	Renderer: {},
+
+	// Audio Outputs accept raw Stero PCM data in 2 separate buffers via the
+	// play() method. Outputs typically play the audio on the user's device.
+	// API:
+	//   .play(sampleRate, left, right) - rate in herz; PCM data as Uint8Arrays
+	//   .stop()
+	//   .enqueuedTime - float, in seconds
+	//   .enabled - wether the output does anything upon receiving data
+	AudioOutput: {}, 
+
+	Now: function() {
+		return window.performance 
+			? window.performance.now() / 1000
+			: Date.now() / 1000;
+	},
+
+	CreateVideoElements: function() {
+		var elements = document.querySelectorAll('.jsmpeg');
+		for (var i = 0; i < elements.length; i++) {
+			new JSMpeg.VideoElement(elements[i]);
+		}
+	},
+
+	Fill: function(array, value) {
+		if (array.fill) {
+			array.fill(value);
+		}
+		else {
+			for (var i = 0; i < array.length; i++) {
+				array[i] = value;
+			}
+		}
+	},
+
+	Base64ToArrayBuffer: function(base64) {
+		var binary =  window.atob(base64);
+		var length = binary.length;
+		var bytes = new Uint8Array(length);
+		for (var i = 0; i < length; i++)        {
+			bytes[i] = binary.charCodeAt(i);
+		}
+		return bytes.buffer;
+	},
+
+	// The build process may append `JSMpeg.WASM_BINARY_INLINED = base64data;` 
+	// to the minified source.
+	// If this property is present, jsmpeg will use the inlined binary data
+	// instead of trying to load a jsmpeg.wasm file via Ajax.
+	WASM_BINARY_INLINED: null
+};
+
+// Automatically create players for all found <div class="jsmpeg"/> elements.
+if (document.readyState === 'complete') {
+	JSMpeg.CreateVideoElements();
+}
+else {
+	document.addEventListener('DOMContentLoaded', JSMpeg.CreateVideoElements);
+}
+
+

+ 124 - 0
jsmpeg-master/src/mp2-wasm.js

@@ -0,0 +1,124 @@
+JSMpeg.Decoder.MP2AudioWASM = (function(){ "use strict";
+
+// Based on kjmp2 by Martin J. Fiedler
+// http://keyj.emphy.de/kjmp2/
+
+var MP2WASM = function(options) {
+	JSMpeg.Decoder.Base.call(this, options);
+
+	this.onDecodeCallback = options.onAudioDecode;
+	this.module = options.wasmModule;
+
+	this.bufferSize = options.audioBufferSize || 128*1024;
+	this.bufferMode = options.streaming
+		? JSMpeg.BitBuffer.MODE.EVICT
+		: JSMpeg.BitBuffer.MODE.EXPAND;
+
+	this.sampleRate = 0;
+};
+
+MP2WASM.prototype = Object.create(JSMpeg.Decoder.Base.prototype);
+MP2WASM.prototype.constructor = MP2WASM;
+
+MP2WASM.prototype.initializeWasmDecoder = function() {
+	if (!this.module.instance) {
+		console.warn('JSMpeg: WASM module not compiled yet');
+		return;
+	}
+	this.instance = this.module.instance;
+	this.functions = this.module.instance.exports;
+	this.decoder = this.functions._mp2_decoder_create(this.bufferSize, this.bufferMode);
+};
+
+MP2WASM.prototype.destroy = function() {
+	if (!this.decoder) {
+		return;
+	}
+	this.functions._mp2_decoder_destroy(this.decoder);
+};
+
+MP2WASM.prototype.bufferGetIndex = function() {
+	if (!this.decoder) {
+		return;
+	}
+	return this.functions._mp2_decoder_get_index(this.decoder);
+};
+
+MP2WASM.prototype.bufferSetIndex = function(index) {
+	if (!this.decoder) {
+		return;
+	}
+	this.functions._mp2_decoder_set_index(this.decoder, index);
+};
+
+MP2WASM.prototype.bufferWrite = function(buffers) {
+	if (!this.decoder) {
+		this.initializeWasmDecoder();
+	}
+
+	var totalLength = 0;
+	for (var i = 0; i < buffers.length; i++) {
+		totalLength += buffers[i].length;
+	}
+
+	var ptr = this.functions._mp2_decoder_get_write_ptr(this.decoder, totalLength);
+	for (var i = 0; i < buffers.length; i++) {
+		this.instance.heapU8.set(buffers[i], ptr);
+		ptr += buffers[i].length;
+	}
+	
+	this.functions._mp2_decoder_did_write(this.decoder, totalLength);
+	return totalLength;
+};
+
+MP2WASM.prototype.decode = function() {
+	var startTime = JSMpeg.Now();
+
+	if (!this.decoder) {
+		return false;
+	}	
+
+	var decodedBytes = this.functions._mp2_decoder_decode(this.decoder);
+	if (decodedBytes === 0) {
+		return false;
+	}
+
+	if (!this.sampleRate) {
+		this.sampleRate = this.functions._mp2_decoder_get_sample_rate(this.decoder);
+	}
+
+	if (this.destination) {
+		// Create a Float32 View into the modules output channel data
+		var leftPtr = this.functions._mp2_decoder_get_left_channel_ptr(this.decoder),
+			rightPtr = this.functions._mp2_decoder_get_right_channel_ptr(this.decoder);
+
+		var leftOffset = leftPtr / Float32Array.BYTES_PER_ELEMENT,
+			rightOffset = rightPtr / Float32Array.BYTES_PER_ELEMENT;
+
+		var left = this.instance.heapF32.subarray(leftOffset, leftOffset + MP2WASM.SAMPLES_PER_FRAME),
+			right = this.instance.heapF32.subarray(rightOffset, rightOffset + MP2WASM.SAMPLES_PER_FRAME);
+
+		this.destination.play(this.sampleRate, left, right);
+	}
+
+	this.advanceDecodedTime(MP2WASM.SAMPLES_PER_FRAME / this.sampleRate);
+
+	var elapsedTime = JSMpeg.Now() - startTime;
+	if (this.onDecodeCallback) {
+		this.onDecodeCallback(this, elapsedTime);
+	}
+	return true;
+};
+
+
+MP2WASM.prototype.getCurrentTime = function() {
+	var enqueuedTime = this.destination ? this.destination.enqueuedTime : 0;
+	return this.decodedTime - enqueuedTime;
+};
+
+MP2WASM.SAMPLES_PER_FRAME = 1152;
+
+return MP2WASM;
+
+})();
+

+ 690 - 0
jsmpeg-master/src/mp2.js

@@ -0,0 +1,690 @@
+JSMpeg.Decoder.MP2Audio = (function(){ "use strict";
+
+// Based on kjmp2 by Martin J. Fiedler
+// http://keyj.emphy.de/kjmp2/
+
+var MP2 = function(options) {
+	JSMpeg.Decoder.Base.call(this, options);
+
+	this.onDecodeCallback = options.onAudioDecode;
+
+	var bufferSize = options.audioBufferSize || 128*1024;
+	var bufferMode = options.streaming
+		? JSMpeg.BitBuffer.MODE.EVICT
+		: JSMpeg.BitBuffer.MODE.EXPAND;
+
+	this.bits = new JSMpeg.BitBuffer(bufferSize, bufferMode);
+
+	this.left = new Float32Array(1152);
+	this.right = new Float32Array(1152);
+	this.sampleRate = 44100;
+	
+	this.D = new Float32Array(1024);
+	this.D.set(MP2.SYNTHESIS_WINDOW, 0);
+	this.D.set(MP2.SYNTHESIS_WINDOW, 512);
+	this.V = [new Float32Array(1024), new Float32Array(1024)];
+	this.U = new Int32Array(32);
+	this.VPos = 0;
+
+	this.allocation = [new Array(32), new Array(32)];
+	this.scaleFactorInfo = [new Uint8Array(32), new Uint8Array(32)];
+	this.scaleFactor = [new Array(32), new Array(32)];
+	this.sample = [new Array(32), new Array(32)];
+	
+	for (var j = 0; j < 2; j++) {
+		for (var i = 0; i < 32; i++) {
+			this.scaleFactor[j][i] = [0, 0, 0];
+			this.sample[j][i] = [0, 0, 0];
+		}
+	}
+};
+
+MP2.prototype = Object.create(JSMpeg.Decoder.Base.prototype);
+MP2.prototype.constructor = MP2;
+
+MP2.prototype.decode = function() {
+	var startTime = JSMpeg.Now();
+
+	var pos = this.bits.index >> 3;
+	if (pos >= this.bits.byteLength) {
+		return false;
+	}
+
+	var decoded = this.decodeFrame(this.left, this.right);
+	this.bits.index = (pos + decoded) << 3;
+	if (!decoded) {
+		return false;
+	}
+
+	if (this.destination) {
+		this.destination.play(this.sampleRate, this.left, this.right);
+	}
+
+	this.advanceDecodedTime(this.left.length / this.sampleRate);
+
+	var elapsedTime = JSMpeg.Now() - startTime;
+	if (this.onDecodeCallback) {
+		this.onDecodeCallback(this, elapsedTime);
+	}
+	return true;
+};
+
+MP2.prototype.getCurrentTime = function() {
+	var enqueuedTime = this.destination ? this.destination.enqueuedTime : 0;
+	return this.decodedTime - enqueuedTime;
+};
+
+MP2.prototype.decodeFrame = function(left, right) {
+	// Check for valid header: syncword OK, MPEG-Audio Layer 2
+	var sync = this.bits.read(11),
+		version = this.bits.read(2),
+		layer = this.bits.read(2),
+		hasCRC = !this.bits.read(1);
+
+	if (
+		sync !== MP2.FRAME_SYNC ||
+		version !== MP2.VERSION.MPEG_1 ||
+		layer !== MP2.LAYER.II
+	) {
+		return 0; // Invalid header or unsupported version
+	}
+
+	var bitrateIndex = this.bits.read(4) - 1;
+	if (bitrateIndex > 13) {
+		return 0;  // Invalid bit rate or 'free format'
+	}
+
+	var sampleRateIndex = this.bits.read(2);
+	var sampleRate = MP2.SAMPLE_RATE[sampleRateIndex];
+	if (sampleRateIndex === 3) {
+		return 0; // Invalid sample rate
+	}
+	if (version === MP2.VERSION.MPEG_2) {
+		sampleRateIndex += 4;
+		bitrateIndex += 14;
+	}
+	var padding = this.bits.read(1),
+		privat = this.bits.read(1),
+		mode = this.bits.read(2);
+
+	// Parse the mode_extension, set up the stereo bound
+	var bound = 0;
+	if (mode === MP2.MODE.JOINT_STEREO) {
+		bound = (this.bits.read(2) + 1) << 2;
+	}
+	else {
+		this.bits.skip(2);
+		bound = (mode === MP2.MODE.MONO) ? 0 : 32;
+	}
+
+	// Discard the last 4 bits of the header and the CRC value, if present
+	this.bits.skip(4);
+	if (hasCRC) {
+		this.bits.skip(16);
+	}
+
+	// Compute the frame size
+	var bitrate = MP2.BIT_RATE[bitrateIndex],
+		sampleRate = MP2.SAMPLE_RATE[sampleRateIndex],
+		frameSize = ((144000 * bitrate / sampleRate) + padding)|0;
+	
+
+	// Prepare the quantizer table lookups
+	var tab3 = 0;
+	var sblimit = 0;
+	if (version === MP2.VERSION.MPEG_2) {
+		// MPEG-2 (LSR)
+		tab3 = 2;
+		sblimit = 30;
+	}
+	else {
+		// MPEG-1
+		var tab1 = (mode === MP2.MODE.MONO) ? 0 : 1;
+		var tab2 = MP2.QUANT_LUT_STEP_1[tab1][bitrateIndex];
+		tab3 = MP2.QUANT_LUT_STEP_2[tab2][sampleRateIndex];
+		sblimit = tab3 & 63;
+		tab3 >>= 6;
+	}
+
+	if (bound > sblimit) {
+		bound = sblimit;
+	}
+
+	// Read the allocation information
+	for (var sb = 0; sb < bound; sb++) {
+		this.allocation[0][sb] = this.readAllocation(sb, tab3);
+		this.allocation[1][sb] = this.readAllocation(sb, tab3);
+	}
+
+	for (var sb = bound; sb < sblimit; sb++) {
+		this.allocation[0][sb] = 
+			this.allocation[1][sb] =
+			this.readAllocation(sb, tab3);
+	}
+
+	// Read scale factor selector information
+	var channels = (mode === MP2.MODE.MONO) ? 1 : 2;
+	for (var sb = 0;  sb < sblimit; sb++) {
+		for (ch = 0;  ch < channels; ch++) {
+			if (this.allocation[ch][sb]) {
+				this.scaleFactorInfo[ch][sb] = this.bits.read(2);
+			}
+		}
+		if (mode === MP2.MODE.MONO) {
+			this.scaleFactorInfo[1][sb] = this.scaleFactorInfo[0][sb];
+		}
+	}
+
+	// Read scale factors
+	for (var sb = 0;  sb < sblimit; sb++) {
+		for (var ch = 0;  ch < channels; ch++) {
+			if (this.allocation[ch][sb]) {
+				var sf = this.scaleFactor[ch][sb];
+				switch (this.scaleFactorInfo[ch][sb]) {
+					case 0:
+						sf[0] = this.bits.read(6);
+						sf[1] = this.bits.read(6);
+						sf[2] = this.bits.read(6);
+						break;
+					case 1:
+						sf[0] =
+						sf[1] = this.bits.read(6);
+						sf[2] = this.bits.read(6);
+						break;
+					case 2:
+						sf[0] =
+						sf[1] =
+						sf[2] = this.bits.read(6);
+						break;
+					case 3:
+						sf[0] = this.bits.read(6);
+						sf[1] =
+						sf[2] = this.bits.read(6);
+						break;
+				}
+			}
+		}
+		if (mode === MP2.MODE.MONO) {
+			this.scaleFactor[1][sb][0] = this.scaleFactor[0][sb][0];
+			this.scaleFactor[1][sb][1] = this.scaleFactor[0][sb][1];
+			this.scaleFactor[1][sb][2] = this.scaleFactor[0][sb][2];
+		}
+	}
+
+	// Coefficient input and reconstruction
+	var outPos = 0;
+	for (var part = 0; part < 3; part++) {
+		for (var granule = 0; granule < 4; granule++) {
+
+			// Read the samples
+			for (var sb = 0; sb < bound; sb++) {
+				this.readSamples(0, sb, part);
+				this.readSamples(1, sb, part);
+			}
+			for (var sb = bound; sb < sblimit; sb++) {
+				this.readSamples(0, sb, part);
+				this.sample[1][sb][0] = this.sample[0][sb][0];
+				this.sample[1][sb][1] = this.sample[0][sb][1];
+				this.sample[1][sb][2] = this.sample[0][sb][2];
+			}
+			for (var sb = sblimit; sb < 32; sb++) {
+				this.sample[0][sb][0] = 0;
+				this.sample[0][sb][1] = 0;
+				this.sample[0][sb][2] = 0;
+				this.sample[1][sb][0] = 0;
+				this.sample[1][sb][1] = 0;
+				this.sample[1][sb][2] = 0;
+			}
+
+			// Synthesis loop
+			for (var p = 0; p < 3; p++) {
+				// Shifting step
+				this.VPos = (this.VPos - 64) & 1023;
+
+				for (var ch = 0;  ch < 2; ch++) {
+					MP2.MatrixTransform(this.sample[ch], p, this.V[ch], this.VPos);
+
+					// Build U, windowing, calculate output
+					JSMpeg.Fill(this.U, 0);
+
+					var dIndex = 512 - (this.VPos >> 1);
+					var vIndex = (this.VPos % 128) >> 1;
+					while (vIndex < 1024) {
+						for (var i = 0; i < 32; ++i) {
+							this.U[i] += this.D[dIndex++] * this.V[ch][vIndex++];
+						}
+
+						vIndex += 128-32;
+						dIndex += 64-32;
+					}
+
+					vIndex = (128-32 + 1024) - vIndex;
+					dIndex -= (512 - 32);
+					while (vIndex < 1024) {
+						for (var i = 0; i < 32; ++i) {
+							this.U[i] += this.D[dIndex++] * this.V[ch][vIndex++];
+						}
+
+						vIndex += 128-32;
+						dIndex += 64-32;
+					}
+
+					// Output samples
+					var outChannel = ch === 0 ? left : right;
+					for (var j = 0; j < 32; j++) {
+						outChannel[outPos + j] = this.U[j] / 2147418112;
+					}
+				} // End of synthesis channel loop
+				outPos += 32;
+			} // End of synthesis sub-block loop
+
+		} // Decoding of the granule finished
+	}
+
+	this.sampleRate = sampleRate;
+	return frameSize;
+};
+
+MP2.prototype.readAllocation = function(sb, tab3) {
+	var tab4 = MP2.QUANT_LUT_STEP_3[tab3][sb];
+	var qtab = MP2.QUANT_LUT_STEP4[tab4 & 15][this.bits.read(tab4 >> 4)];
+	return qtab ? (MP2.QUANT_TAB[qtab - 1]) : 0;
+};
+
+MP2.prototype.readSamples = function(ch, sb, part) {
+	var q = this.allocation[ch][sb],
+		sf = this.scaleFactor[ch][sb][part],
+		sample = this.sample[ch][sb],
+		val = 0;
+
+	if (!q) {
+		// No bits allocated for this subband
+		sample[0] = sample[1] = sample[2] = 0;
+		return;
+	}
+
+	// Resolve scalefactor
+	if (sf === 63) {
+		sf = 0;
+	}
+	else {
+		var shift = (sf / 3)|0;
+		sf = (MP2.SCALEFACTOR_BASE[sf % 3] + ((1 << shift) >> 1)) >> shift;
+	}
+
+	// Decode samples
+	var adj = q.levels;
+	if (q.group) {
+		// Decode grouped samples
+		val = this.bits.read(q.bits);
+		sample[0] = val % adj;
+		val = (val / adj)|0;
+		sample[1] = val % adj;
+		sample[2] = (val / adj)|0;
+	}
+	else {
+		// Decode direct samples
+		sample[0] = this.bits.read(q.bits);
+		sample[1] = this.bits.read(q.bits);
+		sample[2] = this.bits.read(q.bits);
+	}
+
+	// Postmultiply samples
+	var scale = (65536 / (adj + 1))|0;
+	adj = ((adj + 1) >> 1) - 1;
+
+	val = (adj - sample[0]) * scale;
+	sample[0] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
+
+	val = (adj - sample[1]) * scale;
+	sample[1] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
+
+	val = (adj - sample[2]) * scale;
+	sample[2] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
+};
+
+MP2.MatrixTransform = function(s, ss, d, dp) {
+	var t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12,
+		t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24,
+		t25, t26, t27, t28, t29, t30, t31, t32, t33;
+
+	t01 = s[ 0][ss] + s[31][ss]; t02 = (s[ 0][ss] - s[31][ss]) * 0.500602998235;
+	t03 = s[ 1][ss] + s[30][ss]; t04 = (s[ 1][ss] - s[30][ss]) * 0.505470959898;
+	t05 = s[ 2][ss] + s[29][ss]; t06 = (s[ 2][ss] - s[29][ss]) * 0.515447309923;
+	t07 = s[ 3][ss] + s[28][ss]; t08 = (s[ 3][ss] - s[28][ss]) * 0.53104259109;
+	t09 = s[ 4][ss] + s[27][ss]; t10 = (s[ 4][ss] - s[27][ss]) * 0.553103896034;
+	t11 = s[ 5][ss] + s[26][ss]; t12 = (s[ 5][ss] - s[26][ss]) * 0.582934968206;
+	t13 = s[ 6][ss] + s[25][ss]; t14 = (s[ 6][ss] - s[25][ss]) * 0.622504123036;
+	t15 = s[ 7][ss] + s[24][ss]; t16 = (s[ 7][ss] - s[24][ss]) * 0.674808341455;
+	t17 = s[ 8][ss] + s[23][ss]; t18 = (s[ 8][ss] - s[23][ss]) * 0.744536271002;
+	t19 = s[ 9][ss] + s[22][ss]; t20 = (s[ 9][ss] - s[22][ss]) * 0.839349645416;
+	t21 = s[10][ss] + s[21][ss]; t22 = (s[10][ss] - s[21][ss]) * 0.972568237862;
+	t23 = s[11][ss] + s[20][ss]; t24 = (s[11][ss] - s[20][ss]) * 1.16943993343;
+	t25 = s[12][ss] + s[19][ss]; t26 = (s[12][ss] - s[19][ss]) * 1.48416461631;
+	t27 = s[13][ss] + s[18][ss]; t28 = (s[13][ss] - s[18][ss]) * 2.05778100995;
+	t29 = s[14][ss] + s[17][ss]; t30 = (s[14][ss] - s[17][ss]) * 3.40760841847;
+	t31 = s[15][ss] + s[16][ss]; t32 = (s[15][ss] - s[16][ss]) * 10.1900081235;
+
+	t33 = t01 + t31; t31 = (t01 - t31) * 0.502419286188;
+	t01 = t03 + t29; t29 = (t03 - t29) * 0.52249861494;
+	t03 = t05 + t27; t27 = (t05 - t27) * 0.566944034816;
+	t05 = t07 + t25; t25 = (t07 - t25) * 0.64682178336;
+	t07 = t09 + t23; t23 = (t09 - t23) * 0.788154623451;
+	t09 = t11 + t21; t21 = (t11 - t21) * 1.06067768599;
+	t11 = t13 + t19; t19 = (t13 - t19) * 1.72244709824;
+	t13 = t15 + t17; t17 = (t15 - t17) * 5.10114861869;
+	t15 = t33 + t13; t13 = (t33 - t13) * 0.509795579104;
+	t33 = t01 + t11; t01 = (t01 - t11) * 0.601344886935;
+	t11 = t03 + t09; t09 = (t03 - t09) * 0.899976223136;
+	t03 = t05 + t07; t07 = (t05 - t07) * 2.56291544774;
+	t05 = t15 + t03; t15 = (t15 - t03) * 0.541196100146;
+	t03 = t33 + t11; t11 = (t33 - t11) * 1.30656296488;
+	t33 = t05 + t03; t05 = (t05 - t03) * 0.707106781187;
+	t03 = t15 + t11; t15 = (t15 - t11) * 0.707106781187;
+	t03 += t15;
+	t11 = t13 + t07; t13 = (t13 - t07) * 0.541196100146;
+	t07 = t01 + t09; t09 = (t01 - t09) * 1.30656296488;
+	t01 = t11 + t07; t07 = (t11 - t07) * 0.707106781187;
+	t11 = t13 + t09; t13 = (t13 - t09) * 0.707106781187;
+	t11 += t13; t01 += t11; 
+	t11 += t07; t07 += t13;
+	t09 = t31 + t17; t31 = (t31 - t17) * 0.509795579104;
+	t17 = t29 + t19; t29 = (t29 - t19) * 0.601344886935;
+	t19 = t27 + t21; t21 = (t27 - t21) * 0.899976223136;
+	t27 = t25 + t23; t23 = (t25 - t23) * 2.56291544774;
+	t25 = t09 + t27; t09 = (t09 - t27) * 0.541196100146;
+	t27 = t17 + t19; t19 = (t17 - t19) * 1.30656296488;
+	t17 = t25 + t27; t27 = (t25 - t27) * 0.707106781187;
+	t25 = t09 + t19; t19 = (t09 - t19) * 0.707106781187;
+	t25 += t19;
+	t09 = t31 + t23; t31 = (t31 - t23) * 0.541196100146;
+	t23 = t29 + t21; t21 = (t29 - t21) * 1.30656296488;
+	t29 = t09 + t23; t23 = (t09 - t23) * 0.707106781187;
+	t09 = t31 + t21; t31 = (t31 - t21) * 0.707106781187;
+	t09 += t31;	t29 += t09;	t09 += t23;	t23 += t31;
+	t17 += t29;	t29 += t25;	t25 += t09;	t09 += t27;
+	t27 += t23;	t23 += t19; t19 += t31;	
+	t21 = t02 + t32; t02 = (t02 - t32) * 0.502419286188;
+	t32 = t04 + t30; t04 = (t04 - t30) * 0.52249861494;
+	t30 = t06 + t28; t28 = (t06 - t28) * 0.566944034816;
+	t06 = t08 + t26; t08 = (t08 - t26) * 0.64682178336;
+	t26 = t10 + t24; t10 = (t10 - t24) * 0.788154623451;
+	t24 = t12 + t22; t22 = (t12 - t22) * 1.06067768599;
+	t12 = t14 + t20; t20 = (t14 - t20) * 1.72244709824;
+	t14 = t16 + t18; t16 = (t16 - t18) * 5.10114861869;
+	t18 = t21 + t14; t14 = (t21 - t14) * 0.509795579104;
+	t21 = t32 + t12; t32 = (t32 - t12) * 0.601344886935;
+	t12 = t30 + t24; t24 = (t30 - t24) * 0.899976223136;
+	t30 = t06 + t26; t26 = (t06 - t26) * 2.56291544774;
+	t06 = t18 + t30; t18 = (t18 - t30) * 0.541196100146;
+	t30 = t21 + t12; t12 = (t21 - t12) * 1.30656296488;
+	t21 = t06 + t30; t30 = (t06 - t30) * 0.707106781187;
+	t06 = t18 + t12; t12 = (t18 - t12) * 0.707106781187;
+	t06 += t12;
+	t18 = t14 + t26; t26 = (t14 - t26) * 0.541196100146;
+	t14 = t32 + t24; t24 = (t32 - t24) * 1.30656296488;
+	t32 = t18 + t14; t14 = (t18 - t14) * 0.707106781187;
+	t18 = t26 + t24; t24 = (t26 - t24) * 0.707106781187;
+	t18 += t24; t32 += t18; 
+	t18 += t14; t26 = t14 + t24;
+	t14 = t02 + t16; t02 = (t02 - t16) * 0.509795579104;
+	t16 = t04 + t20; t04 = (t04 - t20) * 0.601344886935;
+	t20 = t28 + t22; t22 = (t28 - t22) * 0.899976223136;
+	t28 = t08 + t10; t10 = (t08 - t10) * 2.56291544774;
+	t08 = t14 + t28; t14 = (t14 - t28) * 0.541196100146;
+	t28 = t16 + t20; t20 = (t16 - t20) * 1.30656296488;
+	t16 = t08 + t28; t28 = (t08 - t28) * 0.707106781187;
+	t08 = t14 + t20; t20 = (t14 - t20) * 0.707106781187;
+	t08 += t20;
+	t14 = t02 + t10; t02 = (t02 - t10) * 0.541196100146;
+	t10 = t04 + t22; t22 = (t04 - t22) * 1.30656296488;
+	t04 = t14 + t10; t10 = (t14 - t10) * 0.707106781187;
+	t14 = t02 + t22; t02 = (t02 - t22) * 0.707106781187;
+	t14 += t02;	t04 += t14;	t14 += t10;	t10 += t02;
+	t16 += t04;	t04 += t08;	t08 += t14;	t14 += t28;
+	t28 += t10;	t10 += t20;	t20 += t02;	t21 += t16;
+	t16 += t32;	t32 += t04;	t04 += t06;	t06 += t08;
+	t08 += t18;	t18 += t14;	t14 += t30;	t30 += t28;
+	t28 += t26;	t26 += t10;	t10 += t12;	t12 += t20;
+	t20 += t24;	t24 += t02;
+
+	d[dp + 48] = -t33;
+	d[dp + 49] = d[dp + 47] = -t21;
+	d[dp + 50] = d[dp + 46] = -t17;
+	d[dp + 51] = d[dp + 45] = -t16;
+	d[dp + 52] = d[dp + 44] = -t01;
+	d[dp + 53] = d[dp + 43] = -t32;
+	d[dp + 54] = d[dp + 42] = -t29;
+	d[dp + 55] = d[dp + 41] = -t04;
+	d[dp + 56] = d[dp + 40] = -t03;
+	d[dp + 57] = d[dp + 39] = -t06;
+	d[dp + 58] = d[dp + 38] = -t25;
+	d[dp + 59] = d[dp + 37] = -t08;
+	d[dp + 60] = d[dp + 36] = -t11;
+	d[dp + 61] = d[dp + 35] = -t18;
+	d[dp + 62] = d[dp + 34] = -t09;
+	d[dp + 63] = d[dp + 33] = -t14;
+	d[dp + 32] = -t05;
+	d[dp +  0] = t05; d[dp + 31] = -t30;
+	d[dp +  1] = t30; d[dp + 30] = -t27;
+	d[dp +  2] = t27; d[dp + 29] = -t28;
+	d[dp +  3] = t28; d[dp + 28] = -t07;
+	d[dp +  4] = t07; d[dp + 27] = -t26;
+	d[dp +  5] = t26; d[dp + 26] = -t23;
+	d[dp +  6] = t23; d[dp + 25] = -t10;
+	d[dp +  7] = t10; d[dp + 24] = -t15;
+	d[dp +  8] = t15; d[dp + 23] = -t12;
+	d[dp +  9] = t12; d[dp + 22] = -t19;
+	d[dp + 10] = t19; d[dp + 21] = -t20;
+	d[dp + 11] = t20; d[dp + 20] = -t13;
+	d[dp + 12] = t13; d[dp + 19] = -t24;
+	d[dp + 13] = t24; d[dp + 18] = -t31;
+	d[dp + 14] = t31; d[dp + 17] = -t02;
+	d[dp + 15] = t02; d[dp + 16] =  0.0;
+};
+
+MP2.FRAME_SYNC = 0x7ff;
+
+MP2.VERSION = {
+	MPEG_2_5: 0x0,
+	MPEG_2: 0x2,
+	MPEG_1: 0x3
+};
+
+MP2.LAYER = {
+	III: 0x1,
+	II: 0x2,
+	I: 0x3
+};
+
+MP2.MODE = {
+	STEREO: 0x0,
+	JOINT_STEREO: 0x1,
+	DUAL_CHANNEL: 0x2,
+	MONO: 0x3
+};
+
+MP2.SAMPLE_RATE = new Uint16Array([
+	44100, 48000, 32000, 0, // MPEG-1
+	22050, 24000, 16000, 0  // MPEG-2
+]);
+
+MP2.BIT_RATE = new Uint16Array([
+	32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, // MPEG-1
+	 8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160  // MPEG-2
+]);
+
+MP2.SCALEFACTOR_BASE = new Uint32Array([
+	0x02000000, 0x01965FEA, 0x01428A30
+]);
+
+MP2.SYNTHESIS_WINDOW = new Float32Array([
+	     0.0,     -0.5,     -0.5,     -0.5,     -0.5,     -0.5,
+	    -0.5,     -1.0,     -1.0,     -1.0,     -1.0,     -1.5,
+	    -1.5,     -2.0,     -2.0,     -2.5,     -2.5,     -3.0,
+	    -3.5,     -3.5,     -4.0,     -4.5,     -5.0,     -5.5,
+	    -6.5,     -7.0,     -8.0,     -8.5,     -9.5,    -10.5,
+	   -12.0,    -13.0,    -14.5,    -15.5,    -17.5,    -19.0,
+	   -20.5,    -22.5,    -24.5,    -26.5,    -29.0,    -31.5,
+	   -34.0,    -36.5,    -39.5,    -42.5,    -45.5,    -48.5,
+	   -52.0,    -55.5,    -58.5,    -62.5,    -66.0,    -69.5,
+	   -73.5,    -77.0,    -80.5,    -84.5,    -88.0,    -91.5,
+	   -95.0,    -98.0,   -101.0,   -104.0,    106.5,    109.0,
+	   111.0,    112.5,    113.5,    114.0,    114.0,    113.5,
+	   112.0,    110.5,    107.5,    104.0,    100.0,     94.5,
+	    88.5,     81.5,     73.0,     63.5,     53.0,     41.5,
+	    28.5,     14.5,     -1.0,    -18.0,    -36.0,    -55.5,
+	   -76.5,    -98.5,   -122.0,   -147.0,   -173.5,   -200.5,
+	  -229.5,   -259.5,   -290.5,   -322.5,   -355.5,   -389.5,
+	  -424.0,   -459.5,   -495.5,   -532.0,   -568.5,   -605.0,
+	  -641.5,   -678.0,   -714.0,   -749.0,   -783.5,   -817.0,
+	  -849.0,   -879.5,   -908.5,   -935.0,   -959.5,   -981.0,
+	 -1000.5,  -1016.0,  -1028.5,  -1037.5,  -1042.5,  -1043.5,
+	 -1040.0,  -1031.5,   1018.5,   1000.0,    976.0,    946.5,
+	   911.0,    869.5,    822.0,    767.5,    707.0,    640.0,
+	   565.5,    485.0,    397.0,    302.5,    201.0,     92.5,
+	   -22.5,   -144.0,   -272.5,   -407.0,   -547.5,   -694.0,
+	  -846.0,  -1003.0,  -1165.0,  -1331.5,  -1502.0,  -1675.5,
+	 -1852.5,  -2031.5,  -2212.5,  -2394.0,  -2576.5,  -2758.5,
+	 -2939.5,  -3118.5,  -3294.5,  -3467.5,  -3635.5,  -3798.5,
+	 -3955.0,  -4104.5,  -4245.5,  -4377.5,  -4499.0,  -4609.5,
+	 -4708.0,  -4792.5,  -4863.5,  -4919.0,  -4958.0,  -4979.5,
+	 -4983.0,  -4967.5,  -4931.5,  -4875.0,  -4796.0,  -4694.5,
+	 -4569.5,  -4420.0,  -4246.0,  -4046.0,  -3820.0,  -3567.0,
+	  3287.0,   2979.5,   2644.0,   2280.5,   1888.0,   1467.5,
+	  1018.5,    541.0,     35.0,   -499.0,  -1061.0,  -1650.0,
+	 -2266.5,  -2909.0,  -3577.0,  -4270.0,  -4987.5,  -5727.5,
+	 -6490.0,  -7274.0,  -8077.5,  -8899.5,  -9739.0, -10594.5,
+	-11464.5, -12347.0, -13241.0, -14144.5, -15056.0, -15973.5,
+	-16895.5, -17820.0, -18744.5, -19668.0, -20588.0, -21503.0,
+	-22410.5, -23308.5, -24195.0, -25068.5, -25926.5, -26767.0,
+	-27589.0, -28389.0, -29166.5, -29919.0, -30644.5, -31342.0,
+	-32009.5, -32645.0, -33247.0, -33814.5, -34346.0, -34839.5,
+	-35295.0, -35710.0, -36084.5, -36417.5, -36707.5, -36954.0,
+	-37156.5, -37315.0, -37428.0, -37496.0,  37519.0,  37496.0,
+	 37428.0,  37315.0,  37156.5,  36954.0,  36707.5,  36417.5,
+	 36084.5,  35710.0,  35295.0,  34839.5,  34346.0,  33814.5,
+	 33247.0,  32645.0,  32009.5,  31342.0,  30644.5,  29919.0,
+	 29166.5,  28389.0,  27589.0,  26767.0,  25926.5,  25068.5,
+	 24195.0,  23308.5,  22410.5,  21503.0,  20588.0,  19668.0,
+	 18744.5,  17820.0,  16895.5,  15973.5,  15056.0,  14144.5,
+	 13241.0,  12347.0,  11464.5,  10594.5,   9739.0,   8899.5,
+	  8077.5,   7274.0,   6490.0,   5727.5,   4987.5,   4270.0,
+	  3577.0,   2909.0,   2266.5,   1650.0,   1061.0,    499.0,
+	   -35.0,   -541.0,  -1018.5,  -1467.5,  -1888.0,  -2280.5,
+	 -2644.0,  -2979.5,   3287.0,   3567.0,   3820.0,   4046.0,
+	  4246.0,   4420.0,   4569.5,   4694.5,   4796.0,   4875.0,
+	  4931.5,   4967.5,   4983.0,   4979.5,   4958.0,   4919.0,
+	  4863.5,   4792.5,   4708.0,   4609.5,   4499.0,   4377.5,
+	  4245.5,   4104.5,   3955.0,   3798.5,   3635.5,   3467.5,
+	  3294.5,   3118.5,   2939.5,   2758.5,   2576.5,   2394.0,
+	  2212.5,   2031.5,   1852.5,   1675.5,   1502.0,   1331.5,
+	  1165.0,   1003.0,    846.0,    694.0,    547.5,    407.0,
+	   272.5,    144.0,     22.5,    -92.5,   -201.0,   -302.5,
+	  -397.0,   -485.0,   -565.5,   -640.0,   -707.0,   -767.5,
+	  -822.0,   -869.5,   -911.0,   -946.5,   -976.0,  -1000.0,
+	  1018.5,   1031.5,   1040.0,   1043.5,   1042.5,   1037.5,
+	  1028.5,   1016.0,   1000.5,    981.0,    959.5,    935.0,
+	   908.5,    879.5,    849.0,    817.0,    783.5,    749.0,
+	   714.0,    678.0,    641.5,    605.0,    568.5,    532.0,
+	   495.5,    459.5,    424.0,    389.5,    355.5,    322.5,
+	   290.5,    259.5,    229.5,    200.5,    173.5,    147.0,
+	   122.0,     98.5,     76.5,     55.5,     36.0,     18.0,
+		1.0,    -14.5,    -28.5,    -41.5,    -53.0,    -63.5,
+	   -73.0,    -81.5,    -88.5,    -94.5,   -100.0,   -104.0,
+	  -107.5,   -110.5,   -112.0,   -113.5,   -114.0,   -114.0,
+	  -113.5,   -112.5,   -111.0,   -109.0,    106.5,    104.0,
+	   101.0,     98.0,     95.0,     91.5,     88.0,     84.5,
+	    80.5,     77.0,     73.5,     69.5,     66.0,     62.5,
+	    58.5,     55.5,     52.0,     48.5,     45.5,     42.5,
+	    39.5,     36.5,     34.0,     31.5,     29.0,     26.5,
+	    24.5,     22.5,     20.5,     19.0,     17.5,     15.5,
+	    14.5,     13.0,     12.0,     10.5,      9.5,      8.5,
+	     8.0,      7.0,      6.5,      5.5,      5.0,      4.5,
+	     4.0,      3.5,      3.5,      3.0,      2.5,      2.5,
+	     2.0,      2.0,      1.5,      1.5,      1.0,      1.0,
+	     1.0,      1.0,      0.5,      0.5,      0.5,      0.5,
+	     0.5,      0.5
+]);
+
+// Quantizer lookup, step 1: bitrate classes
+MP2.QUANT_LUT_STEP_1 = [
+ 	// 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384 <- bitrate
+	[   0,  0,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2], // mono
+	// 16, 24, 28, 32, 40, 48, 56, 64, 80, 96,112,128,160,192 <- bitrate / chan
+	[   0,  0,  0,  0,  0,  0,  1,  1,  1,  2,  2,  2,  2,  2] // stereo
+];
+
+// Quantizer lookup, step 2: bitrate class, sample rate -> B2 table idx, sblimit
+MP2.QUANT_TAB = {
+	A: (27 | 64), // Table 3-B.2a: high-rate, sblimit = 27
+	B: (30 | 64), // Table 3-B.2b: high-rate, sblimit = 30
+	C:   8,       // Table 3-B.2c:  low-rate, sblimit =  8
+	D:  12        // Table 3-B.2d:  low-rate, sblimit = 12
+};
+
+MP2.QUANT_LUT_STEP_2 = [
+	//   44.1 kHz,        48 kHz,          32 kHz
+	[MP2.QUANT_TAB.C, MP2.QUANT_TAB.C, MP2.QUANT_TAB.D], // 32 - 48 kbit/sec/ch
+	[MP2.QUANT_TAB.A, MP2.QUANT_TAB.A, MP2.QUANT_TAB.A], // 56 - 80 kbit/sec/ch
+	[MP2.QUANT_TAB.B, MP2.QUANT_TAB.A, MP2.QUANT_TAB.B]  // 96+	 kbit/sec/ch
+];
+
+// Quantizer lookup, step 3: B2 table, subband -> nbal, row index
+// (upper 4 bits: nbal, lower 4 bits: row index)
+MP2.QUANT_LUT_STEP_3 = [
+	// Low-rate table (3-B.2c and 3-B.2d)
+	[
+		0x44,0x44,
+	  	0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34
+	],
+	// High-rate table (3-B.2a and 3-B.2b)
+	[
+		0x43,0x43,0x43,
+		0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,
+		0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,
+		0x20,0x20,0x20,0x20,0x20,0x20,0x20
+	],
+	// MPEG-2 LSR table (B.2 in ISO 13818-3)
+	[
+		0x45,0x45,0x45,0x45,
+		0x34,0x34,0x34,0x34,0x34,0x34,0x34,
+		0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,
+					   0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24	
+	]
+];
+
+// Quantizer lookup, step 4: table row, allocation[] value -> quant table index
+MP2.QUANT_LUT_STEP4 = [
+	[0, 1, 2, 17],
+	[0, 1, 2, 3, 4, 5, 6, 17],
+	[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17],
+	[0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
+	[0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17],
+	[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
+];
+
+MP2.QUANT_TAB = [
+	{levels:     3, group: 1, bits:  5},  //  1
+	{levels:     5, group: 1, bits:  7},  //  2
+	{levels:     7, group: 0, bits:  3},  //  3
+	{levels:     9, group: 1, bits: 10},  //  4
+	{levels:    15, group: 0, bits:  4},  //  5
+	{levels:    31, group: 0, bits:  5},  //  6
+	{levels:    63, group: 0, bits:  6},  //  7
+	{levels:   127, group: 0, bits:  7},  //  8
+	{levels:   255, group: 0, bits:  8},  //  9
+	{levels:   511, group: 0, bits:  9},  // 10
+	{levels:  1023, group: 0, bits: 10},  // 11
+	{levels:  2047, group: 0, bits: 11},  // 12
+	{levels:  4095, group: 0, bits: 12},  // 13
+	{levels:  8191, group: 0, bits: 13},  // 14
+	{levels: 16383, group: 0, bits: 14},  // 15
+	{levels: 32767, group: 0, bits: 15},  // 16
+	{levels: 65535, group: 0, bits: 16}   // 17
+];
+
+return MP2;
+
+})();
+

+ 133 - 0
jsmpeg-master/src/mpeg1-wasm.js

@@ -0,0 +1,133 @@
+JSMpeg.Decoder.MPEG1VideoWASM = (function(){ "use strict";
+
+var MPEG1WASM = function(options) {
+	JSMpeg.Decoder.Base.call(this, options);
+
+	this.onDecodeCallback = options.onVideoDecode;
+	this.module = options.wasmModule;
+
+	this.bufferSize = options.videoBufferSize || 512*1024;
+	this.bufferMode = options.streaming
+		? JSMpeg.BitBuffer.MODE.EVICT
+		: JSMpeg.BitBuffer.MODE.EXPAND;
+
+	this.decodeFirstFrame = options.decodeFirstFrame !== false;
+	this.hasSequenceHeader = false;
+};
+
+MPEG1WASM.prototype = Object.create(JSMpeg.Decoder.Base.prototype);
+MPEG1WASM.prototype.constructor = MPEG1WASM;
+
+MPEG1WASM.prototype.initializeWasmDecoder = function() {
+	if (!this.module.instance) {
+		console.warn('JSMpeg: WASM module not compiled yet');
+		return;
+	}
+	this.instance = this.module.instance;
+	this.functions = this.module.instance.exports;
+	this.decoder = this.functions._mpeg1_decoder_create(this.bufferSize, this.bufferMode);
+};
+
+MPEG1WASM.prototype.destroy = function() {
+	if (!this.decoder) {
+		return;
+	}
+	this.functions._mpeg1_decoder_destroy(this.decoder);
+};
+
+MPEG1WASM.prototype.bufferGetIndex = function() {
+	if (!this.decoder) {
+		return;
+	}
+	return this.functions._mpeg1_decoder_get_index(this.decoder);
+};
+
+MPEG1WASM.prototype.bufferSetIndex = function(index) {
+	if (!this.decoder) {
+		return;
+	}
+	this.functions._mpeg1_decoder_set_index(this.decoder, index);
+};
+
+MPEG1WASM.prototype.bufferWrite = function(buffers) {
+	if (!this.decoder) {
+		this.initializeWasmDecoder();
+	}
+
+	var totalLength = 0;
+	for (var i = 0; i < buffers.length; i++) {
+		totalLength += buffers[i].length;
+	}
+
+	var ptr = this.functions._mpeg1_decoder_get_write_ptr(this.decoder, totalLength);
+	for (var i = 0; i < buffers.length; i++) {
+		this.instance.heapU8.set(buffers[i], ptr);
+		ptr += buffers[i].length;
+	}
+	
+	this.functions._mpeg1_decoder_did_write(this.decoder, totalLength);
+	return totalLength;
+};
+
+MPEG1WASM.prototype.write = function(pts, buffers) {
+	JSMpeg.Decoder.Base.prototype.write.call(this, pts, buffers);
+
+	if (!this.hasSequenceHeader && this.functions._mpeg1_decoder_has_sequence_header(this.decoder)) {
+		this.loadSequnceHeader();
+	}
+};
+
+MPEG1WASM.prototype.loadSequnceHeader = function() {
+	this.hasSequenceHeader = true;
+	this.frameRate = this.functions._mpeg1_decoder_get_frame_rate(this.decoder);
+	this.codedSize = this.functions._mpeg1_decoder_get_coded_size(this.decoder);
+
+	if (this.destination) {
+		var w = this.functions._mpeg1_decoder_get_width(this.decoder);
+		var h = this.functions._mpeg1_decoder_get_height(this.decoder);
+		this.destination.resize(w, h);
+	}
+
+	if (this.decodeFirstFrame) {
+		this.decode();
+	}
+};
+
+MPEG1WASM.prototype.decode = function() {
+	var startTime = JSMpeg.Now();
+
+	if (!this.decoder) {
+		return false;
+	}
+
+	var didDecode = this.functions._mpeg1_decoder_decode(this.decoder);
+	if (!didDecode) {
+		return false;
+	}
+
+	// Invoke decode callbacks
+	if (this.destination) {
+		var ptrY = this.functions._mpeg1_decoder_get_y_ptr(this.decoder),
+			ptrCr = this.functions._mpeg1_decoder_get_cr_ptr(this.decoder),
+			ptrCb = this.functions._mpeg1_decoder_get_cb_ptr(this.decoder);
+
+		var dy = this.instance.heapU8.subarray(ptrY, ptrY + this.codedSize);
+		var dcr = this.instance.heapU8.subarray(ptrCr, ptrCr + (this.codedSize >> 2));
+		var dcb = this.instance.heapU8.subarray(ptrCb, ptrCb + (this.codedSize >> 2));
+
+		this.destination.render(dy, dcr, dcb, false);
+	}
+
+	this.advanceDecodedTime(1/this.frameRate);
+
+	var elapsedTime = JSMpeg.Now() - startTime;
+	if (this.onDecodeCallback) {
+		this.onDecodeCallback(this, elapsedTime);
+	}
+	return true;
+};
+
+return MPEG1WASM;
+
+})();
+

+ 1683 - 0
jsmpeg-master/src/mpeg1.js

@@ -0,0 +1,1683 @@
+JSMpeg.Decoder.MPEG1Video = (function(){ "use strict";
+
+// Inspired by Java MPEG-1 Video Decoder and Player by Zoltan Korandi 
+// https://sourceforge.net/projects/javampeg1video/
+
+var MPEG1 = function(options) {
+	JSMpeg.Decoder.Base.call(this, options);
+
+	this.onDecodeCallback = options.onVideoDecode;
+
+	var bufferSize = options.videoBufferSize || 512*1024;
+	var bufferMode = options.streaming
+		? JSMpeg.BitBuffer.MODE.EVICT
+		: JSMpeg.BitBuffer.MODE.EXPAND;
+
+	this.bits = new JSMpeg.BitBuffer(bufferSize, bufferMode);
+
+	this.customIntraQuantMatrix = new Uint8Array(64);
+	this.customNonIntraQuantMatrix = new Uint8Array(64);
+	this.blockData = new Int32Array(64);
+
+	this.currentFrame = 0;
+	this.decodeFirstFrame = options.decodeFirstFrame !== false;
+};
+
+MPEG1.prototype = Object.create(JSMpeg.Decoder.Base.prototype);
+MPEG1.prototype.constructor = MPEG1;
+
+MPEG1.prototype.write = function(pts, buffers) {
+	JSMpeg.Decoder.Base.prototype.write.call(this, pts, buffers);
+
+	if (!this.hasSequenceHeader) {
+		if (this.bits.findStartCode(MPEG1.START.SEQUENCE) === -1) {
+			return false;
+		}
+		this.decodeSequenceHeader();
+
+		if (this.decodeFirstFrame) {
+			this.decode();
+		}
+	}
+};
+
+MPEG1.prototype.decode = function() {
+	var startTime = JSMpeg.Now();
+	
+	if (!this.hasSequenceHeader) {
+		return false;
+	}
+
+	if (this.bits.findStartCode(MPEG1.START.PICTURE) === -1) {
+		var bufferedBytes = this.bits.byteLength - (this.bits.index >> 3);
+		return false;
+	}
+
+	this.decodePicture();
+	this.advanceDecodedTime(1/this.frameRate);
+
+	var elapsedTime = JSMpeg.Now() - startTime;
+	if (this.onDecodeCallback) {
+		this.onDecodeCallback(this, elapsedTime);
+	}
+	return true;
+};
+
+MPEG1.prototype.readHuffman = function(codeTable) {
+	var state = 0;
+	do {
+		state = codeTable[state + this.bits.read(1)];
+	} while (state >= 0 && codeTable[state] !== 0);
+	return codeTable[state+2];
+};
+
+
+// Sequence Layer
+
+MPEG1.prototype.frameRate = 30;
+MPEG1.prototype.decodeSequenceHeader = function() {
+	var newWidth = this.bits.read(12),
+		newHeight = this.bits.read(12);
+
+	// skip pixel aspect ratio
+	this.bits.skip(4);
+
+	this.frameRate = MPEG1.PICTURE_RATE[this.bits.read(4)];
+
+	// skip bitRate, marker, bufferSize and constrained bit
+	this.bits.skip(18 + 1 + 10 + 1);
+
+	if (newWidth !== this.width || newHeight !== this.height) {
+		this.width = newWidth;
+		this.height = newHeight;
+
+		this.initBuffers();
+
+		if (this.destination) {
+			this.destination.resize(newWidth, newHeight);
+		}
+	}
+
+	if (this.bits.read(1)) { // load custom intra quant matrix?
+		for (var i = 0; i < 64; i++) {
+			this.customIntraQuantMatrix[MPEG1.ZIG_ZAG[i]] = this.bits.read(8);
+		}
+		this.intraQuantMatrix = this.customIntraQuantMatrix;
+	}
+
+	if (this.bits.read(1)) { // load custom non intra quant matrix?
+		for (var i = 0; i < 64; i++) {
+			var idx = MPEG1.ZIG_ZAG[i];
+			this.customNonIntraQuantMatrix[idx] = this.bits.read(8);
+		}
+		this.nonIntraQuantMatrix = this.customNonIntraQuantMatrix;
+	}
+
+	this.hasSequenceHeader = true;
+};
+
+MPEG1.prototype.initBuffers = function() {
+	this.intraQuantMatrix = MPEG1.DEFAULT_INTRA_QUANT_MATRIX;
+	this.nonIntraQuantMatrix = MPEG1.DEFAULT_NON_INTRA_QUANT_MATRIX;
+
+	this.mbWidth = (this.width + 15) >> 4;
+	this.mbHeight = (this.height + 15) >> 4;
+	this.mbSize = this.mbWidth * this.mbHeight;
+
+	this.codedWidth = this.mbWidth << 4;
+	this.codedHeight = this.mbHeight << 4;
+	this.codedSize = this.codedWidth * this.codedHeight;
+
+	this.halfWidth = this.mbWidth << 3;
+	this.halfHeight = this.mbHeight << 3;
+
+	// Allocated buffers and resize the canvas
+	this.currentY = new Uint8ClampedArray(this.codedSize);
+	this.currentY32 = new Uint32Array(this.currentY.buffer);
+
+	this.currentCr = new Uint8ClampedArray(this.codedSize >> 2);
+	this.currentCr32 = new Uint32Array(this.currentCr.buffer);
+
+	this.currentCb = new Uint8ClampedArray(this.codedSize >> 2);
+	this.currentCb32 = new Uint32Array(this.currentCb.buffer);
+
+
+	this.forwardY = new Uint8ClampedArray(this.codedSize);
+	this.forwardY32 = new Uint32Array(this.forwardY.buffer);
+
+	this.forwardCr = new Uint8ClampedArray(this.codedSize >> 2);
+	this.forwardCr32 = new Uint32Array(this.forwardCr.buffer);
+
+	this.forwardCb = new Uint8ClampedArray(this.codedSize >> 2);
+	this.forwardCb32 = new Uint32Array(this.forwardCb.buffer);
+};
+
+
+// Picture Layer
+
+MPEG1.prototype.currentY = null;
+MPEG1.prototype.currentCr = null;
+MPEG1.prototype.currentCb = null;
+
+MPEG1.prototype.pictureType = 0;
+
+// Buffers for motion compensation
+MPEG1.prototype.forwardY = null;
+MPEG1.prototype.forwardCr = null;
+MPEG1.prototype.forwardCb = null;
+
+MPEG1.prototype.fullPelForward = false;
+MPEG1.prototype.forwardFCode = 0;
+MPEG1.prototype.forwardRSize = 0;
+MPEG1.prototype.forwardF = 0;
+
+MPEG1.prototype.decodePicture = function(skipOutput) {
+	this.currentFrame++;
+
+	this.bits.skip(10); // skip temporalReference
+	this.pictureType = this.bits.read(3);
+	this.bits.skip(16); // skip vbv_delay
+
+	// Skip B and D frames or unknown coding type
+	if (this.pictureType <= 0 || this.pictureType >= MPEG1.PICTURE_TYPE.B) {
+		return;
+	}
+
+	// full_pel_forward, forward_f_code
+	if (this.pictureType === MPEG1.PICTURE_TYPE.PREDICTIVE) {
+		this.fullPelForward = this.bits.read(1);
+		this.forwardFCode = this.bits.read(3);
+		if (this.forwardFCode === 0) {
+			// Ignore picture with zero forward_f_code
+			return;
+		}
+		this.forwardRSize = this.forwardFCode - 1;
+		this.forwardF = 1 << this.forwardRSize;
+	}
+
+	var code = 0;
+	do {
+		code = this.bits.findNextStartCode();
+	} while (code === MPEG1.START.EXTENSION || code === MPEG1.START.USER_DATA );
+
+
+	while (code >= MPEG1.START.SLICE_FIRST && code <= MPEG1.START.SLICE_LAST) {
+		this.decodeSlice(code & 0x000000FF);
+		code = this.bits.findNextStartCode();
+	}
+
+	if (code !== -1) {
+		// We found the next start code; rewind 32bits and let the main loop
+		// handle it.
+		this.bits.rewind(32);
+	}
+
+	// Invoke decode callbacks
+	if (this.destination) {
+		this.destination.render(this.currentY, this.currentCr, this.currentCb, true);
+	}
+
+	// If this is a reference picutre then rotate the prediction pointers
+	if (
+		this.pictureType === MPEG1.PICTURE_TYPE.INTRA ||
+		this.pictureType === MPEG1.PICTURE_TYPE.PREDICTIVE
+	) {
+		var
+			tmpY = this.forwardY,
+			tmpY32 = this.forwardY32,
+			tmpCr = this.forwardCr,
+			tmpCr32 = this.forwardCr32,
+			tmpCb = this.forwardCb,
+			tmpCb32 = this.forwardCb32;
+
+		this.forwardY = this.currentY;
+		this.forwardY32 = this.currentY32;
+		this.forwardCr = this.currentCr;
+		this.forwardCr32 = this.currentCr32;
+		this.forwardCb = this.currentCb;
+		this.forwardCb32 = this.currentCb32;
+
+		this.currentY = tmpY;
+		this.currentY32 = tmpY32;
+		this.currentCr = tmpCr;
+		this.currentCr32 = tmpCr32;
+		this.currentCb = tmpCb;
+		this.currentCb32 = tmpCb32;
+	}
+};
+
+
+// Slice Layer
+
+MPEG1.prototype.quantizerScale = 0;
+MPEG1.prototype.sliceBegin = false;
+
+MPEG1.prototype.decodeSlice = function(slice) {
+	this.sliceBegin = true;
+	this.macroblockAddress = (slice - 1) * this.mbWidth - 1;
+
+	// Reset motion vectors and DC predictors
+	this.motionFwH = this.motionFwHPrev = 0;
+	this.motionFwV = this.motionFwVPrev = 0;
+	this.dcPredictorY  = 128;
+	this.dcPredictorCr = 128;
+	this.dcPredictorCb = 128;
+
+	this.quantizerScale = this.bits.read(5);
+
+	// skip extra bits
+	while (this.bits.read(1)) {
+		this.bits.skip(8);
+	}
+
+	do {
+		this.decodeMacroblock();
+	} while (!this.bits.nextBytesAreStartCode());
+};
+
+
+// Macroblock Layer
+
+MPEG1.prototype.macroblockAddress = 0;
+MPEG1.prototype.mbRow = 0;
+MPEG1.prototype.mbCol = 0;
+
+MPEG1.prototype.macroblockType = 0;
+MPEG1.prototype.macroblockIntra = false;
+MPEG1.prototype.macroblockMotFw = false;
+
+MPEG1.prototype.motionFwH = 0;
+MPEG1.prototype.motionFwV = 0;
+MPEG1.prototype.motionFwHPrev = 0;
+MPEG1.prototype.motionFwVPrev = 0;
+
+MPEG1.prototype.decodeMacroblock = function() {
+	// Decode macroblock_address_increment
+	var
+		increment = 0,
+		t = this.readHuffman(MPEG1.MACROBLOCK_ADDRESS_INCREMENT);
+
+	while (t === 34) {
+		// macroblock_stuffing
+		t = this.readHuffman(MPEG1.MACROBLOCK_ADDRESS_INCREMENT);
+	}
+	while (t === 35) {
+		// macroblock_escape
+		increment += 33;
+		t = this.readHuffman(MPEG1.MACROBLOCK_ADDRESS_INCREMENT);
+	}
+	increment += t;
+
+	// Process any skipped macroblocks
+	if (this.sliceBegin) {
+		// The first macroblock_address_increment of each slice is relative
+		// to beginning of the preverious row, not the preverious macroblock
+		this.sliceBegin = false;
+		this.macroblockAddress += increment;
+	}
+	else {
+		if (this.macroblockAddress + increment >= this.mbSize) {
+			// Illegal (too large) macroblock_address_increment
+			return;
+		}
+		if (increment > 1) {
+			// Skipped macroblocks reset DC predictors
+			this.dcPredictorY  = 128;
+			this.dcPredictorCr = 128;
+			this.dcPredictorCb = 128;
+
+			// Skipped macroblocks in P-pictures reset motion vectors
+			if (this.pictureType === MPEG1.PICTURE_TYPE.PREDICTIVE) {
+				this.motionFwH = this.motionFwHPrev = 0;
+				this.motionFwV = this.motionFwVPrev = 0;
+			}
+		}
+
+		// Predict skipped macroblocks
+		while (increment > 1) {
+			this.macroblockAddress++;
+			this.mbRow = (this.macroblockAddress / this.mbWidth)|0;
+			this.mbCol = this.macroblockAddress % this.mbWidth;
+			this.copyMacroblock(
+				this.motionFwH, this.motionFwV,
+				this.forwardY, this.forwardCr, this.forwardCb
+			);
+			increment--;
+		}
+		this.macroblockAddress++;
+	}
+	this.mbRow = (this.macroblockAddress / this.mbWidth)|0;
+	this.mbCol = this.macroblockAddress % this.mbWidth;
+
+	// Process the current macroblock
+	var mbTable = MPEG1.MACROBLOCK_TYPE[this.pictureType];
+	this.macroblockType = this.readHuffman(mbTable);
+	this.macroblockIntra = (this.macroblockType & 0x01);
+	this.macroblockMotFw = (this.macroblockType & 0x08);
+
+	// Quantizer scale
+	if ((this.macroblockType & 0x10) !== 0) {
+		this.quantizerScale = this.bits.read(5);
+	}
+
+	if (this.macroblockIntra) {
+		// Intra-coded macroblocks reset motion vectors
+		this.motionFwH = this.motionFwHPrev = 0;
+		this.motionFwV = this.motionFwVPrev = 0;
+	}
+	else {
+		// Non-intra macroblocks reset DC predictors
+		this.dcPredictorY = 128;
+		this.dcPredictorCr = 128;
+		this.dcPredictorCb = 128;
+
+		this.decodeMotionVectors();
+		this.copyMacroblock(
+			this.motionFwH, this.motionFwV,
+			this.forwardY, this.forwardCr, this.forwardCb
+		);
+	}
+
+	// Decode blocks
+	var cbp = ((this.macroblockType & 0x02) !== 0)
+		? this.readHuffman(MPEG1.CODE_BLOCK_PATTERN)
+		: (this.macroblockIntra ? 0x3f : 0);
+
+	for (var block = 0, mask = 0x20; block < 6; block++) {
+		if ((cbp & mask) !== 0) {
+			this.decodeBlock(block);
+		}
+		mask >>= 1;
+	}
+};
+
+
+MPEG1.prototype.decodeMotionVectors = function() {
+	var code, d, r = 0;
+
+	// Forward
+	if (this.macroblockMotFw) {
+		// Horizontal forward
+		code = this.readHuffman(MPEG1.MOTION);
+		if ((code !== 0) && (this.forwardF !== 1)) {
+			r = this.bits.read(this.forwardRSize);
+			d = ((Math.abs(code) - 1) << this.forwardRSize) + r + 1;
+			if (code < 0) {
+				d = -d;
+			}
+		}
+		else {
+			d = code;
+		}
+
+		this.motionFwHPrev += d;
+		if (this.motionFwHPrev > (this.forwardF << 4) - 1) {
+			this.motionFwHPrev -= this.forwardF << 5;
+		}
+		else if (this.motionFwHPrev < ((-this.forwardF) << 4)) {
+			this.motionFwHPrev += this.forwardF << 5;
+		}
+
+		this.motionFwH = this.motionFwHPrev;
+		if (this.fullPelForward) {
+			this.motionFwH <<= 1;
+		}
+
+		// Vertical forward
+		code = this.readHuffman(MPEG1.MOTION);
+		if ((code !== 0) && (this.forwardF !== 1)) {
+			r = this.bits.read(this.forwardRSize);
+			d = ((Math.abs(code) - 1) << this.forwardRSize) + r + 1;
+			if (code < 0) {
+				d = -d;
+			}
+		}
+		else {
+			d = code;
+		}
+
+		this.motionFwVPrev += d;
+		if (this.motionFwVPrev > (this.forwardF << 4) - 1) {
+			this.motionFwVPrev -= this.forwardF << 5;
+		}
+		else if (this.motionFwVPrev < ((-this.forwardF) << 4)) {
+			this.motionFwVPrev += this.forwardF << 5;
+		}
+
+		this.motionFwV = this.motionFwVPrev;
+		if (this.fullPelForward) {
+			this.motionFwV <<= 1;
+		}
+	}
+	else if (this.pictureType === MPEG1.PICTURE_TYPE.PREDICTIVE) {
+		// No motion information in P-picture, reset vectors
+		this.motionFwH = this.motionFwHPrev = 0;
+		this.motionFwV = this.motionFwVPrev = 0;
+	}
+};
+
+MPEG1.prototype.copyMacroblock = function(motionH, motionV, sY, sCr, sCb) {
+	var
+		width, scan,
+		H, V, oddH, oddV,
+		src, dest, last;
+
+	// We use 32bit writes here
+	var dY = this.currentY32,
+		dCb = this.currentCb32,
+		dCr = this.currentCr32;
+
+	// Luminance
+	width = this.codedWidth;
+	scan = width - 16;
+
+	H = motionH >> 1;
+	V = motionV >> 1;
+	oddH = (motionH & 1) === 1;
+	oddV = (motionV & 1) === 1;
+
+	src = ((this.mbRow << 4) + V) * width + (this.mbCol << 4) + H;
+	dest = (this.mbRow * width + this.mbCol) << 2;
+	last = dest + (width << 2);
+
+	var x, y1, y2, y;
+	if (oddH) {
+		if (oddV) {
+			while (dest < last) {
+				y1 = sY[src] + sY[src+width]; src++;
+				for (x = 0; x < 4; x++) {
+					y2 = sY[src] + sY[src+width]; src++;
+					y = (((y1 + y2 + 2) >> 2) & 0xff);
+
+					y1 = sY[src] + sY[src+width]; src++;
+					y |= (((y1 + y2 + 2) << 6) & 0xff00);
+
+					y2 = sY[src] + sY[src+width]; src++;
+					y |= (((y1 + y2 + 2) << 14) & 0xff0000);
+
+					y1 = sY[src] + sY[src+width]; src++;
+					y |= (((y1 + y2 + 2) << 22) & 0xff000000);
+
+					dY[dest++] = y;
+				}
+				dest += scan >> 2; src += scan-1;
+			}
+		}
+		else {
+			while (dest < last) {
+				y1 = sY[src++];
+				for (x = 0; x < 4; x++) {
+					y2 = sY[src++];
+					y = (((y1 + y2 + 1) >> 1) & 0xff);
+
+					y1 = sY[src++];
+					y |= (((y1 + y2 + 1) << 7) & 0xff00);
+
+					y2 = sY[src++];
+					y |= (((y1 + y2 + 1) << 15) & 0xff0000);
+
+					y1 = sY[src++];
+					y |= (((y1 + y2 + 1) << 23) & 0xff000000);
+
+					dY[dest++] = y;
+				}
+				dest += scan >> 2; src += scan-1;
+			}
+		}
+	}
+	else {
+		if (oddV) {
+			while (dest < last) {
+				for (x = 0; x < 4; x++) {
+					y = (((sY[src] + sY[src+width] + 1) >> 1) & 0xff); src++;
+					y |= (((sY[src] + sY[src+width] + 1) << 7) & 0xff00); src++;
+					y |= (((sY[src] + sY[src+width] + 1) << 15) & 0xff0000); src++;
+					y |= (((sY[src] + sY[src+width] + 1) << 23) & 0xff000000); src++;
+
+					dY[dest++] = y;
+				}
+				dest += scan >> 2; src += scan;
+			}
+		}
+		else {
+			while (dest < last) {
+				for (x = 0; x < 4; x++) {
+					y = sY[src]; src++;
+					y |= sY[src] << 8; src++;
+					y |= sY[src] << 16; src++;
+					y |= sY[src] << 24; src++;
+
+					dY[dest++] = y;
+				}
+				dest += scan >> 2; src += scan;
+			}
+		}
+	}
+
+	// Chrominance
+
+	width = this.halfWidth;
+	scan = width - 8;
+
+	H = (motionH/2) >> 1;
+	V = (motionV/2) >> 1;
+	oddH = ((motionH/2) & 1) === 1;
+	oddV = ((motionV/2) & 1) === 1;
+
+	src = ((this.mbRow << 3) + V) * width + (this.mbCol << 3) + H;
+	dest = (this.mbRow * width + this.mbCol) << 1;
+	last = dest + (width << 1);
+
+	var cr1, cr2, cr,
+		cb1, cb2, cb;
+	if (oddH) {
+		if (oddV) {
+			while (dest < last) {
+				cr1 = sCr[src] + sCr[src+width];
+				cb1 = sCb[src] + sCb[src+width];
+				src++;
+				for (x = 0; x < 2; x++) {
+					cr2 = sCr[src] + sCr[src+width];
+					cb2 = sCb[src] + sCb[src+width]; src++;
+					cr = (((cr1 + cr2 + 2) >> 2) & 0xff);
+					cb = (((cb1 + cb2 + 2) >> 2) & 0xff);
+
+					cr1 = sCr[src] + sCr[src+width];
+					cb1 = sCb[src] + sCb[src+width]; src++;
+					cr |= (((cr1 + cr2 + 2) << 6) & 0xff00);
+					cb |= (((cb1 + cb2 + 2) << 6) & 0xff00);
+
+					cr2 = sCr[src] + sCr[src+width];
+					cb2 = sCb[src] + sCb[src+width]; src++;
+					cr |= (((cr1 + cr2 + 2) << 14) & 0xff0000);
+					cb |= (((cb1 + cb2 + 2) << 14) & 0xff0000);
+
+					cr1 = sCr[src] + sCr[src+width];
+					cb1 = sCb[src] + sCb[src+width]; src++;
+					cr |= (((cr1 + cr2 + 2) << 22) & 0xff000000);
+					cb |= (((cb1 + cb2 + 2) << 22) & 0xff000000);
+
+					dCr[dest] = cr;
+					dCb[dest] = cb;
+					dest++;
+				}
+				dest += scan >> 2; src += scan-1;
+			}
+		}
+		else {
+			while (dest < last) {
+				cr1 = sCr[src];
+				cb1 = sCb[src];
+				src++;
+				for (x = 0; x < 2; x++) {
+					cr2 = sCr[src];
+					cb2 = sCb[src++];
+					cr = (((cr1 + cr2 + 1) >> 1) & 0xff);
+					cb = (((cb1 + cb2 + 1) >> 1) & 0xff);
+
+					cr1 = sCr[src];
+					cb1 = sCb[src++];
+					cr |= (((cr1 + cr2 + 1) << 7) & 0xff00);
+					cb |= (((cb1 + cb2 + 1) << 7) & 0xff00);
+
+					cr2 = sCr[src];
+					cb2 = sCb[src++];
+					cr |= (((cr1 + cr2 + 1) << 15) & 0xff0000);
+					cb |= (((cb1 + cb2 + 1) << 15) & 0xff0000);
+
+					cr1 = sCr[src];
+					cb1 = sCb[src++];
+					cr |= (((cr1 + cr2 + 1) << 23) & 0xff000000);
+					cb |= (((cb1 + cb2 + 1) << 23) & 0xff000000);
+
+					dCr[dest] = cr;
+					dCb[dest] = cb;
+					dest++;
+				}
+				dest += scan >> 2; src += scan-1;
+			}
+		}
+	}
+	else {
+		if (oddV) {
+			while (dest < last) {
+				for (x = 0; x < 2; x++) {
+					cr = (((sCr[src] + sCr[src+width] + 1) >> 1) & 0xff);
+					cb = (((sCb[src] + sCb[src+width] + 1) >> 1) & 0xff); src++;
+
+					cr |= (((sCr[src] + sCr[src+width] + 1) << 7) & 0xff00);
+					cb |= (((sCb[src] + sCb[src+width] + 1) << 7) & 0xff00); src++;
+
+					cr |= (((sCr[src] + sCr[src+width] + 1) << 15) & 0xff0000);
+					cb |= (((sCb[src] + sCb[src+width] + 1) << 15) & 0xff0000); src++;
+
+					cr |= (((sCr[src] + sCr[src+width] + 1) << 23) & 0xff000000);
+					cb |= (((sCb[src] + sCb[src+width] + 1) << 23) & 0xff000000); src++;
+
+					dCr[dest] = cr;
+					dCb[dest] = cb;
+					dest++;
+				}
+				dest += scan >> 2; src += scan;
+			}
+		}
+		else {
+			while (dest < last) {
+				for (x = 0; x < 2; x++) {
+					cr = sCr[src];
+					cb = sCb[src]; src++;
+
+					cr |= sCr[src] << 8;
+					cb |= sCb[src] << 8; src++;
+
+					cr |= sCr[src] << 16;
+					cb |= sCb[src] << 16; src++;
+
+					cr |= sCr[src] << 24;
+					cb |= sCb[src] << 24; src++;
+
+					dCr[dest] = cr;
+					dCb[dest] = cb;
+					dest++;
+				}
+				dest += scan >> 2; src += scan;
+			}
+		}
+	}
+};
+
+
+// Block layer
+
+MPEG1.prototype.dcPredictorY = 0;
+MPEG1.prototype.dcPredictorCr = 0;
+MPEG1.prototype.dcPredictorCb = 0;
+
+MPEG1.prototype.blockData = null;
+
+MPEG1.prototype.decodeBlock = function(block) {
+
+	var
+		n = 0,
+		quantMatrix;
+
+	// Decode DC coefficient of intra-coded blocks
+	if (this.macroblockIntra) {
+		var
+			predictor,
+			dctSize;
+
+		// DC prediction
+
+		if (block < 4) {
+			predictor = this.dcPredictorY;
+			dctSize = this.readHuffman(MPEG1.DCT_DC_SIZE_LUMINANCE);
+		}
+		else {
+			predictor = (block === 4 ? this.dcPredictorCr : this.dcPredictorCb);
+			dctSize = this.readHuffman(MPEG1.DCT_DC_SIZE_CHROMINANCE);
+		}
+
+		// Read DC coeff
+		if (dctSize > 0) {
+			var differential = this.bits.read(dctSize);
+			if ((differential & (1 << (dctSize - 1))) !== 0) {
+				this.blockData[0] = predictor + differential;
+			}
+			else {
+				this.blockData[0] = predictor + ((-1 << dctSize)|(differential+1));
+			}
+		}
+		else {
+			this.blockData[0] = predictor;
+		}
+
+		// Save predictor value
+		if (block < 4) {
+			this.dcPredictorY = this.blockData[0];
+		}
+		else if (block === 4) {
+			this.dcPredictorCr = this.blockData[0];
+		}
+		else {
+			this.dcPredictorCb = this.blockData[0];
+		}
+
+		// Dequantize + premultiply
+		this.blockData[0] <<= (3 + 5);
+
+		quantMatrix = this.intraQuantMatrix;
+		n = 1;
+	}
+	else {
+		quantMatrix = this.nonIntraQuantMatrix;
+	}
+
+	// Decode AC coefficients (+DC for non-intra)
+	var level = 0;
+	while (true) {
+		var
+			run = 0,
+			coeff = this.readHuffman(MPEG1.DCT_COEFF);
+
+		if ((coeff === 0x0001) && (n > 0) && (this.bits.read(1) === 0)) {
+			// end_of_block
+			break;
+		}
+		if (coeff === 0xffff) {
+			// escape
+			run = this.bits.read(6);
+			level = this.bits.read(8);
+			if (level === 0) {
+				level = this.bits.read(8);
+			}
+			else if (level === 128) {
+				level = this.bits.read(8) - 256;
+			}
+			else if (level > 128) {
+				level = level - 256;
+			}
+		}
+		else {
+			run = coeff >> 8;
+			level = coeff & 0xff;
+			if (this.bits.read(1)) {
+				level = -level;
+			}
+		}
+
+		n += run;
+		var dezigZagged = MPEG1.ZIG_ZAG[n];
+		n++;
+
+		// Dequantize, oddify, clip
+		level <<= 1;
+		if (!this.macroblockIntra) {
+			level += (level < 0 ? -1 : 1);
+		}
+		level = (level * this.quantizerScale * quantMatrix[dezigZagged]) >> 4;
+		if ((level & 1) === 0) {
+			level -= level > 0 ? 1 : -1;
+		}
+		if (level > 2047) {
+			level = 2047;
+		}
+		else if (level < -2048) {
+			level = -2048;
+		}
+
+		// Save premultiplied coefficient
+		this.blockData[dezigZagged] = level * MPEG1.PREMULTIPLIER_MATRIX[dezigZagged];
+	}
+
+	// Move block to its place
+	var
+		destArray,
+		destIndex,
+		scan;
+
+	if (block < 4) {
+		destArray = this.currentY;
+		scan = this.codedWidth - 8;
+		destIndex = (this.mbRow * this.codedWidth + this.mbCol) << 4;
+		if ((block & 1) !== 0) {
+			destIndex += 8;
+		}
+		if ((block & 2) !== 0) {
+			destIndex += this.codedWidth << 3;
+		}
+	}
+	else {
+		destArray = (block === 4) ? this.currentCb : this.currentCr;
+		scan = (this.codedWidth >> 1) - 8;
+		destIndex = ((this.mbRow * this.codedWidth) << 2) + (this.mbCol << 3);
+	}
+
+	if (this.macroblockIntra) {
+		// Overwrite (no prediction)
+		if (n === 1) {
+			MPEG1.CopyValueToDestination((this.blockData[0] + 128) >> 8, destArray, destIndex, scan);
+			this.blockData[0] = 0;
+		}
+		else {
+			MPEG1.IDCT(this.blockData);
+			MPEG1.CopyBlockToDestination(this.blockData, destArray, destIndex, scan);
+			JSMpeg.Fill(this.blockData, 0);
+		}
+	}
+	else {
+		// Add data to the predicted macroblock
+		if (n === 1) {
+			MPEG1.AddValueToDestination((this.blockData[0] + 128) >> 8, destArray, destIndex, scan);
+			this.blockData[0] = 0;
+		}
+		else {
+			MPEG1.IDCT(this.blockData);
+			MPEG1.AddBlockToDestination(this.blockData, destArray, destIndex, scan);
+			JSMpeg.Fill(this.blockData, 0);
+		}
+	}
+
+	n = 0;
+};
+
+MPEG1.CopyBlockToDestination = function(block, dest, index, scan) {
+	for (var n = 0; n < 64; n += 8, index += scan+8) {
+		dest[index+0] = block[n+0];
+		dest[index+1] = block[n+1];
+		dest[index+2] = block[n+2];
+		dest[index+3] = block[n+3];
+		dest[index+4] = block[n+4];
+		dest[index+5] = block[n+5];
+		dest[index+6] = block[n+6];
+		dest[index+7] = block[n+7];
+	}
+};
+
+MPEG1.AddBlockToDestination = function(block, dest, index, scan) {
+	for (var n = 0; n < 64; n += 8, index += scan+8) {
+		dest[index+0] += block[n+0];
+		dest[index+1] += block[n+1];
+		dest[index+2] += block[n+2];
+		dest[index+3] += block[n+3];
+		dest[index+4] += block[n+4];
+		dest[index+5] += block[n+5];
+		dest[index+6] += block[n+6];
+		dest[index+7] += block[n+7];
+	}
+};
+
+MPEG1.CopyValueToDestination = function(value, dest, index, scan) {
+	for (var n = 0; n < 64; n += 8, index += scan+8) {
+		dest[index+0] = value;
+		dest[index+1] = value;
+		dest[index+2] = value;
+		dest[index+3] = value;
+		dest[index+4] = value;
+		dest[index+5] = value;
+		dest[index+6] = value;
+		dest[index+7] = value;
+	}
+};
+
+MPEG1.AddValueToDestination = function(value, dest, index, scan) {
+	for (var n = 0; n < 64; n += 8, index += scan+8) {
+		dest[index+0] += value;
+		dest[index+1] += value;
+		dest[index+2] += value;
+		dest[index+3] += value;
+		dest[index+4] += value;
+		dest[index+5] += value;
+		dest[index+6] += value;
+		dest[index+7] += value;
+	}
+};
+
+MPEG1.IDCT = function(block) {
+	// See http://vsr.informatik.tu-chemnitz.de/~jan/MPEG/HTML/IDCT.html
+	// for more info.
+
+	var
+		b1, b3, b4, b6, b7, tmp1, tmp2, m0,
+		x0, x1, x2, x3, x4, y3, y4, y5, y6, y7;
+
+	// Transform columns
+	for (var i = 0; i < 8; ++i) {
+		b1 = block[4*8+i];
+		b3 = block[2*8+i] + block[6*8+i];
+		b4 = block[5*8+i] - block[3*8+i];
+		tmp1 = block[1*8+i] + block[7*8+i];
+		tmp2 = block[3*8+i] + block[5*8+i];
+		b6 = block[1*8+i] - block[7*8+i];
+		b7 = tmp1 + tmp2;
+		m0 = block[0*8+i];
+		x4 = ((b6*473 - b4*196 + 128) >> 8) - b7;
+		x0 = x4 - (((tmp1 - tmp2)*362 + 128) >> 8);
+		x1 = m0 - b1;
+		x2 = (((block[2*8+i] - block[6*8+i])*362 + 128) >> 8) - b3;
+		x3 = m0 + b1;
+		y3 = x1 + x2;
+		y4 = x3 + b3;
+		y5 = x1 - x2;
+		y6 = x3 - b3;
+		y7 = -x0 - ((b4*473 + b6*196 + 128) >> 8);
+		block[0*8+i] = b7 + y4;
+		block[1*8+i] = x4 + y3;
+		block[2*8+i] = y5 - x0;
+		block[3*8+i] = y6 - y7;
+		block[4*8+i] = y6 + y7;
+		block[5*8+i] = x0 + y5;
+		block[6*8+i] = y3 - x4;
+		block[7*8+i] = y4 - b7;
+	}
+
+	// Transform rows
+	for (var i = 0; i < 64; i += 8) {
+		b1 = block[4+i];
+		b3 = block[2+i] + block[6+i];
+		b4 = block[5+i] - block[3+i];
+		tmp1 = block[1+i] + block[7+i];
+		tmp2 = block[3+i] + block[5+i];
+		b6 = block[1+i] - block[7+i];
+		b7 = tmp1 + tmp2;
+		m0 = block[0+i];
+		x4 = ((b6*473 - b4*196 + 128) >> 8) - b7;
+		x0 = x4 - (((tmp1 - tmp2)*362 + 128) >> 8);
+		x1 = m0 - b1;
+		x2 = (((block[2+i] - block[6+i])*362 + 128) >> 8) - b3;
+		x3 = m0 + b1;
+		y3 = x1 + x2;
+		y4 = x3 + b3;
+		y5 = x1 - x2;
+		y6 = x3 - b3;
+		y7 = -x0 - ((b4*473 + b6*196 + 128) >> 8);
+		block[0+i] = (b7 + y4 + 128) >> 8;
+		block[1+i] = (x4 + y3 + 128) >> 8;
+		block[2+i] = (y5 - x0 + 128) >> 8;
+		block[3+i] = (y6 - y7 + 128) >> 8;
+		block[4+i] = (y6 + y7 + 128) >> 8;
+		block[5+i] = (x0 + y5 + 128) >> 8;
+		block[6+i] = (y3 - x4 + 128) >> 8;
+		block[7+i] = (y4 - b7 + 128) >> 8;
+	}
+};
+
+
+// VLC Tables and Constants
+
+MPEG1.PICTURE_RATE = [
+	0.000, 23.976, 24.000, 25.000, 29.970, 30.000, 50.000, 59.940,
+	60.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
+];
+
+MPEG1.ZIG_ZAG = new Uint8Array([
+	 0,  1,  8, 16,  9,  2,  3, 10,
+	17, 24, 32, 25, 18, 11,  4,  5,
+	12, 19, 26, 33, 40, 48, 41, 34,
+	27, 20, 13,  6,  7, 14, 21, 28,
+	35, 42, 49, 56, 57, 50, 43, 36,
+	29, 22, 15, 23, 30, 37, 44, 51,
+	58, 59, 52, 45, 38, 31, 39, 46,
+	53, 60, 61, 54, 47, 55, 62, 63
+]);
+
+MPEG1.DEFAULT_INTRA_QUANT_MATRIX = new Uint8Array([
+	 8, 16, 19, 22, 26, 27, 29, 34,
+	16, 16, 22, 24, 27, 29, 34, 37,
+	19, 22, 26, 27, 29, 34, 34, 38,
+	22, 22, 26, 27, 29, 34, 37, 40,
+	22, 26, 27, 29, 32, 35, 40, 48,
+	26, 27, 29, 32, 35, 40, 48, 58,
+	26, 27, 29, 34, 38, 46, 56, 69,
+	27, 29, 35, 38, 46, 56, 69, 83
+]);
+
+MPEG1.DEFAULT_NON_INTRA_QUANT_MATRIX = new Uint8Array([
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16
+]);
+
+MPEG1.PREMULTIPLIER_MATRIX = new Uint8Array([
+	32, 44, 42, 38, 32, 25, 17,  9,
+	44, 62, 58, 52, 44, 35, 24, 12,
+	42, 58, 55, 49, 42, 33, 23, 12,
+	38, 52, 49, 44, 38, 30, 20, 10,
+	32, 44, 42, 38, 32, 25, 17,  9,
+	25, 35, 33, 30, 25, 20, 14,  7,
+	17, 24, 23, 20, 17, 14,  9,  5,
+	 9, 12, 12, 10,  9,  7,  5,  2
+]);
+
+// MPEG-1 VLC
+
+//  macroblock_stuffing decodes as 34.
+//  macroblock_escape decodes as 35.
+
+MPEG1.MACROBLOCK_ADDRESS_INCREMENT = new Int16Array([
+	 1*3,  2*3,  0, //   0
+	 3*3,  4*3,  0, //   1  0
+	   0,    0,  1, //   2  1.
+	 5*3,  6*3,  0, //   3  00
+	 7*3,  8*3,  0, //   4  01
+	 9*3, 10*3,  0, //   5  000
+	11*3, 12*3,  0, //   6  001
+	   0,    0,  3, //   7  010.
+	   0,    0,  2, //   8  011.
+	13*3, 14*3,  0, //   9  0000
+	15*3, 16*3,  0, //  10  0001
+	   0,    0,  5, //  11  0010.
+	   0,    0,  4, //  12  0011.
+	17*3, 18*3,  0, //  13  0000 0
+	19*3, 20*3,  0, //  14  0000 1
+	   0,    0,  7, //  15  0001 0.
+	   0,    0,  6, //  16  0001 1.
+	21*3, 22*3,  0, //  17  0000 00
+	23*3, 24*3,  0, //  18  0000 01
+	25*3, 26*3,  0, //  19  0000 10
+	27*3, 28*3,  0, //  20  0000 11
+	  -1, 29*3,  0, //  21  0000 000
+	  -1, 30*3,  0, //  22  0000 001
+	31*3, 32*3,  0, //  23  0000 010
+	33*3, 34*3,  0, //  24  0000 011
+	35*3, 36*3,  0, //  25  0000 100
+	37*3, 38*3,  0, //  26  0000 101
+	   0,    0,  9, //  27  0000 110.
+	   0,    0,  8, //  28  0000 111.
+	39*3, 40*3,  0, //  29  0000 0001
+	41*3, 42*3,  0, //  30  0000 0011
+	43*3, 44*3,  0, //  31  0000 0100
+	45*3, 46*3,  0, //  32  0000 0101
+	   0,    0, 15, //  33  0000 0110.
+	   0,    0, 14, //  34  0000 0111.
+	   0,    0, 13, //  35  0000 1000.
+	   0,    0, 12, //  36  0000 1001.
+	   0,    0, 11, //  37  0000 1010.
+	   0,    0, 10, //  38  0000 1011.
+	47*3,   -1,  0, //  39  0000 0001 0
+	  -1, 48*3,  0, //  40  0000 0001 1
+	49*3, 50*3,  0, //  41  0000 0011 0
+	51*3, 52*3,  0, //  42  0000 0011 1
+	53*3, 54*3,  0, //  43  0000 0100 0
+	55*3, 56*3,  0, //  44  0000 0100 1
+	57*3, 58*3,  0, //  45  0000 0101 0
+	59*3, 60*3,  0, //  46  0000 0101 1
+	61*3,   -1,  0, //  47  0000 0001 00
+	  -1, 62*3,  0, //  48  0000 0001 11
+	63*3, 64*3,  0, //  49  0000 0011 00
+	65*3, 66*3,  0, //  50  0000 0011 01
+	67*3, 68*3,  0, //  51  0000 0011 10
+	69*3, 70*3,  0, //  52  0000 0011 11
+	71*3, 72*3,  0, //  53  0000 0100 00
+	73*3, 74*3,  0, //  54  0000 0100 01
+	   0,    0, 21, //  55  0000 0100 10.
+	   0,    0, 20, //  56  0000 0100 11.
+	   0,    0, 19, //  57  0000 0101 00.
+	   0,    0, 18, //  58  0000 0101 01.
+	   0,    0, 17, //  59  0000 0101 10.
+	   0,    0, 16, //  60  0000 0101 11.
+	   0,    0, 35, //  61  0000 0001 000. -- macroblock_escape
+	   0,    0, 34, //  62  0000 0001 111. -- macroblock_stuffing
+	   0,    0, 33, //  63  0000 0011 000.
+	   0,    0, 32, //  64  0000 0011 001.
+	   0,    0, 31, //  65  0000 0011 010.
+	   0,    0, 30, //  66  0000 0011 011.
+	   0,    0, 29, //  67  0000 0011 100.
+	   0,    0, 28, //  68  0000 0011 101.
+	   0,    0, 27, //  69  0000 0011 110.
+	   0,    0, 26, //  70  0000 0011 111.
+	   0,    0, 25, //  71  0000 0100 000.
+	   0,    0, 24, //  72  0000 0100 001.
+	   0,    0, 23, //  73  0000 0100 010.
+	   0,    0, 22  //  74  0000 0100 011.
+]);
+
+//  macroblock_type bitmap:
+//    0x10  macroblock_quant
+//    0x08  macroblock_motion_forward
+//    0x04  macroblock_motion_backward
+//    0x02  macrobkock_pattern
+//    0x01  macroblock_intra
+//
+
+MPEG1.MACROBLOCK_TYPE_INTRA = new Int8Array([
+	 1*3,  2*3,     0, //   0
+	  -1,  3*3,     0, //   1  0
+	   0,    0,  0x01, //   2  1.
+	   0,    0,  0x11  //   3  01.
+]);
+
+MPEG1.MACROBLOCK_TYPE_PREDICTIVE = new Int8Array([
+	 1*3,  2*3,     0, //  0
+	 3*3,  4*3,     0, //  1  0
+	   0,    0,  0x0a, //  2  1.
+	 5*3,  6*3,     0, //  3  00
+	   0,    0,  0x02, //  4  01.
+	 7*3,  8*3,     0, //  5  000
+	   0,    0,  0x08, //  6  001.
+	 9*3, 10*3,     0, //  7  0000
+	11*3, 12*3,     0, //  8  0001
+	  -1, 13*3,     0, //  9  00000
+	   0,    0,  0x12, // 10  00001.
+	   0,    0,  0x1a, // 11  00010.
+	   0,    0,  0x01, // 12  00011.
+	   0,    0,  0x11  // 13  000001.
+]);
+
+MPEG1.MACROBLOCK_TYPE_B = new Int8Array([
+	 1*3,  2*3,     0,  //  0
+	 3*3,  5*3,     0,  //  1  0
+	 4*3,  6*3,     0,  //  2  1
+	 8*3,  7*3,     0,  //  3  00
+	   0,    0,  0x0c,  //  4  10.
+	 9*3, 10*3,     0,  //  5  01
+	   0,    0,  0x0e,  //  6  11.
+	13*3, 14*3,     0,  //  7  001
+	12*3, 11*3,     0,  //  8  000
+	   0,    0,  0x04,  //  9  010.
+	   0,    0,  0x06,  // 10  011.
+	18*3, 16*3,     0,  // 11  0001
+	15*3, 17*3,     0,  // 12  0000
+	   0,    0,  0x08,  // 13  0010.
+	   0,    0,  0x0a,  // 14  0011.
+	  -1, 19*3,     0,  // 15  00000
+	   0,    0,  0x01,  // 16  00011.
+	20*3, 21*3,     0,  // 17  00001
+	   0,    0,  0x1e,  // 18  00010.
+	   0,    0,  0x11,  // 19  000001.
+	   0,    0,  0x16,  // 20  000010.
+	   0,    0,  0x1a   // 21  000011.
+]);
+
+MPEG1.MACROBLOCK_TYPE = [
+	null,
+	MPEG1.MACROBLOCK_TYPE_INTRA,
+	MPEG1.MACROBLOCK_TYPE_PREDICTIVE,
+	MPEG1.MACROBLOCK_TYPE_B
+];
+
+MPEG1.CODE_BLOCK_PATTERN = new Int16Array([
+	  2*3,   1*3,   0,  //   0
+	  3*3,   6*3,   0,  //   1  1
+	  4*3,   5*3,   0,  //   2  0
+	  8*3,  11*3,   0,  //   3  10
+	 12*3,  13*3,   0,  //   4  00
+	  9*3,   7*3,   0,  //   5  01
+	 10*3,  14*3,   0,  //   6  11
+	 20*3,  19*3,   0,  //   7  011
+	 18*3,  16*3,   0,  //   8  100
+	 23*3,  17*3,   0,  //   9  010
+	 27*3,  25*3,   0,  //  10  110
+	 21*3,  28*3,   0,  //  11  101
+	 15*3,  22*3,   0,  //  12  000
+	 24*3,  26*3,   0,  //  13  001
+	    0,     0,  60,  //  14  111.
+	 35*3,  40*3,   0,  //  15  0000
+	 44*3,  48*3,   0,  //  16  1001
+	 38*3,  36*3,   0,  //  17  0101
+	 42*3,  47*3,   0,  //  18  1000
+	 29*3,  31*3,   0,  //  19  0111
+	 39*3,  32*3,   0,  //  20  0110
+	    0,     0,  32,  //  21  1010.
+	 45*3,  46*3,   0,  //  22  0001
+	 33*3,  41*3,   0,  //  23  0100
+	 43*3,  34*3,   0,  //  24  0010
+	    0,     0,   4,  //  25  1101.
+	 30*3,  37*3,   0,  //  26  0011
+	    0,     0,   8,  //  27  1100.
+	    0,     0,  16,  //  28  1011.
+	    0,     0,  44,  //  29  0111 0.
+	 50*3,  56*3,   0,  //  30  0011 0
+	    0,     0,  28,  //  31  0111 1.
+	    0,     0,  52,  //  32  0110 1.
+	    0,     0,  62,  //  33  0100 0.
+	 61*3,  59*3,   0,  //  34  0010 1
+	 52*3,  60*3,   0,  //  35  0000 0
+	    0,     0,   1,  //  36  0101 1.
+	 55*3,  54*3,   0,  //  37  0011 1
+	    0,     0,  61,  //  38  0101 0.
+	    0,     0,  56,  //  39  0110 0.
+	 57*3,  58*3,   0,  //  40  0000 1
+	    0,     0,   2,  //  41  0100 1.
+	    0,     0,  40,  //  42  1000 0.
+	 51*3,  62*3,   0,  //  43  0010 0
+	    0,     0,  48,  //  44  1001 0.
+	 64*3,  63*3,   0,  //  45  0001 0
+	 49*3,  53*3,   0,  //  46  0001 1
+	    0,     0,  20,  //  47  1000 1.
+	    0,     0,  12,  //  48  1001 1.
+	 80*3,  83*3,   0,  //  49  0001 10
+	    0,     0,  63,  //  50  0011 00.
+	 77*3,  75*3,   0,  //  51  0010 00
+	 65*3,  73*3,   0,  //  52  0000 00
+	 84*3,  66*3,   0,  //  53  0001 11
+	    0,     0,  24,  //  54  0011 11.
+	    0,     0,  36,  //  55  0011 10.
+	    0,     0,   3,  //  56  0011 01.
+	 69*3,  87*3,   0,  //  57  0000 10
+	 81*3,  79*3,   0,  //  58  0000 11
+	 68*3,  71*3,   0,  //  59  0010 11
+	 70*3,  78*3,   0,  //  60  0000 01
+	 67*3,  76*3,   0,  //  61  0010 10
+	 72*3,  74*3,   0,  //  62  0010 01
+	 86*3,  85*3,   0,  //  63  0001 01
+	 88*3,  82*3,   0,  //  64  0001 00
+	   -1,  94*3,   0,  //  65  0000 000
+	 95*3,  97*3,   0,  //  66  0001 111
+	    0,     0,  33,  //  67  0010 100.
+	    0,     0,   9,  //  68  0010 110.
+	106*3, 110*3,   0,  //  69  0000 100
+	102*3, 116*3,   0,  //  70  0000 010
+	    0,     0,   5,  //  71  0010 111.
+	    0,     0,  10,  //  72  0010 010.
+	 93*3,  89*3,   0,  //  73  0000 001
+	    0,     0,   6,  //  74  0010 011.
+	    0,     0,  18,  //  75  0010 001.
+	    0,     0,  17,  //  76  0010 101.
+	    0,     0,  34,  //  77  0010 000.
+	113*3, 119*3,   0,  //  78  0000 011
+	103*3, 104*3,   0,  //  79  0000 111
+	 90*3,  92*3,   0,  //  80  0001 100
+	109*3, 107*3,   0,  //  81  0000 110
+	117*3, 118*3,   0,  //  82  0001 001
+	101*3,  99*3,   0,  //  83  0001 101
+	 98*3,  96*3,   0,  //  84  0001 110
+	100*3,  91*3,   0,  //  85  0001 011
+	114*3, 115*3,   0,  //  86  0001 010
+	105*3, 108*3,   0,  //  87  0000 101
+	112*3, 111*3,   0,  //  88  0001 000
+	121*3, 125*3,   0,  //  89  0000 0011
+	    0,     0,  41,  //  90  0001 1000.
+	    0,     0,  14,  //  91  0001 0111.
+	    0,     0,  21,  //  92  0001 1001.
+	124*3, 122*3,   0,  //  93  0000 0010
+	120*3, 123*3,   0,  //  94  0000 0001
+	    0,     0,  11,  //  95  0001 1110.
+	    0,     0,  19,  //  96  0001 1101.
+	    0,     0,   7,  //  97  0001 1111.
+	    0,     0,  35,  //  98  0001 1100.
+	    0,     0,  13,  //  99  0001 1011.
+	    0,     0,  50,  // 100  0001 0110.
+	    0,     0,  49,  // 101  0001 1010.
+	    0,     0,  58,  // 102  0000 0100.
+	    0,     0,  37,  // 103  0000 1110.
+	    0,     0,  25,  // 104  0000 1111.
+	    0,     0,  45,  // 105  0000 1010.
+	    0,     0,  57,  // 106  0000 1000.
+	    0,     0,  26,  // 107  0000 1101.
+	    0,     0,  29,  // 108  0000 1011.
+	    0,     0,  38,  // 109  0000 1100.
+	    0,     0,  53,  // 110  0000 1001.
+	    0,     0,  23,  // 111  0001 0001.
+	    0,     0,  43,  // 112  0001 0000.
+	    0,     0,  46,  // 113  0000 0110.
+	    0,     0,  42,  // 114  0001 0100.
+	    0,     0,  22,  // 115  0001 0101.
+	    0,     0,  54,  // 116  0000 0101.
+	    0,     0,  51,  // 117  0001 0010.
+	    0,     0,  15,  // 118  0001 0011.
+	    0,     0,  30,  // 119  0000 0111.
+	    0,     0,  39,  // 120  0000 0001 0.
+	    0,     0,  47,  // 121  0000 0011 0.
+	    0,     0,  55,  // 122  0000 0010 1.
+	    0,     0,  27,  // 123  0000 0001 1.
+	    0,     0,  59,  // 124  0000 0010 0.
+	    0,     0,  31   // 125  0000 0011 1.
+]);
+
+MPEG1.MOTION = new Int16Array([
+	  1*3,   2*3,   0,  //   0
+	  4*3,   3*3,   0,  //   1  0
+	    0,     0,   0,  //   2  1.
+	  6*3,   5*3,   0,  //   3  01
+	  8*3,   7*3,   0,  //   4  00
+	    0,     0,  -1,  //   5  011.
+	    0,     0,   1,  //   6  010.
+	  9*3,  10*3,   0,  //   7  001
+	 12*3,  11*3,   0,  //   8  000
+	    0,     0,   2,  //   9  0010.
+	    0,     0,  -2,  //  10  0011.
+	 14*3,  15*3,   0,  //  11  0001
+	 16*3,  13*3,   0,  //  12  0000
+	 20*3,  18*3,   0,  //  13  0000 1
+	    0,     0,   3,  //  14  0001 0.
+	    0,     0,  -3,  //  15  0001 1.
+	 17*3,  19*3,   0,  //  16  0000 0
+	   -1,  23*3,   0,  //  17  0000 00
+	 27*3,  25*3,   0,  //  18  0000 11
+	 26*3,  21*3,   0,  //  19  0000 01
+	 24*3,  22*3,   0,  //  20  0000 10
+	 32*3,  28*3,   0,  //  21  0000 011
+	 29*3,  31*3,   0,  //  22  0000 101
+	   -1,  33*3,   0,  //  23  0000 001
+	 36*3,  35*3,   0,  //  24  0000 100
+	    0,     0,  -4,  //  25  0000 111.
+	 30*3,  34*3,   0,  //  26  0000 010
+	    0,     0,   4,  //  27  0000 110.
+	    0,     0,  -7,  //  28  0000 0111.
+	    0,     0,   5,  //  29  0000 1010.
+	 37*3,  41*3,   0,  //  30  0000 0100
+	    0,     0,  -5,  //  31  0000 1011.
+	    0,     0,   7,  //  32  0000 0110.
+	 38*3,  40*3,   0,  //  33  0000 0011
+	 42*3,  39*3,   0,  //  34  0000 0101
+	    0,     0,  -6,  //  35  0000 1001.
+	    0,     0,   6,  //  36  0000 1000.
+	 51*3,  54*3,   0,  //  37  0000 0100 0
+	 50*3,  49*3,   0,  //  38  0000 0011 0
+	 45*3,  46*3,   0,  //  39  0000 0101 1
+	 52*3,  47*3,   0,  //  40  0000 0011 1
+	 43*3,  53*3,   0,  //  41  0000 0100 1
+	 44*3,  48*3,   0,  //  42  0000 0101 0
+	    0,     0,  10,  //  43  0000 0100 10.
+	    0,     0,   9,  //  44  0000 0101 00.
+	    0,     0,   8,  //  45  0000 0101 10.
+	    0,     0,  -8,  //  46  0000 0101 11.
+	 57*3,  66*3,   0,  //  47  0000 0011 11
+	    0,     0,  -9,  //  48  0000 0101 01.
+	 60*3,  64*3,   0,  //  49  0000 0011 01
+	 56*3,  61*3,   0,  //  50  0000 0011 00
+	 55*3,  62*3,   0,  //  51  0000 0100 00
+	 58*3,  63*3,   0,  //  52  0000 0011 10
+	    0,     0, -10,  //  53  0000 0100 11.
+	 59*3,  65*3,   0,  //  54  0000 0100 01
+	    0,     0,  12,  //  55  0000 0100 000.
+	    0,     0,  16,  //  56  0000 0011 000.
+	    0,     0,  13,  //  57  0000 0011 110.
+	    0,     0,  14,  //  58  0000 0011 100.
+	    0,     0,  11,  //  59  0000 0100 010.
+	    0,     0,  15,  //  60  0000 0011 010.
+	    0,     0, -16,  //  61  0000 0011 001.
+	    0,     0, -12,  //  62  0000 0100 001.
+	    0,     0, -14,  //  63  0000 0011 101.
+	    0,     0, -15,  //  64  0000 0011 011.
+	    0,     0, -11,  //  65  0000 0100 011.
+	    0,     0, -13   //  66  0000 0011 111.
+]);
+
+MPEG1.DCT_DC_SIZE_LUMINANCE = new Int8Array([
+	  2*3,   1*3, 0,  //   0
+	  6*3,   5*3, 0,  //   1  1
+	  3*3,   4*3, 0,  //   2  0
+	    0,     0, 1,  //   3  00.
+	    0,     0, 2,  //   4  01.
+	  9*3,   8*3, 0,  //   5  11
+	  7*3,  10*3, 0,  //   6  10
+	    0,     0, 0,  //   7  100.
+	 12*3,  11*3, 0,  //   8  111
+	    0,     0, 4,  //   9  110.
+	    0,     0, 3,  //  10  101.
+	 13*3,  14*3, 0,  //  11  1111
+	    0,     0, 5,  //  12  1110.
+	    0,     0, 6,  //  13  1111 0.
+	 16*3,  15*3, 0,  //  14  1111 1
+	 17*3,    -1, 0,  //  15  1111 11
+	    0,     0, 7,  //  16  1111 10.
+	    0,     0, 8   //  17  1111 110.
+]);
+
+MPEG1.DCT_DC_SIZE_CHROMINANCE = new Int8Array([
+	  2*3,   1*3, 0,  //   0
+	  4*3,   3*3, 0,  //   1  1
+	  6*3,   5*3, 0,  //   2  0
+	  8*3,   7*3, 0,  //   3  11
+	    0,     0, 2,  //   4  10.
+	    0,     0, 1,  //   5  01.
+	    0,     0, 0,  //   6  00.
+	 10*3,   9*3, 0,  //   7  111
+	    0,     0, 3,  //   8  110.
+	 12*3,  11*3, 0,  //   9  1111
+	    0,     0, 4,  //  10  1110.
+	 14*3,  13*3, 0,  //  11  1111 1
+	    0,     0, 5,  //  12  1111 0.
+	 16*3,  15*3, 0,  //  13  1111 11
+	    0,     0, 6,  //  14  1111 10.
+	 17*3,    -1, 0,  //  15  1111 111
+	    0,     0, 7,  //  16  1111 110.
+	    0,     0, 8   //  17  1111 1110.
+]);
+
+//  dct_coeff bitmap:
+//    0xff00  run
+//    0x00ff  level
+
+//  Decoded values are unsigned. Sign bit follows in the stream.
+
+//  Interpretation of the value 0x0001
+//    for dc_coeff_first:  run=0, level=1
+//    for dc_coeff_next:   If the next bit is 1: run=0, level=1
+//                         If the next bit is 0: end_of_block
+
+//  escape decodes as 0xffff.
+
+MPEG1.DCT_COEFF = new Int32Array([
+	  1*3,   2*3,      0,  //   0
+	  4*3,   3*3,      0,  //   1  0
+	    0,     0, 0x0001,  //   2  1.
+	  7*3,   8*3,      0,  //   3  01
+	  6*3,   5*3,      0,  //   4  00
+	 13*3,   9*3,      0,  //   5  001
+	 11*3,  10*3,      0,  //   6  000
+	 14*3,  12*3,      0,  //   7  010
+	    0,     0, 0x0101,  //   8  011.
+	 20*3,  22*3,      0,  //   9  0011
+	 18*3,  21*3,      0,  //  10  0001
+	 16*3,  19*3,      0,  //  11  0000
+	    0,     0, 0x0201,  //  12  0101.
+	 17*3,  15*3,      0,  //  13  0010
+	    0,     0, 0x0002,  //  14  0100.
+	    0,     0, 0x0003,  //  15  0010 1.
+	 27*3,  25*3,      0,  //  16  0000 0
+	 29*3,  31*3,      0,  //  17  0010 0
+	 24*3,  26*3,      0,  //  18  0001 0
+	 32*3,  30*3,      0,  //  19  0000 1
+	    0,     0, 0x0401,  //  20  0011 0.
+	 23*3,  28*3,      0,  //  21  0001 1
+	    0,     0, 0x0301,  //  22  0011 1.
+	    0,     0, 0x0102,  //  23  0001 10.
+	    0,     0, 0x0701,  //  24  0001 00.
+	    0,     0, 0xffff,  //  25  0000 01. -- escape
+	    0,     0, 0x0601,  //  26  0001 01.
+	 37*3,  36*3,      0,  //  27  0000 00
+	    0,     0, 0x0501,  //  28  0001 11.
+	 35*3,  34*3,      0,  //  29  0010 00
+	 39*3,  38*3,      0,  //  30  0000 11
+	 33*3,  42*3,      0,  //  31  0010 01
+	 40*3,  41*3,      0,  //  32  0000 10
+	 52*3,  50*3,      0,  //  33  0010 010
+	 54*3,  53*3,      0,  //  34  0010 001
+	 48*3,  49*3,      0,  //  35  0010 000
+	 43*3,  45*3,      0,  //  36  0000 001
+	 46*3,  44*3,      0,  //  37  0000 000
+	    0,     0, 0x0801,  //  38  0000 111.
+	    0,     0, 0x0004,  //  39  0000 110.
+	    0,     0, 0x0202,  //  40  0000 100.
+	    0,     0, 0x0901,  //  41  0000 101.
+	 51*3,  47*3,      0,  //  42  0010 011
+	 55*3,  57*3,      0,  //  43  0000 0010
+	 60*3,  56*3,      0,  //  44  0000 0001
+	 59*3,  58*3,      0,  //  45  0000 0011
+	 61*3,  62*3,      0,  //  46  0000 0000
+	    0,     0, 0x0a01,  //  47  0010 0111.
+	    0,     0, 0x0d01,  //  48  0010 0000.
+	    0,     0, 0x0006,  //  49  0010 0001.
+	    0,     0, 0x0103,  //  50  0010 0101.
+	    0,     0, 0x0005,  //  51  0010 0110.
+	    0,     0, 0x0302,  //  52  0010 0100.
+	    0,     0, 0x0b01,  //  53  0010 0011.
+	    0,     0, 0x0c01,  //  54  0010 0010.
+	 76*3,  75*3,      0,  //  55  0000 0010 0
+	 67*3,  70*3,      0,  //  56  0000 0001 1
+	 73*3,  71*3,      0,  //  57  0000 0010 1
+	 78*3,  74*3,      0,  //  58  0000 0011 1
+	 72*3,  77*3,      0,  //  59  0000 0011 0
+	 69*3,  64*3,      0,  //  60  0000 0001 0
+	 68*3,  63*3,      0,  //  61  0000 0000 0
+	 66*3,  65*3,      0,  //  62  0000 0000 1
+	 81*3,  87*3,      0,  //  63  0000 0000 01
+	 91*3,  80*3,      0,  //  64  0000 0001 01
+	 82*3,  79*3,      0,  //  65  0000 0000 11
+	 83*3,  86*3,      0,  //  66  0000 0000 10
+	 93*3,  92*3,      0,  //  67  0000 0001 10
+	 84*3,  85*3,      0,  //  68  0000 0000 00
+	 90*3,  94*3,      0,  //  69  0000 0001 00
+	 88*3,  89*3,      0,  //  70  0000 0001 11
+	    0,     0, 0x0203,  //  71  0000 0010 11.
+	    0,     0, 0x0104,  //  72  0000 0011 00.
+	    0,     0, 0x0007,  //  73  0000 0010 10.
+	    0,     0, 0x0402,  //  74  0000 0011 11.
+	    0,     0, 0x0502,  //  75  0000 0010 01.
+	    0,     0, 0x1001,  //  76  0000 0010 00.
+	    0,     0, 0x0f01,  //  77  0000 0011 01.
+	    0,     0, 0x0e01,  //  78  0000 0011 10.
+	105*3, 107*3,      0,  //  79  0000 0000 111
+	111*3, 114*3,      0,  //  80  0000 0001 011
+	104*3,  97*3,      0,  //  81  0000 0000 010
+	125*3, 119*3,      0,  //  82  0000 0000 110
+	 96*3,  98*3,      0,  //  83  0000 0000 100
+	   -1, 123*3,      0,  //  84  0000 0000 000
+	 95*3, 101*3,      0,  //  85  0000 0000 001
+	106*3, 121*3,      0,  //  86  0000 0000 101
+	 99*3, 102*3,      0,  //  87  0000 0000 011
+	113*3, 103*3,      0,  //  88  0000 0001 110
+	112*3, 116*3,      0,  //  89  0000 0001 111
+	110*3, 100*3,      0,  //  90  0000 0001 000
+	124*3, 115*3,      0,  //  91  0000 0001 010
+	117*3, 122*3,      0,  //  92  0000 0001 101
+	109*3, 118*3,      0,  //  93  0000 0001 100
+	120*3, 108*3,      0,  //  94  0000 0001 001
+	127*3, 136*3,      0,  //  95  0000 0000 0010
+	139*3, 140*3,      0,  //  96  0000 0000 1000
+	130*3, 126*3,      0,  //  97  0000 0000 0101
+	145*3, 146*3,      0,  //  98  0000 0000 1001
+	128*3, 129*3,      0,  //  99  0000 0000 0110
+	    0,     0, 0x0802,  // 100  0000 0001 0001.
+	132*3, 134*3,      0,  // 101  0000 0000 0011
+	155*3, 154*3,      0,  // 102  0000 0000 0111
+	    0,     0, 0x0008,  // 103  0000 0001 1101.
+	137*3, 133*3,      0,  // 104  0000 0000 0100
+	143*3, 144*3,      0,  // 105  0000 0000 1110
+	151*3, 138*3,      0,  // 106  0000 0000 1010
+	142*3, 141*3,      0,  // 107  0000 0000 1111
+	    0,     0, 0x000a,  // 108  0000 0001 0011.
+	    0,     0, 0x0009,  // 109  0000 0001 1000.
+	    0,     0, 0x000b,  // 110  0000 0001 0000.
+	    0,     0, 0x1501,  // 111  0000 0001 0110.
+	    0,     0, 0x0602,  // 112  0000 0001 1110.
+	    0,     0, 0x0303,  // 113  0000 0001 1100.
+	    0,     0, 0x1401,  // 114  0000 0001 0111.
+	    0,     0, 0x0702,  // 115  0000 0001 0101.
+	    0,     0, 0x1101,  // 116  0000 0001 1111.
+	    0,     0, 0x1201,  // 117  0000 0001 1010.
+	    0,     0, 0x1301,  // 118  0000 0001 1001.
+	148*3, 152*3,      0,  // 119  0000 0000 1101
+	    0,     0, 0x0403,  // 120  0000 0001 0010.
+	153*3, 150*3,      0,  // 121  0000 0000 1011
+	    0,     0, 0x0105,  // 122  0000 0001 1011.
+	131*3, 135*3,      0,  // 123  0000 0000 0001
+	    0,     0, 0x0204,  // 124  0000 0001 0100.
+	149*3, 147*3,      0,  // 125  0000 0000 1100
+	172*3, 173*3,      0,  // 126  0000 0000 0101 1
+	162*3, 158*3,      0,  // 127  0000 0000 0010 0
+	170*3, 161*3,      0,  // 128  0000 0000 0110 0
+	168*3, 166*3,      0,  // 129  0000 0000 0110 1
+	157*3, 179*3,      0,  // 130  0000 0000 0101 0
+	169*3, 167*3,      0,  // 131  0000 0000 0001 0
+	174*3, 171*3,      0,  // 132  0000 0000 0011 0
+	178*3, 177*3,      0,  // 133  0000 0000 0100 1
+	156*3, 159*3,      0,  // 134  0000 0000 0011 1
+	164*3, 165*3,      0,  // 135  0000 0000 0001 1
+	183*3, 182*3,      0,  // 136  0000 0000 0010 1
+	175*3, 176*3,      0,  // 137  0000 0000 0100 0
+	    0,     0, 0x0107,  // 138  0000 0000 1010 1.
+	    0,     0, 0x0a02,  // 139  0000 0000 1000 0.
+	    0,     0, 0x0902,  // 140  0000 0000 1000 1.
+	    0,     0, 0x1601,  // 141  0000 0000 1111 1.
+	    0,     0, 0x1701,  // 142  0000 0000 1111 0.
+	    0,     0, 0x1901,  // 143  0000 0000 1110 0.
+	    0,     0, 0x1801,  // 144  0000 0000 1110 1.
+	    0,     0, 0x0503,  // 145  0000 0000 1001 0.
+	    0,     0, 0x0304,  // 146  0000 0000 1001 1.
+	    0,     0, 0x000d,  // 147  0000 0000 1100 1.
+	    0,     0, 0x000c,  // 148  0000 0000 1101 0.
+	    0,     0, 0x000e,  // 149  0000 0000 1100 0.
+	    0,     0, 0x000f,  // 150  0000 0000 1011 1.
+	    0,     0, 0x0205,  // 151  0000 0000 1010 0.
+	    0,     0, 0x1a01,  // 152  0000 0000 1101 1.
+	    0,     0, 0x0106,  // 153  0000 0000 1011 0.
+	180*3, 181*3,      0,  // 154  0000 0000 0111 1
+	160*3, 163*3,      0,  // 155  0000 0000 0111 0
+	196*3, 199*3,      0,  // 156  0000 0000 0011 10
+	    0,     0, 0x001b,  // 157  0000 0000 0101 00.
+	203*3, 185*3,      0,  // 158  0000 0000 0010 01
+	202*3, 201*3,      0,  // 159  0000 0000 0011 11
+	    0,     0, 0x0013,  // 160  0000 0000 0111 00.
+	    0,     0, 0x0016,  // 161  0000 0000 0110 01.
+	197*3, 207*3,      0,  // 162  0000 0000 0010 00
+	    0,     0, 0x0012,  // 163  0000 0000 0111 01.
+	191*3, 192*3,      0,  // 164  0000 0000 0001 10
+	188*3, 190*3,      0,  // 165  0000 0000 0001 11
+	    0,     0, 0x0014,  // 166  0000 0000 0110 11.
+	184*3, 194*3,      0,  // 167  0000 0000 0001 01
+	    0,     0, 0x0015,  // 168  0000 0000 0110 10.
+	186*3, 193*3,      0,  // 169  0000 0000 0001 00
+	    0,     0, 0x0017,  // 170  0000 0000 0110 00.
+	204*3, 198*3,      0,  // 171  0000 0000 0011 01
+	    0,     0, 0x0019,  // 172  0000 0000 0101 10.
+	    0,     0, 0x0018,  // 173  0000 0000 0101 11.
+	200*3, 205*3,      0,  // 174  0000 0000 0011 00
+	    0,     0, 0x001f,  // 175  0000 0000 0100 00.
+	    0,     0, 0x001e,  // 176  0000 0000 0100 01.
+	    0,     0, 0x001c,  // 177  0000 0000 0100 11.
+	    0,     0, 0x001d,  // 178  0000 0000 0100 10.
+	    0,     0, 0x001a,  // 179  0000 0000 0101 01.
+	    0,     0, 0x0011,  // 180  0000 0000 0111 10.
+	    0,     0, 0x0010,  // 181  0000 0000 0111 11.
+	189*3, 206*3,      0,  // 182  0000 0000 0010 11
+	187*3, 195*3,      0,  // 183  0000 0000 0010 10
+	218*3, 211*3,      0,  // 184  0000 0000 0001 010
+	    0,     0, 0x0025,  // 185  0000 0000 0010 011.
+	215*3, 216*3,      0,  // 186  0000 0000 0001 000
+	    0,     0, 0x0024,  // 187  0000 0000 0010 100.
+	210*3, 212*3,      0,  // 188  0000 0000 0001 110
+	    0,     0, 0x0022,  // 189  0000 0000 0010 110.
+	213*3, 209*3,      0,  // 190  0000 0000 0001 111
+	221*3, 222*3,      0,  // 191  0000 0000 0001 100
+	219*3, 208*3,      0,  // 192  0000 0000 0001 101
+	217*3, 214*3,      0,  // 193  0000 0000 0001 001
+	223*3, 220*3,      0,  // 194  0000 0000 0001 011
+	    0,     0, 0x0023,  // 195  0000 0000 0010 101.
+	    0,     0, 0x010b,  // 196  0000 0000 0011 100.
+	    0,     0, 0x0028,  // 197  0000 0000 0010 000.
+	    0,     0, 0x010c,  // 198  0000 0000 0011 011.
+	    0,     0, 0x010a,  // 199  0000 0000 0011 101.
+	    0,     0, 0x0020,  // 200  0000 0000 0011 000.
+	    0,     0, 0x0108,  // 201  0000 0000 0011 111.
+	    0,     0, 0x0109,  // 202  0000 0000 0011 110.
+	    0,     0, 0x0026,  // 203  0000 0000 0010 010.
+	    0,     0, 0x010d,  // 204  0000 0000 0011 010.
+	    0,     0, 0x010e,  // 205  0000 0000 0011 001.
+	    0,     0, 0x0021,  // 206  0000 0000 0010 111.
+	    0,     0, 0x0027,  // 207  0000 0000 0010 001.
+	    0,     0, 0x1f01,  // 208  0000 0000 0001 1011.
+	    0,     0, 0x1b01,  // 209  0000 0000 0001 1111.
+	    0,     0, 0x1e01,  // 210  0000 0000 0001 1100.
+	    0,     0, 0x1002,  // 211  0000 0000 0001 0101.
+	    0,     0, 0x1d01,  // 212  0000 0000 0001 1101.
+	    0,     0, 0x1c01,  // 213  0000 0000 0001 1110.
+	    0,     0, 0x010f,  // 214  0000 0000 0001 0011.
+	    0,     0, 0x0112,  // 215  0000 0000 0001 0000.
+	    0,     0, 0x0111,  // 216  0000 0000 0001 0001.
+	    0,     0, 0x0110,  // 217  0000 0000 0001 0010.
+	    0,     0, 0x0603,  // 218  0000 0000 0001 0100.
+	    0,     0, 0x0b02,  // 219  0000 0000 0001 1010.
+	    0,     0, 0x0e02,  // 220  0000 0000 0001 0111.
+	    0,     0, 0x0d02,  // 221  0000 0000 0001 1000.
+	    0,     0, 0x0c02,  // 222  0000 0000 0001 1001.
+	    0,     0, 0x0f02   // 223  0000 0000 0001 0110.
+]);
+
+MPEG1.PICTURE_TYPE = {
+	INTRA: 1,
+	PREDICTIVE: 2,
+	B: 3
+};
+
+MPEG1.START = {
+	SEQUENCE: 0xB3,
+	SLICE_FIRST: 0x01,
+	SLICE_LAST: 0xAF,
+	PICTURE: 0x00,
+	EXTENSION: 0xB5,
+	USER_DATA: 0xB2
+};
+
+return MPEG1;
+
+})();
+

+ 324 - 0
jsmpeg-master/src/player.js

@@ -0,0 +1,324 @@
+JSMpeg.Player = (function(){ "use strict";
+
+var Player = function(url, options) {
+	this.options = options || {};
+
+	if (options.source) {
+		this.source = new options.source(url, options);
+		options.streaming = !!this.source.streaming;
+	}
+	else if (url.match(/^wss?:\/\//)) {
+		this.source = new JSMpeg.Source.WebSocket(url, options);
+		options.streaming = true;
+	}
+	else if (options.progressive !== false) {
+		this.source = new JSMpeg.Source.AjaxProgressive(url, options);
+		options.streaming = false;
+	}
+	else {
+		this.source = new JSMpeg.Source.Ajax(url, options);
+		options.streaming = false;
+	}
+
+	this.maxAudioLag = options.maxAudioLag || 0.25;
+	this.loop = options.loop !== false;
+	this.autoplay = !!options.autoplay || options.streaming;
+
+	this.demuxer = new JSMpeg.Demuxer.TS(options);
+	this.source.connect(this.demuxer);
+
+	if (!options.disableWebAssembly && JSMpeg.WASMModule.IsSupported()) {
+		this.wasmModule = JSMpeg.WASMModule.GetModule();
+		options.wasmModule = this.wasmModule;
+	}
+
+	if (options.video !== false) {
+		this.video = options.wasmModule
+			? new JSMpeg.Decoder.MPEG1VideoWASM(options)
+			: new JSMpeg.Decoder.MPEG1Video(options);
+
+		this.renderer = !options.disableGl && JSMpeg.Renderer.WebGL.IsSupported()
+			? new JSMpeg.Renderer.WebGL(options)
+			: new JSMpeg.Renderer.Canvas2D(options);
+		
+		this.demuxer.connect(JSMpeg.Demuxer.TS.STREAM.VIDEO_1, this.video);
+		this.video.connect(this.renderer);
+	}
+
+	if (options.audio !== false && JSMpeg.AudioOutput.WebAudio.IsSupported()) {
+		this.audio = options.wasmModule
+			? new JSMpeg.Decoder.MP2AudioWASM(options)
+			: new JSMpeg.Decoder.MP2Audio(options);
+		this.audioOut = new JSMpeg.AudioOutput.WebAudio(options);
+		this.demuxer.connect(JSMpeg.Demuxer.TS.STREAM.AUDIO_1, this.audio);
+		this.audio.connect(this.audioOut);
+	}
+
+	Object.defineProperty(this, 'currentTime', {
+		get: this.getCurrentTime,
+		set: this.setCurrentTime
+	});
+	Object.defineProperty(this, 'volume', {
+		get: this.getVolume,
+		set: this.setVolume
+	});
+
+	this.paused = true;
+	this.unpauseOnShow = false;
+	if (options.pauseWhenHidden !== false) {
+		document.addEventListener('visibilitychange', this.showHide.bind(this));
+	}
+
+	// If we have WebAssembly support, wait until the module is compiled before
+	// loading the source. Otherwise the decoders won't know what to do with 
+	// the source data.
+	if (this.wasmModule) {
+		if (this.wasmModule.ready) {
+			this.startLoading();
+		}
+		else if (JSMpeg.WASM_BINARY_INLINED) {
+			var wasm = JSMpeg.Base64ToArrayBuffer(JSMpeg.WASM_BINARY_INLINED);
+			this.wasmModule.loadFromBuffer(wasm, this.startLoading.bind(this));
+		}
+		else {
+			this.wasmModule.loadFromFile('jsmpeg.wasm',  this.startLoading.bind(this));
+		}
+	}
+	else {
+		this.startLoading();
+		
+	}
+};
+
+Player.prototype.startLoading = function() {
+	this.source.start();
+	if (this.autoplay) {
+		this.play();
+	}
+};
+
+Player.prototype.showHide = function(ev) {
+	if (document.visibilityState === 'hidden') {
+		this.unpauseOnShow = this.wantsToPlay;
+		this.pause();
+	}
+	else if (this.unpauseOnShow) {
+		this.play();
+	}
+};
+
+Player.prototype.play = function(ev) {
+	if (this.animationId) {
+		return;
+	}
+
+	this.animationId = requestAnimationFrame(this.update.bind(this));
+	this.wantsToPlay = true;
+	this.paused = false;
+};
+
+Player.prototype.pause = function(ev) {
+	if (this.paused) {
+		return;
+	}
+
+	cancelAnimationFrame(this.animationId);
+	this.animationId = null;
+	this.wantsToPlay = false;
+	this.isPlaying = false;
+	this.paused = true;
+
+	if (this.audio && this.audio.canPlay) {
+		// Seek to the currentTime again - audio may already be enqueued a bit
+		// further, so we have to rewind it.
+		this.audioOut.stop();
+		this.seek(this.currentTime);
+	}
+
+	if (this.options.onPause) {
+		this.options.onPause(this);
+	}
+};
+
+Player.prototype.getVolume = function() {
+	return this.audioOut ? this.audioOut.volume : 0;
+};
+
+Player.prototype.setVolume = function(volume) {
+	if (this.audioOut) {
+		this.audioOut.volume = volume;
+	}
+};
+
+Player.prototype.stop = function(ev) {
+	this.pause();
+	this.seek(0);
+	if (this.video && this.options.decodeFirstFrame !== false) {
+		this.video.decode();
+	}
+};
+
+Player.prototype.destroy = function() {
+	this.pause();
+	this.source.destroy();
+	this.video && this.video.destroy();
+	this.renderer && this.renderer.destroy();
+	this.audio && this.audio.destroy();
+	this.audioOut && this.audioOut.destroy();
+};
+
+Player.prototype.seek = function(time) {
+	var startOffset = this.audio && this.audio.canPlay
+		? this.audio.startTime
+		: this.video.startTime;
+
+	if (this.video) {
+		this.video.seek(time + startOffset);
+	}
+	if (this.audio) {
+		this.audio.seek(time + startOffset);
+	}
+
+	this.startTime = JSMpeg.Now() - time;
+};
+
+Player.prototype.getCurrentTime = function() {
+	return this.audio && this.audio.canPlay
+		? this.audio.currentTime - this.audio.startTime
+		: this.video.currentTime - this.video.startTime;
+};
+
+Player.prototype.setCurrentTime = function(time) {
+	this.seek(time);
+};
+
+Player.prototype.update = function() {
+	this.animationId = requestAnimationFrame(this.update.bind(this));
+
+	if (!this.source.established) {
+		if (this.renderer) {
+			this.renderer.renderProgress(this.source.progress);
+		}
+		return;
+	}
+
+	if (!this.isPlaying) {
+		this.isPlaying = true;
+		this.startTime = JSMpeg.Now() - this.currentTime;
+
+		if (this.options.onPlay) {
+			this.options.onPlay(this);
+		}
+	}
+
+	if (this.options.streaming) {
+		this.updateForStreaming();
+	}
+	else {
+		this.updateForStaticFile();
+	}
+};
+
+Player.prototype.updateForStreaming = function() {
+	// When streaming, immediately decode everything we have buffered up until
+	// now to minimize playback latency.
+
+	if (this.video) {
+		this.video.decode();
+	}
+
+	if (this.audio) {
+		var decoded = false;
+		do {
+			// If there's a lot of audio enqueued already, disable output and
+			// catch up with the encoding.
+			if (this.audioOut.enqueuedTime > this.maxAudioLag) {
+				this.audioOut.resetEnqueuedTime();
+				this.audioOut.enabled = false;
+			}
+			decoded = this.audio.decode();		
+		} while (decoded);
+		this.audioOut.enabled = true;
+	}
+};
+
+Player.prototype.nextFrame = function() {
+	if (this.source.established && this.video) {
+		return this.video.decode();
+	}
+	return false;
+};
+
+Player.prototype.updateForStaticFile = function() {
+	var notEnoughData = false,
+		headroom = 0;
+
+	// If we have an audio track, we always try to sync the video to the audio.
+	// Gaps and discontinuities are far more percetable in audio than in video.
+
+	if (this.audio && this.audio.canPlay) {
+		// Do we have to decode and enqueue some more audio data?
+		while (
+			!notEnoughData && 
+			this.audio.decodedTime - this.audio.currentTime < 0.25
+		) {
+			notEnoughData = !this.audio.decode();
+		}
+
+		// Sync video to audio
+		if (this.video && this.video.currentTime < this.audio.currentTime) {
+			notEnoughData = !this.video.decode();
+		}
+
+		headroom = this.demuxer.currentTime - this.audio.currentTime;
+	}
+
+
+	else if (this.video) {
+		// Video only - sync it to player's wallclock
+		var targetTime = (JSMpeg.Now() - this.startTime) + this.video.startTime,
+			lateTime = targetTime - this.video.currentTime,
+			frameTime = 1/this.video.frameRate;
+
+		if (this.video && lateTime > 0) {
+			// If the video is too far behind (>2 frames), simply reset the
+			// target time to the next frame instead of trying to catch up.
+			if (lateTime > frameTime * 2) {
+				this.startTime += lateTime;
+			}
+
+			notEnoughData = !this.video.decode();
+		}
+
+		headroom = this.demuxer.currentTime - targetTime;
+	}
+
+	// Notify the source of the playhead headroom, so it can decide whether to
+	// continue loading further data.
+	this.source.resume(headroom);
+
+	// If we failed to decode and the source is complete, it means we reached
+	// the end of our data. We may want to loop.
+	if (notEnoughData && this.source.completed) {
+		if (this.loop) {
+			this.seek(0);
+		}
+		else {
+			this.pause();
+			if (this.options.onEnded) {
+				this.options.onEnded(this);
+			}
+		}
+	}
+
+	// If there's not enough data and the source is not completed, we have
+	// just stalled.
+	else if (notEnoughData && this.options.onStalled) {
+		this.options.onStalled(this);
+	}
+};
+
+return Player;
+
+})();
+

+ 228 - 0
jsmpeg-master/src/ts.js

@@ -0,0 +1,228 @@
+JSMpeg.Demuxer.TS = (function(){ "use strict";
+
+var TS = function(options) {
+	this.bits = null;
+	this.leftoverBytes = null;
+
+	this.guessVideoFrameEnd = true;
+	this.pidsToStreamIds = {};
+
+	this.pesPacketInfo = {};
+	this.startTime = 0;
+	this.currentTime = 0;
+};
+
+TS.prototype.connect = function(streamId, destination) {
+	this.pesPacketInfo[streamId] = {
+		destination: destination,
+		currentLength: 0,
+		totalLength: 0,
+		pts: 0,
+		buffers: []
+	};
+};
+
+TS.prototype.write = function(buffer) {
+	if (this.leftoverBytes) {
+		var totalLength = buffer.byteLength + this.leftoverBytes.byteLength;
+		this.bits = new JSMpeg.BitBuffer(totalLength);
+		this.bits.write([this.leftoverBytes, buffer]);
+	}
+	else {
+		this.bits = new JSMpeg.BitBuffer(buffer);
+	}
+
+	while (this.bits.has(188 << 3) && this.parsePacket()) {}
+
+	var leftoverCount = this.bits.byteLength - (this.bits.index >> 3);
+	this.leftoverBytes = leftoverCount > 0
+		? this.bits.bytes.subarray(this.bits.index >> 3)
+		: null;
+};
+
+TS.prototype.parsePacket = function() {
+	// Check if we're in sync with packet boundaries; attempt to resync if not.
+	if (this.bits.read(8) !== 0x47) {
+		if (!this.resync()) {
+			// Couldn't resync; maybe next time...
+			return false;
+		}
+	}
+
+	var end = (this.bits.index >> 3) + 187;
+	var transportError = this.bits.read(1),
+		payloadStart = this.bits.read(1),
+		transportPriority = this.bits.read(1),
+		pid = this.bits.read(13),
+		transportScrambling = this.bits.read(2),
+		adaptationField = this.bits.read(2),
+		continuityCounter = this.bits.read(4);
+
+
+	// If this is the start of a new payload; signal the end of the previous
+	// frame, if we didn't do so already.
+	var streamId = this.pidsToStreamIds[pid];
+	if (payloadStart && streamId) {
+		var pi = this.pesPacketInfo[streamId];
+		if (pi && pi.currentLength) {
+			this.packetComplete(pi);
+		}
+	}
+
+	// Extract current payload
+	if (adaptationField & 0x1) {
+		if ((adaptationField & 0x2)) {
+			var adaptationFieldLength = this.bits.read(8);
+			this.bits.skip(adaptationFieldLength << 3);
+		}
+
+		if (payloadStart && this.bits.nextBytesAreStartCode()) {
+			this.bits.skip(24);
+			streamId = this.bits.read(8);
+			this.pidsToStreamIds[pid] = streamId;
+
+			var packetLength = this.bits.read(16)
+			this.bits.skip(8);
+			var ptsDtsFlag = this.bits.read(2);
+			this.bits.skip(6);
+			var headerLength = this.bits.read(8);
+			var payloadBeginIndex = this.bits.index + (headerLength << 3);
+			
+			var pi = this.pesPacketInfo[streamId];
+			if (pi) {
+				var pts = 0;
+				if (ptsDtsFlag & 0x2) {
+					// The Presentation Timestamp is encoded as 33(!) bit
+					// integer, but has a "marker bit" inserted at weird places
+					// in between, making the whole thing 5 bytes in size.
+					// You can't make this shit up...
+					this.bits.skip(4);
+					var p32_30 = this.bits.read(3);
+					this.bits.skip(1);
+					var p29_15 = this.bits.read(15);
+					this.bits.skip(1);
+					var p14_0 = this.bits.read(15);
+					this.bits.skip(1);
+
+					// Can't use bit shifts here; we need 33 bits of precision,
+					// so we're using JavaScript's double number type. Also
+					// divide by the 90khz clock to get the pts in seconds.
+					pts = (p32_30 * 1073741824 + p29_15 * 32768 + p14_0)/90000;
+					
+					this.currentTime = pts;
+					if (this.startTime === -1) {
+						this.startTime = pts;
+					}
+				}
+
+				var payloadLength = packetLength 
+					? packetLength - headerLength - 3
+					: 0;
+				this.packetStart(pi, pts, payloadLength);
+			}
+
+			// Skip the rest of the header without parsing it
+			this.bits.index = payloadBeginIndex;
+		}
+
+		if (streamId) {
+			// Attempt to detect if the PES packet is complete. For Audio (and
+			// other) packets, we received a total packet length with the PES 
+			// header, so we can check the current length.
+
+			// For Video packets, we have to guess the end by detecting if this
+			// TS packet was padded - there's no good reason to pad a TS packet 
+			// in between, but it might just fit exactly. If this fails, we can
+			// only wait for the next PES header for that stream.
+
+			var pi = this.pesPacketInfo[streamId];
+			if (pi) {
+				var start = this.bits.index >> 3;
+				var complete = this.packetAddData(pi, start, end);
+
+				var hasPadding = !payloadStart && (adaptationField & 0x2);
+				if (complete || (this.guessVideoFrameEnd && hasPadding)) {
+					this.packetComplete(pi);	
+				}
+			}
+		}
+	}
+
+	this.bits.index = end << 3;
+	return true;
+};
+
+TS.prototype.resync = function() {
+	// Check if we have enough data to attempt a resync. We need 5 full packets.
+	if (!this.bits.has((188 * 6) << 3)) {
+		return false;
+	}
+
+	var byteIndex = this.bits.index >> 3;
+
+	// Look for the first sync token in the first 187 bytes
+	for (var i = 0; i < 187; i++) {
+		if (this.bits.bytes[byteIndex + i] === 0x47) {
+
+			// Look for 4 more sync tokens, each 188 bytes appart
+			var foundSync = true;
+			for (var j = 1; j < 5; j++) {
+				if (this.bits.bytes[byteIndex + i + 188 * j] !== 0x47) {
+					foundSync = false;
+					break;
+				}
+			}
+
+			if (foundSync) {
+				this.bits.index = (byteIndex + i + 1) << 3;
+				return true;
+			}
+		}
+	}
+
+	// In theory, we shouldn't arrive here. If we do, we had enough data but
+	// still didn't find sync - this can only happen if we were fed garbage
+	// data. Check your source!
+	console.warn('JSMpeg: Possible garbage data. Skipping.');
+	this.bits.skip(187 << 3);
+	return false;
+};
+
+TS.prototype.packetStart = function(pi, pts, payloadLength) {
+	pi.totalLength = payloadLength;
+	pi.currentLength = 0;
+	pi.pts = pts;
+};
+
+TS.prototype.packetAddData = function(pi, start, end) {
+	pi.buffers.push(this.bits.bytes.subarray(start, end));
+	pi.currentLength += end - start;
+
+	var complete = (pi.totalLength !== 0 && pi.currentLength >= pi.totalLength);
+	return complete;
+};
+
+TS.prototype.packetComplete = function(pi) {
+	pi.destination.write(pi.pts, pi.buffers);
+	pi.totalLength = 0;
+	pi.currentLength = 0;
+	pi.buffers = [];
+};
+
+TS.STREAM = {
+	PACK_HEADER: 0xBA,
+	SYSTEM_HEADER: 0xBB,
+	PROGRAM_MAP: 0xBC,
+	PRIVATE_1: 0xBD,
+	PADDING: 0xBE,
+	PRIVATE_2: 0xBF,
+	AUDIO_1: 0xC0,
+	VIDEO_1: 0xE0,
+	DIRECTORY: 0xFF
+};
+
+return TS;
+
+})();
+
+

+ 162 - 0
jsmpeg-master/src/video-element.js

@@ -0,0 +1,162 @@
+JSMpeg.VideoElement = (function(){ "use strict";
+
+var VideoElement = function(element) {	
+	var url = element.dataset.url;
+
+	if (!url) {
+		throw ("VideoElement has no `data-url` attribute");
+	}
+
+	// Setup the div container, canvas and play button
+	var addStyles = function(element, styles) {
+		for (var name in styles) {
+			element.style[name] = styles[name];
+		}
+	};
+
+	this.container = element;
+	addStyles(this.container, {
+		display: 'inline-block',
+		position: 'relative',
+		minWidth: '80px', minHeight: '80px'
+	});
+
+	this.canvas = document.createElement('canvas');
+	this.canvas.width = 960;
+	this.canvas.height = 540;
+	addStyles(this.canvas, {
+		display: 'block',
+		width: '100%'
+	});
+	this.container.appendChild(this.canvas);
+
+	this.playButton = document.createElement('div');
+	this.playButton.innerHTML = VideoElement.PLAY_BUTTON;
+	addStyles(this.playButton, {
+		zIndex: 2, position: 'absolute',
+		top: '0', bottom: '0', left: '0', right: '0',
+		maxWidth: '75px', maxHeight: '75px',
+		margin: 'auto',
+		opacity: '0.7',
+		cursor: 'pointer'
+	});
+	this.container.appendChild(this.playButton);	
+
+	// Parse the data-options - we try to decode the values as json. This way
+	// we can get proper boolean and number values. If JSON.parse() fails,
+	// treat it as a string.
+	var options = {canvas: this.canvas};
+	for (var option in element.dataset) {
+		try {
+			options[option] = JSON.parse(element.dataset[option]);
+		}
+		catch(err) {
+			options[option] = element.dataset[option];
+		}
+	}
+
+	// Create the player instance
+	this.player = new JSMpeg.Player(url, options);
+	element.playerInstance = this.player;
+
+	// Setup the poster element, if any
+	if (options.poster && !options.autoplay && !this.player.options.streaming) {
+		options.decodeFirstFrame = false;
+		this.poster = new Image();
+		this.poster.src = options.poster;
+		this.poster.addEventListener('load', this.posterLoaded)
+		addStyles(this.poster, {
+			display: 'block', zIndex: 1, position: 'absolute',
+			top: 0, left: 0, bottom: 0, right: 0
+		});
+		this.container.appendChild(this.poster);
+	}
+
+	// Add the click handler if this video is pausable
+	if (!this.player.options.streaming) {
+		this.container.addEventListener('click', this.onClick.bind(this));
+	}
+
+	// Hide the play button if this video immediately begins playing
+	if (options.autoplay || this.player.options.streaming) {
+		this.playButton.style.display = 'none';
+	}
+
+	// Set up the unlock audio buton for iOS devices. iOS only allows us to 
+	// play audio after a user action has initiated playing. For autoplay or 
+	// streaming players we set up a muted speaker icon as the button. For all
+	// others, we can simply use the play button.
+	if (this.player.audioOut && !this.player.audioOut.unlocked) {
+		var unlockAudioElement = this.container;
+
+		if (options.autoplay || this.player.options.streaming) {
+			this.unmuteButton = document.createElement('div');
+			this.unmuteButton.innerHTML = VideoElement.UNMUTE_BUTTON;
+			addStyles(this.unmuteButton, {
+				zIndex: 2, position: 'absolute',
+				bottom: '10px', right: '20px',
+				width: '75px', height: '75px',
+				margin: 'auto',
+				opacity: '0.7',
+				cursor: 'pointer'				
+			});
+			this.container.appendChild(this.unmuteButton);
+			unlockAudioElement = this.unmuteButton;
+		}
+
+		this.unlockAudioBound = this.onUnlockAudio.bind(this, unlockAudioElement);
+		unlockAudioElement.addEventListener('touchstart', this.unlockAudioBound, false);
+		unlockAudioElement.addEventListener('click', this.unlockAudioBound, true);
+	}
+};
+
+VideoElement.prototype.onUnlockAudio = function(element, ev) {
+	if (this.unmuteButton) {
+		ev.preventDefault();
+		ev.stopPropagation();
+	}
+	this.player.audioOut.unlock(function(){
+		if (this.unmuteButton) {
+			this.unmuteButton.style.display = 'none';
+		}
+		element.removeEventListener('touchstart', this.unlockAudioBound);
+		element.removeEventListener('click', this.unlockAudioBound);
+	}.bind(this));
+};
+
+VideoElement.prototype.onClick = function(ev) {
+	if (this.player.isPlaying) {
+		this.player.pause();
+		this.playButton.style.display = 'block';
+	}
+	else {
+		this.player.play();
+		this.playButton.style.display = 'none';
+		if (this.poster) {
+			this.poster.style.display = 'none';
+		}
+	}
+};
+
+VideoElement.PLAY_BUTTON = 
+	'<svg style="max-width: 75px; max-height: 75px;" ' +
+		'viewBox="0 0 200 200" alt="Play video">' +
+		'<circle cx="100" cy="100" r="90" fill="none" '+
+			'stroke-width="15" stroke="#fff"/>' +
+		'<polygon points="70, 55 70, 145 145, 100" fill="#fff"/>' +
+	'</svg>';
+
+VideoElement.UNMUTE_BUTTON = 
+	'<svg style="max-width: 75px; max-height: 75px;" viewBox="0 0 75 75">' +
+		'<polygon class="audio-speaker" stroke="none" fill="#fff" '+
+			'points="39,13 22,28 6,28 6,47 21,47 39,62 39,13"/>' +
+		'<g stroke="#fff" stroke-width="5">' +
+			'<path d="M 49,50 69,26"/>' +
+			'<path d="M 69,50 49,26"/>' +
+		'</g>' +
+	'</svg>';
+
+return VideoElement;
+
+})();
+

+ 157 - 0
jsmpeg-master/src/wasm-module.js

@@ -0,0 +1,157 @@
+JSMpeg.WASMModule = (function(){ "use strict";
+
+var WASM = function() {
+	this.stackSize = 5 * 1024 * 1024; // emscripten default
+	this.pageSize = 64 * 1024; // wasm page size
+	this.onInitCallback = null;
+	this.ready = false;
+};
+
+WASM.prototype.write = function(buffer) {
+	this.loadFromBuffer(buffer, this.onInitCallback);
+};
+
+WASM.prototype.loadFromFile = function(url, callback) {
+	this.onInitCallback = callback;
+	var ajax = new JSMpeg.Source.Ajax(url, {});
+	ajax.connect(this);
+	ajax.start();
+};
+
+WASM.prototype.loadFromBuffer = function(buffer, callback) {
+	this.moduleInfo = this.readDylinkSection(buffer);
+	if (!this.moduleInfo) {
+		this.callback && this.callback(null);
+		return;
+	}
+
+	this.memory = new WebAssembly.Memory({initial: 256});
+	var env = {
+		memory: this.memory,
+		memoryBase: 0,
+		__memory_base: 0,
+		table: new WebAssembly.Table({initial: this.moduleInfo.tableSize, element: 'anyfunc'}),
+		tableBase: 0,
+		__table_base: 0,
+		abort: this.c_abort.bind(this),
+		___assert_fail: this.c_assertFail.bind(this),
+		_sbrk: this.c_sbrk.bind(this)
+	};
+
+	this.brk = this.align(this.moduleInfo.memorySize + this.stackSize);
+	WebAssembly.instantiate(buffer, {env: env}).then(function(results){
+		this.instance = results.instance;
+		if (this.instance.exports.__post_instantiate) {
+			this.instance.exports.__post_instantiate();
+		}
+		this.createHeapViews();
+		this.ready = true;
+		callback && callback(this);
+	}.bind(this))
+};
+
+WASM.prototype.createHeapViews = function() {
+	this.instance.heapU8 = new Uint8Array(this.memory.buffer);
+	this.instance.heapU32 = new Uint32Array(this.memory.buffer);
+	this.instance.heapF32 = new Float32Array(this.memory.buffer);
+};
+
+WASM.prototype.align = function(addr) {
+	var a = Math.pow(2, this.moduleInfo.memoryAlignment);
+	return Math.ceil(addr / a) * a;
+};
+
+WASM.prototype.c_sbrk = function(size) {
+	var previousBrk = this.brk;
+	this.brk += size;
+
+	if (this.brk > this.memory.buffer.byteLength) {
+		var bytesNeeded = this.brk - this.memory.buffer.byteLength;
+		var pagesNeeded = Math.ceil(bytesNeeded / this.pageSize);
+		this.memory.grow(pagesNeeded);
+		this.createHeapViews();
+	}
+	return previousBrk;
+};
+
+WASM.prototype.c_abort = function(size) {
+	console.warn('JSMPeg: WASM abort', arguments);
+};
+
+WASM.prototype.c_assertFail = function(size) {
+	console.warn('JSMPeg: WASM ___assert_fail', arguments);
+};
+
+
+WASM.prototype.readDylinkSection = function(buffer) {
+	// Read the WASM header and dylink section of the .wasm binary data
+	// to get the needed table size and static data size.
+
+	// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+	// https://github.com/kripken/emscripten/blob/20602efb955a7c6c20865a495932427e205651d2/src/support.js
+
+	var bytes = new Uint8Array(buffer);
+	var next = 0;
+
+	var readVarUint = function () {
+		var ret = 0;
+		var mul = 1;
+		while (1) {
+			var byte = bytes[next++];
+			ret += ((byte & 0x7f) * mul);
+			mul *= 0x80;
+			if (!(byte & 0x80)) {
+				return ret
+			}
+		}
+	}
+
+	var matchNextBytes = function(expected) {
+		for (var i = 0; i < expected.length; i++) {
+			var b = typeof(expected[i]) === 'string' 
+				? expected[i].charCodeAt(0)
+				: expected[i];
+			if (bytes[next++] !== b) {
+				return false;
+			}
+		}
+		return true;
+	};
+
+	
+
+	// Make sure we have a wasm header
+	if (!matchNextBytes([0, 'a', 's', 'm'])) {
+		console.warn('JSMpeg: WASM header not found');
+		return null;
+	}
+
+	// Make sure we have a dylink section
+	var next = 9;
+	var sectionSize = readVarUint();
+	if (!matchNextBytes([6, 'd', 'y', 'l', 'i', 'n', 'k'])) {
+		console.warn('JSMpeg: No dylink section found in WASM');
+		return null;
+	}
+
+	return {
+		memorySize: readVarUint(),
+		memoryAlignment: readVarUint(),
+		tableSize: readVarUint(),
+		tableAlignment: readVarUint()
+	};
+};
+
+WASM.IsSupported = function() {
+	return (!!window.WebAssembly);
+};
+
+WASM.GetModule = function() {
+	WASM.CACHED_MODULE = WASM.CACHED_MODULE || new WASM();
+	return WASM.CACHED_MODULE;
+};
+
+return WASM;
+
+})();
+

+ 190 - 0
jsmpeg-master/src/wasm/buffer.c

@@ -0,0 +1,190 @@
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "buffer.h"
+
+typedef struct bit_buffer_t {
+	uint8_t *bytes;
+	unsigned int index;
+	unsigned int byte_capacity;
+	unsigned int byte_length;
+	bit_buffer_mode_t mode;
+} bit_buffer_t;
+
+void bit_buffer_resize(bit_buffer_t *self, unsigned int byte_capacity);
+void bit_buffer_evict(bit_buffer_t *self, unsigned int bytes_needed);
+
+
+
+bit_buffer_t *bit_buffer_create(unsigned int initial_byte_capacity, bit_buffer_mode_t mode) {
+	bit_buffer_t *self = malloc(sizeof(bit_buffer_t));
+	memset(self, 0, sizeof(bit_buffer_t));
+	self->mode = mode;
+	self->bytes = malloc(initial_byte_capacity);
+	self->byte_capacity = initial_byte_capacity;
+	self->byte_length = 0;
+	self->index = 0;
+	return self;
+}
+
+
+void bit_buffer_destroy(bit_buffer_t *self) {
+	free(self->bytes);
+	free(self);
+}
+
+
+int bit_buffer_get_index(bit_buffer_t *self) {
+	return self->index;
+}
+
+
+void bit_buffer_set_index(bit_buffer_t *self, unsigned int index) {
+	self->index = index; // TODO check validity!
+}
+
+
+uint8_t *bit_buffer_get_write_ptr(bit_buffer_t *self, unsigned int bytes_to_write) {
+	int bytes_available = self->byte_capacity - self->byte_length;
+
+	if (bytes_to_write > bytes_available) {
+		if (self->mode == BIT_BUFFER_MODE_EXPAND) {
+			int new_byte_capacity = self->byte_capacity * 2;
+			if (new_byte_capacity + bytes_available < bytes_to_write) {
+				new_byte_capacity = bytes_to_write - bytes_available;
+			}
+			bit_buffer_resize(self, new_byte_capacity);
+		}
+		else {
+			bit_buffer_evict(self, bytes_to_write);
+		}
+	}
+
+	return self->bytes + self->byte_length;
+};
+
+
+void bit_buffer_did_write(bit_buffer_t *self, unsigned int bytes_written) {
+	self->byte_length += bytes_written;
+}
+
+
+int bit_buffer_find_next_start_code(bit_buffer_t *self) {
+	for (int i = ((self->index + 7) >> 3); i < self->byte_length; i++) {
+		if(
+			self->bytes[i] == 0x00 &&
+			self->bytes[i+1] == 0x00 &&
+			self->bytes[i+2] == 0x01
+		) {
+			self->index = (i+4) << 3;
+			return self->bytes[i+3];
+		}
+	}
+	self->index = (self->byte_length << 3);
+	return -1;
+}
+
+
+int bit_buffer_find_start_code(bit_buffer_t *self, int code) {
+	int current = 0;
+	while (true) {
+		current = bit_buffer_find_next_start_code(self);
+		if (current == code || current == -1) {
+			return current;
+		}
+	}
+	return -1;
+}
+
+
+int bit_buffer_next_bytes_are_start_code(bit_buffer_t *self) {
+	int i = ((self->index + 7) >> 3);
+	return (
+		i >= self->byte_length || (
+			self->bytes[i] == 0x00 && 
+			self->bytes[i+1] == 0x00 &&
+			self->bytes[i+2] == 0x01
+		)
+	);
+}
+
+
+int bit_buffer_peek(bit_buffer_t *self, unsigned int count) {
+	int offset = self->index;
+	int value = 0;
+	while (count) {
+		int current_byte = self->bytes[offset >> 3];
+		int remaining = 8 - (offset & 7); // remaining bits in byte
+		int read = remaining < count ? remaining : count; // bits in self run
+		int shift = remaining - read;
+		int mask = (0xff >> (8-read));
+
+		value = (value << read) | ((current_byte & (mask << shift)) >> shift);
+
+		offset += read;
+		count -= read;
+	}
+
+	return value;
+}
+
+
+int bit_buffer_read(bit_buffer_t *self, unsigned int count) {
+	int value = bit_buffer_peek(self, count);
+	self->index += count;
+	return value;
+}
+
+
+int bit_buffer_skip(bit_buffer_t *self, unsigned int count) {
+	return (self->index += count);
+}
+
+
+void bit_buffer_rewind(bit_buffer_t *self, unsigned int count) {
+	self->index = self->index - count;
+	if (self->index < 0) {
+		self->index = 0;
+	}
+}
+
+
+int bit_buffer_has(bit_buffer_t *self, unsigned int count) {
+	return ((self->byte_length << 3) - self->index) >= count;
+}
+
+
+void bit_buffer_resize(bit_buffer_t *self, unsigned int byte_capacity) {
+	self->bytes = realloc(self->bytes, byte_capacity);
+	self->byte_capacity = byte_capacity;
+	if (self->index > self->byte_length << 3) {
+		self->index = self->byte_length << 3;
+	}
+}
+
+
+void bit_buffer_evict(bit_buffer_t *self, unsigned int bytes_needed) {
+	int byte_pos = self->index >> 3;
+	int bytes_available = self->byte_capacity - self->byte_length;
+	
+	// If the current index is the write position, we can simply reset both
+	// to 0. Also reset (and throw away yet unread data) if we won't be able
+	// to fit the new data in even after a normal eviction.
+	if (
+		byte_pos == self->byte_length ||
+		bytes_needed > bytes_available + byte_pos // emergency evac
+	) {
+		self->byte_length = 0;
+		self->index = 0;
+		return;
+	}
+	else if (byte_pos == 0) {
+		// Nothing read yet - we can't evict anything
+		return;
+	}
+
+	memmove(self->bytes, self->bytes + byte_pos, self->byte_length - byte_pos);
+	self->byte_length -= byte_pos;
+	self->index -= byte_pos << 3;
+}

+ 31 - 0
jsmpeg-master/src/wasm/buffer.h

@@ -0,0 +1,31 @@
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include <stdint.h>
+
+typedef struct bit_buffer_t bit_buffer_t;
+
+typedef enum {
+	BIT_BUFFER_MODE_EVICT = 1,
+	BIT_BUFFER_MODE_EXPAND = 2
+} bit_buffer_mode_t;
+
+
+bit_buffer_t *bit_buffer_create(unsigned int initial_byte_capacity, bit_buffer_mode_t mode);
+void bit_buffer_destroy(bit_buffer_t *self);
+
+int bit_buffer_get_index(bit_buffer_t *self);
+void bit_buffer_set_index(bit_buffer_t *self, unsigned int index);
+
+uint8_t *bit_buffer_get_write_ptr(bit_buffer_t *self, unsigned int bytes_to_write);
+void bit_buffer_did_write(bit_buffer_t *self, unsigned int bytes_written);
+int bit_buffer_find_next_start_code(bit_buffer_t *self);
+int bit_buffer_find_start_code(bit_buffer_t *self, int code);
+int bit_buffer_next_bytes_are_start_code(bit_buffer_t *self);
+int bit_buffer_peek(bit_buffer_t *self, unsigned int count);
+int bit_buffer_read(bit_buffer_t *self, unsigned int count);
+int bit_buffer_skip(bit_buffer_t *self, unsigned int count);
+int bit_buffer_has(bit_buffer_t *self, unsigned int count);
+void bit_buffer_rewind(bit_buffer_t *self, unsigned int count);
+
+#endif

+ 704 - 0
jsmpeg-master/src/wasm/mp2.c

@@ -0,0 +1,704 @@
+#include <string.h>
+#include <stdlib.h>
+#include "mp2.h"
+
+const static int FRAME_SYNC = 0x7ff;
+
+const static int VERSION_MPEG_2_5 = 0x0;
+const static int VERSION_MPEG_2 = 0x2;
+const static int VERSION_MPEG_1 = 0x3;
+
+const static int LAYER_III = 0x1;
+const static int LAYER_II = 0x2;
+const static int LAYER_I = 0x3;
+
+const static int MODE_STEREO = 0x0;
+const static int MODE_JOINT_STEREO = 0x1;
+const static int MODE_DUAL_CHANNEL = 0x2;
+const static int MODE_MONO = 0x3;
+
+const static unsigned short SAMPLE_RATE[] = {
+	44100, 48000, 32000, 0, // MPEG-1
+	22050, 24000, 16000, 0  // MPEG-2
+};
+
+const static short BIT_RATE[] = {
+	32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, // MPEG-1
+	 8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160  // MPEG-2
+};
+
+const static int SCALEFACTOR_BASE[] = {
+	0x02000000, 0x01965FEA, 0x01428A30
+};
+
+const static float SYNTHESIS_WINDOW[] = {
+	     0.0,     -0.5,     -0.5,     -0.5,     -0.5,     -0.5,
+	    -0.5,     -1.0,     -1.0,     -1.0,     -1.0,     -1.5,
+	    -1.5,     -2.0,     -2.0,     -2.5,     -2.5,     -3.0,
+	    -3.5,     -3.5,     -4.0,     -4.5,     -5.0,     -5.5,
+	    -6.5,     -7.0,     -8.0,     -8.5,     -9.5,    -10.5,
+	   -12.0,    -13.0,    -14.5,    -15.5,    -17.5,    -19.0,
+	   -20.5,    -22.5,    -24.5,    -26.5,    -29.0,    -31.5,
+	   -34.0,    -36.5,    -39.5,    -42.5,    -45.5,    -48.5,
+	   -52.0,    -55.5,    -58.5,    -62.5,    -66.0,    -69.5,
+	   -73.5,    -77.0,    -80.5,    -84.5,    -88.0,    -91.5,
+	   -95.0,    -98.0,   -101.0,   -104.0,    106.5,    109.0,
+	   111.0,    112.5,    113.5,    114.0,    114.0,    113.5,
+	   112.0,    110.5,    107.5,    104.0,    100.0,     94.5,
+	    88.5,     81.5,     73.0,     63.5,     53.0,     41.5,
+	    28.5,     14.5,     -1.0,    -18.0,    -36.0,    -55.5,
+	   -76.5,    -98.5,   -122.0,   -147.0,   -173.5,   -200.5,
+	  -229.5,   -259.5,   -290.5,   -322.5,   -355.5,   -389.5,
+	  -424.0,   -459.5,   -495.5,   -532.0,   -568.5,   -605.0,
+	  -641.5,   -678.0,   -714.0,   -749.0,   -783.5,   -817.0,
+	  -849.0,   -879.5,   -908.5,   -935.0,   -959.5,   -981.0,
+	 -1000.5,  -1016.0,  -1028.5,  -1037.5,  -1042.5,  -1043.5,
+	 -1040.0,  -1031.5,   1018.5,   1000.0,    976.0,    946.5,
+	   911.0,    869.5,    822.0,    767.5,    707.0,    640.0,
+	   565.5,    485.0,    397.0,    302.5,    201.0,     92.5,
+	   -22.5,   -144.0,   -272.5,   -407.0,   -547.5,   -694.0,
+	  -846.0,  -1003.0,  -1165.0,  -1331.5,  -1502.0,  -1675.5,
+	 -1852.5,  -2031.5,  -2212.5,  -2394.0,  -2576.5,  -2758.5,
+	 -2939.5,  -3118.5,  -3294.5,  -3467.5,  -3635.5,  -3798.5,
+	 -3955.0,  -4104.5,  -4245.5,  -4377.5,  -4499.0,  -4609.5,
+	 -4708.0,  -4792.5,  -4863.5,  -4919.0,  -4958.0,  -4979.5,
+	 -4983.0,  -4967.5,  -4931.5,  -4875.0,  -4796.0,  -4694.5,
+	 -4569.5,  -4420.0,  -4246.0,  -4046.0,  -3820.0,  -3567.0,
+	  3287.0,   2979.5,   2644.0,   2280.5,   1888.0,   1467.5,
+	  1018.5,    541.0,     35.0,   -499.0,  -1061.0,  -1650.0,
+	 -2266.5,  -2909.0,  -3577.0,  -4270.0,  -4987.5,  -5727.5,
+	 -6490.0,  -7274.0,  -8077.5,  -8899.5,  -9739.0, -10594.5,
+	-11464.5, -12347.0, -13241.0, -14144.5, -15056.0, -15973.5,
+	-16895.5, -17820.0, -18744.5, -19668.0, -20588.0, -21503.0,
+	-22410.5, -23308.5, -24195.0, -25068.5, -25926.5, -26767.0,
+	-27589.0, -28389.0, -29166.5, -29919.0, -30644.5, -31342.0,
+	-32009.5, -32645.0, -33247.0, -33814.5, -34346.0, -34839.5,
+	-35295.0, -35710.0, -36084.5, -36417.5, -36707.5, -36954.0,
+	-37156.5, -37315.0, -37428.0, -37496.0,  37519.0,  37496.0,
+	 37428.0,  37315.0,  37156.5,  36954.0,  36707.5,  36417.5,
+	 36084.5,  35710.0,  35295.0,  34839.5,  34346.0,  33814.5,
+	 33247.0,  32645.0,  32009.5,  31342.0,  30644.5,  29919.0,
+	 29166.5,  28389.0,  27589.0,  26767.0,  25926.5,  25068.5,
+	 24195.0,  23308.5,  22410.5,  21503.0,  20588.0,  19668.0,
+	 18744.5,  17820.0,  16895.5,  15973.5,  15056.0,  14144.5,
+	 13241.0,  12347.0,  11464.5,  10594.5,   9739.0,   8899.5,
+	  8077.5,   7274.0,   6490.0,   5727.5,   4987.5,   4270.0,
+	  3577.0,   2909.0,   2266.5,   1650.0,   1061.0,    499.0,
+	   -35.0,   -541.0,  -1018.5,  -1467.5,  -1888.0,  -2280.5,
+	 -2644.0,  -2979.5,   3287.0,   3567.0,   3820.0,   4046.0,
+	  4246.0,   4420.0,   4569.5,   4694.5,   4796.0,   4875.0,
+	  4931.5,   4967.5,   4983.0,   4979.5,   4958.0,   4919.0,
+	  4863.5,   4792.5,   4708.0,   4609.5,   4499.0,   4377.5,
+	  4245.5,   4104.5,   3955.0,   3798.5,   3635.5,   3467.5,
+	  3294.5,   3118.5,   2939.5,   2758.5,   2576.5,   2394.0,
+	  2212.5,   2031.5,   1852.5,   1675.5,   1502.0,   1331.5,
+	  1165.0,   1003.0,    846.0,    694.0,    547.5,    407.0,
+	   272.5,    144.0,     22.5,    -92.5,   -201.0,   -302.5,
+	  -397.0,   -485.0,   -565.5,   -640.0,   -707.0,   -767.5,
+	  -822.0,   -869.5,   -911.0,   -946.5,   -976.0,  -1000.0,
+	  1018.5,   1031.5,   1040.0,   1043.5,   1042.5,   1037.5,
+	  1028.5,   1016.0,   1000.5,    981.0,    959.5,    935.0,
+	   908.5,    879.5,    849.0,    817.0,    783.5,    749.0,
+	   714.0,    678.0,    641.5,    605.0,    568.5,    532.0,
+	   495.5,    459.5,    424.0,    389.5,    355.5,    322.5,
+	   290.5,    259.5,    229.5,    200.5,    173.5,    147.0,
+	   122.0,     98.5,     76.5,     55.5,     36.0,     18.0,
+		1.0,    -14.5,    -28.5,    -41.5,    -53.0,    -63.5,
+	   -73.0,    -81.5,    -88.5,    -94.5,   -100.0,   -104.0,
+	  -107.5,   -110.5,   -112.0,   -113.5,   -114.0,   -114.0,
+	  -113.5,   -112.5,   -111.0,   -109.0,    106.5,    104.0,
+	   101.0,     98.0,     95.0,     91.5,     88.0,     84.5,
+	    80.5,     77.0,     73.5,     69.5,     66.0,     62.5,
+	    58.5,     55.5,     52.0,     48.5,     45.5,     42.5,
+	    39.5,     36.5,     34.0,     31.5,     29.0,     26.5,
+	    24.5,     22.5,     20.5,     19.0,     17.5,     15.5,
+	    14.5,     13.0,     12.0,     10.5,      9.5,      8.5,
+	     8.0,      7.0,      6.5,      5.5,      5.0,      4.5,
+	     4.0,      3.5,      3.5,      3.0,      2.5,      2.5,
+	     2.0,      2.0,      1.5,      1.5,      1.0,      1.0,
+	     1.0,      1.0,      0.5,      0.5,      0.5,      0.5,
+	     0.5,      0.5
+};
+
+// Quantizer lookup, step 1: bitrate classes
+const static uint8_t QUANT_LUT_STEP_1[2][16] = {
+ 	// 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384 <- bitrate
+	{   0,  0,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2}, // mono
+	// 16, 24, 28, 32, 40, 48, 56, 64, 80, 96,112,128,160,192 <- bitrate / chan
+	{   0,  0,  0,  0,  0,  0,  1,  1,  1,  2,  2,  2,  2,  2} // stereo
+};
+
+// Quantizer lookup, step 2: bitrate class, sample rate -> B2 table idx, sblimit
+const static uint8_t QUANT_TAB_A = (27 | 64);   // Table 3-B.2a: high-rate, sblimit = 27
+const static uint8_t QUANT_TAB_B = (30 | 64);   // Table 3-B.2b: high-rate, sblimit = 30
+const static uint8_t QUANT_TAB_C =   8;         // Table 3-B.2c:  low-rate, sblimit =  8
+const static uint8_t QUANT_TAB_D =  12;         // Table 3-B.2d:  low-rate, sblimit = 12
+
+const static uint8_t QUANT_LUT_STEP_2[3][3] = {
+	// 44.1 kHz,    48 kHz,      32 kHz
+	{QUANT_TAB_C, QUANT_TAB_C, QUANT_TAB_D}, // 32 - 48 kbit/sec/ch
+	{QUANT_TAB_A, QUANT_TAB_A, QUANT_TAB_A}, // 56 - 80 kbit/sec/ch
+	{QUANT_TAB_B, QUANT_TAB_A, QUANT_TAB_B}  // 96+	 kbit/sec/ch
+};
+
+// Quantizer lookup, step 3: B2 table, subband -> nbal, row index
+// (upper 4 bits: nbal, lower 4 bits: row index)
+const static uint8_t QUANT_LUT_STEP_3[3][32] = {
+	// Low-rate table (3-B.2c and 3-B.2d)
+	{
+		0x44,0x44,
+	  	0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34
+	},
+	// High-rate table (3-B.2a and 3-B.2b)
+	{
+		0x43,0x43,0x43,
+		0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,
+		0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,
+		0x20,0x20,0x20,0x20,0x20,0x20,0x20
+	},
+	// MPEG-2 LSR table (B.2 in ISO 13818-3)
+	{
+		0x45,0x45,0x45,0x45,
+		0x34,0x34,0x34,0x34,0x34,0x34,0x34,
+		0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,
+					   0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24	
+	}
+};
+
+// Quantizer lookup, step 4: table row, allocation[] value -> quant table index
+const static uint8_t QUANT_LUT_STEP4[6][16] = {
+	{0, 1, 2, 17},
+	{0, 1, 2, 3, 4, 5, 6, 17},
+	{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17},
+	{0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
+	{0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17},
+	{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
+};
+
+typedef struct quantizer_spec_t {
+	unsigned short levels;
+	unsigned char group;
+	unsigned char bits;
+} quantizer_spec_t;
+
+const static quantizer_spec_t QUANT_TAB[] = {
+	{.levels =     3, .group = 1, .bits =  5},  //  1
+	{.levels =     5, .group = 1, .bits =  7},  //  2
+	{.levels =     7, .group = 0, .bits =  3},  //  3
+	{.levels =     9, .group = 1, .bits = 10},  //  4
+	{.levels =    15, .group = 0, .bits =  4},  //  5
+	{.levels =    31, .group = 0, .bits =  5},  //  6
+	{.levels =    63, .group = 0, .bits =  6},  //  7
+	{.levels =   127, .group = 0, .bits =  7},  //  8
+	{.levels =   255, .group = 0, .bits =  8},  //  9
+	{.levels =   511, .group = 0, .bits =  9},  // 10
+	{.levels =  1023, .group = 0, .bits = 10},  // 11
+	{.levels =  2047, .group = 0, .bits = 11},  // 12
+	{.levels =  4095, .group = 0, .bits = 12},  // 13
+	{.levels =  8191, .group = 0, .bits = 13},  // 14
+	{.levels = 16383, .group = 0, .bits = 14},  // 15
+	{.levels = 32767, .group = 0, .bits = 15},  // 16
+	{.levels = 65535, .group = 0, .bits = 16}   // 17
+};
+
+#define SAMPLES_PER_FRAME 1152
+
+typedef struct mp2_decoder_t {
+	int sample_rate;
+	int v_pos;
+
+	bit_buffer_t *bits;
+
+	const quantizer_spec_t *allocation[2][32];
+	uint8_t scale_factor_info[2][32];
+	int scale_factor[2][32][3];
+	int sample[2][32][3];
+
+	float channel_left[SAMPLES_PER_FRAME];
+	float channel_right[SAMPLES_PER_FRAME];
+	float D[1024];
+	float V[2][1024];
+	int U[32];
+} mp2_decoder_t;
+
+
+void matrix_transform(int s[32][3], int ss, float *d, int dp);
+void read_samples(mp2_decoder_t *self, int ch, int sb, int part);
+const quantizer_spec_t *read_allocation(mp2_decoder_t *self, int sb, int tab3);
+int decode_frame(mp2_decoder_t *self);
+
+
+// -----------------------------------------------------------------------------
+// Public interface
+
+mp2_decoder_t *mp2_decoder_create(unsigned int buffer_size, bit_buffer_mode_t buffer_mode) {
+	mp2_decoder_t *self = malloc(sizeof(mp2_decoder_t));
+	memset(self, 0, sizeof(mp2_decoder_t));
+	self->bits = bit_buffer_create(buffer_size, buffer_mode);
+
+	self->sample_rate = 44100;
+	memcpy(self->D, SYNTHESIS_WINDOW, 512 * sizeof(float));
+	memcpy(self->D + 512, SYNTHESIS_WINDOW, 512 * sizeof(float));
+
+	return self;
+}
+
+void mp2_decoder_destroy(mp2_decoder_t *self) {
+	bit_buffer_destroy(self->bits);
+	free(self);
+}
+
+void *mp2_decoder_get_write_ptr(mp2_decoder_t *self, unsigned int byte_size) {
+	return bit_buffer_get_write_ptr(self->bits, byte_size);
+}
+
+int mp2_decoder_get_index(mp2_decoder_t *self) {
+	return bit_buffer_get_index(self->bits);
+}
+
+void mp2_decoder_set_index(mp2_decoder_t *self, unsigned int index) {
+	bit_buffer_set_index(self->bits, index);
+}
+
+void mp2_decoder_did_write(mp2_decoder_t *self, unsigned int byte_size) {
+	bit_buffer_did_write(self->bits, byte_size);
+}
+
+int mp2_decoder_get_sample_rate(mp2_decoder_t *self) {
+	return self->sample_rate;
+}
+
+void *mp2_decoder_get_left_channel_ptr(mp2_decoder_t *self) {
+	return self->channel_left;
+}
+
+void *mp2_decoder_get_right_channel_ptr(mp2_decoder_t *self) {
+	return self->channel_right;
+}
+
+int mp2_decoder_decode(mp2_decoder_t *self) {
+	int byte_pos = bit_buffer_get_index(self->bits) >> 3;
+
+	if (!bit_buffer_has(self->bits, 16)) {
+		return 0;
+	}
+
+	int decoded_bytes = decode_frame(self);
+	bit_buffer_set_index(self->bits, (byte_pos + decoded_bytes) << 3);
+	return decoded_bytes;
+}
+
+
+
+
+int decode_frame(mp2_decoder_t *self) {
+	// Check for valid header: syncword OK, MPEG-Audio Layer 2
+	int sync = bit_buffer_read(self->bits, 11);
+	int version = bit_buffer_read(self->bits, 2);
+	int layer = bit_buffer_read(self->bits, 2);
+	int hasCRC = !bit_buffer_read(self->bits, 1);
+
+	if (
+		sync != FRAME_SYNC ||
+		version != VERSION_MPEG_1 ||
+		layer != LAYER_II
+	) {
+		return 0; // Invalid header or unsupported version
+	}
+
+	int bitrate_index = bit_buffer_read(self->bits, 4) - 1;
+	if (bitrate_index > 13) {
+		return 0;  // Invalid bit rate or 'free format'
+	}
+
+	int sample_rate_index = bit_buffer_read(self->bits, 2);
+	int sample_rate = SAMPLE_RATE[sample_rate_index];
+	if (sample_rate_index == 3) {
+		return 0; // Invalid sample rate
+	}
+	if (version == VERSION_MPEG_2) {
+		sample_rate_index += 4;
+		bitrate_index += 14;
+	}
+	int padding = bit_buffer_read(self->bits, 1),
+		privat = bit_buffer_read(self->bits, 1),
+		mode = bit_buffer_read(self->bits, 2);
+
+	// Parse the mode_extension, set up the stereo bound
+	int bound = 0;
+	if (mode == MODE_JOINT_STEREO) {
+		bound = (bit_buffer_read(self->bits, 2) + 1) << 2;
+	}
+	else {
+		bit_buffer_skip(self->bits, 2);
+		bound = (mode == MODE_MONO) ? 0 : 32;
+	}
+
+	// Discard the last 4 bits of the header and the CRC value, if present
+	bit_buffer_skip(self->bits, 4);
+	if (hasCRC) {
+		bit_buffer_skip(self->bits, 16);
+	}
+
+	// Compute the frame size
+	int bitrate = BIT_RATE[bitrate_index];
+	sample_rate = SAMPLE_RATE[sample_rate_index];
+	int frame_size = ((144000 * bitrate / sample_rate) + padding)|0;
+	
+
+	// Prepare the quantizer table lookups
+	int tab3 = 0;
+	int sblimit = 0;
+	if (version == VERSION_MPEG_2) {
+		// MPEG-2 (LSR)
+		tab3 = 2;
+		sblimit = 30;
+	}
+	else {
+		// MPEG-1
+		int tab1 = (mode == MODE_MONO) ? 0 : 1;
+		int tab2 = QUANT_LUT_STEP_1[tab1][bitrate_index];
+		tab3 = QUANT_LUT_STEP_2[tab2][sample_rate_index];
+		sblimit = tab3 & 63;
+		tab3 >>= 6;
+	}
+
+	if (bound > sblimit) {
+		bound = sblimit;
+	}
+
+	// Read the allocation information
+	for (int sb = 0; sb < bound; sb++) {
+		self->allocation[0][sb] = read_allocation(self, sb, tab3);
+		self->allocation[1][sb] = read_allocation(self, sb, tab3);
+	}
+
+	for (int sb = bound; sb < sblimit; sb++) {
+		self->allocation[0][sb] = 
+			self->allocation[1][sb] =
+			read_allocation(self, sb, tab3);
+	}
+
+	// Read scale factor selector information
+	int channels = (mode == MODE_MONO) ? 1 : 2;
+	for (int sb = 0;  sb < sblimit; sb++) {
+		for (int ch = 0;  ch < channels; ch++) {
+			if (self->allocation[ch][sb]) {
+				self->scale_factor_info[ch][sb] = bit_buffer_read(self->bits, 2);
+			}
+		}
+		if (mode == MODE_MONO) {
+			self->scale_factor_info[1][sb] = self->scale_factor_info[0][sb];
+		}
+	}
+
+	// Read scale factors
+	for (int sb = 0;  sb < sblimit; sb++) {
+		for (int ch = 0;  ch < channels; ch++) {
+			if (self->allocation[ch][sb]) {
+				int *sf = self->scale_factor[ch][sb];
+				switch (self->scale_factor_info[ch][sb]) {
+					case 0:
+						sf[0] = bit_buffer_read(self->bits, 6);
+						sf[1] = bit_buffer_read(self->bits, 6);
+						sf[2] = bit_buffer_read(self->bits, 6);
+						break;
+					case 1:
+						sf[0] =
+						sf[1] = bit_buffer_read(self->bits, 6);
+						sf[2] = bit_buffer_read(self->bits, 6);
+						break;
+					case 2:
+						sf[0] =
+						sf[1] =
+						sf[2] = bit_buffer_read(self->bits, 6);
+						break;
+					case 3:
+						sf[0] = bit_buffer_read(self->bits, 6);
+						sf[1] =
+						sf[2] = bit_buffer_read(self->bits, 6);
+						break;
+				}
+			}
+		}
+		if (mode == MODE_MONO) {
+			self->scale_factor[1][sb][0] = self->scale_factor[0][sb][0];
+			self->scale_factor[1][sb][1] = self->scale_factor[0][sb][1];
+			self->scale_factor[1][sb][2] = self->scale_factor[0][sb][2];
+		}
+	}
+
+	// Coefficient input and reconstruction
+	int out_pos = 0;
+	for (int part = 0; part < 3; part++) {
+		for (int granule = 0; granule < 4; granule++) {
+
+			// Read the samples
+			for (int sb = 0; sb < bound; sb++) {
+				read_samples(self, 0, sb, part);
+				read_samples(self, 1, sb, part);
+			}
+			for (int sb = bound; sb < sblimit; sb++) {
+				read_samples(self, 0, sb, part);
+				self->sample[1][sb][0] = self->sample[0][sb][0];
+				self->sample[1][sb][1] = self->sample[0][sb][1];
+				self->sample[1][sb][2] = self->sample[0][sb][2];
+			}
+			for (int sb = sblimit; sb < 32; sb++) {
+				self->sample[0][sb][0] = 0;
+				self->sample[0][sb][1] = 0;
+				self->sample[0][sb][2] = 0;
+				self->sample[1][sb][0] = 0;
+				self->sample[1][sb][1] = 0;
+				self->sample[1][sb][2] = 0;
+			}
+
+			// Synthesis loop
+			for (int p = 0; p < 3; p++) {
+				// Shifting step
+				self->v_pos = (self->v_pos - 64) & 1023;
+
+				for (int ch = 0;  ch < 2; ch++) {
+					matrix_transform(self->sample[ch], p, self->V[ch], self->v_pos);
+
+					// Build U, windowing, calculate output
+					memset(self->U, 0, sizeof(self->U));
+
+					int d_index = 512 - (self->v_pos >> 1);
+					int v_index = (self->v_pos % 128) >> 1;
+					while (v_index < 1024) {
+						for (int i = 0; i < 32; ++i) {
+							self->U[i] += self->D[d_index++] * self->V[ch][v_index++];
+						}
+
+						v_index += 128-32;
+						d_index += 64-32;
+					}
+
+					v_index = (128-32 + 1024) - v_index;
+					d_index -= (512 - 32);
+					while (v_index < 1024) {
+						for (int i = 0; i < 32; ++i) {
+							self->U[i] += self->D[d_index++] * self->V[ch][v_index++];
+						}
+
+						v_index += 128-32;
+						d_index += 64-32;
+					}
+
+					// Output samples
+					float *out_channel = ch == 0 
+						? self->channel_left 
+						: self->channel_right;
+					for (int j = 0; j < 32; j++) {
+						out_channel[out_pos + j] = (float)self->U[j] / 2147418112.0;
+					}
+				} // End of synthesis channel loop
+				out_pos += 32;
+			} // End of synthesis sub-block loop
+
+		} // Decoding of the granule finished
+	}
+
+	self->sample_rate = sample_rate;
+	return frame_size;
+}
+
+const quantizer_spec_t *read_allocation(mp2_decoder_t *self, int sb, int tab3) {
+	int tab4 = QUANT_LUT_STEP_3[tab3][sb];
+	int qtab = QUANT_LUT_STEP4[tab4 & 15][bit_buffer_read(self->bits, tab4 >> 4)];
+	return qtab ? (&QUANT_TAB[qtab - 1]) : 0;
+}
+
+void read_samples(mp2_decoder_t *self, int ch, int sb, int part) {
+	const quantizer_spec_t *q = self->allocation[ch][sb];
+	int sf = self->scale_factor[ch][sb][part];
+	int *sample = self->sample[ch][sb];
+	int val = 0;
+
+	if (!q) {
+		// No bits allocated for this subband
+		sample[0] = sample[1] = sample[2] = 0;
+		return;
+	}
+
+	// Resolve scalefactor
+	if (sf == 63) {
+		sf = 0;
+	}
+	else {
+		int shift = (sf / 3)|0;
+		sf = (SCALEFACTOR_BASE[sf % 3] + ((1 << shift) >> 1)) >> shift;
+	}
+
+	// Decode samples
+	int adj = q->levels;
+	if (q->group) {
+		// Decode grouped samples
+		val = bit_buffer_read(self->bits, q->bits);
+		sample[0] = val % adj;
+		val /= adj;
+		sample[1] = val % adj;
+		sample[2] = val / adj;
+	}
+	else {
+		// Decode direct samples
+		sample[0] = bit_buffer_read(self->bits, q->bits);
+		sample[1] = bit_buffer_read(self->bits, q->bits);
+		sample[2] = bit_buffer_read(self->bits, q->bits);
+	}
+
+	// Postmultiply samples
+	int scale = 65536 / (adj + 1);
+	adj = ((adj + 1) >> 1) - 1;
+
+	val = (adj - sample[0]) * scale;
+	sample[0] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
+
+	val = (adj - sample[1]) * scale;
+	sample[1] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
+
+	val = (adj - sample[2]) * scale;
+	sample[2] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
+}
+
+void matrix_transform(int s[32][3], int ss, float *d, int dp) {
+	float t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12,
+		t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24,
+		t25, t26, t27, t28, t29, t30, t31, t32, t33;
+
+	t01 = s[ 0][ss] + s[31][ss]; t02 = (float)(s[ 0][ss] - s[31][ss]) * 0.500602998235;
+	t03 = s[ 1][ss] + s[30][ss]; t04 = (float)(s[ 1][ss] - s[30][ss]) * 0.505470959898;
+	t05 = s[ 2][ss] + s[29][ss]; t06 = (float)(s[ 2][ss] - s[29][ss]) * 0.515447309923;
+	t07 = s[ 3][ss] + s[28][ss]; t08 = (float)(s[ 3][ss] - s[28][ss]) * 0.53104259109;
+	t09 = s[ 4][ss] + s[27][ss]; t10 = (float)(s[ 4][ss] - s[27][ss]) * 0.553103896034;
+	t11 = s[ 5][ss] + s[26][ss]; t12 = (float)(s[ 5][ss] - s[26][ss]) * 0.582934968206;
+	t13 = s[ 6][ss] + s[25][ss]; t14 = (float)(s[ 6][ss] - s[25][ss]) * 0.622504123036;
+	t15 = s[ 7][ss] + s[24][ss]; t16 = (float)(s[ 7][ss] - s[24][ss]) * 0.674808341455;
+	t17 = s[ 8][ss] + s[23][ss]; t18 = (float)(s[ 8][ss] - s[23][ss]) * 0.744536271002;
+	t19 = s[ 9][ss] + s[22][ss]; t20 = (float)(s[ 9][ss] - s[22][ss]) * 0.839349645416;
+	t21 = s[10][ss] + s[21][ss]; t22 = (float)(s[10][ss] - s[21][ss]) * 0.972568237862;
+	t23 = s[11][ss] + s[20][ss]; t24 = (float)(s[11][ss] - s[20][ss]) * 1.16943993343;
+	t25 = s[12][ss] + s[19][ss]; t26 = (float)(s[12][ss] - s[19][ss]) * 1.48416461631;
+	t27 = s[13][ss] + s[18][ss]; t28 = (float)(s[13][ss] - s[18][ss]) * 2.05778100995;
+	t29 = s[14][ss] + s[17][ss]; t30 = (float)(s[14][ss] - s[17][ss]) * 3.40760841847;
+	t31 = s[15][ss] + s[16][ss]; t32 = (float)(s[15][ss] - s[16][ss]) * 10.1900081235;
+
+	t33 = t01 + t31; t31 = (t01 - t31) * 0.502419286188;
+	t01 = t03 + t29; t29 = (t03 - t29) * 0.52249861494;
+	t03 = t05 + t27; t27 = (t05 - t27) * 0.566944034816;
+	t05 = t07 + t25; t25 = (t07 - t25) * 0.64682178336;
+	t07 = t09 + t23; t23 = (t09 - t23) * 0.788154623451;
+	t09 = t11 + t21; t21 = (t11 - t21) * 1.06067768599;
+	t11 = t13 + t19; t19 = (t13 - t19) * 1.72244709824;
+	t13 = t15 + t17; t17 = (t15 - t17) * 5.10114861869;
+	t15 = t33 + t13; t13 = (t33 - t13) * 0.509795579104;
+	t33 = t01 + t11; t01 = (t01 - t11) * 0.601344886935;
+	t11 = t03 + t09; t09 = (t03 - t09) * 0.899976223136;
+	t03 = t05 + t07; t07 = (t05 - t07) * 2.56291544774;
+	t05 = t15 + t03; t15 = (t15 - t03) * 0.541196100146;
+	t03 = t33 + t11; t11 = (t33 - t11) * 1.30656296488;
+	t33 = t05 + t03; t05 = (t05 - t03) * 0.707106781187;
+	t03 = t15 + t11; t15 = (t15 - t11) * 0.707106781187;
+	t03 += t15;
+	t11 = t13 + t07; t13 = (t13 - t07) * 0.541196100146;
+	t07 = t01 + t09; t09 = (t01 - t09) * 1.30656296488;
+	t01 = t11 + t07; t07 = (t11 - t07) * 0.707106781187;
+	t11 = t13 + t09; t13 = (t13 - t09) * 0.707106781187;
+	t11 += t13; t01 += t11; 
+	t11 += t07; t07 += t13;
+	t09 = t31 + t17; t31 = (t31 - t17) * 0.509795579104;
+	t17 = t29 + t19; t29 = (t29 - t19) * 0.601344886935;
+	t19 = t27 + t21; t21 = (t27 - t21) * 0.899976223136;
+	t27 = t25 + t23; t23 = (t25 - t23) * 2.56291544774;
+	t25 = t09 + t27; t09 = (t09 - t27) * 0.541196100146;
+	t27 = t17 + t19; t19 = (t17 - t19) * 1.30656296488;
+	t17 = t25 + t27; t27 = (t25 - t27) * 0.707106781187;
+	t25 = t09 + t19; t19 = (t09 - t19) * 0.707106781187;
+	t25 += t19;
+	t09 = t31 + t23; t31 = (t31 - t23) * 0.541196100146;
+	t23 = t29 + t21; t21 = (t29 - t21) * 1.30656296488;
+	t29 = t09 + t23; t23 = (t09 - t23) * 0.707106781187;
+	t09 = t31 + t21; t31 = (t31 - t21) * 0.707106781187;
+	t09 += t31;	t29 += t09;	t09 += t23;	t23 += t31;
+	t17 += t29;	t29 += t25;	t25 += t09;	t09 += t27;
+	t27 += t23;	t23 += t19; t19 += t31;	
+	t21 = t02 + t32; t02 = (t02 - t32) * 0.502419286188;
+	t32 = t04 + t30; t04 = (t04 - t30) * 0.52249861494;
+	t30 = t06 + t28; t28 = (t06 - t28) * 0.566944034816;
+	t06 = t08 + t26; t08 = (t08 - t26) * 0.64682178336;
+	t26 = t10 + t24; t10 = (t10 - t24) * 0.788154623451;
+	t24 = t12 + t22; t22 = (t12 - t22) * 1.06067768599;
+	t12 = t14 + t20; t20 = (t14 - t20) * 1.72244709824;
+	t14 = t16 + t18; t16 = (t16 - t18) * 5.10114861869;
+	t18 = t21 + t14; t14 = (t21 - t14) * 0.509795579104;
+	t21 = t32 + t12; t32 = (t32 - t12) * 0.601344886935;
+	t12 = t30 + t24; t24 = (t30 - t24) * 0.899976223136;
+	t30 = t06 + t26; t26 = (t06 - t26) * 2.56291544774;
+	t06 = t18 + t30; t18 = (t18 - t30) * 0.541196100146;
+	t30 = t21 + t12; t12 = (t21 - t12) * 1.30656296488;
+	t21 = t06 + t30; t30 = (t06 - t30) * 0.707106781187;
+	t06 = t18 + t12; t12 = (t18 - t12) * 0.707106781187;
+	t06 += t12;
+	t18 = t14 + t26; t26 = (t14 - t26) * 0.541196100146;
+	t14 = t32 + t24; t24 = (t32 - t24) * 1.30656296488;
+	t32 = t18 + t14; t14 = (t18 - t14) * 0.707106781187;
+	t18 = t26 + t24; t24 = (t26 - t24) * 0.707106781187;
+	t18 += t24; t32 += t18; 
+	t18 += t14; t26 = t14 + t24;
+	t14 = t02 + t16; t02 = (t02 - t16) * 0.509795579104;
+	t16 = t04 + t20; t04 = (t04 - t20) * 0.601344886935;
+	t20 = t28 + t22; t22 = (t28 - t22) * 0.899976223136;
+	t28 = t08 + t10; t10 = (t08 - t10) * 2.56291544774;
+	t08 = t14 + t28; t14 = (t14 - t28) * 0.541196100146;
+	t28 = t16 + t20; t20 = (t16 - t20) * 1.30656296488;
+	t16 = t08 + t28; t28 = (t08 - t28) * 0.707106781187;
+	t08 = t14 + t20; t20 = (t14 - t20) * 0.707106781187;
+	t08 += t20;
+	t14 = t02 + t10; t02 = (t02 - t10) * 0.541196100146;
+	t10 = t04 + t22; t22 = (t04 - t22) * 1.30656296488;
+	t04 = t14 + t10; t10 = (t14 - t10) * 0.707106781187;
+	t14 = t02 + t22; t02 = (t02 - t22) * 0.707106781187;
+	t14 += t02;	t04 += t14;	t14 += t10;	t10 += t02;
+	t16 += t04;	t04 += t08;	t08 += t14;	t14 += t28;
+	t28 += t10;	t10 += t20;	t20 += t02;	t21 += t16;
+	t16 += t32;	t32 += t04;	t04 += t06;	t06 += t08;
+	t08 += t18;	t18 += t14;	t14 += t30;	t30 += t28;
+	t28 += t26;	t26 += t10;	t10 += t12;	t12 += t20;
+	t20 += t24;	t24 += t02;
+
+	d[dp + 48] = -t33;
+	d[dp + 49] = d[dp + 47] = -t21;
+	d[dp + 50] = d[dp + 46] = -t17;
+	d[dp + 51] = d[dp + 45] = -t16;
+	d[dp + 52] = d[dp + 44] = -t01;
+	d[dp + 53] = d[dp + 43] = -t32;
+	d[dp + 54] = d[dp + 42] = -t29;
+	d[dp + 55] = d[dp + 41] = -t04;
+	d[dp + 56] = d[dp + 40] = -t03;
+	d[dp + 57] = d[dp + 39] = -t06;
+	d[dp + 58] = d[dp + 38] = -t25;
+	d[dp + 59] = d[dp + 37] = -t08;
+	d[dp + 60] = d[dp + 36] = -t11;
+	d[dp + 61] = d[dp + 35] = -t18;
+	d[dp + 62] = d[dp + 34] = -t09;
+	d[dp + 63] = d[dp + 33] = -t14;
+	d[dp + 32] = -t05;
+	d[dp +  0] = t05; d[dp + 31] = -t30;
+	d[dp +  1] = t30; d[dp + 30] = -t27;
+	d[dp +  2] = t27; d[dp + 29] = -t28;
+	d[dp +  3] = t28; d[dp + 28] = -t07;
+	d[dp +  4] = t07; d[dp + 27] = -t26;
+	d[dp +  5] = t26; d[dp + 26] = -t23;
+	d[dp +  6] = t23; d[dp + 25] = -t10;
+	d[dp +  7] = t10; d[dp + 24] = -t15;
+	d[dp +  8] = t15; d[dp + 23] = -t12;
+	d[dp +  9] = t12; d[dp + 22] = -t19;
+	d[dp + 10] = t19; d[dp + 21] = -t20;
+	d[dp + 11] = t20; d[dp + 20] = -t13;
+	d[dp + 12] = t13; d[dp + 19] = -t24;
+	d[dp + 13] = t24; d[dp + 18] = -t31;
+	d[dp + 14] = t31; d[dp + 17] = -t02;
+	d[dp + 15] = t02; d[dp + 16] =  0.0;
+};
+

+ 22 - 0
jsmpeg-master/src/wasm/mp2.h

@@ -0,0 +1,22 @@
+#ifndef MP2_H
+#define MP2_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "buffer.h"
+
+typedef struct mp2_decoder_t mp2_decoder_t;
+
+mp2_decoder_t *mp2_decoder_create(unsigned int buffer_size, bit_buffer_mode_t buffer_mode);
+void mp2_decoder_destroy(mp2_decoder_t *self);
+void *mp2_decoder_get_write_ptr(mp2_decoder_t *self, unsigned int byte_size);
+int mp2_decoder_get_index(mp2_decoder_t *self);
+void mp2_decoder_set_index(mp2_decoder_t *self, unsigned int index);
+void mp2_decoder_did_write(mp2_decoder_t *self, unsigned int byte_size);
+
+void *mp2_decoder_get_left_channel_ptr(mp2_decoder_t *self);
+void *mp2_decoder_get_right_channel_ptr(mp2_decoder_t *self);
+int mp2_decoder_get_sample_rate(mp2_decoder_t *self);
+int mp2_decoder_decode(mp2_decoder_t *self);
+
+#endif

+ 1748 - 0
jsmpeg-master/src/wasm/mpeg1.c

@@ -0,0 +1,1748 @@
+#include <string.h>
+#include <stdlib.h>
+#include "mpeg1.h"
+
+static const float PICTURE_RATE[] = {
+	0.000, 23.976, 24.000, 25.000, 29.970, 30.000, 50.000, 59.940,
+	60.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
+};
+
+static const uint8_t ZIG_ZAG[] = {
+	 0,  1,  8, 16,  9,  2,  3, 10,
+	17, 24, 32, 25, 18, 11,  4,  5,
+	12, 19, 26, 33, 40, 48, 41, 34,
+	27, 20, 13,  6,  7, 14, 21, 28,
+	35, 42, 49, 56, 57, 50, 43, 36,
+	29, 22, 15, 23, 30, 37, 44, 51,
+	58, 59, 52, 45, 38, 31, 39, 46,
+	53, 60, 61, 54, 47, 55, 62, 63
+};
+
+static const uint8_t DEFAULT_INTRA_QUANT_MATRIX[] = {
+	 8, 16, 19, 22, 26, 27, 29, 34,
+	16, 16, 22, 24, 27, 29, 34, 37,
+	19, 22, 26, 27, 29, 34, 34, 38,
+	22, 22, 26, 27, 29, 34, 37, 40,
+	22, 26, 27, 29, 32, 35, 40, 48,
+	26, 27, 29, 32, 35, 40, 48, 58,
+	26, 27, 29, 34, 38, 46, 56, 69,
+	27, 29, 35, 38, 46, 56, 69, 83
+};
+
+static const uint8_t DEFAULT_NON_INTRA_QUANT_MATRIX[] = {
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16
+};
+
+static const uint8_t PREMULTIPLIER_MATRIX[] = {
+	32, 44, 42, 38, 32, 25, 17,  9,
+	44, 62, 58, 52, 44, 35, 24, 12,
+	42, 58, 55, 49, 42, 33, 23, 12,
+	38, 52, 49, 44, 38, 30, 20, 10,
+	32, 44, 42, 38, 32, 25, 17,  9,
+	25, 35, 33, 30, 25, 20, 14,  7,
+	17, 24, 23, 20, 17, 14,  9,  5,
+	 9, 12, 12, 10,  9,  7,  5,  2
+};
+
+// MPEG-1 VLC
+
+//  macroblock_stuffing decodes as 34.
+//  macroblock_escape decodes as 35.
+
+static const int MACROBLOCK_ADDRESS_INCREMENT[] = {
+	 1*3,  2*3,  0, //   0
+	 3*3,  4*3,  0, //   1  0
+	   0,    0,  1, //   2  1.
+	 5*3,  6*3,  0, //   3  00
+	 7*3,  8*3,  0, //   4  01
+	 9*3, 10*3,  0, //   5  000
+	11*3, 12*3,  0, //   6  001
+	   0,    0,  3, //   7  010.
+	   0,    0,  2, //   8  011.
+	13*3, 14*3,  0, //   9  0000
+	15*3, 16*3,  0, //  10  0001
+	   0,    0,  5, //  11  0010.
+	   0,    0,  4, //  12  0011.
+	17*3, 18*3,  0, //  13  0000 0
+	19*3, 20*3,  0, //  14  0000 1
+	   0,    0,  7, //  15  0001 0.
+	   0,    0,  6, //  16  0001 1.
+	21*3, 22*3,  0, //  17  0000 00
+	23*3, 24*3,  0, //  18  0000 01
+	25*3, 26*3,  0, //  19  0000 10
+	27*3, 28*3,  0, //  20  0000 11
+	  -1, 29*3,  0, //  21  0000 000
+	  -1, 30*3,  0, //  22  0000 001
+	31*3, 32*3,  0, //  23  0000 010
+	33*3, 34*3,  0, //  24  0000 011
+	35*3, 36*3,  0, //  25  0000 100
+	37*3, 38*3,  0, //  26  0000 101
+	   0,    0,  9, //  27  0000 110.
+	   0,    0,  8, //  28  0000 111.
+	39*3, 40*3,  0, //  29  0000 0001
+	41*3, 42*3,  0, //  30  0000 0011
+	43*3, 44*3,  0, //  31  0000 0100
+	45*3, 46*3,  0, //  32  0000 0101
+	   0,    0, 15, //  33  0000 0110.
+	   0,    0, 14, //  34  0000 0111.
+	   0,    0, 13, //  35  0000 1000.
+	   0,    0, 12, //  36  0000 1001.
+	   0,    0, 11, //  37  0000 1010.
+	   0,    0, 10, //  38  0000 1011.
+	47*3,   -1,  0, //  39  0000 0001 0
+	  -1, 48*3,  0, //  40  0000 0001 1
+	49*3, 50*3,  0, //  41  0000 0011 0
+	51*3, 52*3,  0, //  42  0000 0011 1
+	53*3, 54*3,  0, //  43  0000 0100 0
+	55*3, 56*3,  0, //  44  0000 0100 1
+	57*3, 58*3,  0, //  45  0000 0101 0
+	59*3, 60*3,  0, //  46  0000 0101 1
+	61*3,   -1,  0, //  47  0000 0001 00
+	  -1, 62*3,  0, //  48  0000 0001 11
+	63*3, 64*3,  0, //  49  0000 0011 00
+	65*3, 66*3,  0, //  50  0000 0011 01
+	67*3, 68*3,  0, //  51  0000 0011 10
+	69*3, 70*3,  0, //  52  0000 0011 11
+	71*3, 72*3,  0, //  53  0000 0100 00
+	73*3, 74*3,  0, //  54  0000 0100 01
+	   0,    0, 21, //  55  0000 0100 10.
+	   0,    0, 20, //  56  0000 0100 11.
+	   0,    0, 19, //  57  0000 0101 00.
+	   0,    0, 18, //  58  0000 0101 01.
+	   0,    0, 17, //  59  0000 0101 10.
+	   0,    0, 16, //  60  0000 0101 11.
+	   0,    0, 35, //  61  0000 0001 000. -- macroblock_escape
+	   0,    0, 34, //  62  0000 0001 111. -- macroblock_stuffing
+	   0,    0, 33, //  63  0000 0011 000.
+	   0,    0, 32, //  64  0000 0011 001.
+	   0,    0, 31, //  65  0000 0011 010.
+	   0,    0, 30, //  66  0000 0011 011.
+	   0,    0, 29, //  67  0000 0011 100.
+	   0,    0, 28, //  68  0000 0011 101.
+	   0,    0, 27, //  69  0000 0011 110.
+	   0,    0, 26, //  70  0000 0011 111.
+	   0,    0, 25, //  71  0000 0100 000.
+	   0,    0, 24, //  72  0000 0100 001.
+	   0,    0, 23, //  73  0000 0100 010.
+	   0,    0, 22  //  74  0000 0100 011.
+};
+
+//  macroblock_type bitmap:
+//    0x10  macroblock_quant
+//    0x08  macroblock_motion_forward
+//    0x04  macroblock_motion_backward
+//    0x02  macrobkock_pattern
+//    0x01  macroblock_intra
+//
+
+static const int MACROBLOCK_TYPE_INTRA[] = {
+	 1*3,  2*3,     0, //   0
+	  -1,  3*3,     0, //   1  0
+	   0,    0,  0x01, //   2  1.
+	   0,    0,  0x11  //   3  01.
+};
+
+static const int MACROBLOCK_TYPE_PREDICTIVE[] = {
+	 1*3,  2*3,     0, //  0
+	 3*3,  4*3,     0, //  1  0
+	   0,    0,  0x0a, //  2  1.
+	 5*3,  6*3,     0, //  3  00
+	   0,    0,  0x02, //  4  01.
+	 7*3,  8*3,     0, //  5  000
+	   0,    0,  0x08, //  6  001.
+	 9*3, 10*3,     0, //  7  0000
+	11*3, 12*3,     0, //  8  0001
+	  -1, 13*3,     0, //  9  00000
+	   0,    0,  0x12, // 10  00001.
+	   0,    0,  0x1a, // 11  00010.
+	   0,    0,  0x01, // 12  00011.
+	   0,    0,  0x11  // 13  000001.
+};
+
+static const int MACROBLOCK_TYPE_B[] = {
+	 1*3,  2*3,     0,  //  0
+	 3*3,  5*3,     0,  //  1  0
+	 4*3,  6*3,     0,  //  2  1
+	 8*3,  7*3,     0,  //  3  00
+	   0,    0,  0x0c,  //  4  10.
+	 9*3, 10*3,     0,  //  5  01
+	   0,    0,  0x0e,  //  6  11.
+	13*3, 14*3,     0,  //  7  001
+	12*3, 11*3,     0,  //  8  000
+	   0,    0,  0x04,  //  9  010.
+	   0,    0,  0x06,  // 10  011.
+	18*3, 16*3,     0,  // 11  0001
+	15*3, 17*3,     0,  // 12  0000
+	   0,    0,  0x08,  // 13  0010.
+	   0,    0,  0x0a,  // 14  0011.
+	  -1, 19*3,     0,  // 15  00000
+	   0,    0,  0x01,  // 16  00011.
+	20*3, 21*3,     0,  // 17  00001
+	   0,    0,  0x1e,  // 18  00010.
+	   0,    0,  0x11,  // 19  000001.
+	   0,    0,  0x16,  // 20  000010.
+	   0,    0,  0x1a   // 21  000011.
+};
+
+static const int *MACROBLOCK_TYPE[] = {
+	NULL,
+	MACROBLOCK_TYPE_INTRA,
+	MACROBLOCK_TYPE_PREDICTIVE,
+	MACROBLOCK_TYPE_B
+};
+
+static const int CODE_BLOCK_PATTERN[] = {
+	  2*3,   1*3,   0,  //   0
+	  3*3,   6*3,   0,  //   1  1
+	  4*3,   5*3,   0,  //   2  0
+	  8*3,  11*3,   0,  //   3  10
+	 12*3,  13*3,   0,  //   4  00
+	  9*3,   7*3,   0,  //   5  01
+	 10*3,  14*3,   0,  //   6  11
+	 20*3,  19*3,   0,  //   7  011
+	 18*3,  16*3,   0,  //   8  100
+	 23*3,  17*3,   0,  //   9  010
+	 27*3,  25*3,   0,  //  10  110
+	 21*3,  28*3,   0,  //  11  101
+	 15*3,  22*3,   0,  //  12  000
+	 24*3,  26*3,   0,  //  13  001
+	    0,     0,  60,  //  14  111.
+	 35*3,  40*3,   0,  //  15  0000
+	 44*3,  48*3,   0,  //  16  1001
+	 38*3,  36*3,   0,  //  17  0101
+	 42*3,  47*3,   0,  //  18  1000
+	 29*3,  31*3,   0,  //  19  0111
+	 39*3,  32*3,   0,  //  20  0110
+	    0,     0,  32,  //  21  1010.
+	 45*3,  46*3,   0,  //  22  0001
+	 33*3,  41*3,   0,  //  23  0100
+	 43*3,  34*3,   0,  //  24  0010
+	    0,     0,   4,  //  25  1101.
+	 30*3,  37*3,   0,  //  26  0011
+	    0,     0,   8,  //  27  1100.
+	    0,     0,  16,  //  28  1011.
+	    0,     0,  44,  //  29  0111 0.
+	 50*3,  56*3,   0,  //  30  0011 0
+	    0,     0,  28,  //  31  0111 1.
+	    0,     0,  52,  //  32  0110 1.
+	    0,     0,  62,  //  33  0100 0.
+	 61*3,  59*3,   0,  //  34  0010 1
+	 52*3,  60*3,   0,  //  35  0000 0
+	    0,     0,   1,  //  36  0101 1.
+	 55*3,  54*3,   0,  //  37  0011 1
+	    0,     0,  61,  //  38  0101 0.
+	    0,     0,  56,  //  39  0110 0.
+	 57*3,  58*3,   0,  //  40  0000 1
+	    0,     0,   2,  //  41  0100 1.
+	    0,     0,  40,  //  42  1000 0.
+	 51*3,  62*3,   0,  //  43  0010 0
+	    0,     0,  48,  //  44  1001 0.
+	 64*3,  63*3,   0,  //  45  0001 0
+	 49*3,  53*3,   0,  //  46  0001 1
+	    0,     0,  20,  //  47  1000 1.
+	    0,     0,  12,  //  48  1001 1.
+	 80*3,  83*3,   0,  //  49  0001 10
+	    0,     0,  63,  //  50  0011 00.
+	 77*3,  75*3,   0,  //  51  0010 00
+	 65*3,  73*3,   0,  //  52  0000 00
+	 84*3,  66*3,   0,  //  53  0001 11
+	    0,     0,  24,  //  54  0011 11.
+	    0,     0,  36,  //  55  0011 10.
+	    0,     0,   3,  //  56  0011 01.
+	 69*3,  87*3,   0,  //  57  0000 10
+	 81*3,  79*3,   0,  //  58  0000 11
+	 68*3,  71*3,   0,  //  59  0010 11
+	 70*3,  78*3,   0,  //  60  0000 01
+	 67*3,  76*3,   0,  //  61  0010 10
+	 72*3,  74*3,   0,  //  62  0010 01
+	 86*3,  85*3,   0,  //  63  0001 01
+	 88*3,  82*3,   0,  //  64  0001 00
+	   -1,  94*3,   0,  //  65  0000 000
+	 95*3,  97*3,   0,  //  66  0001 111
+	    0,     0,  33,  //  67  0010 100.
+	    0,     0,   9,  //  68  0010 110.
+	106*3, 110*3,   0,  //  69  0000 100
+	102*3, 116*3,   0,  //  70  0000 010
+	    0,     0,   5,  //  71  0010 111.
+	    0,     0,  10,  //  72  0010 010.
+	 93*3,  89*3,   0,  //  73  0000 001
+	    0,     0,   6,  //  74  0010 011.
+	    0,     0,  18,  //  75  0010 001.
+	    0,     0,  17,  //  76  0010 101.
+	    0,     0,  34,  //  77  0010 000.
+	113*3, 119*3,   0,  //  78  0000 011
+	103*3, 104*3,   0,  //  79  0000 111
+	 90*3,  92*3,   0,  //  80  0001 100
+	109*3, 107*3,   0,  //  81  0000 110
+	117*3, 118*3,   0,  //  82  0001 001
+	101*3,  99*3,   0,  //  83  0001 101
+	 98*3,  96*3,   0,  //  84  0001 110
+	100*3,  91*3,   0,  //  85  0001 011
+	114*3, 115*3,   0,  //  86  0001 010
+	105*3, 108*3,   0,  //  87  0000 101
+	112*3, 111*3,   0,  //  88  0001 000
+	121*3, 125*3,   0,  //  89  0000 0011
+	    0,     0,  41,  //  90  0001 1000.
+	    0,     0,  14,  //  91  0001 0111.
+	    0,     0,  21,  //  92  0001 1001.
+	124*3, 122*3,   0,  //  93  0000 0010
+	120*3, 123*3,   0,  //  94  0000 0001
+	    0,     0,  11,  //  95  0001 1110.
+	    0,     0,  19,  //  96  0001 1101.
+	    0,     0,   7,  //  97  0001 1111.
+	    0,     0,  35,  //  98  0001 1100.
+	    0,     0,  13,  //  99  0001 1011.
+	    0,     0,  50,  // 100  0001 0110.
+	    0,     0,  49,  // 101  0001 1010.
+	    0,     0,  58,  // 102  0000 0100.
+	    0,     0,  37,  // 103  0000 1110.
+	    0,     0,  25,  // 104  0000 1111.
+	    0,     0,  45,  // 105  0000 1010.
+	    0,     0,  57,  // 106  0000 1000.
+	    0,     0,  26,  // 107  0000 1101.
+	    0,     0,  29,  // 108  0000 1011.
+	    0,     0,  38,  // 109  0000 1100.
+	    0,     0,  53,  // 110  0000 1001.
+	    0,     0,  23,  // 111  0001 0001.
+	    0,     0,  43,  // 112  0001 0000.
+	    0,     0,  46,  // 113  0000 0110.
+	    0,     0,  42,  // 114  0001 0100.
+	    0,     0,  22,  // 115  0001 0101.
+	    0,     0,  54,  // 116  0000 0101.
+	    0,     0,  51,  // 117  0001 0010.
+	    0,     0,  15,  // 118  0001 0011.
+	    0,     0,  30,  // 119  0000 0111.
+	    0,     0,  39,  // 120  0000 0001 0.
+	    0,     0,  47,  // 121  0000 0011 0.
+	    0,     0,  55,  // 122  0000 0010 1.
+	    0,     0,  27,  // 123  0000 0001 1.
+	    0,     0,  59,  // 124  0000 0010 0.
+	    0,     0,  31   // 125  0000 0011 1.
+};
+
+static const int MOTION[] = {
+	  1*3,   2*3,   0,  //   0
+	  4*3,   3*3,   0,  //   1  0
+	    0,     0,   0,  //   2  1.
+	  6*3,   5*3,   0,  //   3  01
+	  8*3,   7*3,   0,  //   4  00
+	    0,     0,  -1,  //   5  011.
+	    0,     0,   1,  //   6  010.
+	  9*3,  10*3,   0,  //   7  001
+	 12*3,  11*3,   0,  //   8  000
+	    0,     0,   2,  //   9  0010.
+	    0,     0,  -2,  //  10  0011.
+	 14*3,  15*3,   0,  //  11  0001
+	 16*3,  13*3,   0,  //  12  0000
+	 20*3,  18*3,   0,  //  13  0000 1
+	    0,     0,   3,  //  14  0001 0.
+	    0,     0,  -3,  //  15  0001 1.
+	 17*3,  19*3,   0,  //  16  0000 0
+	   -1,  23*3,   0,  //  17  0000 00
+	 27*3,  25*3,   0,  //  18  0000 11
+	 26*3,  21*3,   0,  //  19  0000 01
+	 24*3,  22*3,   0,  //  20  0000 10
+	 32*3,  28*3,   0,  //  21  0000 011
+	 29*3,  31*3,   0,  //  22  0000 101
+	   -1,  33*3,   0,  //  23  0000 001
+	 36*3,  35*3,   0,  //  24  0000 100
+	    0,     0,  -4,  //  25  0000 111.
+	 30*3,  34*3,   0,  //  26  0000 010
+	    0,     0,   4,  //  27  0000 110.
+	    0,     0,  -7,  //  28  0000 0111.
+	    0,     0,   5,  //  29  0000 1010.
+	 37*3,  41*3,   0,  //  30  0000 0100
+	    0,     0,  -5,  //  31  0000 1011.
+	    0,     0,   7,  //  32  0000 0110.
+	 38*3,  40*3,   0,  //  33  0000 0011
+	 42*3,  39*3,   0,  //  34  0000 0101
+	    0,     0,  -6,  //  35  0000 1001.
+	    0,     0,   6,  //  36  0000 1000.
+	 51*3,  54*3,   0,  //  37  0000 0100 0
+	 50*3,  49*3,   0,  //  38  0000 0011 0
+	 45*3,  46*3,   0,  //  39  0000 0101 1
+	 52*3,  47*3,   0,  //  40  0000 0011 1
+	 43*3,  53*3,   0,  //  41  0000 0100 1
+	 44*3,  48*3,   0,  //  42  0000 0101 0
+	    0,     0,  10,  //  43  0000 0100 10.
+	    0,     0,   9,  //  44  0000 0101 00.
+	    0,     0,   8,  //  45  0000 0101 10.
+	    0,     0,  -8,  //  46  0000 0101 11.
+	 57*3,  66*3,   0,  //  47  0000 0011 11
+	    0,     0,  -9,  //  48  0000 0101 01.
+	 60*3,  64*3,   0,  //  49  0000 0011 01
+	 56*3,  61*3,   0,  //  50  0000 0011 00
+	 55*3,  62*3,   0,  //  51  0000 0100 00
+	 58*3,  63*3,   0,  //  52  0000 0011 10
+	    0,     0, -10,  //  53  0000 0100 11.
+	 59*3,  65*3,   0,  //  54  0000 0100 01
+	    0,     0,  12,  //  55  0000 0100 000.
+	    0,     0,  16,  //  56  0000 0011 000.
+	    0,     0,  13,  //  57  0000 0011 110.
+	    0,     0,  14,  //  58  0000 0011 100.
+	    0,     0,  11,  //  59  0000 0100 010.
+	    0,     0,  15,  //  60  0000 0011 010.
+	    0,     0, -16,  //  61  0000 0011 001.
+	    0,     0, -12,  //  62  0000 0100 001.
+	    0,     0, -14,  //  63  0000 0011 101.
+	    0,     0, -15,  //  64  0000 0011 011.
+	    0,     0, -11,  //  65  0000 0100 011.
+	    0,     0, -13   //  66  0000 0011 111.
+};
+
+static const int DCT_DC_SIZE_LUMINANCE[] = {
+	  2*3,   1*3, 0,  //   0
+	  6*3,   5*3, 0,  //   1  1
+	  3*3,   4*3, 0,  //   2  0
+	    0,     0, 1,  //   3  00.
+	    0,     0, 2,  //   4  01.
+	  9*3,   8*3, 0,  //   5  11
+	  7*3,  10*3, 0,  //   6  10
+	    0,     0, 0,  //   7  100.
+	 12*3,  11*3, 0,  //   8  111
+	    0,     0, 4,  //   9  110.
+	    0,     0, 3,  //  10  101.
+	 13*3,  14*3, 0,  //  11  1111
+	    0,     0, 5,  //  12  1110.
+	    0,     0, 6,  //  13  1111 0.
+	 16*3,  15*3, 0,  //  14  1111 1
+	 17*3,    -1, 0,  //  15  1111 11
+	    0,     0, 7,  //  16  1111 10.
+	    0,     0, 8   //  17  1111 110.
+};
+
+static const int DCT_DC_SIZE_CHROMINANCE[] = {
+	  2*3,   1*3, 0,  //   0
+	  4*3,   3*3, 0,  //   1  1
+	  6*3,   5*3, 0,  //   2  0
+	  8*3,   7*3, 0,  //   3  11
+	    0,     0, 2,  //   4  10.
+	    0,     0, 1,  //   5  01.
+	    0,     0, 0,  //   6  00.
+	 10*3,   9*3, 0,  //   7  111
+	    0,     0, 3,  //   8  110.
+	 12*3,  11*3, 0,  //   9  1111
+	    0,     0, 4,  //  10  1110.
+	 14*3,  13*3, 0,  //  11  1111 1
+	    0,     0, 5,  //  12  1111 0.
+	 16*3,  15*3, 0,  //  13  1111 11
+	    0,     0, 6,  //  14  1111 10.
+	 17*3,    -1, 0,  //  15  1111 111
+	    0,     0, 7,  //  16  1111 110.
+	    0,     0, 8   //  17  1111 1110.
+};
+
+//  dct_coeff bitmap:
+//    0xff00  run
+//    0x00ff  level
+
+//  Decoded values are unsigned. Sign bit follows in the stream.
+
+//  Interpretation of the value 0x0001
+//    for dc_coeff_first:  run=0, level=1
+//    for dc_coeff_next:   If the next bit is 1: run=0, level=1
+//                         If the next bit is 0: end_of_block
+
+//  escape decodes as 0xffff.
+
+static const int DCT_COEFF[] = {
+	  1*3,   2*3,      0,  //   0
+	  4*3,   3*3,      0,  //   1  0
+	    0,     0, 0x0001,  //   2  1.
+	  7*3,   8*3,      0,  //   3  01
+	  6*3,   5*3,      0,  //   4  00
+	 13*3,   9*3,      0,  //   5  001
+	 11*3,  10*3,      0,  //   6  000
+	 14*3,  12*3,      0,  //   7  010
+	    0,     0, 0x0101,  //   8  011.
+	 20*3,  22*3,      0,  //   9  0011
+	 18*3,  21*3,      0,  //  10  0001
+	 16*3,  19*3,      0,  //  11  0000
+	    0,     0, 0x0201,  //  12  0101.
+	 17*3,  15*3,      0,  //  13  0010
+	    0,     0, 0x0002,  //  14  0100.
+	    0,     0, 0x0003,  //  15  0010 1.
+	 27*3,  25*3,      0,  //  16  0000 0
+	 29*3,  31*3,      0,  //  17  0010 0
+	 24*3,  26*3,      0,  //  18  0001 0
+	 32*3,  30*3,      0,  //  19  0000 1
+	    0,     0, 0x0401,  //  20  0011 0.
+	 23*3,  28*3,      0,  //  21  0001 1
+	    0,     0, 0x0301,  //  22  0011 1.
+	    0,     0, 0x0102,  //  23  0001 10.
+	    0,     0, 0x0701,  //  24  0001 00.
+	    0,     0, 0xffff,  //  25  0000 01. -- escape
+	    0,     0, 0x0601,  //  26  0001 01.
+	 37*3,  36*3,      0,  //  27  0000 00
+	    0,     0, 0x0501,  //  28  0001 11.
+	 35*3,  34*3,      0,  //  29  0010 00
+	 39*3,  38*3,      0,  //  30  0000 11
+	 33*3,  42*3,      0,  //  31  0010 01
+	 40*3,  41*3,      0,  //  32  0000 10
+	 52*3,  50*3,      0,  //  33  0010 010
+	 54*3,  53*3,      0,  //  34  0010 001
+	 48*3,  49*3,      0,  //  35  0010 000
+	 43*3,  45*3,      0,  //  36  0000 001
+	 46*3,  44*3,      0,  //  37  0000 000
+	    0,     0, 0x0801,  //  38  0000 111.
+	    0,     0, 0x0004,  //  39  0000 110.
+	    0,     0, 0x0202,  //  40  0000 100.
+	    0,     0, 0x0901,  //  41  0000 101.
+	 51*3,  47*3,      0,  //  42  0010 011
+	 55*3,  57*3,      0,  //  43  0000 0010
+	 60*3,  56*3,      0,  //  44  0000 0001
+	 59*3,  58*3,      0,  //  45  0000 0011
+	 61*3,  62*3,      0,  //  46  0000 0000
+	    0,     0, 0x0a01,  //  47  0010 0111.
+	    0,     0, 0x0d01,  //  48  0010 0000.
+	    0,     0, 0x0006,  //  49  0010 0001.
+	    0,     0, 0x0103,  //  50  0010 0101.
+	    0,     0, 0x0005,  //  51  0010 0110.
+	    0,     0, 0x0302,  //  52  0010 0100.
+	    0,     0, 0x0b01,  //  53  0010 0011.
+	    0,     0, 0x0c01,  //  54  0010 0010.
+	 76*3,  75*3,      0,  //  55  0000 0010 0
+	 67*3,  70*3,      0,  //  56  0000 0001 1
+	 73*3,  71*3,      0,  //  57  0000 0010 1
+	 78*3,  74*3,      0,  //  58  0000 0011 1
+	 72*3,  77*3,      0,  //  59  0000 0011 0
+	 69*3,  64*3,      0,  //  60  0000 0001 0
+	 68*3,  63*3,      0,  //  61  0000 0000 0
+	 66*3,  65*3,      0,  //  62  0000 0000 1
+	 81*3,  87*3,      0,  //  63  0000 0000 01
+	 91*3,  80*3,      0,  //  64  0000 0001 01
+	 82*3,  79*3,      0,  //  65  0000 0000 11
+	 83*3,  86*3,      0,  //  66  0000 0000 10
+	 93*3,  92*3,      0,  //  67  0000 0001 10
+	 84*3,  85*3,      0,  //  68  0000 0000 00
+	 90*3,  94*3,      0,  //  69  0000 0001 00
+	 88*3,  89*3,      0,  //  70  0000 0001 11
+	    0,     0, 0x0203,  //  71  0000 0010 11.
+	    0,     0, 0x0104,  //  72  0000 0011 00.
+	    0,     0, 0x0007,  //  73  0000 0010 10.
+	    0,     0, 0x0402,  //  74  0000 0011 11.
+	    0,     0, 0x0502,  //  75  0000 0010 01.
+	    0,     0, 0x1001,  //  76  0000 0010 00.
+	    0,     0, 0x0f01,  //  77  0000 0011 01.
+	    0,     0, 0x0e01,  //  78  0000 0011 10.
+	105*3, 107*3,      0,  //  79  0000 0000 111
+	111*3, 114*3,      0,  //  80  0000 0001 011
+	104*3,  97*3,      0,  //  81  0000 0000 010
+	125*3, 119*3,      0,  //  82  0000 0000 110
+	 96*3,  98*3,      0,  //  83  0000 0000 100
+	   -1, 123*3,      0,  //  84  0000 0000 000
+	 95*3, 101*3,      0,  //  85  0000 0000 001
+	106*3, 121*3,      0,  //  86  0000 0000 101
+	 99*3, 102*3,      0,  //  87  0000 0000 011
+	113*3, 103*3,      0,  //  88  0000 0001 110
+	112*3, 116*3,      0,  //  89  0000 0001 111
+	110*3, 100*3,      0,  //  90  0000 0001 000
+	124*3, 115*3,      0,  //  91  0000 0001 010
+	117*3, 122*3,      0,  //  92  0000 0001 101
+	109*3, 118*3,      0,  //  93  0000 0001 100
+	120*3, 108*3,      0,  //  94  0000 0001 001
+	127*3, 136*3,      0,  //  95  0000 0000 0010
+	139*3, 140*3,      0,  //  96  0000 0000 1000
+	130*3, 126*3,      0,  //  97  0000 0000 0101
+	145*3, 146*3,      0,  //  98  0000 0000 1001
+	128*3, 129*3,      0,  //  99  0000 0000 0110
+	    0,     0, 0x0802,  // 100  0000 0001 0001.
+	132*3, 134*3,      0,  // 101  0000 0000 0011
+	155*3, 154*3,      0,  // 102  0000 0000 0111
+	    0,     0, 0x0008,  // 103  0000 0001 1101.
+	137*3, 133*3,      0,  // 104  0000 0000 0100
+	143*3, 144*3,      0,  // 105  0000 0000 1110
+	151*3, 138*3,      0,  // 106  0000 0000 1010
+	142*3, 141*3,      0,  // 107  0000 0000 1111
+	    0,     0, 0x000a,  // 108  0000 0001 0011.
+	    0,     0, 0x0009,  // 109  0000 0001 1000.
+	    0,     0, 0x000b,  // 110  0000 0001 0000.
+	    0,     0, 0x1501,  // 111  0000 0001 0110.
+	    0,     0, 0x0602,  // 112  0000 0001 1110.
+	    0,     0, 0x0303,  // 113  0000 0001 1100.
+	    0,     0, 0x1401,  // 114  0000 0001 0111.
+	    0,     0, 0x0702,  // 115  0000 0001 0101.
+	    0,     0, 0x1101,  // 116  0000 0001 1111.
+	    0,     0, 0x1201,  // 117  0000 0001 1010.
+	    0,     0, 0x1301,  // 118  0000 0001 1001.
+	148*3, 152*3,      0,  // 119  0000 0000 1101
+	    0,     0, 0x0403,  // 120  0000 0001 0010.
+	153*3, 150*3,      0,  // 121  0000 0000 1011
+	    0,     0, 0x0105,  // 122  0000 0001 1011.
+	131*3, 135*3,      0,  // 123  0000 0000 0001
+	    0,     0, 0x0204,  // 124  0000 0001 0100.
+	149*3, 147*3,      0,  // 125  0000 0000 1100
+	172*3, 173*3,      0,  // 126  0000 0000 0101 1
+	162*3, 158*3,      0,  // 127  0000 0000 0010 0
+	170*3, 161*3,      0,  // 128  0000 0000 0110 0
+	168*3, 166*3,      0,  // 129  0000 0000 0110 1
+	157*3, 179*3,      0,  // 130  0000 0000 0101 0
+	169*3, 167*3,      0,  // 131  0000 0000 0001 0
+	174*3, 171*3,      0,  // 132  0000 0000 0011 0
+	178*3, 177*3,      0,  // 133  0000 0000 0100 1
+	156*3, 159*3,      0,  // 134  0000 0000 0011 1
+	164*3, 165*3,      0,  // 135  0000 0000 0001 1
+	183*3, 182*3,      0,  // 136  0000 0000 0010 1
+	175*3, 176*3,      0,  // 137  0000 0000 0100 0
+	    0,     0, 0x0107,  // 138  0000 0000 1010 1.
+	    0,     0, 0x0a02,  // 139  0000 0000 1000 0.
+	    0,     0, 0x0902,  // 140  0000 0000 1000 1.
+	    0,     0, 0x1601,  // 141  0000 0000 1111 1.
+	    0,     0, 0x1701,  // 142  0000 0000 1111 0.
+	    0,     0, 0x1901,  // 143  0000 0000 1110 0.
+	    0,     0, 0x1801,  // 144  0000 0000 1110 1.
+	    0,     0, 0x0503,  // 145  0000 0000 1001 0.
+	    0,     0, 0x0304,  // 146  0000 0000 1001 1.
+	    0,     0, 0x000d,  // 147  0000 0000 1100 1.
+	    0,     0, 0x000c,  // 148  0000 0000 1101 0.
+	    0,     0, 0x000e,  // 149  0000 0000 1100 0.
+	    0,     0, 0x000f,  // 150  0000 0000 1011 1.
+	    0,     0, 0x0205,  // 151  0000 0000 1010 0.
+	    0,     0, 0x1a01,  // 152  0000 0000 1101 1.
+	    0,     0, 0x0106,  // 153  0000 0000 1011 0.
+	180*3, 181*3,      0,  // 154  0000 0000 0111 1
+	160*3, 163*3,      0,  // 155  0000 0000 0111 0
+	196*3, 199*3,      0,  // 156  0000 0000 0011 10
+	    0,     0, 0x001b,  // 157  0000 0000 0101 00.
+	203*3, 185*3,      0,  // 158  0000 0000 0010 01
+	202*3, 201*3,      0,  // 159  0000 0000 0011 11
+	    0,     0, 0x0013,  // 160  0000 0000 0111 00.
+	    0,     0, 0x0016,  // 161  0000 0000 0110 01.
+	197*3, 207*3,      0,  // 162  0000 0000 0010 00
+	    0,     0, 0x0012,  // 163  0000 0000 0111 01.
+	191*3, 192*3,      0,  // 164  0000 0000 0001 10
+	188*3, 190*3,      0,  // 165  0000 0000 0001 11
+	    0,     0, 0x0014,  // 166  0000 0000 0110 11.
+	184*3, 194*3,      0,  // 167  0000 0000 0001 01
+	    0,     0, 0x0015,  // 168  0000 0000 0110 10.
+	186*3, 193*3,      0,  // 169  0000 0000 0001 00
+	    0,     0, 0x0017,  // 170  0000 0000 0110 00.
+	204*3, 198*3,      0,  // 171  0000 0000 0011 01
+	    0,     0, 0x0019,  // 172  0000 0000 0101 10.
+	    0,     0, 0x0018,  // 173  0000 0000 0101 11.
+	200*3, 205*3,      0,  // 174  0000 0000 0011 00
+	    0,     0, 0x001f,  // 175  0000 0000 0100 00.
+	    0,     0, 0x001e,  // 176  0000 0000 0100 01.
+	    0,     0, 0x001c,  // 177  0000 0000 0100 11.
+	    0,     0, 0x001d,  // 178  0000 0000 0100 10.
+	    0,     0, 0x001a,  // 179  0000 0000 0101 01.
+	    0,     0, 0x0011,  // 180  0000 0000 0111 10.
+	    0,     0, 0x0010,  // 181  0000 0000 0111 11.
+	189*3, 206*3,      0,  // 182  0000 0000 0010 11
+	187*3, 195*3,      0,  // 183  0000 0000 0010 10
+	218*3, 211*3,      0,  // 184  0000 0000 0001 010
+	    0,     0, 0x0025,  // 185  0000 0000 0010 011.
+	215*3, 216*3,      0,  // 186  0000 0000 0001 000
+	    0,     0, 0x0024,  // 187  0000 0000 0010 100.
+	210*3, 212*3,      0,  // 188  0000 0000 0001 110
+	    0,     0, 0x0022,  // 189  0000 0000 0010 110.
+	213*3, 209*3,      0,  // 190  0000 0000 0001 111
+	221*3, 222*3,      0,  // 191  0000 0000 0001 100
+	219*3, 208*3,      0,  // 192  0000 0000 0001 101
+	217*3, 214*3,      0,  // 193  0000 0000 0001 001
+	223*3, 220*3,      0,  // 194  0000 0000 0001 011
+	    0,     0, 0x0023,  // 195  0000 0000 0010 101.
+	    0,     0, 0x010b,  // 196  0000 0000 0011 100.
+	    0,     0, 0x0028,  // 197  0000 0000 0010 000.
+	    0,     0, 0x010c,  // 198  0000 0000 0011 011.
+	    0,     0, 0x010a,  // 199  0000 0000 0011 101.
+	    0,     0, 0x0020,  // 200  0000 0000 0011 000.
+	    0,     0, 0x0108,  // 201  0000 0000 0011 111.
+	    0,     0, 0x0109,  // 202  0000 0000 0011 110.
+	    0,     0, 0x0026,  // 203  0000 0000 0010 010.
+	    0,     0, 0x010d,  // 204  0000 0000 0011 010.
+	    0,     0, 0x010e,  // 205  0000 0000 0011 001.
+	    0,     0, 0x0021,  // 206  0000 0000 0010 111.
+	    0,     0, 0x0027,  // 207  0000 0000 0010 001.
+	    0,     0, 0x1f01,  // 208  0000 0000 0001 1011.
+	    0,     0, 0x1b01,  // 209  0000 0000 0001 1111.
+	    0,     0, 0x1e01,  // 210  0000 0000 0001 1100.
+	    0,     0, 0x1002,  // 211  0000 0000 0001 0101.
+	    0,     0, 0x1d01,  // 212  0000 0000 0001 1101.
+	    0,     0, 0x1c01,  // 213  0000 0000 0001 1110.
+	    0,     0, 0x010f,  // 214  0000 0000 0001 0011.
+	    0,     0, 0x0112,  // 215  0000 0000 0001 0000.
+	    0,     0, 0x0111,  // 216  0000 0000 0001 0001.
+	    0,     0, 0x0110,  // 217  0000 0000 0001 0010.
+	    0,     0, 0x0603,  // 218  0000 0000 0001 0100.
+	    0,     0, 0x0b02,  // 219  0000 0000 0001 1010.
+	    0,     0, 0x0e02,  // 220  0000 0000 0001 0111.
+	    0,     0, 0x0d02,  // 221  0000 0000 0001 1000.
+	    0,     0, 0x0c02,  // 222  0000 0000 0001 1001.
+	    0,     0, 0x0f02   // 223  0000 0000 0001 0110.
+};
+
+static const int PICTURE_TYPE_INTRA = 1;
+static const int PICTURE_TYPE_PREDICTIVE = 2;
+static const int PICTURE_TYPE_B = 3;
+
+static const int START_SEQUENCE = 0xB3;
+static const int START_SLICE_FIRST = 0x01;
+static const int START_SLICE_LAST = 0xAF;
+static const int START_PICTURE = 0x00;
+static const int START_EXTENSION = 0xB5;
+static const int START_USER_DATA = 0xB2;
+
+
+typedef struct mpeg1_planes_t {
+	uint8_t *y;
+	uint8_t *cr;
+	uint8_t *cb;
+} mpeg1_planes_t;
+
+typedef struct mpeg1_decoder_t {
+	float frame_rate;
+	int width;
+	int height;
+	int mb_width;
+	int mb_height;
+	int mb_size;
+
+	int coded_width;
+	int coded_height;
+	int coded_size;
+
+	int half_width;
+	int half_height;
+
+	int picture_type;
+	int full_pel_forward;
+	int forward_f_code;
+	int forward_r_size;
+	int forward_f;
+
+	int has_sequence_header;
+
+	int quantizer_scale;
+	int slice_begin;
+	int macroblock_address;
+
+	int mb_row;
+	int mb_col;
+
+	int macroblock_type;
+	int macroblock_intra;
+	int macroblock_motion_fw;
+
+	int motion_fw_h;
+	int motion_fw_v;
+	int motion_fw_h_prev;
+	int motion_fw_v_prev;
+
+	int dc_predictor_y;
+	int dc_predictor_cr;
+	int dc_predictor_cb;
+
+	bit_buffer_t *bits;
+
+	mpeg1_planes_t planes_current;
+	mpeg1_planes_t planes_forward;
+
+	int block_data[64];
+	uint8_t intra_quant_matrix[64];
+	uint8_t non_intra_quant_matrix[64];
+} mpeg1_decoder_t;
+
+
+void decode_sequence_header(mpeg1_decoder_t *self);
+void decode_picture(mpeg1_decoder_t *self);
+void init_buffers(mpeg1_decoder_t *self);
+void decode_slice(mpeg1_decoder_t *self, int slice);
+void decode_macroblock(mpeg1_decoder_t *self);
+void decode_motion_vectors(mpeg1_decoder_t *self);
+void copy_macroblock(mpeg1_decoder_t *self, int motion_h, int motion_v, uint8_t *s_y, uint8_t *s_cr, uint8_t *s_cb);
+void decode_block(mpeg1_decoder_t *self, int block);
+
+void copy_block_to_destination(int *block, uint8_t *dest, int index, int scan);
+void add_block_to_destination(int *block, uint8_t *dest, int index, int scan);
+void copy_value_to_destination(int value, uint8_t *dest, int index, int scan);
+void add_value_to_destination(int value, uint8_t *dest, int index, int scan);
+void idct(int *block);
+void zero_block_data(mpeg1_decoder_t *self);
+int read_huffman(bit_buffer_t *bits, const int *code_table);
+
+
+
+
+// -----------------------------------------------------------------------------
+// Public interface
+
+mpeg1_decoder_t *mpeg1_decoder_create(unsigned int buffer_size, bit_buffer_mode_t buffer_mode) {
+	mpeg1_decoder_t *self = malloc(sizeof(mpeg1_decoder_t));
+	memset(self, 0, sizeof(mpeg1_decoder_t));
+	self->bits = bit_buffer_create(buffer_size, buffer_mode);
+	return self;
+}
+
+void mpeg1_decoder_destroy(mpeg1_decoder_t *self) {
+	bit_buffer_destroy(self->bits);
+
+	if (self->has_sequence_header) {
+		free(self->planes_current.y);
+		free(self->planes_current.cr);
+		free(self->planes_current.cb);
+
+		free(self->planes_forward.y);
+		free(self->planes_forward.cr);
+		free(self->planes_forward.cb);
+	}
+
+	free(self);
+}
+
+void *mpeg1_decoder_get_write_ptr(mpeg1_decoder_t *self, unsigned int byte_size) {
+	return bit_buffer_get_write_ptr(self->bits, byte_size);
+}
+
+int mpeg1_decoder_get_index(mpeg1_decoder_t *self) {
+	return bit_buffer_get_index(self->bits);
+}
+
+void mpeg1_decoder_set_index(mpeg1_decoder_t *self, unsigned int index) {
+	bit_buffer_set_index(self->bits, index);
+}
+
+void mpeg1_decoder_did_write(mpeg1_decoder_t *self, unsigned int byte_size) {
+	bit_buffer_did_write(self->bits, byte_size);
+	if (!self->has_sequence_header) {
+		if (bit_buffer_find_start_code(self->bits, START_SEQUENCE) != -1) {
+			decode_sequence_header(self);
+		}
+	}
+}
+
+int mpeg1_decoder_has_sequence_header(mpeg1_decoder_t *self) {
+	return self->has_sequence_header;
+}
+
+float mpeg1_decoder_get_frame_rate(mpeg1_decoder_t *self) {
+	return self->frame_rate;
+}
+
+int mpeg1_decoder_get_coded_size(mpeg1_decoder_t *self) {
+	return self->coded_size;
+}
+
+int mpeg1_decoder_get_width(mpeg1_decoder_t *self) {
+	return self->width;
+}
+
+int mpeg1_decoder_get_height(mpeg1_decoder_t *self) {
+	return self->height;
+}
+
+void *mpeg1_decoder_get_y_ptr(mpeg1_decoder_t *self) {
+	return self->planes_forward.y;
+}
+
+void *mpeg1_decoder_get_cr_ptr(mpeg1_decoder_t *self) {
+	return self->planes_forward.cr;
+}
+
+void *mpeg1_decoder_get_cb_ptr(mpeg1_decoder_t *self) {
+	return self->planes_forward.cb;
+}
+
+bool mpeg1_decoder_decode(mpeg1_decoder_t *self) {
+	if (!self->has_sequence_header) {
+		return false;
+	}
+
+	if (bit_buffer_find_start_code(self->bits, START_PICTURE) == -1) {
+		return false;
+	}
+
+	decode_picture(self);
+	return true;
+}
+
+
+
+
+// -----------------------------------------------------------------------------
+// Private methods
+
+void decode_sequence_header(mpeg1_decoder_t *self) {
+	int previous_width = self->width; 
+	int previous_height = self->height;
+
+	self->width = bit_buffer_read(self->bits, 12);
+	self->height = bit_buffer_read(self->bits, 12);
+
+	// skip pixel aspect ratio
+	bit_buffer_skip(self->bits, 4);
+
+	self->frame_rate = PICTURE_RATE[bit_buffer_read(self->bits, 4)];
+
+	// skip bitRate, marker, bufferSize and constrained bit
+	bit_buffer_skip(self->bits, 18 + 1 + 10 + 1);
+
+	if (bit_buffer_read(self->bits, 1)) { // load custom intra quant matrix?
+		for (int i = 0; i < 64; i++) {
+			self->intra_quant_matrix[ZIG_ZAG[i]] = bit_buffer_read(self->bits, 8);
+		}
+	}
+	else {
+		memcpy(self->intra_quant_matrix, DEFAULT_INTRA_QUANT_MATRIX, 64);
+	}
+
+	if (bit_buffer_read(self->bits, 1)) { // load custom non intra quant matrix?
+		for (int i = 0; i < 64; i++) {
+			int idx = ZIG_ZAG[i];
+			self->non_intra_quant_matrix[idx] = bit_buffer_read(self->bits, 8);
+		}
+	}
+	else {
+		memcpy(self->non_intra_quant_matrix, DEFAULT_NON_INTRA_QUANT_MATRIX, 64);
+	}
+
+	if (self->has_sequence_header) {
+		if (self->width == previous_width && self->height == previous_height) {
+			// We already had a sequence header with the same width/height;
+			// nothing else to do here.
+			return;
+		}
+
+		// We had a sequence header but with different dimensions;
+		// delete the previous planes and allocate new.
+		free(self->planes_current.y);
+		free(self->planes_current.cr);
+		free(self->planes_current.cb);
+
+		free(self->planes_forward.y);
+		free(self->planes_forward.cr);
+		free(self->planes_forward.cb);
+	}
+
+	self->mb_width = (self->width + 15) >> 4;
+	self->mb_height = (self->height + 15) >> 4;
+	self->mb_size = self->mb_width * self->mb_height;
+
+	self->coded_width = self->mb_width << 4;
+	self->coded_height = self->mb_height << 4;
+	self->coded_size = self->coded_width * self->coded_height;
+
+	self->half_width = self->mb_width << 3;
+	self->half_height = self->mb_height << 3;
+
+	self->planes_current.y = (uint8_t*)malloc(self->coded_size);
+	self->planes_current.cr = (uint8_t*)malloc(self->coded_size >> 2);
+	self->planes_current.cb = (uint8_t*)malloc(self->coded_size >> 2);
+
+	self->planes_forward.y = (uint8_t*)malloc(self->coded_size);
+	self->planes_forward.cr = (uint8_t*)malloc(self->coded_size >> 2);
+	self->planes_forward.cb = (uint8_t*)malloc(self->coded_size >> 2);
+
+	self->has_sequence_header = true;
+}
+
+
+void decode_picture(mpeg1_decoder_t *self) {
+	bit_buffer_skip(self->bits, 10); // skip temporalReference
+	self->picture_type = bit_buffer_read(self->bits, 3);
+	bit_buffer_skip(self->bits, 16); // skip vbv_delay
+
+	// Skip B and D frames or unknown coding type
+	if (self->picture_type <= 0 || self->picture_type >= PICTURE_TYPE_B) {
+		return;
+	}
+
+	// full_pel_forward, forward_f_code
+	if (self->picture_type == PICTURE_TYPE_PREDICTIVE) {
+		self->full_pel_forward = bit_buffer_read(self->bits, 1);
+		self->forward_f_code = bit_buffer_read(self->bits, 3);
+		if (self->forward_f_code == 0) {
+			// Ignore picture with zero self->forward_f_code
+			return;
+		}
+		self->forward_r_size = self->forward_f_code - 1;
+		self->forward_f = 1 << self->forward_r_size;
+	}
+
+	int code = 0;
+	do {
+		code = bit_buffer_find_next_start_code(self->bits);
+	} while (code == START_EXTENSION || code == START_USER_DATA);
+
+
+	while (code >= START_SLICE_FIRST && code <= START_SLICE_LAST) {
+		decode_slice(self, code & 0x000000FF);
+		code = bit_buffer_find_next_start_code(self->bits);
+	}
+
+	if (code != -1) {
+		// We found the next start code; rewind 32self->bits and let the main loop
+		// handle it.
+		bit_buffer_rewind(self->bits, 32);
+	}
+
+	// If this is a reference picutre then rotate the prediction pointers
+	if (
+		self->picture_type == PICTURE_TYPE_INTRA ||
+		self->picture_type == PICTURE_TYPE_PREDICTIVE
+	) {
+		mpeg1_planes_t temp = self->planes_forward;
+		self->planes_forward = self->planes_current;
+		self->planes_current = temp;
+	}
+}
+
+
+// Slice Layer
+
+void decode_slice(mpeg1_decoder_t *self, int slice) {
+	self->slice_begin = true;
+	self->macroblock_address = (slice - 1) * self->mb_width - 1;
+
+	// Reset motion vectors and DC predictors
+	self->motion_fw_h = self->motion_fw_h_prev = 0;
+	self->motion_fw_v = self->motion_fw_v_prev = 0;
+	self->dc_predictor_y  = 128;
+	self->dc_predictor_cr = 128;
+	self->dc_predictor_cb = 128;
+
+	self->quantizer_scale = bit_buffer_read(self->bits, 5);
+
+	// skip extra self->bits
+	while (bit_buffer_read(self->bits, 1)) {
+		bit_buffer_skip(self->bits, 8);
+	}
+
+	do {
+		decode_macroblock(self);
+	} while (!bit_buffer_next_bytes_are_start_code(self->bits));
+};
+
+
+// Macroblock Layer
+
+void decode_macroblock(mpeg1_decoder_t *self) {
+	// Decode self->macroblock_address_increment
+	int increment = 0;
+	int x = 0;
+	int t = read_huffman(self->bits, MACROBLOCK_ADDRESS_INCREMENT);
+
+	while (t == 34) {
+		// macroblock_stuffing
+		x = 1;
+		t = read_huffman(self->bits, MACROBLOCK_ADDRESS_INCREMENT);
+	}
+	while (t == 35) {
+		// macroblock_escape
+		increment += 33;
+		x =
+		t = read_huffman(self->bits, MACROBLOCK_ADDRESS_INCREMENT);
+	}
+	increment += t;
+
+	// Process any skipped macroblocks
+	if (self->slice_begin) {
+		// The first self->macroblock_address_increment of each slice is relative
+		// to beginning of the preverious row, not the preverious macroblock
+		self->slice_begin = false;
+		self->macroblock_address += increment;
+	}
+	else {
+		if (self->macroblock_address + increment >= self->mb_size) {
+			// Illegal (too large) self->macroblock_address_increment
+			// abort();
+			return;
+		}
+		if (increment > 1) {
+			// Skipped macroblocks reset DC predictors
+			self->dc_predictor_y  = 128;
+			self->dc_predictor_cr = 128;
+			self->dc_predictor_cb = 128;
+
+			// Skipped macroblocks in P-pictures reset motion vectors
+			if (self->picture_type == PICTURE_TYPE_PREDICTIVE) {
+				self->motion_fw_h = self->motion_fw_h_prev = 0;
+				self->motion_fw_v = self->motion_fw_v_prev = 0;
+			}
+		}
+
+		// Predict skipped macroblocks
+		while (increment > 1) {
+			self->macroblock_address++;
+			self->mb_row = (self->macroblock_address / self->mb_width)|0;
+			self->mb_col = self->macroblock_address % self->mb_width;
+			copy_macroblock(
+				self,
+				self->motion_fw_h, self->motion_fw_v,
+				self->planes_forward.y, self->planes_forward.cr, self->planes_forward.cb
+			);
+			increment--;
+		}
+		self->macroblock_address++;
+	}
+
+	self->mb_row = (self->macroblock_address / self->mb_width)|0;
+	self->mb_col = self->macroblock_address % self->mb_width;
+
+	// Process the current macroblock
+	// static const s16 *mbTable = MACROBLOCK_TYPE[self->picture_type];
+	// macroblock_type = read_huffman(self->bits, mbTable);
+	if (self->picture_type == PICTURE_TYPE_INTRA) {
+		self->macroblock_type = read_huffman(self->bits, MACROBLOCK_TYPE_INTRA);
+	}
+	else if (self->picture_type == PICTURE_TYPE_PREDICTIVE) {
+		self->macroblock_type = read_huffman(self->bits, MACROBLOCK_TYPE_PREDICTIVE);
+	}
+	else {
+		// Unhandled picture type
+		// abort();
+	}
+	self->macroblock_intra = (self->macroblock_type & 0x01);
+	self->macroblock_motion_fw = (self->macroblock_type & 0x08);
+
+	// Quantizer scale
+	if ((self->macroblock_type & 0x10) != 0) {
+		self->quantizer_scale = bit_buffer_read(self->bits, 5);
+	}
+
+	if (self->macroblock_intra) {
+		// Intra-coded macroblocks reset motion vectors
+		self->motion_fw_h = self->motion_fw_h_prev = 0;
+		self->motion_fw_v = self->motion_fw_v_prev = 0;
+	}
+	else {
+		// Non-intra macroblocks reset DC predictors
+		self->dc_predictor_y = 128;
+		self->dc_predictor_cr = 128;
+		self->dc_predictor_cb = 128;
+
+		decode_motion_vectors(self);
+		copy_macroblock(
+			self,
+			self->motion_fw_h, self->motion_fw_v,
+			self->planes_forward.y, self->planes_forward.cr, self->planes_forward.cb
+		);
+	}
+
+	// Decode blocks
+	int cbp = ((self->macroblock_type & 0x02) != 0)
+		? read_huffman(self->bits, CODE_BLOCK_PATTERN)
+		: (self->macroblock_intra ? 0x3f : 0);
+
+	for (int block = 0, mask = 0x20; block < 6; block++) {
+		if ((cbp & mask) != 0) {
+			decode_block(self, block);
+		}
+		mask >>= 1;
+	}
+};
+
+
+void decode_motion_vectors(mpeg1_decoder_t *self) {
+	int code, d, r = 0;
+
+	// Forward
+	if (self->macroblock_motion_fw) {
+		// Horizontal forward
+		code = read_huffman(self->bits, MOTION);
+		if ((code != 0) && (self->forward_f != 1)) {
+			r = bit_buffer_read(self->bits, self->forward_r_size);
+			d = ((abs(code) - 1) << self->forward_r_size) + r + 1;
+			if (code < 0) {
+				d = -d;
+			}
+		}
+		else {
+			d = code;
+		}
+
+		self->motion_fw_h_prev += d;
+		if (self->motion_fw_h_prev > (self->forward_f << 4) - 1) {
+			self->motion_fw_h_prev -= self->forward_f << 5;
+		}
+		else if (self->motion_fw_h_prev < ((-self->forward_f) << 4)) {
+			self->motion_fw_h_prev += self->forward_f << 5;
+		}
+
+		self->motion_fw_h = self->motion_fw_h_prev;
+		if (self->full_pel_forward) {
+			self->motion_fw_h <<= 1;
+		}
+
+		// Vertical forward
+		code = read_huffman(self->bits, MOTION);
+		if ((code != 0) && (self->forward_f != 1)) {
+			r = bit_buffer_read(self->bits, self->forward_r_size);
+			d = ((abs(code) - 1) << self->forward_r_size) + r + 1;
+			if (code < 0) {
+				d = -d;
+			}
+		}
+		else {
+			d = code;
+		}
+
+		self->motion_fw_v_prev += d;
+		if (self->motion_fw_v_prev > (self->forward_f << 4) - 1) {
+			self->motion_fw_v_prev -= self->forward_f << 5;
+		}
+		else if (self->motion_fw_v_prev < ((-self->forward_f) << 4)) {
+			self->motion_fw_v_prev += self->forward_f << 5;
+		}
+
+		self->motion_fw_v = self->motion_fw_v_prev;
+		if (self->full_pel_forward) {
+			self->motion_fw_v <<= 1;
+		}
+	}
+	else if (self->picture_type == PICTURE_TYPE_PREDICTIVE) {
+		// No motion information in P-picture, reset vectors
+		self->motion_fw_h = self->motion_fw_h_prev = 0;
+		self->motion_fw_v = self->motion_fw_v_prev = 0;
+	}
+}
+
+
+void copy_macroblock(mpeg1_decoder_t *self, int motion_h, int motion_v, uint8_t *s_y, uint8_t *s_cr, uint8_t *s_cb) {
+	int
+		width, scan,
+		H, V,
+		src, dest, last;
+	bool odd_h, odd_v;
+
+	// We use 32bit writes here
+	int *d_y = (int*)self->planes_current.y;
+	int *d_cb = (int*)self->planes_current.cb;
+	int *d_cr = (int*)self->planes_current.cr;
+
+	// Luminance
+	width = self->coded_width;
+	scan = width - 16;
+
+	H = motion_h >> 1;
+	V = motion_v >> 1;
+	odd_h = (motion_h & 1) == 1;
+	odd_v = (motion_v & 1) == 1;
+
+	src = ((self->mb_row << 4) + V) * width + (self->mb_col << 4) + H;
+	dest = (self->mb_row * width + self->mb_col) << 2;
+	last = dest + (width << 2);
+
+	int x, y1, y2, y;
+	if (odd_h) {
+		if (odd_v) {
+			while (dest < last) {
+				y1 = s_y[src] + s_y[src+width]; src++;
+				for (x = 0; x < 4; x++) {
+					y2 = s_y[src] + s_y[src+width]; src++;
+					y = (((y1 + y2 + 2) >> 2) & 0xff);
+
+					y1 = s_y[src] + s_y[src+width]; src++;
+					y |= (((y1 + y2 + 2) << 6) & 0xff00);
+
+					y2 = s_y[src] + s_y[src+width]; src++;
+					y |= (((y1 + y2 + 2) << 14) & 0xff0000);
+
+					y1 = s_y[src] + s_y[src+width]; src++;
+					y |= (((y1 + y2 + 2) << 22) & 0xff000000);
+
+					d_y[dest++] = y;
+				}
+				dest += scan >> 2; src += scan-1;
+			}
+		}
+		else {
+			while (dest < last) {
+				y1 = s_y[src++];
+				for (x = 0; x < 4; x++) {
+					y2 = s_y[src++];
+					y = (((y1 + y2 + 1) >> 1) & 0xff);
+
+					y1 = s_y[src++];
+					y |= (((y1 + y2 + 1) << 7) & 0xff00);
+
+					y2 = s_y[src++];
+					y |= (((y1 + y2 + 1) << 15) & 0xff0000);
+
+					y1 = s_y[src++];
+					y |= (((y1 + y2 + 1) << 23) & 0xff000000);
+
+					d_y[dest++] = y;
+				}
+				dest += scan >> 2; src += scan-1;
+			}
+		}
+	}
+	else {
+		if (odd_v) {
+			while (dest < last) {
+				for (x = 0; x < 4; x++) {
+					y = (((s_y[src] + s_y[src+width] + 1) >> 1) & 0xff); src++;
+					y |= (((s_y[src] + s_y[src+width] + 1) << 7) & 0xff00); src++;
+					y |= (((s_y[src] + s_y[src+width] + 1) << 15) & 0xff0000); src++;
+					y |= (((s_y[src] + s_y[src+width] + 1) << 23) & 0xff000000); src++;
+
+					d_y[dest++] = y;
+				}
+				dest += scan >> 2; src += scan;
+			}
+		}
+		else {
+			while (dest < last) {
+				for (x = 0; x < 4; x++) {
+					y = s_y[src]; src++;
+					y |= s_y[src] << 8; src++;
+					y |= s_y[src] << 16; src++;
+					y |= s_y[src] << 24; src++;
+
+					d_y[dest++] = y;
+				}
+				dest += scan >> 2; src += scan;
+			}
+		}
+	}
+
+	// Chrominance
+
+	width = self->half_width;
+	scan = width - 8;
+
+	H = (motion_h/2) >> 1;
+	V = (motion_v/2) >> 1;
+	odd_h = ((motion_h/2) & 1) == 1;
+	odd_v = ((motion_v/2) & 1) == 1;
+
+	src = ((self->mb_row << 3) + V) * width + (self->mb_col << 3) + H;
+	dest = (self->mb_row * width + self->mb_col) << 1;
+	last = dest + (width << 1);
+
+	int cr1, cr2, cr,
+		cb1, cb2, cb;
+	if (odd_h) {
+		if (odd_v) {
+			while (dest < last) {
+				cr1 = s_cr[src] + s_cr[src+width];
+				cb1 = s_cb[src] + s_cb[src+width];
+				src++;
+				for (x = 0; x < 2; x++) {
+					cr2 = s_cr[src] + s_cr[src+width];
+					cb2 = s_cb[src] + s_cb[src+width]; src++;
+					cr = (((cr1 + cr2 + 2) >> 2) & 0xff);
+					cb = (((cb1 + cb2 + 2) >> 2) & 0xff);
+
+					cr1 = s_cr[src] + s_cr[src+width];
+					cb1 = s_cb[src] + s_cb[src+width]; src++;
+					cr |= (((cr1 + cr2 + 2) << 6) & 0xff00);
+					cb |= (((cb1 + cb2 + 2) << 6) & 0xff00);
+
+					cr2 = s_cr[src] + s_cr[src+width];
+					cb2 = s_cb[src] + s_cb[src+width]; src++;
+					cr |= (((cr1 + cr2 + 2) << 14) & 0xff0000);
+					cb |= (((cb1 + cb2 + 2) << 14) & 0xff0000);
+
+					cr1 = s_cr[src] + s_cr[src+width];
+					cb1 = s_cb[src] + s_cb[src+width]; src++;
+					cr |= (((cr1 + cr2 + 2) << 22) & 0xff000000);
+					cb |= (((cb1 + cb2 + 2) << 22) & 0xff000000);
+
+					d_cr[dest] = cr;
+					d_cb[dest] = cb;
+					dest++;
+				}
+				dest += scan >> 2; src += scan-1;
+			}
+		}
+		else {
+			while (dest < last) {
+				cr1 = s_cr[src];
+				cb1 = s_cb[src];
+				src++;
+				for (x = 0; x < 2; x++) {
+					cr2 = s_cr[src];
+					cb2 = s_cb[src++];
+					cr = (((cr1 + cr2 + 1) >> 1) & 0xff);
+					cb = (((cb1 + cb2 + 1) >> 1) & 0xff);
+
+					cr1 = s_cr[src];
+					cb1 = s_cb[src++];
+					cr |= (((cr1 + cr2 + 1) << 7) & 0xff00);
+					cb |= (((cb1 + cb2 + 1) << 7) & 0xff00);
+
+					cr2 = s_cr[src];
+					cb2 = s_cb[src++];
+					cr |= (((cr1 + cr2 + 1) << 15) & 0xff0000);
+					cb |= (((cb1 + cb2 + 1) << 15) & 0xff0000);
+
+					cr1 = s_cr[src];
+					cb1 = s_cb[src++];
+					cr |= (((cr1 + cr2 + 1) << 23) & 0xff000000);
+					cb |= (((cb1 + cb2 + 1) << 23) & 0xff000000);
+
+					d_cr[dest] = cr;
+					d_cb[dest] = cb;
+					dest++;
+				}
+				dest += scan >> 2; src += scan-1;
+			}
+		}
+	}
+	else {
+		if (odd_v) {
+			while (dest < last) {
+				for (x = 0; x < 2; x++) {
+					cr = (((s_cr[src] + s_cr[src+width] + 1) >> 1) & 0xff);
+					cb = (((s_cb[src] + s_cb[src+width] + 1) >> 1) & 0xff); src++;
+
+					cr |= (((s_cr[src] + s_cr[src+width] + 1) << 7) & 0xff00);
+					cb |= (((s_cb[src] + s_cb[src+width] + 1) << 7) & 0xff00); src++;
+
+					cr |= (((s_cr[src] + s_cr[src+width] + 1) << 15) & 0xff0000);
+					cb |= (((s_cb[src] + s_cb[src+width] + 1) << 15) & 0xff0000); src++;
+
+					cr |= (((s_cr[src] + s_cr[src+width] + 1) << 23) & 0xff000000);
+					cb |= (((s_cb[src] + s_cb[src+width] + 1) << 23) & 0xff000000); src++;
+
+					d_cr[dest] = cr;
+					d_cb[dest] = cb;
+					dest++;
+				}
+				dest += scan >> 2; src += scan;
+			}
+		}
+		else {
+			while (dest < last) {
+				for (x = 0; x < 2; x++) {
+					cr = s_cr[src];
+					cb = s_cb[src]; src++;
+
+					cr |= s_cr[src] << 8;
+					cb |= s_cb[src] << 8; src++;
+
+					cr |= s_cr[src] << 16;
+					cb |= s_cb[src] << 16; src++;
+
+					cr |= s_cr[src] << 24;
+					cb |= s_cb[src] << 24; src++;
+
+					d_cr[dest] = cr;
+					d_cb[dest] = cb;
+					dest++;
+				}
+				dest += scan >> 2; src += scan;
+			}
+		}
+	}
+}
+
+
+// Block layer
+
+void decode_block(mpeg1_decoder_t *self, int block) {
+
+	int n = 0;
+	uint8_t *quant_matrix;
+
+	// Decode DC coefficient of intra-coded blocks
+	if (self->macroblock_intra) {
+		int predictor;
+		int dctSize;
+
+		// DC prediction
+
+		if (block < 4) {
+			predictor = self->dc_predictor_y;
+			dctSize = read_huffman(self->bits, DCT_DC_SIZE_LUMINANCE);
+		}
+		else {
+			predictor = (block == 4 ? self->dc_predictor_cr : self->dc_predictor_cb);
+			dctSize = read_huffman(self->bits, DCT_DC_SIZE_CHROMINANCE);
+		}
+
+		// Read DC coeff
+		if (dctSize > 0) {
+			int differential = bit_buffer_read(self->bits, dctSize);
+			if ((differential & (1 << (dctSize - 1))) != 0) {
+				self->block_data[0] = predictor + differential;
+			}
+			else {
+				self->block_data[0] = predictor + ((-1 << dctSize)|(differential+1));
+			}
+		}
+		else {
+			self->block_data[0] = predictor;
+		}
+
+		// Save predictor value
+		if (block < 4) {
+			self->dc_predictor_y = self->block_data[0];
+		}
+		else if (block == 4) {
+			self->dc_predictor_cr = self->block_data[0];
+		}
+		else {
+			self->dc_predictor_cb = self->block_data[0];
+		}
+
+		// Dequantize + premultiply
+		self->block_data[0] <<= (3 + 5);
+
+		quant_matrix = self->intra_quant_matrix;
+		n = 1;
+	}
+	else {
+		quant_matrix = self->non_intra_quant_matrix;
+	}
+
+	// Decode AC coefficients (+DC for non-intra)
+	int level = 0;
+	while (true) {
+		int run = 0;
+		int coeff = read_huffman(self->bits, DCT_COEFF);
+
+		if ((coeff == 0x0001) && (n > 0) && (bit_buffer_read(self->bits, 1) == 0)) {
+			// end_of_block
+			break;
+		}
+		if (coeff == 0xffff) {
+			// escape
+			run = bit_buffer_read(self->bits, 6);
+			level = bit_buffer_read(self->bits, 8);
+			if (level == 0) {
+				level = bit_buffer_read(self->bits, 8);
+			}
+			else if (level == 128) {
+				level = bit_buffer_read(self->bits, 8) - 256;
+			}
+			else if (level > 128) {
+				level = level - 256;
+			}
+		}
+		else {
+			run = coeff >> 8;
+			level = coeff & 0xff;
+			if (bit_buffer_read(self->bits, 1)) {
+				level = -level;
+			}
+		}
+
+		n += run;
+		int dezigZagged = ZIG_ZAG[n];
+		n++;
+
+		// Dequantize, oddify, clip
+		level <<= 1;
+		if (!self->macroblock_intra) {
+			level += (level < 0 ? -1 : 1);
+		}
+		level = (level * self->quantizer_scale * quant_matrix[dezigZagged]) >> 4;
+		if ((level & 1) == 0) {
+			level -= level > 0 ? 1 : -1;
+		}
+		if (level > 2047) {
+			level = 2047;
+		}
+		else if (level < -2048) {
+			level = -2048;
+		}
+
+		// Save premultiplied coefficient
+		self->block_data[dezigZagged] = level * PREMULTIPLIER_MATRIX[dezigZagged];
+	}
+
+	// Move block to its place
+	uint8_t *dest_array;
+	int dest_index;
+	int scan;
+
+	if (block < 4) {
+		dest_array = self->planes_current.y;
+		scan = self->coded_width - 8;
+		dest_index = (self->mb_row * self->coded_width + self->mb_col) << 4;
+		if ((block & 1) != 0) {
+			dest_index += 8;
+		}
+		if ((block & 2) != 0) {
+			dest_index += self->coded_width << 3;
+		}
+	}
+	else {
+		dest_array = (block == 4) ? self->planes_current.cb : self->planes_current.cr;
+		scan = (self->coded_width >> 1) - 8;
+		dest_index = ((self->mb_row * self->coded_width) << 2) + (self->mb_col << 3);
+	}
+
+	if (self->macroblock_intra) {
+		// Overwrite (no prediction)
+		if (n == 1) {
+			copy_value_to_destination((self->block_data[0] + 128) >> 8, dest_array, dest_index, scan);
+			self->block_data[0] = 0;
+		}
+		else {
+			idct(self->block_data);
+			copy_block_to_destination(self->block_data, dest_array, dest_index, scan);
+			zero_block_data(self);
+		}
+	}
+	else {
+		// Add data to the predicted macroblock
+		if (n == 1) {
+			add_value_to_destination((self->block_data[0] + 128) >> 8, dest_array, dest_index, scan);
+			self->block_data[0] = 0;
+		}
+		else {
+			idct(self->block_data);
+			add_block_to_destination(self->block_data, dest_array, dest_index, scan);
+			zero_block_data(self);
+		}
+	}
+
+	n = 0;
+}
+
+
+// -----------------------------------------------------------------------------
+// Private functions
+
+void zero_block_data(mpeg1_decoder_t *self) {
+	for (int i = 0; i < 64; i++) {
+		self->block_data[i] = 0;
+	}
+}
+
+inline uint8_t clamp_to_uint8(int n) {
+	return n > 255 
+		? 255 
+		: (n < 0 ? 0 : n);
+}
+
+void copy_block_to_destination(int *block, uint8_t *dest, int index, int scan) {
+	for (int n = 0; n < 64; n += 8, index += scan+8) {
+		dest[index+0] = clamp_to_uint8(block[n+0]);
+		dest[index+1] = clamp_to_uint8(block[n+1]);
+		dest[index+2] = clamp_to_uint8(block[n+2]);
+		dest[index+3] = clamp_to_uint8(block[n+3]);
+		dest[index+4] = clamp_to_uint8(block[n+4]);
+		dest[index+5] = clamp_to_uint8(block[n+5]);
+		dest[index+6] = clamp_to_uint8(block[n+6]);
+		dest[index+7] = clamp_to_uint8(block[n+7]);
+	}
+}
+
+void add_block_to_destination(int *block, uint8_t *dest, int index, int scan) {
+	for (int n = 0; n < 64; n += 8, index += scan+8) {
+		dest[index+0] = clamp_to_uint8(dest[index+0] + block[n+0]);
+		dest[index+1] = clamp_to_uint8(dest[index+1] + block[n+1]);
+		dest[index+2] = clamp_to_uint8(dest[index+2] + block[n+2]);
+		dest[index+3] = clamp_to_uint8(dest[index+3] + block[n+3]);
+		dest[index+4] = clamp_to_uint8(dest[index+4] + block[n+4]);
+		dest[index+5] = clamp_to_uint8(dest[index+5] + block[n+5]);
+		dest[index+6] = clamp_to_uint8(dest[index+6] + block[n+6]);
+		dest[index+7] = clamp_to_uint8(dest[index+7] + block[n+7]);
+	}
+}
+
+void copy_value_to_destination(int value, uint8_t *dest, int index, int scan) {
+	value = clamp_to_uint8(value);
+	for (int n = 0; n < 64; n += 8, index += scan+8) {
+		dest[index+0] = value;
+		dest[index+1] = value;
+		dest[index+2] = value;
+		dest[index+3] = value;
+		dest[index+4] = value;
+		dest[index+5] = value;
+		dest[index+6] = value;
+		dest[index+7] = value;
+	}
+}
+
+void add_value_to_destination(int value, uint8_t *dest, int index, int scan) {
+	for (int n = 0; n < 64; n += 8, index += scan+8) {
+		dest[index+0] = clamp_to_uint8(dest[index+0] + value);
+		dest[index+1] = clamp_to_uint8(dest[index+1] + value);
+		dest[index+2] = clamp_to_uint8(dest[index+2] + value);
+		dest[index+3] = clamp_to_uint8(dest[index+3] + value);
+		dest[index+4] = clamp_to_uint8(dest[index+4] + value);
+		dest[index+5] = clamp_to_uint8(dest[index+5] + value);
+		dest[index+6] = clamp_to_uint8(dest[index+6] + value);
+		dest[index+7] = clamp_to_uint8(dest[index+7] + value);
+	}
+}
+
+void idct(int *block) {
+	// See http://vsr.informatik.tu-chemnitz.de/~jan/MPEG/HTML/IDCT.html
+	// for more info.
+
+	int
+		b1, b3, b4, b6, b7, tmp1, tmp2, m0,
+		x0, x1, x2, x3, x4, y3, y4, y5, y6, y7;
+
+	// Transform columns
+	for (int i = 0; i < 8; ++i) {
+		b1 = block[4*8+i];
+		b3 = block[2*8+i] + block[6*8+i];
+		b4 = block[5*8+i] - block[3*8+i];
+		tmp1 = block[1*8+i] + block[7*8+i];
+		tmp2 = block[3*8+i] + block[5*8+i];
+		b6 = block[1*8+i] - block[7*8+i];
+		b7 = tmp1 + tmp2;
+		m0 = block[0*8+i];
+		x4 = ((b6*473 - b4*196 + 128) >> 8) - b7;
+		x0 = x4 - (((tmp1 - tmp2)*362 + 128) >> 8);
+		x1 = m0 - b1;
+		x2 = (((block[2*8+i] - block[6*8+i])*362 + 128) >> 8) - b3;
+		x3 = m0 + b1;
+		y3 = x1 + x2;
+		y4 = x3 + b3;
+		y5 = x1 - x2;
+		y6 = x3 - b3;
+		y7 = -x0 - ((b4*473 + b6*196 + 128) >> 8);
+		block[0*8+i] = b7 + y4;
+		block[1*8+i] = x4 + y3;
+		block[2*8+i] = y5 - x0;
+		block[3*8+i] = y6 - y7;
+		block[4*8+i] = y6 + y7;
+		block[5*8+i] = x0 + y5;
+		block[6*8+i] = y3 - x4;
+		block[7*8+i] = y4 - b7;
+	}
+
+	// Transform rows
+	for (int i = 0; i < 64; i += 8) {
+		b1 = block[4+i];
+		b3 = block[2+i] + block[6+i];
+		b4 = block[5+i] - block[3+i];
+		tmp1 = block[1+i] + block[7+i];
+		tmp2 = block[3+i] + block[5+i];
+		b6 = block[1+i] - block[7+i];
+		b7 = tmp1 + tmp2;
+		m0 = block[0+i];
+		x4 = ((b6*473 - b4*196 + 128) >> 8) - b7;
+		x0 = x4 - (((tmp1 - tmp2)*362 + 128) >> 8);
+		x1 = m0 - b1;
+		x2 = (((block[2+i] - block[6+i])*362 + 128) >> 8) - b3;
+		x3 = m0 + b1;
+		y3 = x1 + x2;
+		y4 = x3 + b3;
+		y5 = x1 - x2;
+		y6 = x3 - b3;
+		y7 = -x0 - ((b4*473 + b6*196 + 128) >> 8);
+		block[0+i] = (b7 + y4 + 128) >> 8;
+		block[1+i] = (x4 + y3 + 128) >> 8;
+		block[2+i] = (y5 - x0 + 128) >> 8;
+		block[3+i] = (y6 - y7 + 128) >> 8;
+		block[4+i] = (y6 + y7 + 128) >> 8;
+		block[5+i] = (x0 + y5 + 128) >> 8;
+		block[6+i] = (y3 - x4 + 128) >> 8;
+		block[7+i] = (y4 - b7 + 128) >> 8;
+	}
+}
+
+int read_huffman(bit_buffer_t *bits, const int *code_table) {
+	int state = 0;
+	do {
+		state = code_table[state + bit_buffer_read(bits, 1)];
+	} while (state >= 0 && code_table[state] != 0);
+	return code_table[state+2];
+}

+ 27 - 0
jsmpeg-master/src/wasm/mpeg1.h

@@ -0,0 +1,27 @@
+#ifndef MPEG1_H
+#define MPEG1_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "buffer.h"
+
+typedef struct mpeg1_decoder_t mpeg1_decoder_t;
+
+mpeg1_decoder_t *mpeg1_decoder_create(unsigned int buffer_size, bit_buffer_mode_t buffer_mode);
+void mpeg1_decoder_destroy(mpeg1_decoder_t *self);
+void *mpeg1_decoder_get_write_ptr(mpeg1_decoder_t *self, unsigned int byte_size);
+int mpeg1_decoder_get_index(mpeg1_decoder_t *self);
+void mpeg1_decoder_set_index(mpeg1_decoder_t *self, unsigned int index);
+void mpeg1_decoder_did_write(mpeg1_decoder_t *self, unsigned int byte_size);
+
+int mpeg1_decoder_has_sequence_header(mpeg1_decoder_t *self);
+float mpeg1_decoder_get_frame_rate(mpeg1_decoder_t *self);
+int mpeg1_decoder_get_coded_size(mpeg1_decoder_t *self);
+int mpeg1_decoder_get_width(mpeg1_decoder_t *self);
+int mpeg1_decoder_get_height(mpeg1_decoder_t *self);
+void *mpeg1_decoder_get_y_ptr(mpeg1_decoder_t *self);
+void *mpeg1_decoder_get_cr_ptr(mpeg1_decoder_t *self);
+void *mpeg1_decoder_get_cb_ptr(mpeg1_decoder_t *self);
+bool mpeg1_decoder_decode(mpeg1_decoder_t *self);
+
+#endif

+ 145 - 0
jsmpeg-master/src/webaudio.js

@@ -0,0 +1,145 @@
+JSMpeg.AudioOutput.WebAudio = (function() { "use strict";
+
+var WebAudioOut = function(options) {
+	this.context = WebAudioOut.CachedContext =
+		WebAudioOut.CachedContext ||
+		new (window.AudioContext || window.webkitAudioContext)();
+
+	this.gain = this.context.createGain();
+	this.destination = this.gain;
+
+	// Keep track of the number of connections to this AudioContext, so we
+	// can safely close() it when we're the only one connected to it.
+	this.gain.connect(this.context.destination);
+	this.context._connections = (this.context._connections || 0) + 1;
+	
+	this.startTime = 0;
+	this.buffer = null;
+	this.wallclockStartTime = 0;
+	this.volume = 1;
+	this.enabled = true;
+
+	this.unlocked = !WebAudioOut.NeedsUnlocking();
+	
+	Object.defineProperty(this, 'enqueuedTime', {get: this.getEnqueuedTime});
+};
+
+WebAudioOut.prototype.destroy = function() {
+	this.gain.disconnect();
+	this.context._connections--;
+
+	if (this.context._connections === 0) {
+		this.context.close();
+		WebAudioOut.CachedContext = null;
+	}
+};
+
+WebAudioOut.prototype.play = function(sampleRate, left, right) {
+	if (!this.enabled) {
+		return;
+	}
+
+	// If the context is not unlocked yet, we simply advance the start time
+	// to "fake" actually playing audio. This will keep the video in sync.
+	if (!this.unlocked) {
+		var ts = JSMpeg.Now()
+		if (this.wallclockStartTime < ts) {
+			this.wallclockStartTime = ts;
+		}
+		this.wallclockStartTime += left.length / sampleRate;
+		return;
+	}
+
+
+	this.gain.gain.value = this.volume;
+
+	var buffer = this.context.createBuffer(2, left.length, sampleRate);
+	buffer.getChannelData(0).set(left);
+	buffer.getChannelData(1).set(right);
+
+	var source = this.context.createBufferSource();
+	source.buffer = buffer;
+	source.connect(this.destination);
+
+	var now = this.context.currentTime;
+	var duration = buffer.duration;
+	if (this.startTime < now) {
+		this.startTime = now;
+		this.wallclockStartTime = JSMpeg.Now();
+	}
+
+	source.start(this.startTime);
+	this.startTime += duration;
+	this.wallclockStartTime += duration;
+};
+
+WebAudioOut.prototype.stop = function() {
+	// Meh; there seems to be no simple way to get a list of currently
+	// active source nodes from the Audio Context, and maintaining this
+	// list ourselfs would be a pain, so we just set the gain to 0
+	// to cut off all enqueued audio instantly.
+	this.gain.gain.value = 0;
+};
+
+WebAudioOut.prototype.getEnqueuedTime = function() {
+	// The AudioContext.currentTime is only updated every so often, so if we
+	// want to get exact timing, we need to rely on the system time.
+	return Math.max(this.wallclockStartTime - JSMpeg.Now(), 0)
+};
+
+WebAudioOut.prototype.resetEnqueuedTime = function() {
+	this.startTime = this.context.currentTime;
+	this.wallclockStartTime = JSMpeg.Now();
+};
+
+WebAudioOut.prototype.unlock = function(callback) {
+	if (this.unlocked) {
+		if (callback) {
+			callback();
+		}
+		return;
+	}
+
+	this.unlockCallback = callback;
+	
+	// Create empty buffer and play it
+	var buffer = this.context.createBuffer(1, 1, 22050);
+	var source = this.context.createBufferSource();
+	source.buffer = buffer;
+	source.connect(this.destination);
+	source.start(0);
+
+	setTimeout(this.checkIfUnlocked.bind(this, source, 0), 0);
+};
+
+WebAudioOut.prototype.checkIfUnlocked = function(source, attempt) {
+	if (
+		source.playbackState === source.PLAYING_STATE || 
+		source.playbackState === source.FINISHED_STATE
+	) {
+		this.unlocked = true;
+		if (this.unlockCallback) {
+			this.unlockCallback();
+			this.unlockCallback = null;	
+		}
+	}
+	else if (attempt < 10) {
+		// Jeez, what a shit show. Thanks iOS!
+		setTimeout(this.checkIfUnlocked.bind(this, source, attempt+1), 100);
+	}
+};
+
+WebAudioOut.NeedsUnlocking = function() {
+	return /iPhone|iPad|iPod/i.test(navigator.userAgent);
+};
+
+WebAudioOut.IsSupported = function() {
+	return (window.AudioContext || window.webkitAudioContext);
+};
+
+WebAudioOut.CachedContext = null;
+
+return WebAudioOut;
+
+})();
+

+ 277 - 0
jsmpeg-master/src/webgl.js

@@ -0,0 +1,277 @@
+JSMpeg.Renderer.WebGL = (function(){ "use strict";
+
+var WebGLRenderer = function(options) {
+	this.canvas = options.canvas || document.createElement('canvas');
+	this.width = this.canvas.width;
+	this.height = this.canvas.height;
+	this.enabled = true;
+
+	this.hasTextureData = {};
+
+	var contextCreateOptions = {
+		preserveDrawingBuffer: !!options.preserveDrawingBuffer,
+		alpha: false,
+		depth: false,
+		stencil: false,
+		antialias: false,
+		premultipliedAlpha: false
+	};
+
+	this.gl = 
+		this.canvas.getContext('webgl', contextCreateOptions) || 
+		this.canvas.getContext('experimental-webgl', contextCreateOptions);
+
+	if (!this.gl) {
+		throw new Error('Failed to get WebGL Context');
+	}
+
+	var gl = this.gl;
+	var vertexAttr = null;
+
+	gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
+
+	// Init buffers
+	this.vertexBuffer = gl.createBuffer();
+	var vertexCoords = new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]);
+	gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+	gl.bufferData(gl.ARRAY_BUFFER, vertexCoords, gl.STATIC_DRAW);
+
+	// Setup the main YCrCbToRGBA shader
+	this.program = this.createProgram(
+		WebGLRenderer.SHADER.VERTEX_IDENTITY,
+		WebGLRenderer.SHADER.FRAGMENT_YCRCB_TO_RGBA
+	);
+	vertexAttr = gl.getAttribLocation(this.program, 'vertex');
+	gl.enableVertexAttribArray(vertexAttr);
+	gl.vertexAttribPointer(vertexAttr, 2, gl.FLOAT, false, 0, 0);
+
+	this.textureY = this.createTexture(0, 'textureY');
+	this.textureCb = this.createTexture(1, 'textureCb');
+	this.textureCr = this.createTexture(2, 'textureCr');
+
+
+	// Setup the loading animation shader
+	this.loadingProgram = this.createProgram(
+		WebGLRenderer.SHADER.VERTEX_IDENTITY,
+		WebGLRenderer.SHADER.FRAGMENT_LOADING
+	);
+	vertexAttr = gl.getAttribLocation(this.loadingProgram, 'vertex');
+	gl.enableVertexAttribArray(vertexAttr);
+	gl.vertexAttribPointer(vertexAttr, 2, gl.FLOAT, false, 0, 0);
+
+	this.shouldCreateUnclampedViews = !this.allowsClampedTextureData();
+};
+
+WebGLRenderer.prototype.destroy = function() {
+	var gl = this.gl;
+	
+	this.deleteTexture(gl.TEXTURE0, this.textureY);
+	this.deleteTexture(gl.TEXTURE1, this.textureCb);
+	this.deleteTexture(gl.TEXTURE2, this.textureCr);
+
+	gl.useProgram(null);
+	gl.deleteProgram(this.program);
+	gl.deleteProgram(this.loadingProgram);
+
+	gl.bindBuffer(gl.ARRAY_BUFFER, null);
+	gl.deleteBuffer(this.vertexBuffer);
+
+	gl.getExtension('WEBGL_lose_context').loseContext();
+	this.canvas.remove();
+};
+
+WebGLRenderer.prototype.resize = function(width, height) {
+	this.width = width|0;
+	this.height = height|0;
+
+	this.canvas.width = this.width;
+	this.canvas.height = this.height;
+
+	this.gl.useProgram(this.program);
+
+	var codedWidth = ((this.width + 15) >> 4) << 4;
+	this.gl.viewport(0, 0, codedWidth, this.height);
+};
+
+WebGLRenderer.prototype.createTexture = function(index, name) {
+	var gl = this.gl;
+	var texture = gl.createTexture();
+
+	gl.bindTexture(gl.TEXTURE_2D, texture);
+	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+	gl.uniform1i(gl.getUniformLocation(this.program, name), index);
+
+	return texture;
+};
+
+WebGLRenderer.prototype.createProgram = function(vsh, fsh) {
+	var gl = this.gl;
+	var program = gl.createProgram();
+
+	gl.attachShader(program, this.compileShader(gl.VERTEX_SHADER, vsh));
+	gl.attachShader(program, this.compileShader(gl.FRAGMENT_SHADER, fsh));
+	gl.linkProgram(program);
+	gl.useProgram(program);
+
+	return program;
+};
+
+WebGLRenderer.prototype.compileShader = function(type, source) {
+	var gl = this.gl;
+	var shader = gl.createShader(type);
+	gl.shaderSource(shader, source);
+	gl.compileShader(shader);
+
+	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+		throw new Error(gl.getShaderInfoLog(shader));
+	}
+
+	return shader;
+};
+
+WebGLRenderer.prototype.allowsClampedTextureData = function() {
+	var gl = this.gl;
+	var texture = gl.createTexture();
+
+	gl.bindTexture(gl.TEXTURE_2D, texture);
+	gl.texImage2D(
+		gl.TEXTURE_2D, 0, gl.LUMINANCE, 1, 1, 0,
+		gl.LUMINANCE, gl.UNSIGNED_BYTE, new Uint8ClampedArray([0])
+	);
+	return (gl.getError() === 0);
+};
+
+WebGLRenderer.prototype.renderProgress = function(progress) {
+	var gl = this.gl;
+
+	gl.useProgram(this.loadingProgram);
+
+	var loc = gl.getUniformLocation(this.loadingProgram, 'progress');
+	gl.uniform1f(loc, progress);
+	
+	gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+};
+
+WebGLRenderer.prototype.render = function(y, cb, cr, isClampedArray) {
+	if (!this.enabled) {
+		return;
+	}
+
+	var gl = this.gl;
+	var w = ((this.width + 15) >> 4) << 4,
+		h = this.height,
+		w2 = w >> 1,
+		h2 = h >> 1;
+
+	// In some browsers WebGL doesn't like Uint8ClampedArrays (this is a bug
+	// and should be fixed soon-ish), so we have to create a Uint8Array view 
+	// for each plane.
+	if (isClampedArray && this.shouldCreateUnclampedViews) {
+		y = new Uint8Array(y.buffer),
+		cb = new Uint8Array(cb.buffer),
+		cr = new Uint8Array(cr.buffer);	
+	}
+
+	gl.useProgram(this.program);
+
+	this.updateTexture(gl.TEXTURE0, this.textureY, w, h, y);
+	this.updateTexture(gl.TEXTURE1, this.textureCb, w2, h2, cb);
+	this.updateTexture(gl.TEXTURE2, this.textureCr, w2, h2, cr);
+
+	gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+};
+
+WebGLRenderer.prototype.updateTexture = function(unit, texture, w, h, data) {
+	var gl = this.gl;
+	gl.activeTexture(unit);
+	gl.bindTexture(gl.TEXTURE_2D, texture);
+
+	if (this.hasTextureData[unit]) {
+		gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, w, h, gl.LUMINANCE, gl.UNSIGNED_BYTE, data);
+	}
+	else {
+		this.hasTextureData[unit] = true;
+		gl.texImage2D(
+			gl.TEXTURE_2D, 0, gl.LUMINANCE, w, h, 0, 
+			gl.LUMINANCE, gl.UNSIGNED_BYTE, data
+		);
+	}
+};
+
+WebGLRenderer.prototype.deleteTexture = function(unit, texture) {
+	var gl = this.gl;
+	gl.activeTexture(unit);
+	gl.bindTexture(gl.TEXTURE_2D, null);
+	gl.deleteTexture(texture);
+};
+
+WebGLRenderer.IsSupported = function() {
+	try {
+		if (!window.WebGLRenderingContext) {
+			return false;
+		}
+
+		var canvas = document.createElement('canvas'); 
+		return !!(
+			canvas.getContext('webgl') || 
+			canvas.getContext('experimental-webgl')
+		);
+	}
+	catch (err) {
+		return false;
+	} 
+};
+
+WebGLRenderer.SHADER = {
+	FRAGMENT_YCRCB_TO_RGBA: [
+		'precision mediump float;',
+		'uniform sampler2D textureY;',
+		'uniform sampler2D textureCb;',
+		'uniform sampler2D textureCr;',
+		'varying vec2 texCoord;',
+
+		'mat4 rec601 = mat4(',
+			'1.16438,  0.00000,  1.59603, -0.87079,',
+			'1.16438, -0.39176, -0.81297,  0.52959,',
+			'1.16438,  2.01723,  0.00000, -1.08139,',
+			'0, 0, 0, 1',
+		');',
+
+		'void main() {',
+			'float y = texture2D(textureY, texCoord).r;',
+			'float cb = texture2D(textureCb, texCoord).r;',
+			'float cr = texture2D(textureCr, texCoord).r;',
+
+			'gl_FragColor = vec4(y, cr, cb, 1.0) * rec601;',
+		'}'
+	].join('\n'),
+
+	FRAGMENT_LOADING: [
+		'precision mediump float;',
+		'uniform float progress;',
+		'varying vec2 texCoord;',
+
+		'void main() {',
+			'float c = ceil(progress-(1.0-texCoord.y));',
+			'gl_FragColor = vec4(c,c,c,1);',
+		'}'
+	].join('\n'),
+
+	VERTEX_IDENTITY: [
+		'attribute vec2 vertex;',
+		'varying vec2 texCoord;',
+
+		'void main() {',
+			'texCoord = vertex;',
+			'gl_Position = vec4((vertex * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);',
+		'}'
+	].join('\n')
+};
+
+return WebGLRenderer;
+
+})();
+

+ 83 - 0
jsmpeg-master/src/websocket.js

@@ -0,0 +1,83 @@
+JSMpeg.Source.WebSocket = (function(){ "use strict";
+
+var WSSource = function(url, options) {
+	this.url = url;
+	this.options = options;
+	this.socket = null;
+	this.streaming = true;
+
+	this.callbacks = {connect: [], data: []};
+	this.destination = null;
+
+	this.reconnectInterval = options.reconnectInterval !== undefined
+		? options.reconnectInterval
+		: 5;
+	this.shouldAttemptReconnect = !!this.reconnectInterval;
+
+	this.completed = false;
+	this.established = false;
+	this.progress = 0;
+
+	this.reconnectTimeoutId = 0;
+
+	this.onEstablishedCallback = options.onSourceEstablished;
+	this.onCompletedCallback = options.onSourceCompleted; // Never used
+};
+
+WSSource.prototype.connect = function(destination) {
+	this.destination = destination;
+};
+
+WSSource.prototype.destroy = function() {
+	clearTimeout(this.reconnectTimeoutId);
+	this.shouldAttemptReconnect = false;
+	this.socket.close();
+};
+
+WSSource.prototype.start = function() {
+	this.shouldAttemptReconnect = !!this.reconnectInterval;
+	this.progress = 0;
+	this.established = false;
+	
+	this.socket = new WebSocket(this.url, this.options.protocols || null);
+	this.socket.binaryType = 'arraybuffer';
+	this.socket.onmessage = this.onMessage.bind(this);
+	this.socket.onopen = this.onOpen.bind(this);
+	this.socket.onerror = this.onClose.bind(this);
+	this.socket.onclose = this.onClose.bind(this);
+};
+
+WSSource.prototype.resume = function(secondsHeadroom) {
+	// Nothing to do here
+};
+
+WSSource.prototype.onOpen = function() {
+	this.progress = 1;
+};
+
+WSSource.prototype.onClose = function() {
+	if (this.shouldAttemptReconnect) {
+		clearTimeout(this.reconnectTimeoutId);
+		this.reconnectTimeoutId = setTimeout(function(){
+			this.start();	
+		}.bind(this), this.reconnectInterval*1000);
+	}
+};
+
+WSSource.prototype.onMessage = function(ev) {
+	var isFirstChunk = !this.established;
+	this.established = true;
+
+	if (isFirstChunk && this.onEstablishedCallback) {
+		this.onEstablishedCallback(this);
+	}
+
+	if (this.destination) {
+		this.destination.write(ev.data);
+	}
+};
+
+return WSSource;
+
+})();
+

+ 22 - 0
jsmpeg-master/view-stream.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>JSMpeg Stream Client</title>
+	<style type="text/css">
+		html, body {
+			background-color: #111;
+			text-align: center;
+		}
+	</style>
+	
+</head>
+<body>
+	<canvas id="video-canvas"></canvas>
+	<script type="text/javascript" src="jsmpeg.min.js"></script>
+	<script type="text/javascript">
+		var canvas = document.getElementById('video-canvas');
+		var url = 'ws://'+document.location.hostname+':8082/';
+		var player = new JSMpeg.Player(url, {canvas: canvas});
+	</script>
+</body>
+</html>

+ 92 - 0
jsmpeg-master/websocket-relay.js

@@ -0,0 +1,92 @@
+// Use the websocket-relay to serve a raw MPEG-TS over WebSockets. You can use
+// ffmpeg to feed the relay. ffmpeg -> websocket-relay -> browser
+// Example:
+// node websocket-relay yoursecret 8081 8082
+// ffmpeg -i <some input> -f mpegts http://localhost:8081/yoursecret
+
+var fs = require('fs'),
+	http = require('http'),
+	WebSocket = require('ws');
+
+if (process.argv.length < 3) {
+	console.log(
+		'Usage: \n' +
+		'node websocket-relay.js <secret> [<stream-port> <websocket-port>]'
+	);
+	process.exit();
+}
+
+var STREAM_SECRET = process.argv[2],
+	STREAM_PORT = process.argv[3] || 8081,
+	WEBSOCKET_PORT = process.argv[4] || 8082,
+	RECORD_STREAM = false;
+
+// Websocket Server
+var socketServer = new WebSocket.Server({port: WEBSOCKET_PORT, perMessageDeflate: false});
+socketServer.connectionCount = 0;
+socketServer.on('connection', function(socket, upgradeReq) {
+	socketServer.connectionCount++;
+	console.log(
+		'New WebSocket Connection: ',
+		(upgradeReq || socket.upgradeReq).socket.remoteAddress,
+		(upgradeReq || socket.upgradeReq).headers['user-agent'],
+		'('+socketServer.connectionCount+' total)'
+	);
+	socket.on('close', function(code, message){
+		socketServer.connectionCount--;
+		console.log(
+			'Disconnected WebSocket ('+socketServer.connectionCount+' total)'
+		);
+	});
+});
+socketServer.broadcast = function(data) {
+	socketServer.clients.forEach(function each(client) {
+		if (client.readyState === WebSocket.OPEN) {
+			client.send(data);
+		}
+	});
+};
+
+// HTTP Server to accept incomming MPEG-TS Stream from ffmpeg
+var streamServer = http.createServer( function(request, response) {
+	var params = request.url.substr(1).split('/');
+
+	if (params[0] !== STREAM_SECRET) {
+		console.log(
+			'Failed Stream Connection: '+ request.socket.remoteAddress + ':' +
+			request.socket.remotePort + ' - wrong secret.'
+		);
+		response.end();
+	}
+
+	response.connection.setTimeout(0);
+	console.log(
+		'Stream Connected: ' +
+		request.socket.remoteAddress + ':' +
+		request.socket.remotePort
+	);
+	request.on('data', function(data){
+		socketServer.broadcast(data);
+		if (request.socket.recording) {
+			request.socket.recording.write(data);
+		}
+	});
+	request.on('end',function(){
+		console.log('close');
+		if (request.socket.recording) {
+			request.socket.recording.close();
+		}
+	});
+
+	// Record the stream to a local file?
+	if (RECORD_STREAM) {
+		var path = 'recordings/' + Date.now() + '.ts';
+		request.socket.recording = fs.createWriteStream(path);
+	}
+})
+// Keep the socket open for streaming
+streamServer.headersTimeout = 0;
+streamServer.listen(STREAM_PORT);
+
+console.log('Listening for incomming MPEG-TS Stream on http://127.0.0.1:'+STREAM_PORT+'/<secret>');
+console.log('Awaiting WebSocket connections on ws://127.0.0.1:'+WEBSOCKET_PORT+'/');

+ 106 - 0
package.json

@@ -0,0 +1,106 @@
+{
+  "name": "zkstation",
+  "version": "4.4.0",
+  "description": "zkstation",
+  "author": "1140836302@qq.com",
+  "scripts": {
+    "dev": "vue-cli-service serve",
+    "lint": "eslint --ext .js,.vue src",
+    "build:prod": "vue-cli-service build",
+    "build:stage": "vue-cli-service build --mode staging",
+    "preview": "node build/index.js --preview",
+    "new": "plop",
+    "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
+    "test:unit": "jest --clearCache && vue-cli-service test:unit",
+    "test:ci": "npm run lint && npm run test:unit"
+  },
+  "dependencies": {
+    "af-table-column": "^1.0.3",
+    "axios": "0.18.1",
+    "clipboard": "2.0.4",
+    "codemirror": "5.45.0",
+    "core-js": "3.6.5",
+    "driver.js": "0.9.5",
+    "dropzone": "5.5.1",
+    "echarts": "4.2.1",
+    "element-ui": "2.13.2",
+    "file-saver": "2.0.1",
+    "fuse.js": "3.4.4",
+    "js-cookie": "2.2.0",
+    "jsonlint": "1.6.3",
+    "jszip": "3.2.1",
+    "moment": "^2.29.4",
+    "normalize.css": "7.0.0",
+    "nprogress": "0.2.0",
+    "path-to-regexp": "2.4.0",
+    "postcss-px2rem": "^0.3.0",
+    "px2rem-loader": "^0.1.9",
+    "screenfull": "4.2.0",
+    "script-loader": "0.7.2",
+    "sortablejs": "1.8.4",
+    "tui-editor": "1.3.3",
+    "vue": "2.6.10",
+    "vue-count-to": "1.0.13",
+    "vue-loading-easy": "^1.0.0",
+    "vue-router": "3.0.2",
+    "vue-splitpane": "1.0.4",
+    "vuedraggable": "2.20.0",
+    "vuex": "3.1.0",
+    "ws": "^8.10.0",
+    "xlsx": "0.14.1"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "4.4.4",
+    "@vue/cli-plugin-eslint": "4.4.4",
+    "@vue/cli-plugin-unit-jest": "4.4.4",
+    "@vue/cli-service": "4.4.4",
+    "@vue/test-utils": "1.0.0-beta.29",
+    "autoprefixer": "9.5.1",
+    "babel-eslint": "10.1.0",
+    "babel-jest": "23.6.0",
+    "babel-plugin-dynamic-import-node": "2.3.3",
+    "chalk": "2.4.2",
+    "chokidar": "2.1.5",
+    "connect": "3.6.6",
+    "eslint": "6.7.2",
+    "eslint-plugin-vue": "6.2.2",
+    "html-webpack-plugin": "3.2.0",
+    "husky": "1.3.1",
+    "lint-staged": "8.1.5",
+    "mockjs": "1.0.1-beta3",
+    "plop": "2.3.0",
+    "runjs": "4.3.2",
+    "sass": "1.26.2",
+    "sass-loader": "8.0.2",
+    "script-ext-html-webpack-plugin": "2.1.3",
+    "serve-static": "1.13.2",
+    "svg-sprite-loader": "4.1.3",
+    "svgo": "1.2.0",
+    "vue-template-compiler": "2.6.10"
+  },
+  "engines": {
+    "node": ">=8.9",
+    "npm": ">= 3.0.0"
+  },
+  "keywords": [
+    "vue",
+    "admin",
+    "dashboard",
+    "element-ui",
+    "boilerplate",
+    "admin-template",
+    "management-system"
+  ],
+  "license": "MIT",
+  "lint-staged": {
+    "src/**/*.{js,vue}": [
+      "eslint --fix",
+      "git add"
+    ]
+  },
+  "husky": {
+    "hooks": {
+      "pre-commit": "lint-staged"
+    }
+  }
+}

+ 5 - 0
postcss.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  plugins: {
+    autoprefixer: {}
+  }
+}

+ 12 - 0
src/App.vue

@@ -0,0 +1,12 @@
+<template>
+  <div id="app">
+    <router-view />
+  </div>
+</template>
+
+<script>
+import router from './router'
+export default {
+  name: 'App'
+}
+</script>

+ 65 - 0
src/api/car.js

@@ -0,0 +1,65 @@
+import request from '@/utils/request'
+
+//车辆列表
+export function vehicleList(data) {
+    return request({
+      url: '/vehicle/list',
+      method: 'post',
+      data
+    })
+}
+//车辆车型司机手动同步
+export function vehicleSync(data) {
+    return request({
+      url: '/vehicle/sync',
+      method: 'get',
+      params:data
+    })
+}
+//车型列表
+export function modelList(data) {
+    return request({
+      url: '/model/list',
+      method: 'post',
+      data
+    })
+}
+//司机管理
+export function driverList(data) {
+    return request({
+      url: '/driver/list',
+      method: 'post',
+      data
+    })
+}
+//添加车辆
+export function SaveVehicle(data) {
+  return request({
+    url: '/vehicle/save',
+    method: 'post',
+    data
+  })
+}
+//修改车辆
+export function EditVehicle(data) {
+  return request({
+    url: '/vehicle/edit',
+    method: 'post',
+    data
+  })
+}
+//删除车辆
+export function DeleteVehicle(data) {
+  return request({
+    url: '/vehicle/del',
+    method: 'post',
+    data
+  })
+}
+//同步云端
+export function getCarSync() {
+  return request({
+    url: '/vehicle/sync',
+    method: 'post',
+  })
+}

+ 24 - 0
src/api/downfile.js

@@ -0,0 +1,24 @@
+import request from '@/utils/requestfile'
+//导出数据列表
+export function exportList(data) {
+  return request({
+    url: '/export/list',
+    method: 'post',
+    data
+  })
+}
+//生成导出文件
+export function exportFile(data) {
+  return request({
+    url: '/log/export',
+    method: 'post',
+    data
+  })
+}
+//删除数据导出列表
+export function deleteExportData(id) {
+  return request({
+    url: `/export/delete/${id}`,
+    method: 'post',
+  })
+}

+ 46 - 0
src/api/equipment.js

@@ -0,0 +1,46 @@
+import request from '@/utils/request'
+
+// 消防换仓
+export function fireStore() {
+  return request({
+    url: '/device/fireStore',
+    method: 'get'
+  })
+}
+// 检修换仓
+export function mtStore() {
+  return request({
+    url: '/device/mtStore',
+    method: 'get'
+  })
+}
+// 机器人复位
+export function resetRobot() {
+  return request({
+    url: '/device/resetRobot',
+    method: 'get'
+  })
+}
+// 获取仓位
+export function getStoreCode() {
+  return request({
+    url: '/device/getStoreCode',
+    method: 'get'
+  })
+}
+//开始充电
+export function startCharge(data) {
+  return request({
+    url: '/discharge/startCharge',
+    method: 'post',
+    params:data,
+  })
+}
+//断电
+export function stopCharge(data) {
+  return request({
+    url: '/discharge/stopCharge',
+    method: 'post',
+    params:data
+  })
+}

+ 100 - 0
src/api/log.js

@@ -0,0 +1,100 @@
+import request from '@/utils/request'
+
+//日志数据字典
+export function logModel(data) {
+    return request({
+      url: '/dictData/plcList',
+      method: 'post',
+      data
+    })
+}
+//充电机日志数据字典
+export function chargerModel(data) {
+  return request({
+    url: '/dictData/chargerList',
+    method: 'post',
+    data
+  })
+}
+//BMS日志数据字典
+export function BmsModel(data) {
+  return request({
+    url: '/dictData/bmsList',
+    method: 'post',
+    data
+  })
+}
+//机器人日志列表
+export function robotList(data) {
+    return request({
+      url: '/log/robotList',
+      method: 'post',
+      data
+    })
+}
+//充电机日志列表
+export function chargerList(data) {
+  return request({
+    url: '/log/chargerList',
+    method: 'post',
+    data
+  })
+}
+//BMS日志列表
+export function bmsList(data) {
+  return request({
+    url: '/log/bmsList',
+    method: 'post',
+    data
+  })
+}
+//导出数据列表
+export function exportList(data) {
+  return request({
+    url: '/export/list',
+    method: 'post',
+    data
+  })
+}
+//生成导出文件
+export function exportFile(data) {
+  return request({
+    url: '/log/export',
+    method: 'post',
+    data
+  })
+}
+//删除数据导出列表
+export function deleteExportData(id) {
+  return request({
+    url: `/export/delete/${id}`,
+    method: 'post',
+  })
+}
+//导出结果
+export function RecordFile(Fileurl,fileName){
+  return request({
+    url: Fileurl,
+    method: 'get',
+    responseType: 'blob',
+  })
+  // .then(data=>{
+  //   console.log("RecordFile then", data)
+  //   if (!data) return alert('文件下载失败');
+  //   // let fileName = '统计表计';
+  //   if (typeof window.navigator.msSaveBlob !== 'undefined') {
+  //       // window.navigator.msSaveBlob(new Blob([data], { type: 'application/vnd.ms-excel' }), fileName + '.xlsx');
+  //       window.navigator.msSaveBlob(new Blob([data], { type: 'application/vnd.ms-excel' }), fileName);
+  //   } else {
+  //       let url = window.URL.createObjectURL(new Blob([data], { type: 'application/vnd.ms-excel' }));
+  //       let link = document.createElement('a');
+  //       link.style.display = 'none';
+  //       link.href = url;
+  //       link.setAttribute('download', fileName);
+  //       document.body.appendChild(link);
+  //       link.click();
+  //       document.body.removeChild(link); //下载完成移除元素
+  //       window.URL.revokeObjectURL(url); //释放掉blob对象
+  //   }
+  // })
+}

+ 160 - 0
src/api/powerCharge.js

@@ -0,0 +1,160 @@
+import request from '@/utils/request'
+//换电信息
+export function swapInfo(data) {
+    return request({
+      url: '/swap/swapInfo',
+      method: 'post',
+      data
+    })
+}
+//换电记录
+export function swapRecord(data) {
+    return request({
+      url: '/swap/swapRecord',
+      method: 'post',
+      data
+    })
+}
+//告警记录
+export function alarmRecord(data) {
+    return request({
+      url: '/swap/alarmRecord',
+      method: 'post',
+      data
+    })
+}
+//已连接站控车辆列表
+export function CarOnline(data){
+  return request({
+    url: '/vehicle/online',
+    method: 'post',
+    data
+  })
+}
+//修改车牌号
+export function EditPlate(data){
+  return request({
+    url: '/vehicle/editPlate',
+    method: 'post',
+    data
+  })
+}
+//刷新车辆
+export function refreshPlate(){
+  return request({
+    url: '/vehicle/refresh ',
+    method: 'post',
+  })
+}
+//车辆上锁
+export function Updatelock(data){
+  return request({
+    url: '/vehicle/lock',
+    method: 'post',
+    data
+  })
+}
+//车辆解锁
+export function Updateunlock(data){
+  return request({
+    url: '/vehicle/unlock',
+    method: 'post',
+    data
+  })
+}
+//锁止屏蔽
+export function EditforbidLock(data){
+  return request({
+    url: '/vehicle/forbidLock',
+    method: 'post',
+    data
+  })
+}
+//暂停恢复PLC
+export function EditSwapplc(data){
+  return request({
+    url: '/swap/plc',
+    method: 'post',
+    data
+  })
+}
+// 开始换电
+export function beginSwap(data) {
+  return request({
+    url: '/swap/startSwap',
+    method: 'post',
+    data
+  })
+}
+//设置选电模式(0自动、1手动)
+export function setMode(data) {
+  return request({
+    url: '/swap/setMode',
+    method: 'post',
+    data
+  })
+}
+//急停中止
+export function plcStop(data) {
+  return request({
+    url: '/swap/plcStop',
+    method: 'post',
+    data
+  })
+}
+//冻结、解冻
+export function batteryfreeze(data) {
+  return request({
+    url: '/swap/freeze',
+    method: 'post',
+    data
+  })
+}
+//普通换仓
+export function RuleChangestorage(data) {
+  return request({
+    url: '/device/exchangeBattery',
+    method: 'post',
+    data
+  })
+}
+//消防换仓
+export function FireChangestorage(data) {
+  return request({
+    url: '/device/fireProtection',
+    method: 'post',
+    data
+  })
+}
+//设备监控弹窗设置接口
+export function getlimitPower(data) {
+  return request({
+    url: '/discharge/limitPower',
+    method: 'post',
+    data
+  })
+}
+//切换仓内仓外
+export function gettoggleChargeWay(data) {
+  return request({
+    url: '/discharge/toggleChargeWay',
+    method: 'post',
+    data
+  })
+}
+//插枪充电
+export function getgunStartCharge(data) {
+  return request({
+    url: '/discharge/gunStartCharge',
+    method: 'post',
+    data
+  })
+}
+//停止插枪充电
+export function getgunStopCharge(data) {
+  return request({
+    url: '/discharge/gunStopCharge',
+    method: 'post',
+    data
+  })
+}

+ 130 - 0
src/api/records.js

@@ -0,0 +1,130 @@
+import request from '@/utils/request'
+
+// 获取充电机ID
+export function getChargeCode() {
+  return request({
+    url: '/device/getChargeCode',
+    method: 'get'
+  })
+}
+// 充电记录列表
+export function chargeList(data) {
+  return request({
+    url: '/charge/list',
+    method: 'post',
+    data
+  })
+}
+// 充电记录导出
+export function chargeExport(data) {
+  return request({
+    url: '/charge/export',
+    method: 'post',
+    data
+  })
+}
+// 换电记录列表
+export function swapList(data) {
+  return request({
+    url: '/swapRecord/list',
+    method: 'post',
+    data
+  })
+}
+// 换电记录详情
+export function swapRecordDetail(data) {
+  return request({
+    url: '/swapRecord/getInfo',
+    method: 'post',
+    data
+  })
+}
+// 换电记录导出
+export function swapExport(data) {
+  return request({
+    url: '/swapRecord/export',
+    method: 'post',
+    data
+  })
+}
+// 告警记录列表
+export function alarmList(data) {
+  return request({
+    url: '/alarm/list',
+    method: 'post',
+    data
+  })
+}
+// 当前告警列表
+export function getrealList(data) {
+  return request({
+    url: '/alarm/realList',
+    method: 'post',
+    data
+  })
+}
+//消防调仓
+export function FireChangestorage(data) {
+  return request({
+    url: '/device/fireProtection',
+    method: 'post',
+    data
+  })
+}
+// 告警记录导出
+export function alarmExport(data) {
+  return request({
+    url: '/alarm/export',
+    method: 'post',
+    data
+  })
+}
+// 处理告警
+export function alarmDispose(data) {
+  return request({
+    url: '/alarm/dispose',
+    method: 'post',
+    data
+  })
+}
+//导出结果
+export function RecordFile(Fileurl,fileName){
+  return request({
+    url: Fileurl,
+    method: 'get',
+    responseType: 'blob',
+  }).then(data=>{
+    console.log("RecordFile then", data)
+    if (!data) return alert('文件下载失败');
+    // let fileName = '统计表计';
+    if (typeof window.navigator.msSaveBlob !== 'undefined') {
+        // window.navigator.msSaveBlob(new Blob([data], { type: 'application/vnd.ms-excel' }), fileName + '.xlsx');
+        window.navigator.msSaveBlob(new Blob([data], { type: 'application/vnd.ms-excel' }), fileName);
+    } else {
+        let url = window.URL.createObjectURL(new Blob([data], { type: 'application/vnd.ms-excel' }));
+        let link = document.createElement('a');
+        link.style.display = 'none';
+        link.href = url;
+        link.setAttribute('download', fileName);
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link); //下载完成移除元素
+        window.URL.revokeObjectURL(url); //释放掉blob对象
+    }
+  })
+}
+// 费率
+export function getratelist(data) {
+  return request({
+    url: '/rate/list',
+    method: 'post',
+    data
+  })
+}
+// 费率删除
+export function getdeletecode(id) {
+  return request({
+    url: `/rate/delete?id=${id}`,
+    method: 'get',
+  })
+}

+ 84 - 0
src/api/statistics.js

@@ -0,0 +1,84 @@
+import request from '@/utils/request'
+//告警次数
+export function getAlarm(data) {
+  return request({
+    url: '/statistics/alarmNumber',
+    method: 'post',
+    data
+  })
+}
+//告警类型
+export function getAlarmType(data) {
+  return request({
+    url: '/statistics/alarmRatio',
+    method: 'post',
+    data
+  })
+}
+//安全天数、营业状态
+export function getStateList(data) {
+  return request({
+    url: '/swap/other',
+    method: 'post',
+    data
+  })
+}
+//设置营业状态
+export function setBusiness(data) {
+  return request({
+    url: '/swap/setBusiness',
+    method: 'post',
+    data
+  })
+}
+//云端连接状态
+export function cloudState() {
+  return request({
+    url: '/swap/cloudConnState',
+    method: 'post',
+  })
+}
+//换电曲线
+export function changePower(data) {
+  return request({
+    url: '/statistics/swapCurve',
+    method: 'post',
+    data
+  })
+}
+//处置告警消防换仓
+export function getfireProtection(data){
+  return request({
+    url: '/device/fireProtection',
+    method: 'post',
+    data
+  })
+}
+//实时消防告警
+export function getwarnList(){
+  return request({
+    url: '/alarm/toDayAlarm',
+    method: 'post',
+  })
+}
+//查询温度
+export function serchTemp(){
+  return request({
+    url: '/alarm/queryTemp',
+    method: 'post',
+  })
+}
+//设置温度
+export function setTemp(data){
+  return request({
+    url: `/alarm/setTemp?temp=${data}`,
+    method: 'post',
+  })
+}
+//获取换电站编号
+export function getStationCode(){
+  return request({
+    url: '/swap/getStationCode',
+    method: 'post',
+  })
+}

+ 65 - 0
src/api/user.js

@@ -0,0 +1,65 @@
+import request from '@/utils/request'
+//登录
+export function login(data) {
+  return request({
+    url: '/user/login',
+    method: 'post',
+    data,
+  })
+}
+//获取用户信息
+export function getInfo(data) {
+  return request({
+    url: '/user/info',
+    method: 'get',
+    params: { id:data.userId }
+  })
+}
+//添加用户
+export function addUserinfo(data) {
+  return request({
+    url: '/user/save',
+    method: 'post',
+    data,
+  })
+}
+//用户列表
+export function getUserlist(data) {
+  return request({
+    url: '/user/list',
+    method: 'post',
+    data
+  })
+}
+//删除用户
+export function deleteUser(data) {
+  return request({
+    url: '/user/del',
+    method: 'post',
+    data
+  })
+}
+//修改用户
+export function updateUser(data) {
+  return request({
+    url: '/user/edit',
+    method: 'post',
+    data
+  })
+}
+//重置密码
+export function resetpwd(data) {
+  return request({
+    url: '/user/initPwd',
+    method: 'post',
+    data
+  })
+}
+//用户修改密码
+export function updatepwd(data) {
+  return request({
+    url: '/user/editPwd',
+    method: 'post',
+    data
+  })
+}

BIN
src/assets/401_images/401.gif


BIN
src/assets/404_images/404.png


BIN
src/assets/404_images/404_cloud.png


BIN
src/assets/car1x.png


BIN
src/assets/cartype.png


BIN
src/assets/custom-theme/fonts/element-icons.ttf


BIN
src/assets/custom-theme/fonts/element-icons.woff


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
src/assets/custom-theme/index.css


BIN
src/assets/default.png


BIN
src/assets/machine.png


BIN
src/assets/messageb.png


BIN
src/assets/messaget.png


BIN
src/assets/onlineb.png


BIN
src/assets/onlinet.png


BIN
src/assets/record.png


BIN
src/assets/roleb.png


BIN
src/assets/roletop.png


+ 62 - 0
src/components/SvgIcon/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
+  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
+    <use :xlink:href="iconName" />
+  </svg>
+</template>
+
+<script>
+import { isExternal } from '@/utils/validate'
+
+export default {
+  name: 'SvgIcon',
+  props: {
+    iconClass: {
+      type: String,
+      required: true
+    },
+    className: {
+      type: String,
+      default: ''
+    }
+  },
+  computed: {
+    isExternal() {
+      return isExternal(this.iconClass)
+    },
+    iconName() {
+      return `#icon-${this.iconClass}`
+    },
+    svgClass() {
+      if (this.className) {
+        return 'svg-icon ' + this.className
+      } else {
+        return 'svg-icon'
+      }
+    },
+    styleExternalIcon() {
+      return {
+        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
+        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.svg-icon {
+  width: 18px;
+  height: 18px;
+  vertical-align: -2px;
+  fill: currentColor;
+  overflow: hidden;
+  color: #636c97;
+}
+
+.svg-external-icon {
+  background-color: currentColor;
+  mask-size: cover!important;
+  display: inline-block;
+}
+</style>

+ 13 - 0
src/directive/permission/index.js

@@ -0,0 +1,13 @@
+import permission from './permission'
+
+const install = function(Vue) {
+  Vue.directive('permission', permission)
+}
+
+if (window.Vue) {
+  window['permission'] = permission
+  Vue.use(install); // eslint-disable-line
+}
+
+permission.install = install
+export default permission

+ 31 - 0
src/directive/permission/permission.js

@@ -0,0 +1,31 @@
+import store from '@/store'
+
+function checkPermission(el, binding) {
+  const { value } = binding
+  const roles = store.getters && store.getters.roles
+
+  if (value && value instanceof Array) {
+    if (value.length > 0) {
+      const permissionRoles = value
+
+      const hasPermission = roles.some(role => {
+        return permissionRoles.includes(role)
+      })
+
+      if (!hasPermission) {
+        el.parentNode && el.parentNode.removeChild(el)
+      }
+    }
+  } else {
+    throw new Error(`need roles! Like v-permission="['admin','editor']"`)
+  }
+}
+
+export default {
+  inserted(el, binding) {
+    checkPermission(el, binding)
+  },
+  update(el, binding) {
+    checkPermission(el, binding)
+  }
+}

+ 113 - 0
src/filters/index.js

@@ -0,0 +1,113 @@
+export { parseTime, formatTime } from '@/utils'
+
+/**
+ * Show plural label if time is plural number
+ * @param {number} time
+ * @param {string} label
+ * @return {string}
+ */
+function pluralize(time, label) {
+  if (time === 1) {
+    return time + label
+  }
+  return time + label + 's'
+}
+
+/**
+ * @param {number} time
+ */
+export function timeAgo(time) {
+  const between = Date.now() / 1000 - Number(time)
+  if (between < 3600) {
+    return pluralize(~~(between / 60), ' minute')
+  } else if (between < 86400) {
+    return pluralize(~~(between / 3600), ' hour')
+  } else {
+    return pluralize(~~(between / 86400), ' day')
+  }
+}
+
+/**
+ * Number formatting
+ * like 10000 => 10k
+ * @param {number} num
+ * @param {number} digits
+ */
+export function numberFormatter(num, digits) {
+  const si = [
+    { value: 1E18, symbol: 'E' },
+    { value: 1E15, symbol: 'P' },
+    { value: 1E12, symbol: 'T' },
+    { value: 1E9, symbol: 'G' },
+    { value: 1E6, symbol: 'M' },
+    { value: 1E3, symbol: 'k' }
+  ]
+  for (let i = 0; i < si.length; i++) {
+    if (num >= si[i].value) {
+      return (num / si[i].value).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
+    }
+  }
+  return num.toString()
+}
+
+/**
+ * 10000 => "10,000"
+ * @param {number} num
+ */
+export function toThousandFilter(num) {
+  return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
+}
+
+/**
+ * Upper case first char
+ * @param {String} string
+ */
+export function uppercaseFirst(string) {
+  return string.charAt(0).toUpperCase() + string.slice(1)
+}
+export function numFilter(value) {
+  if(value){
+    const realVal = parseFloat(value).toFixed(2)
+    return realVal
+  }else{
+    return '—'
+  }
+}
+export function PercentFilter(value) {
+  if(value > 0){
+    const realVal = parseFloat(value).toFixed(2)
+    return realVal+'%'
+  }else{
+    return '—'
+  }
+}
+export function exchangerFilter(value) {
+  if(value){
+    const realVal = parseFloat(value).toFixed(2)
+     return realVal
+  }else{
+    return ''
+  }
+}
+export function RecordFilter(value){
+  if(value){
+    return value.substring(0,8)+'****'+value.substring(value.length-8)
+  }return ''
+}
+export function HeightFilter(value){
+  if(value){
+    return (value/10)+'rem'
+  }
+}
+//过滤设备监控速度、位置
+export function equipmentFilter(num,len =5){
+  let numStr = typeof(num) == "number" ? `${num}` : num // 判断类型,统一转字符串处理
+  if(!numStr)return ''; // 空值return 空字符串
+  numStr=numStr.replace(/[^0-9.]/g,'') // 限制值只能为0-9和字符‘.’
+  let isDecimalIndex = numStr.indexOf('.') // 判断存不存在小数位
+  if(isDecimalIndex === -1)return `${Number(numStr)}` // 不存在小数位,返回处理后的数字
+  let integer = numStr.slice(0,isDecimalIndex) // 获取整数位
+  let Decimals = numStr.slice(isDecimalIndex+1) // 获取小数位
+  Decimals=Decimals.replace(/[^0-9]/g,'').slice(0,len) // 处理小数位中的无效字符,并截取指定长度
+  return `${Number(integer)}.${Decimals || ''}` // 组装数据。并在处理小数位事,将无效字符处理成空串
+}

+ 9 - 0
src/icons/index.js

@@ -0,0 +1,9 @@
+import Vue from 'vue'
+import SvgIcon from '@/components/SvgIcon'// svg component
+
+// register globally
+Vue.component('svg-icon', SvgIcon)
+
+const req = require.context('./svg', false, /\.svg$/)
+const requireAll = requireContext => requireContext.keys().map(requireContext)
+requireAll(req)

+ 1 - 0
src/icons/svg/404.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg>

+ 1 - 0
src/icons/svg/bug.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg>

+ 1 - 0
src/icons/svg/chart.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h36.571V128H0V54.857zM91.429 27.43H128V128H91.429V27.429zM45.714 0h36.572v128H45.714V0z"/></svg>

+ 1 - 0
src/icons/svg/clipboard.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.857 118.857h64V73.143H89.143c-1.902 0-3.52-.668-4.855-2.002-1.335-1.335-2.002-2.954-2.002-4.855V36.57H54.857v82.286zM73.143 16v-4.571a2.2 2.2 0 0 0-.677-1.61 2.198 2.198 0 0 0-1.609-.676H20.571c-.621 0-1.158.225-1.609.676a2.198 2.198 0 0 0-.676 1.61V16a2.2 2.2 0 0 0 .676 1.61c.451.45.988.676 1.61.676h50.285c.622 0 1.158-.226 1.61-.677.45-.45.676-.987.676-1.609zm18.286 48h21.357L91.43 42.642V64zM128 73.143v48c0 1.902-.667 3.52-2.002 4.855-1.335 1.335-2.953 2.002-4.855 2.002H52.57c-1.901 0-3.52-.667-4.854-2.002-1.335-1.335-2.003-2.953-2.003-4.855v-11.429H6.857c-1.902 0-3.52-.667-4.855-2.002C.667 106.377 0 104.759 0 102.857v-96c0-1.902.667-3.52 2.002-4.855C3.337.667 4.955 0 6.857 0h77.714c1.902 0 3.52.667 4.855 2.002 1.335 1.335 2.003 2.953 2.003 4.855V30.29c1 .622 1.856 1.29 2.569 2.003l29.147 29.147c1.335 1.335 2.478 3.145 3.429 5.43.95 2.287 1.426 4.383 1.426 6.291v-.018z"/></svg>

+ 1 - 0
src/icons/svg/component.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h54.857v54.857H0V0zm0 73.143h54.857V128H0V73.143zm73.143 0H128V128H73.143V73.143zm27.428-18.286C115.72 54.857 128 42.577 128 27.43 128 12.28 115.72 0 100.571 0 85.423 0 73.143 12.28 73.143 27.429c0 15.148 12.28 27.428 27.428 27.428z"/></svg>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
src/icons/svg/dashboard.svg


+ 1 - 0
src/icons/svg/documentation.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M71.984 44.815H115.9L71.984 9.642v35.173zM16.094.05h63.875l47.906 38.37v76.74c0 3.392-1.682 6.645-4.677 9.044-2.995 2.399-7.056 3.746-11.292 3.746H16.094c-4.236 0-8.297-1.347-11.292-3.746-2.995-2.399-4.677-5.652-4.677-9.044V12.84C.125 5.742 7.23.05 16.094.05zm71.86 102.32V89.58h-71.86v12.79h71.86zm23.952-25.58V64H16.094v12.79h95.812z"/></svg>

+ 1 - 0
src/icons/svg/drag.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M73.137 29.08h-9.209 29.7L63.886.093 34.373 29.08h20.49v27.035H27.238v17.948h27.625v27.133h18.274V74.063h27.41V56.115h-27.41V29.08zm-9.245 98.827l27.518-26.711H36.59l27.302 26.71zM.042 64.982l27.196 27.029V38.167L.042 64.982zm100.505-26.815V92.01l27.41-27.029-27.41-26.815z"/></svg>

+ 1 - 0
src/icons/svg/edit.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg>

+ 1 - 0
src/icons/svg/education.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M88.883 119.565c-7.284 0-19.434 2.495-21.333 8.25v.127c-4.232.13-5.222 0-7.108 0-1.895-5.76-14.045-8.256-21.333-8.256H0V0h42.523c9.179 0 17.109 5.47 21.47 13.551C68.352 5.475 76.295 0 85.478 0H128v119.57l-39.113-.005h-.004zM60.442 24.763c0-9.651-8.978-16.507-17.777-16.507H7.108V111.43H39.11c7.054-.14 18.177.082 21.333 6.12v-4.628c-.134-5.722-.004-13.522 0-13.832V27.413l.004-2.655-.004.005zm60.442-16.517h-35.55c-8.802 0-17.78 6.856-17.78 16.493v74.259c.004.32.138 8.115 0 13.813v4.627c3.155-6.022 14.279-6.26 21.333-6.114h32V8.25l-.003-.005z"/></svg>

+ 1 - 0
src/icons/svg/email.svg

@@ -0,0 +1 @@
+<svg width="128" height="96" xmlns="http://www.w3.org/2000/svg"><path d="M64.125 56.975L120.188.912A12.476 12.476 0 0 0 115.5 0h-103c-1.588 0-3.113.3-4.513.838l56.138 56.137z"/><path d="M64.125 68.287l-62.3-62.3A12.42 12.42 0 0 0 0 12.5v71C0 90.4 5.6 96 12.5 96h103c6.9 0 12.5-5.6 12.5-12.5v-71a12.47 12.47 0 0 0-1.737-6.35L64.125 68.287z"/></svg>

+ 1 - 0
src/icons/svg/example.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg>

+ 1 - 0
src/icons/svg/excel.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.208 16.576v8.384h38.72v5.376h-38.72v8.704h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.512h38.72v5.376h-38.72v11.136H128v-94.72H78.208zM0 114.368L72.128 128V0L0 13.632v100.736z"/><path d="M28.672 82.56h-11.2l14.784-23.488-14.08-22.592h11.52l8.192 14.976 8.448-14.976h11.136l-14.08 22.208L58.368 82.56H46.656l-8.768-15.68z"/></svg>

+ 1 - 0
src/icons/svg/exit-fullscreen.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg>

+ 1 - 0
src/icons/svg/eye-open.svg

@@ -0,0 +1 @@
+<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg>

+ 1 - 0
src/icons/svg/eye.svg

@@ -0,0 +1 @@
+<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
src/icons/svg/form.svg


+ 1 - 0
src/icons/svg/fullscreen.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg>

+ 1 - 0
src/icons/svg/guide.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.482 70.131l36.204 16.18 69.932-65.485-61.38 70.594 46.435 18.735c1.119.425 2.397-.17 2.797-1.363v-.085L127.998.047 1.322 65.874c-1.12.597-1.519 1.959-1.04 3.151.32.511.72.937 1.2 1.107zm44.676 57.821L64.22 107.26l-18.062-7.834v28.527z"/></svg>

+ 6 - 0
src/icons/svg/hdjk.svg

@@ -0,0 +1,6 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3.75 12.75H3C2.60218 12.75 2.22064 12.592 1.93934 12.3107C1.65804 12.0294 1.5 11.6478 1.5 11.25V3.75C1.5 3.35218 1.65804 2.97064 1.93934 2.68934C2.22064 2.40804 2.60218 2.25 3 2.25H15C15.3978 2.25 15.7794 2.40804 16.0607 2.68934C16.342 2.97064 16.5 3.35218 16.5 3.75V11.25C16.5 11.6478 16.342 12.0294 16.0607 12.3107C15.7794 12.592 15.3978 12.75 15 12.75H14.25" stroke="#636C97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9 11.25L12.75 15.75H5.25L9 11.25Z" stroke="#636C97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M13 6L10.4091 9.16667L9.04545 7.5L7 10" stroke="#636C97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M11.0938 5.8125H13.1562V7.875" stroke="#636C97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 13 - 0
src/icons/svg/hdjk_active.svg

@@ -0,0 +1,13 @@
+<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3.75 12.9321H3C2.60218 12.9321 2.22064 12.7741 1.93934 12.4928C1.65804 12.2115 1.5 11.83 1.5 11.4321V3.93213C1.5 3.5343 1.65804 3.15277 1.93934 2.87147C2.22064 2.59016 2.60218 2.43213 3 2.43213H15C15.3978 2.43213 15.7794 2.59016 16.0607 2.87147C16.342 3.15277 16.5 3.5343 16.5 3.93213V11.4321C16.5 11.83 16.342 12.2115 16.0607 12.4928C15.7794 12.7741 15.3978 12.9321 15 12.9321H14.25" stroke="#91FDB9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9 11.4321L12.75 15.9321H5.25L9 11.4321Z" stroke="#91FDB9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<g clip-path="url(#clip0_495_69969)">
+<path d="M13 6.18213L10.4091 9.3488L9.04545 7.68213L7 10.1821" stroke="#91FDB9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M11.0938 5.99463H13.1562V8.05713" stroke="#91FDB9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+<defs>
+<clipPath id="clip0_495_69969">
+<rect width="10" height="8" fill="white" transform="matrix(1 0 0 -1 5 12.1821)"/>
+</clipPath>
+</defs>
+</svg>

+ 3 - 0
src/icons/svg/ico.svg

@@ -0,0 +1,3 @@
+<svg width="17" height="10" viewBox="0 0 17 10" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8.86938 8.38135L15.7976 2.38135H1.94118L8.86938 8.38135Z" fill="#91FDB9"/>
+</svg>

+ 1 - 0
src/icons/svg/icon.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.147.062a13 13 0 0 1 4.94.945c1.55.63 2.907 1.526 4.069 2.688a13.148 13.148 0 0 1 2.761 4.069c.678 1.55 1.017 3.245 1.017 5.086v102.3c0 3.681-1.187 6.733-3.56 9.155-2.373 2.422-5.352 3.633-8.937 3.633H12.992c-3.875 0-7-1.26-9.373-3.779-2.373-2.518-3.56-5.667-3.56-9.445V12.704c0-3.39 1.163-6.345 3.488-8.863C5.872 1.32 8.972.062 12.847.062h102.3zM81.434 109.047c1.744 0 3.003-.412 3.778-1.235.775-.824 1.163-1.914 1.163-3.27 0-1.26-.388-2.325-1.163-3.197-.775-.872-2.034-1.307-3.778-1.307H72.57c.097-.194.145-.485.145-.872V27.09h9.01c1.743 0 2.954-.436 3.633-1.308.678-.872 1.017-1.938 1.017-3.197 0-1.26-.34-2.325-1.017-3.197-.679-.872-1.89-1.308-3.633-1.308H46.268c-1.743 0-2.954.436-3.632 1.308-.678.872-1.018 1.938-1.018 3.197 0 1.26.34 2.325 1.018 3.197.678.872 1.889 1.308 3.632 1.308h8.138v72.075c0 .193.024.339.073.436.048.096.072.242.072.436H46.56c-1.744 0-3.003.435-3.778 1.307-.775.872-1.163 1.938-1.163 3.197 0 1.356.388 2.446 1.163 3.27.775.823 2.034 1.235 3.778 1.235h34.875z"/></svg>

+ 1 - 0
src/icons/svg/international.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M83.287 103.01c-1.57-3.84-6.778-10.414-15.447-19.548-2.327-2.444-2.182-4.306-1.338-9.862v-.64c.553-3.81 1.513-6.05 14.313-8.087 6.516-1.018 8.203 1.57 10.589 5.178l.785 1.193a12.625 12.625 0 0 0 6.43 5.207c1.134.524 2.53 1.164 4.421 2.24 4.596 2.53 4.596 5.41 4.596 11.753v.727a26.91 26.91 0 0 1-5.178 17.454 59.055 59.055 0 0 1-19.025 11.026c3.49-6.546.814-14.313 0-16.553l-.146-.087zM64 5.12a58.502 58.502 0 0 1 25.484 5.818 54.313 54.313 0 0 0-12.859 10.327c-.93 1.28-1.716 2.473-2.472 3.579-2.444 3.694-3.637 5.352-5.818 5.614a25.105 25.105 0 0 1-4.219 0c-4.276-.29-10.094-.64-11.956 4.422-1.193 3.23-1.396 11.956 2.444 16.495.66 1.077.778 2.4.32 3.578a7.01 7.01 0 0 1-2.066 3.229 18.938 18.938 0 0 1-2.909-2.91 18.91 18.91 0 0 0-8.32-6.603c-1.25-.349-2.647-.64-3.985-.93-3.782-.786-8.03-1.688-9.019-3.812a14.895 14.895 0 0 1-.727-5.818 21.935 21.935 0 0 0-1.396-9.25 8.873 8.873 0 0 0-5.557-4.946A58.705 58.705 0 0 1 64 5.12zM0 64c0 35.346 28.654 64 64 64 35.346 0 64-28.654 64-64 0-35.346-28.654-64-64-64C28.654 0 0 28.654 0 64z"/></svg>

+ 12 - 0
src/icons/svg/kzpz.svg

@@ -0,0 +1,12 @@
+<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame" clip-path="url(#clip0_11_78901)">
+<path id="Vector" d="M9 16.625C12.935 16.625 16.125 13.435 16.125 9.5C16.125 5.56497 12.935 2.375 9 2.375C5.06497 2.375 1.875 5.56497 1.875 9.5C1.875 13.435 5.06497 16.625 9 16.625Z" stroke="#636C97" stroke-width="2"/>
+<path id="Vector_2" d="M13.502 9.7223V9.72229C13.502 8.63302 12.6189 7.75 11.5297 7.75H6.46956C5.38029 7.75 4.49726 8.63302 4.49726 9.72229V9.7223C4.49726 10.8116 5.38029 11.6946 6.46956 11.6946H11.5297C12.6189 11.6946 13.502 10.8116 13.502 9.7223Z" stroke="#636C97" stroke-width="2"/>
+<path id="Vector_3" d="M9.75195 9.81982C9.75195 10.8554 10.5914 11.6948 11.627 11.6948C12.6625 11.6948 13.502 10.8554 13.502 9.81982C13.502 8.78429 12.6625 7.94482 11.627 7.94482C10.5914 7.94482 9.75195 8.78429 9.75195 9.81982Z" stroke="#636C97" stroke-width="2"/>
+</g>
+<defs>
+<clipPath id="clip0_11_78901">
+<rect width="18" height="18" fill="white" transform="translate(0 0.5)"/>
+</clipPath>
+</defs>
+</svg>

+ 12 - 0
src/icons/svg/kzpz_active.svg

@@ -0,0 +1,12 @@
+<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame" clip-path="url(#clip0_11_78915)">
+<path id="Vector" d="M9 16.625C12.935 16.625 16.125 13.435 16.125 9.5C16.125 5.56497 12.935 2.375 9 2.375C5.06497 2.375 1.875 5.56497 1.875 9.5C1.875 13.435 5.06497 16.625 9 16.625Z" stroke="#91FDB9" stroke-width="2"/>
+<path id="Vector_2" d="M13.502 9.7223V9.72229C13.502 8.63302 12.6189 7.75 11.5297 7.75H6.46956C5.38029 7.75 4.49726 8.63302 4.49726 9.72229V9.7223C4.49726 10.8116 5.38029 11.6946 6.46956 11.6946H11.5297C12.6189 11.6946 13.502 10.8116 13.502 9.7223Z" stroke="#91FDB9" stroke-width="2"/>
+<path id="Vector_3" d="M9.75195 9.81982C9.75195 10.8554 10.5914 11.6948 11.627 11.6948C12.6625 11.6948 13.502 10.8554 13.502 9.81982C13.502 8.78429 12.6625 7.94482 11.627 7.94482C10.5914 7.94482 9.75195 8.78429 9.75195 9.81982Z" stroke="#91FDB9" stroke-width="2"/>
+</g>
+<defs>
+<clipPath id="clip0_11_78915">
+<rect width="18" height="18" fill="white" transform="translate(0 0.5)"/>
+</clipPath>
+</defs>
+</svg>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.