Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add a highly experimental decoder for VP8/WebM, under the name of Rou…

…te9.js.

This decoder is a straightforward translation of libvpx and libnestegg via
emscripten, making extensive use of the scaffolding built for Broadway.
Some serious correctness bugs remain, and speed leaves something to be
desired.  So far, the code has only been tested on Firefox 7.
  • Loading branch information...
commit 33409ef7bb85ae5113b69325f9bc16fc56ae5199 1 parent a4f39aa
@bemasc authored
View
6 .gitmodules
@@ -0,0 +1,6 @@
+[submodule "vp8/libvpx"]
+ path = vp8/libvpx
+ url = https://Benjamin.M.Schwartz@code.google.com/r/benjaminmschwartz-libvpx-emscripten/
+[submodule "vp8/nestegg"]
+ path = vp8/nestegg
+ url = git@github.com:bemasc/nestegg.git
View
1  AUTHORS
@@ -5,3 +5,4 @@ Michael Bebenita <mbebenita@gmail.com>
Alon Zakai <alonzakai@gmail.com>
Andreas Gal <gal@mozilla.com>
Mathieu 'p01' Henri <mathieu@p01.org>
+Benjamin M. Schwartz <bens@alum.mit.edu>
View
BIN  Demo/big-buck-bunny_trailer.webm
Binary file not shown
View
298 Demo/route9.html
@@ -0,0 +1,298 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<style>
+/* MetaWebPro font family licensed from fontshop.com. WOFF-FTW! */
+@font-face {
+ font-family: 'MetaBlack';
+ src: url('http://mozcom-cdn.mozilla.net/img/fonts/MetaWebPro-Black.eot');
+ src: local(''),
+ url('http://mozcom-cdn.mozilla.net/img/fonts/MetaWebPro-Black.woff')
+ format('woff');
+ font-weight: bold;
+}
+</style>
+</head>
+<body>
+ <meta charset="utf-8">
+
+ <style>
+
+ #site-title {
+ display: block;
+ font: normal bold 60px/.738 MetaBlack, "Arial Black", sans-serif;
+ letter-spacing: -0.02em;
+ }
+
+ #doc {
+ margin: 0 auto;
+ width: 1200px;
+ padding: 10px;
+ }
+
+ #setting {
+ margin-top: 10px;
+ }
+
+ #tagline {
+ clear: both;
+ font: 1.538em/1.4 Georgia, sans-serif;
+ color: #484848;
+ padding: 0 430px 0 0px;
+ }
+
+ body {
+ font: normal 16px/.738 MetaBlack, "Arial Black", sans-serif;
+ color: #6D7581;
+ }
+
+ select {
+ font: normal 16px "Arial", sans-serif;
+ padding: 1px;
+ color: #6D7581;
+ }
+
+ th {
+ text-align: left;
+ padding: 4px 4px 4px 12px;
+ background: no-repeat;
+ }
+
+ td {
+ padding-left: 8px;
+ text-align: right;
+ }
+
+ #controls {
+ background-color: black;
+ padding: 10px;
+ height: 40px;
+ }
+
+ #play {
+ width: 100px;
+ float: left;
+ }
+
+ #scrubber {
+ margin-left: 120px;
+ margin-top: 12px;
+ margin-right: 10px;
+ }
+
+ </style>
+
+ <link type="text/css" href="jquery/theme/jquery-ui.css" rel="stylesheet" />
+ <script type="text/javascript" src="jquery/jquery.min.js"></script>
+ <script type="text/javascript" src="jquery/jquery-ui.min.js"></script>
+
+ <script type='text/javascript'>
+ var Module = {
+ noInitialRun : true
+ };
+ </script>
+
+ <script src='route9.js' type='text/javascript'></script>
+ <script src="sylvester.js" type="text/javascript"></script>
+ <script src="glUtils.js" type="text/javascript"></script>
+
+ <script src='util.js' type='text/javascript'></script>
+ <script src='canvas.js' type='text/javascript'></script>
+
+ <script type="text/javascript">
+ $(function() {
+ $("#tabs").tabs();
+ $("#play").button();
+ $("#play").click(function() {
+ play();
+ });
+
+ $( "#scrubber" ).slider({
+ range: false,
+ min: 0,
+ max: 500,
+ value: 0,
+ slide: function( event, ui ) {
+ var pos = ui.value / 500;
+ _setPosition(pos);
+ }
+ });
+
+ });
+
+ var scoreCalculated = false;
+ var steadySampleCounter = 0;
+ var steadyFpsCounter = 0;
+
+ function updateStats(fps, fpsSinceStart, elapsed) {
+ var steadyElapsedThreshold = 5;
+ if (elapsed > steadyElapsedThreshold) {
+ steadySampleCounter++;
+ steadyFpsCounter += fps;
+ }
+
+ document.getElementById('fps').innerHTML = fps.toFixed(2);
+ document.getElementById('fpsSinceStart').innerHTML = fpsSinceStart.toFixed(2);
+ document.getElementById('fpsSinceSteady').innerHTML = (steadyFpsCounter / steadySampleCounter).toFixed(2);
+ document.getElementById('elapsed').innerHTML = elapsed.toFixed(2);
+
+ var scoreTimeout = 60;
+ if (elapsed > scoreTimeout && scoreCalculated == false) {
+ scoreCalculated = true;
+ document.getElementById('score').innerHTML = fpsSinceStart.toFixed(2);
+ }
+ if (scoreCalculated == false) {
+ document.getElementById('score').innerHTML = "Calculating: " + (scoreTimeout - elapsed).toFixed(0);
+ }
+ }
+
+ function load() {
+ document.getElementById('downloadProgress').innerHTML = "Downloading, Please Wait ...";
+ var clip = $("#clip").val();
+ var mode = $("#mode").val();
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", clip, false);
+ xhr.responseType = "arraybuffer";
+ xhr.send(null);
+ var arrayBuffer = xhr.response;
+ if (arrayBuffer) {
+ var byteArray = new Uint8Array(arrayBuffer);
+ var array = Array.prototype.slice.apply(byteArray);
+ Module.FS.createDataFile('/', 'video.webm', array, true, false);
+ } else {
+ alert('load fail :(');
+ }
+ document.getElementById('downloadProgress').innerHTML = "Download Complete, Playing ...";
+
+ // Pass canvas and context to the generated code, and do the actual run() here
+ Module.canvas = document.getElementById('canvas');
+
+ if (mode == "none") {
+ _paint = function() {
+ };
+ _SDL_LockSurface = function() {
+ }
+ } else if (mode == "webgl") {
+ _paint = paint;
+ _SDL_LockSurface = function() {};
+ _SDL_Quit = function() {};
+ } else {
+ Module.ctx2D = Module.canvas.getContext('2d');
+ if (!Module.ctx2D) {
+ alert('Canvas not available :(');
+ return;
+ }
+ }
+
+ console.info("Running: " + clip);
+ Module.run(['video.webm']);
+ }
+
+ var loaded = false;
+ var playing = false;
+
+
+ function play() {
+ if (!loaded) {
+ load();
+ loaded = true;
+ }
+
+ if (playing == false) {
+ $('#play').children().first().html("Pause");
+ playing = true;
+ Module.play();
+ } else {
+ $('#play').children().first().html("Play");
+ playing = false;
+ Module.stop();
+ return;
+ }
+ }
+
+ var webGLCanvas = null;
+
+ function updateScrubber() {
+ $("#scrubber").slider('value', _getPosition() * 500);
+ }
+
+ function paint($luma, $cb, $cr, height, width) {
+ if (!webGLCanvas) {
+ webGLCanvas = new YUVWebGLCanvas(Module.canvas, new Size(width, height));
+ }
+ var luma = HEAPU8.subarray($luma);
+ var cb = HEAPU8.subarray($cb);
+ var cr = HEAPU8.subarray($cr);
+
+ webGLCanvas.YTexture.fill(luma);
+ webGLCanvas.UTexture.fill(cb);
+ webGLCanvas.VTexture.fill(cr);
+ webGLCanvas.drawScene();
+ }
+ </script>
+
+
+
+ <div id="doc">
+ <header>
+ <h1 id="site-title"><b>Route9.js</b></h1>
+ <p id="tagline">A VP8/WebM decoder in JavaScript</p>
+ </header>
+ <div>
+ <div id="setting">
+ <span>Clip:
+ <select id="clip" style="margin-left: 10px;">
+ <option value="big-buck-bunny_trailer.webm">Big Buck Bunny Trailer</option>
+ <option value="sign_irene_cif-V100.webm">"Sign Irene" test clip</option>
+ </select>
+ </span>
+ </div>
+ <div id="setting">
+ <span>Render Mode:
+ <select id="mode" style="margin-left: 10px;">
+ <option value="canvas">Canvas</option>
+ <option value="webgl">Canvas w/ WebGL</option>
+ <option value="none">None</option>
+ </select>
+ </span>
+ <span style="margin-left: 10px; border: 0px" id="downloadProgress"></span>
+ </div>
+ <div id="setting">
+ <div style="margin-left: 10px; border: 0px; clear: both;" id="downloadProgress"></div>
+ </div>
+ <div>
+ <div id="player" style="background-color: black; float: left;">
+ <canvas id='canvas' width="640" height="100" style="background-color: #333333;"></canvas>
+ <div id="controls">
+ <button id="play">Play</button>
+ <div id="scrubber"></div>
+ </div>
+ </div>
+ </div>
+ <!-- Stats -->
+ <div
+ style="height: 480px; margin-top: 30px; margin-bottom: 10px; clear: both;">
+ <table style="margin-top: 10px; width: 300;">
+ <tr>
+ <th>FPS</th>
+ <td><span id='fps' style="color: red;"></span></td>
+ </tr>
+ <tr>
+ <th>Average FPS (All / Steady)</th>
+ <td><span id='fpsSinceStart' style="color: green;"></span> /
+ <span id='fpsSinceSteady' style="color: green;"></span></td>
+ </tr>
+ <tr>
+ <th>Elapsed</th>
+ <td><span id='elapsed'></span></td>
+ </tr>
+ <tr>
+ <th>Score</th>
+ <td><span id='score' style="color: brown;"></span></td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </body>
+</html>
View
1,207 Demo/route9.js
1,207 additions, 0 deletions not shown
View
BIN  Demo/sign_irene_cif-V100.webm
Binary file not shown
View
6 README.markdown
@@ -1,3 +1,9 @@
+Route9.js
+=========
+A JavaScript VP8/Webm decoder, currently pre-alpha. Produced by
+compiling libvpx and libnestegg into javascript using emscripten.
+Based on the build system and infrastructure of Broadway.js.
+
Broadway.js
===========
A JavaScript H.264 decoder.
View
56 vp8/Makefile
@@ -0,0 +1,56 @@
+# Compiler
+# -DMB_BASED_DEBLOCK -DUSE_PRED_BLOCK
+OPTS = -m32 -g -O3 -Wall -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF=
+ifeq ($(LINUX),1)
+OPTS += -DLINUX
+endif
+
+LDFLAGS = -m32
+# Project name
+PROJECT = vp8
+
+# Directories
+OBJDIR = obj
+SRCDIR = .
+
+# Libraries
+ifeq ($(LINUX),1)
+LIBS = -lSDL
+else
+LIBS = -framework Cocoa -framework SDL
+endif
+
+# Files and folders
+#SRCS = $(shell find $(SRCDIR) -name '*.c')
+SRCS = $(SRCDIR)/main.c
+OBJS = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS))
+
+# Targets
+ifeq ($(LINUX),1)
+$(PROJECT): build-dirs $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
+
+$(OBJDIR)/SDLMain.o: SDLMain.m
+ $(CC) $(OPTS) -c $< -o $@
+else
+$(PROJECT): build-dirs $(OBJS) $(OBJDIR)/SDLMain.o
+ $(CC) $(LDFLAGS) $(OBJS) $(OBJDIR)/SDLMain.o $(LIBS) -o $@
+
+$(OBJDIR)/SDLMain.o: SDLMain.m
+ $(CC) $(OPTS) -c $< -o $@
+endif
+
+$(OBJDIR)/%.o: $(SRCDIR)/%.c
+ $(CC) $(OPTS) -c $< -o $@
+
+clean:
+ rm -Rf $(OBJDIR)
+ rm *.ll
+ rm *.bc
+
+build-dirs:
+ @$(call make-dirs)
+
+define make-dirs
+ mkdir -p $(OBJDIR)
+endef
View
27 vp8/README
@@ -0,0 +1,27 @@
+To fetch the dependencies:
+git submodule init
+git submodule update
+
+To compile libnestegg:
+cd nestegg/
+autoreconf --install
+(EMSCRIPTEN_ROOT=...) (PATH/TO/)emconfiguren.py ./configure
+make
+
+To compile libvpx:
+cd libvpx
+(PATH=$PATH:~/local_clang/bin/) ./configure --disable-vp8-encoder --disable-examples --disable-multithread --target=js1-none-clang_emscripten
+(PATH=$PATH:~/local_clang/bin/) make
+
+To build the javascript decoder:
+Install the closure compiler (http://code.google.com/closure/compiler/)
+and edit ~/.emscripten to point to the correct path.
+(EMSCRIPTEN_ROOT=...) python make.py
+(If you have RELOOP=1 in make.py, you may now watch the director's cut of
+a James Cameron movie while you wait. I recommend The Abyss, for its
+excellent depiction of the Mammalian Diving Reflex and hypothermic
+neuroprotection.)
+
+To test the decoder:
+cp js/vp8.cc.js ../Demo/route9.js
+Open route9.html in your favorite modern browser.
View
54 vp8/hooks.js
@@ -0,0 +1,54 @@
+Module['FS'] = FS;
+FS['createDataFile'] = FS.createDataFile;
+
+// Replace main loop handler
+
+var breakLoop = false;
+_runMainLoop = function() {
+ window.addEventListener("message", function() {
+ _mainLoopIteration();
+ if (!breakLoop) {
+ window.postMessage(0, "*")
+ }
+ }, false);
+}
+
+Module['play'] = function() {
+ breakLoop = false;
+ window.postMessage(0, "*")
+};
+
+Module['stop'] = function() {
+ breakLoop = true;
+};
+
+// SDL hook
+
+var frameCounter = 0, totalFrameCounter = 0;
+var frameTime = 0, totalFrameTime = 0;
+
+_SDL_Flip = function(surf) {
+ frameCounter++;
+ totalFrameCounter++;
+
+ if (totalFrameCounter % 20 == 0) {
+ updateScrubber();
+ }
+
+ if (frameTime == 0) {
+ totalFrameTime = frameTime = Date.now();
+ return;
+ }
+ var now = Date.now();
+ var diff = now - frameTime;
+ if (diff > 200) {
+ var fps = frameCounter * 1000 / diff;
+ var fpsSinceStart = totalFrameCounter * 1000 / (now - totalFrameTime);
+ var elapsed = (now - totalFrameTime) / 1000;
+ frameTime = now;
+ frameCounter = 0;
+ updateStats(fps, fpsSinceStart, elapsed);
+ }
+}
+
+_SDL_UnlockSurface = function() {}
1  vp8/libvpx
@@ -0,0 +1 @@
+Subproject commit 939cbcc1cea5da4c77af82dc8f9e68cbee04068e
View
247 vp8/main.c
@@ -0,0 +1,247 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <SDL/SDL.h>
+
+#include "yuv2rgb.h"
+
+#include "SDL/SDL.h"
+
+#include "vpx/vpx_decoder.h"
+#include "vpx/vp8dx.h"
+#include "nestegg/nestegg.h"
+
+#define DEBUG_LOGGING 1
+#if DEBUG_LOGGING
+ #define DLOG printf
+#else
+ #define DLOG(...)
+#endif
+
+#define RENDER 1
+
+#define FASTYUV2RGB 0
+
+// Globals
+
+SDL_Surface* screen = NULL;
+uint8_t* stream = NULL;
+uint8_t* buffer = NULL;
+vpx_codec_ctx_t decoder;
+vpx_codec_dec_cfg_t cfg = {0};
+
+#define VP8_FOURCC (0x00385056)
+static const struct
+{
+ char const *name;
+ const vpx_codec_iface_t *iface;
+ unsigned int fourcc;
+ unsigned int fourcc_mask;
+} ifaces[] =
+{
+ {"vp8", &vpx_codec_vp8_dx_algo, VP8_FOURCC, 0x00FFFFFF},
+};
+#include "vpxdec_helpers.c"
+struct input_ctx input = {.kind=RAW_FILE,
+ .infile=NULL,
+ .nestegg_ctx=NULL,
+ .pkt=NULL,
+ .chunk=0,
+ .chunks=0,
+ .video_track=0};
+int size=0;
+
+
+// Main loop handling
+
+enum mainLoopStatus {
+ MLS_STOP = 0,
+ MLS_CONTINUE = 1,
+ MLS_FRAMERENDERED = 2
+};
+
+// Runs the main loop. This is replaced in JavaScript with an asynchronous loop
+// that calls mainLoopIteration
+void runMainLoop();
+enum mainLoopStatus mainLoopIteration();
+
+#ifdef LINUX
+int main(int argc, char **argv) {
+#else
+int SDL_main(int argc, char **argv) {
+#endif
+
+ FILE *infile;
+ const char * fn = argc == 2 ? argv[1] : "../Media/big-buck-bunny_trailer.webm";
+ unsigned int fourcc;
+ unsigned int width;
+ unsigned int height;
+ unsigned int fps_den;
+ unsigned int fps_num;
+
+ infile = fopen(fn, "rb");
+ if (!infile)
+ {
+ fprintf(stderr, "Failed to open file '%s'\n", fn);
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ fprintf(stderr, "Successfully opened file '%s'\n", fn);
+ }
+
+ input.infile = infile;
+ if(file_is_ivf(infile, &fourcc, &width, &height, &fps_den,
+ &fps_num))
+ input.kind = IVF_FILE;
+ else if(file_is_webm(&input, &fourcc, &width, &height, &fps_den, &fps_num))
+ input.kind = WEBM_FILE;
+ else if(file_is_raw(infile, &fourcc, &width, &height, &fps_den, &fps_num))
+ input.kind = RAW_FILE;
+ else
+ {
+ fprintf(stderr, "Unrecognized input file type.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (vpx_codec_dec_init(&decoder, ifaces[0].iface, &cfg, 0))
+ {
+ fprintf(stderr, "Failed to initialize decoder: %s\n", vpx_codec_error(&decoder));
+ return EXIT_FAILURE;
+ }
+
+#if RENDER
+ SDL_Init(SDL_INIT_VIDEO);
+#endif
+
+ runMainLoop();
+
+ return 0;
+}
+
+void runMainLoop() {
+ enum mainLoopStatus status;
+ while ((status = mainLoopIteration()) != MLS_STOP);
+}
+
+extern float getPosition() {
+ int offset = stream - buffer;
+ return (float)offset / (float)size;
+}
+
+extern void setPosition(float value) {
+ if (value < 0 || value > 1) {
+ return;
+ }
+
+ int offset = (int)((float)size * value);
+ stream = buffer + offset;
+}
+
+extern void paint(uint8_t *luma, uint8_t *cb, uint8_t *cr, int height, int stride) {
+ int chromaStride = stride >> 1;
+#if FASTYUV2RGB
+ uint8_t *dst = (uint8_t *)screen->pixels;
+ yuv420_2_rgb8888( (uint8_t*) dst, luma, cb, cr, width, height, width, chromaWidth, width << 2, yuv2rgb565_table, 0);
+#else
+ uint32_t *dst = (uint32_t *)screen->pixels;
+ for (int y = 0; y < height; y++) {
+ int lineOffLuma = y * stride;
+ int lineOffDst = y * screen->w;
+ int lineOffChroma = (y >> 1) * chromaStride;
+ for (int x = 0; x < screen->w; x++) {
+ int c = luma[lineOffLuma + x] - 16;
+ int d = cb[lineOffChroma + (x >> 1)] - 128;
+ int e = cr[lineOffChroma + (x >> 1)] - 128;
+
+ int red = (298 * c + 409 * e + 128) >> 8;
+ red = red < 0 ? 0 : (red > 255 ? 255 : red);
+ int green = (298 * c - 100 * d - 208 * e + 128) >> 8;
+ green = green < 0 ? 0 : (green > 255 ? 255 : green);
+ int blue = (298 * c + 516 * d + 128) >> 8;
+ blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue);
+ dst[lineOffDst + x] = SDL_MapRGB(screen->format, red & 0xff, green & 0xff, blue & 0xff);
+ }
+ }
+#endif
+}
+
+enum mainLoopStatus mainLoopIteration() {
+
+ enum mainLoopStatus status = MLS_CONTINUE;
+
+
+ uint8_t *buf = NULL;
+ size_t buf_sz = 0, buf_alloc_sz = 0;
+ if (!read_frame(&input, &buf, &buf_sz, &buf_alloc_sz))
+ {
+ vpx_codec_iter_t iter = NULL;
+ vpx_image_t *img;
+ if (vpx_codec_decode(&decoder, buf, buf_sz, NULL, 0))
+ {
+ const char *detail = vpx_codec_error_detail(&decoder);
+ fprintf(stderr, "Failed to decode frame: %s\n", vpx_codec_error(&decoder));
+
+ if (detail)
+ fprintf(stderr, " Additional information: %s\n", detail);
+ }
+
+ if ((img = vpx_codec_get_frame(&decoder, &iter)))
+ {
+#if RENDER
+ if (!screen) {
+ screen = SDL_SetVideoMode(img->d_w, img->d_h, 32, SDL_HWSURFACE | SDL_RESIZABLE);
+ }
+ SDL_LockSurface(screen);
+#else
+ if (!screen) {
+ screen = (SDL_Surface*)malloc(sizeof(SDL_Surface));
+ screen->pixels = malloc(img->stride[VPX_PLANE_Y] * img->d_h * 32);
+ }
+#endif
+ paint(img->planes[VPX_PLANE_Y],
+ img->planes[VPX_PLANE_U],
+ img->planes[VPX_PLANE_V],
+ img->d_h, img->stride[VPX_PLANE_Y]);
+#if RENDER
+ SDL_UnlockSurface(screen);
+ SDL_Flip(screen);
+#else
+ printf("painted a frame\n");
+#endif
+ }
+
+ status = MLS_FRAMERENDERED;
+ } else
+ {
+#if RENDER
+ SDL_Quit();
+#endif
+ return MLS_STOP;
+ }
+#if RENDER
+#if !JS
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_QUIT:
+ exit(0);
+ break;
+#if !LINUX
+ case SDL_KEYDOWN:
+ // printf("Key: %s\n", SDL_GetKeyName( event.key.keysym.sym ));
+ // printf("Key: %d\n", event.key.keysym.scancode);
+ if (event.key.keysym.scancode == 1) {
+ setPosition(0.5f);
+ return status;
+ } else {
+ exit(0);
+ }
+ break;
+#endif
+ }
+ }
+#endif
+#endif
+
+ return status;
+}
View
149 vp8/make.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+
+import os, sys, re, json, shutil
+from subprocess import Popen, PIPE, STDOUT
+
+exec(open(os.path.expanduser('~/.emscripten'), 'r').read())
+
+sys.path.append(EMSCRIPTEN_ROOT)
+import tools.shared as emscripten
+
+EMSCRIPTEN_SETTINGS = {
+ 'USE_TYPED_ARRAYS': 2,
+ 'I64_MODE': 1,
+ 'CORRECT_ROUNDINGS': 0,
+# Emscripten cannot currently trace correct-sign requirements across
+# multiple libraries linked together, and the decoder produces garbage
+# with CORRECT_SIGNS=0, so we have no choice but to enable
+# correct signs across the board.
+ 'CORRECT_SIGNS': 1,
+# 'CORRECT_SIGNS_LINES': emscripten.read_pgo_data('vp8.pgo')['signs_lines'],
+ 'CORRECT_OVERFLOWS': 0, # Setting this to 1 seems to improve correctness only slightly
+ 'CORRECT_SIGNED_OVERFLOWS': 0,
+ 'CHECK_SIGNS': 0,
+ 'CHECK_OVERFLOWS': 0,
+ 'CHECK_SIGNED_OVERFLOWS': 0,
+ 'SAFE_HEAP': 0,
+ 'SAFE_HEAP_LOG': 0,
+ 'INVOKE_RUN': 0, # we do it ourselves
+ 'EXPORTED_FUNCTIONS': ['_main'],
+ 'IGNORED_FUNCTIONS': ['_paint'],
+ 'RELOOP': 1, # Unbelievably slow. Set this to 0 during development.
+ 'MICRO_OPTS': 1,
+ 'PGO': 0
+}
+DEPENDENCIES = ['libvpx/libvpx_g.a','nestegg/src/.libs/libnestegg.a.bc']
+EMSCRIPTEN_ARGS = [] #Emscripten's optimize (-O) appears to be broken
+
+JS_DIR = "js"
+
+if not os.path.exists(JS_DIR):
+ os.makedirs(JS_DIR)
+
+print 'Build'
+
+env = os.environ.copy()
+env['CC'] = env['CXX'] = env['RANLIB'] = env['AR'] = emscripten.EMMAKEN
+env['LINUX'] = '1'
+env['EMMAKEN_CFLAGS'] = '-U__APPLE__ -DJS -Inestegg/include -Ilibvpx'
+
+Popen(['make', '-j', '4'], env=env).communicate()
+
+if 0:
+ print 'LLVM optimizations'
+
+ shutil.move('vp8.bc', 'vp8.orig.bc')
+ output = Popen([emscripten.LLVM_OPT, 'vp8.orig.bc'] +
+ emscripten.Building.pick_llvm_opts(1, safe=True) +
+ ['-o=vp8.bc']).communicate()[0]
+ assert os.path.exists('vp8.bc'), 'Failed to run llvm optimizations: ' + output
+
+print 'Linking LLVM library with its dependencies'
+shutil.move('vp8.bc', 'vp8.orig.bc')
+
+cmd = ['/home/bens/vp8/local/bin/llvm-ld', '-link-as-library', '-disable-opt', 'vp8.orig.bc'] + DEPENDENCIES + ['-o=vp8.bc']
+print ' '.join(cmd)
+print Popen(cmd).communicate()
+#print Popen([emscripten.LLVM_LINK] + ['vp8.orig.bc', '-o=vp8.bc'] + DEPENDENCIES).communicate()
+
+print 'LLVM binary => LL assembly'
+
+print ' '.join([emscripten.LLVM_DIS] + emscripten.LLVM_DIS_OPTS + ['vp8.bc', '-o=vp8.ll'])
+print Popen([emscripten.LLVM_DIS] + emscripten.LLVM_DIS_OPTS + ['vp8.bc', '-o=vp8.ll']).communicate()
+
+if 0:
+ print '[Autodebugger]'
+
+ shutil.move('vp8.ll', 'vp8.orig.ll')
+ output = Popen(['python', emscripten.AUTODEBUGGER, 'vp8.orig.ll', 'vp8.ll'], stdout=PIPE, stderr=STDOUT).communicate()[0]
+ assert 'Success.' in output, output
+
+ shutil.move('vp8.bc', 'vp8.orig.bc')
+ print Popen([emscripten.LLVM_AS, 'vp8.ll', '-o=vp8.bc']).communicate()
+
+print 'Emscripten: LL assembly => JavaScript'
+
+settings = ['-s %s=%s' % (k, json.dumps(v)) for k, v in EMSCRIPTEN_SETTINGS.items()]
+
+filename = JS_DIR + '/vp8.js'
+
+print Popen(['python', os.path.join(EMSCRIPTEN_ROOT, 'emscripten.py')] + EMSCRIPTEN_ARGS + ['vp8.ll'] + settings,# ).communicate()
+ stdout=open(filename, 'w'), stderr=STDOUT).communicate()
+
+print 'Appending stuff'
+
+src = open(filename, 'a')
+
+if 0: #EMSCRIPTEN_SETTINGS['QUANTUM_SIZE'] == 1:
+ src.write(
+ '''
+ _malloc = function(size) {
+ while (STATICTOP % 4 != 0) STATICTOP++;
+ var ret = STATICTOP;
+ STATICTOP += size;
+ return ret;
+ }
+ '''
+ )
+
+if 0: # Console debugging
+ src.write(
+ '''
+ _paint = _SDL_Init = _SDL_LockSurface = _SDL_UnlockSurface = function() {
+ };
+
+ _SDL_SetVideoMode = function() {
+ return _malloc(1024);
+ };
+
+ FS.createDataFile('/', 'admiral.264', %s, true, false);
+ FS.root.write = true;
+ print('zz go!');
+ run(['admiral.264']);
+ print('zz gone');
+
+ ''' % str(map(ord, open('../Media/admiral.264').read()[0:1024*100]))
+ )
+ # ~/Dev/mozilla-central/js/src/fast/js -m avc.js
+else:
+ src.write(open('hooks.js').read())
+ src.write(open('paint_%s.js' % EMSCRIPTEN_SETTINGS['USE_TYPED_ARRAYS'], 'r').read())
+src.close()
+
+if 0:
+ print 'Eliminating unneeded variables'
+
+ eliminated = Popen([emscripten.COFFEESCRIPT, emscripten.VARIABLE_ELIMINATOR], stdin=PIPE, stdout=PIPE).communicate(open(filename, 'r').read())[0]
+ filename = JS_DIR + '/vp8.elim.js'
+ f = open(filename, 'w')
+ f.write(eliminated)
+ f.close()
+
+if 1:
+ print 'Closure compiler'
+
+ cmd = ['java', '-jar', emscripten.CLOSURE_COMPILER,
+ '--compilation_level', 'SIMPLE_OPTIMIZATIONS', # XXX TODO: use advanced opts for code size (they cause slow startup though)
+ '--js', filename, '--js_output_file', JS_DIR + '/vp8.cc.js']
+ print(' '.join(cmd))
+ Popen(cmd).communicate()
1  vp8/nestegg
@@ -0,0 +1 @@
+Subproject commit 421df36ee50200a2f356add53765f064792f0f64
View
37 vp8/paint_2.js
@@ -0,0 +1,37 @@
+function _paint($luma, $cb, $cr, h, stride) {
+ for (var y1,y2,u,v,ruv,guv,buv,j,surface=SDL.surfaces[SDL.screen],w=surface.width,w_2=w>>1,W=w*4, d=surface.image.data, r=0; h-=2;) {
+ for (j=w_2; j--;) {
+ u = HEAPU8[$cr++];
+ v = HEAPU8[$cb++];
+ ruv = 409*u-56992;
+ guv = 34784-208*u-100*v;
+ buv = 516*v-70688;
+
+ y2 = HEAPU8[$luma+stride]*298;
+ y1 = HEAPU8[$luma++]*298;
+ d[r+W] = y2+ruv>>8;
+ d[r++] = y1+ruv>>8;
+ d[r+W] = y2+guv>>8;
+ d[r++] = y1+guv>>8;
+ d[r+W] = y2+buv>>8;
+ d[r++] = y1+buv>>8;
+ r++;
+
+ y2 = HEAPU8[$luma+stride]*298;
+ y1 = HEAPU8[$luma++]*298;
+ d[r+W] = y2+ruv>>8;
+ d[r++] = y1+ruv>>8;
+ d[r+W] = y2+guv>>8;
+ d[r++] = y1+guv>>8;
+ d[r+W] = y2+buv>>8;
+ d[r++] = y1+buv>>8;
+ r++;
+ }
+ r+=W;
+ $luma+=2*stride-w;
+ $cr+=stride-w>>1;
+ $cb+=stride-w>>1;
+ }
+ surface.ctx.putImageData(surface.image, 0, 0 );
+}
+
View
410 vp8/vpxdec_helpers.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+
+static unsigned int mem_get_le16(const void *vmem)
+{
+ unsigned int val;
+ const unsigned char *mem = (const unsigned char *)vmem;
+
+ val = mem[1] << 8;
+ val |= mem[0];
+ return val;
+}
+
+static unsigned int mem_get_le32(const void *vmem)
+{
+ unsigned int val;
+ const unsigned char *mem = (const unsigned char *)vmem;
+
+ val = mem[3] << 24;
+ val |= mem[2] << 16;
+ val |= mem[1] << 8;
+ val |= mem[0];
+ return val;
+}
+
+enum file_kind
+{
+ RAW_FILE,
+ IVF_FILE,
+ WEBM_FILE
+};
+
+struct input_ctx
+{
+ enum file_kind kind;
+ FILE *infile;
+ nestegg *nestegg_ctx;
+ nestegg_packet *pkt;
+ unsigned int chunk;
+ unsigned int chunks;
+ unsigned int video_track;
+};
+
+#define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t))
+#define RAW_FRAME_HDR_SZ (sizeof(uint32_t))
+static int read_frame(struct input_ctx *input,
+ uint8_t **buf,
+ size_t *buf_sz,
+ size_t *buf_alloc_sz)
+{
+ char raw_hdr[IVF_FRAME_HDR_SZ];
+ size_t new_buf_sz;
+ FILE *infile = input->infile;
+ enum file_kind kind = input->kind;
+ if(kind == WEBM_FILE)
+ {
+ if(input->chunk >= input->chunks)
+ {
+ unsigned int track;
+
+ do
+ {
+ /* End of this packet, get another. */
+ if(input->pkt)
+ nestegg_free_packet(input->pkt);
+
+ if(nestegg_read_packet(input->nestegg_ctx, &input->pkt) <= 0
+ || nestegg_packet_track(input->pkt, &track))
+ return 1;
+
+ } while(track != input->video_track);
+
+ if(nestegg_packet_count(input->pkt, &input->chunks))
+ return 1;
+ input->chunk = 0;
+ }
+
+ if(nestegg_packet_data(input->pkt, input->chunk, buf, buf_sz))
+ return 1;
+ input->chunk++;
+
+ return 0;
+ }
+ /* For both the raw and ivf formats, the frame size is the first 4 bytes
+ * of the frame header. We just need to special case on the header
+ * size.
+ */
+ else if (fread(raw_hdr, kind==IVF_FILE
+ ? IVF_FRAME_HDR_SZ : RAW_FRAME_HDR_SZ, 1, infile) != 1)
+ {
+ if (!feof(infile))
+ fprintf(stderr, "Failed to read frame size\n");
+
+ new_buf_sz = 0;
+ }
+ else
+ {
+ new_buf_sz = mem_get_le32(raw_hdr);
+
+ if (new_buf_sz > 256 * 1024 * 1024)
+ {
+ fprintf(stderr, "Error: Read invalid frame size (%u)\n",
+ (unsigned int)new_buf_sz);
+ new_buf_sz = 0;
+ }
+
+ if (kind == RAW_FILE && new_buf_sz > 256 * 1024)
+ fprintf(stderr, "Warning: Read invalid frame size (%u)"
+ " - not a raw file?\n", (unsigned int)new_buf_sz);
+
+ if (new_buf_sz > *buf_alloc_sz)
+ {
+ uint8_t *new_buf = (uint8_t *)realloc(*buf, 2 * new_buf_sz);
+
+ if (new_buf)
+ {
+ *buf = new_buf;
+ *buf_alloc_sz = 2 * new_buf_sz;
+ }
+ else
+ {
+ fprintf(stderr, "Failed to allocate compressed data buffer\n");
+ new_buf_sz = 0;
+ }
+ }
+ }
+
+ *buf_sz = new_buf_sz;
+
+ if (!feof(infile))
+ {
+ if (fread(*buf, 1, *buf_sz, infile) != *buf_sz)
+ {
+ fprintf(stderr, "Failed to read full frame\n");
+ return 1;
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+#define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t))
+#define RAW_FRAME_HDR_SZ (sizeof(uint32_t))
+unsigned int file_is_ivf(FILE *infile,
+ unsigned int *fourcc,
+ unsigned int *width,
+ unsigned int *height,
+ unsigned int *fps_den,
+ unsigned int *fps_num)
+{
+ char raw_hdr[32];
+ int is_ivf = 0;
+
+ if (fread(raw_hdr, 1, 32, infile) == 32)
+ {
+ if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K'
+ && raw_hdr[2] == 'I' && raw_hdr[3] == 'F')
+ {
+ is_ivf = 1;
+
+ if (mem_get_le16(raw_hdr + 4) != 0)
+ fprintf(stderr, "Error: Unrecognized IVF version! This file may not"
+ " decode properly.");
+
+ *fourcc = mem_get_le32(raw_hdr + 8);
+ *width = mem_get_le16(raw_hdr + 12);
+ *height = mem_get_le16(raw_hdr + 14);
+ *fps_num = mem_get_le32(raw_hdr + 16);
+ *fps_den = mem_get_le32(raw_hdr + 20);
+
+ /* Some versions of vpxenc used 1/(2*fps) for the timebase, so
+ * we can guess the framerate using only the timebase in this
+ * case. Other files would require reading ahead to guess the
+ * timebase, like we do for webm.
+ */
+ if(*fps_num < 1000)
+ {
+ /* Correct for the factor of 2 applied to the timebase in the
+ * encoder.
+ */
+ if(*fps_num&1)*fps_den<<=1;
+ else *fps_num>>=1;
+ }
+ else
+ {
+ /* Don't know FPS for sure, and don't have readahead code
+ * (yet?), so just default to 30fps.
+ */
+ *fps_num = 30;
+ *fps_den = 1;
+ }
+ }
+ }
+
+ if (!is_ivf)
+ rewind(infile);
+
+ return is_ivf;
+}
+
+
+unsigned int file_is_raw(FILE *infile,
+ unsigned int *fourcc,
+ unsigned int *width,
+ unsigned int *height,
+ unsigned int *fps_den,
+ unsigned int *fps_num)
+{
+ unsigned char buf[32];
+ int is_raw = 0;
+ vpx_codec_stream_info_t si;
+
+ si.sz = sizeof(si);
+
+ if (fread(buf, 1, 32, infile) == 32)
+ {
+ int i;
+
+ if(mem_get_le32(buf) < 256 * 1024 * 1024)
+ for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++)
+ if(!vpx_codec_peek_stream_info(ifaces[i].iface,
+ buf + 4, 32 - 4, &si))
+ {
+ is_raw = 1;
+ *fourcc = ifaces[i].fourcc;
+ *width = si.w;
+ *height = si.h;
+ *fps_num = 30;
+ *fps_den = 1;
+ break;
+ }
+ }
+
+ rewind(infile);
+ return is_raw;
+}
+
+
+int
+nestegg_read_cb(void *buffer, size_t length, void *userdata)
+{
+ FILE *f = (FILE *)userdata;
+
+ if(fread(buffer, 1, length, f) < length)
+ {
+ if (ferror(f))
+ return -1;
+ if (feof(f))
+ return 0;
+ }
+ /* fprintf(stderr,"Read %d bytes: ",length);
+ while (length > 0) {
+ fprintf(stderr,"%hhx",*(char*)buffer);
+ length--;
+ buffer++;
+ }
+ fprintf(stderr,"\n");*/
+ return 1;
+}
+
+
+int
+nestegg_seek_cb(int64_t offset, int whence, void * userdata)
+{
+ switch(whence) {
+ case NESTEGG_SEEK_SET: whence = SEEK_SET; break;
+ case NESTEGG_SEEK_CUR: whence = SEEK_CUR; break;
+ case NESTEGG_SEEK_END: whence = SEEK_END; break;
+ };
+ return fseek((FILE *)userdata, offset, whence)? -1 : 0;
+}
+
+
+int64_t
+nestegg_tell_cb(void * userdata)
+{
+ return ftell((FILE *)userdata);
+}
+
+
+void
+nestegg_log_cb(nestegg * context, unsigned int severity, char const * format,
+ ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+
+int
+webm_guess_framerate(struct input_ctx *input,
+ unsigned int *fps_den,
+ unsigned int *fps_num)
+{
+ unsigned int i;
+ uint64_t tstamp=0;
+
+ /* Guess the framerate. Read up to 1 second, or 50 video packets,
+ * whichever comes first.
+ */
+ for(i=0; tstamp < 1000000000 && i < 50;)
+ {
+ nestegg_packet * pkt;
+ unsigned int track;
+
+ if(nestegg_read_packet(input->nestegg_ctx, &pkt) <= 0)
+ break;
+
+ nestegg_packet_track(pkt, &track);
+ if(track == input->video_track)
+ {
+ nestegg_packet_tstamp(pkt, &tstamp);
+ i++;
+ }
+
+ nestegg_free_packet(pkt);
+ }
+
+ if(nestegg_track_seek(input->nestegg_ctx, input->video_track, 0))
+ goto fail;
+
+ *fps_num = (i - 1) * 1000000;
+ *fps_den = tstamp / 1000;
+ return 0;
+fail:
+ nestegg_destroy(input->nestegg_ctx);
+ input->nestegg_ctx = NULL;
+ rewind(input->infile);
+ return 1;
+}
+
+int
+file_is_webm(struct input_ctx *input,
+ unsigned int *fourcc,
+ unsigned int *width,
+ unsigned int *height,
+ unsigned int *fps_den,
+ unsigned int *fps_num)
+{
+ unsigned int i, n;
+ int track_type = -1;
+
+ nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb,
+ input->infile};
+ nestegg_video_params params;
+
+ if(nestegg_init(&input->nestegg_ctx, io, NULL))
+ {
+ fprintf(stderr, "Failed to initialize nestegg io.\n");
+ goto fail;
+ }
+
+ if(nestegg_track_count(input->nestegg_ctx, &n))
+ {
+ fprintf(stderr, "Failed to count tracks.\n");
+ goto fail;
+ }
+
+ for(i=0; i<n; i++)
+ {
+ track_type = nestegg_track_type(input->nestegg_ctx, i);
+
+ if(track_type == NESTEGG_TRACK_VIDEO)
+ break;
+ else if(track_type < 0)
+ {
+ fprintf(stderr, "Failed to locate video track.\n");
+ goto fail;
+ }
+ }
+
+ if(nestegg_track_codec_id(input->nestegg_ctx, i) != NESTEGG_CODEC_VP8)
+ {
+ fprintf(stderr, "Not VP8 video, quitting.\n");
+ exit(1);
+ }
+
+ input->video_track = i;
+
+ if(nestegg_track_video_params(input->nestegg_ctx, i, &params))
+ {
+ fprintf(stderr, "Failed to determine video parameters.\n");
+ goto fail;
+ }
+
+ *fps_den = 0;
+ *fps_num = 0;
+ *fourcc = VP8_FOURCC;
+ *width = params.width;
+ *height = params.height;
+ return 1;
+fail:
+ input->nestegg_ctx = NULL;
+ rewind(input->infile);
+ return 0;
+}
View
51 vp8/yuv2rgb.h
@@ -0,0 +1,51 @@
+/* YUV-> RGB conversion code.
+ *
+ * Copyright (C) 2011 Robin Watts (robin@wss.co.uk) for Pinknoise
+ * Productions Ltd.
+ *
+ * Licensed under the BSD license. See 'COPYING' for details of
+ * (non-)warranty.
+ *
+ */
+
+#include <stdint.h>
+
+#ifndef YUV2RGB_H
+
+#define YUV2RGB_H
+
+/* Define these to something appropriate in your build */
+
+//typedef unsigned int uint32_t;
+//typedef signed int int32_t;
+//typedef unsigned short uint16_t;
+//typedef unsigned char uint8_t;
+
+extern const uint32_t yuv2rgb565_table[];
+extern const uint32_t yuv2bgr565_table[];
+
+void yuv420_2_rgb888(uint8_t *dst_ptr,
+ const uint8_t *y_ptr,
+ const uint8_t *u_ptr,
+ const uint8_t *v_ptr,
+ int32_t width,
+ int32_t height,
+ int32_t y_span,
+ int32_t uv_span,
+ int32_t dst_span,
+ const uint32_t *tables,
+ int32_t dither);
+
+void yuv420_2_rgb8888(uint8_t *dst_ptr,
+ const uint8_t *y_ptr,
+ const uint8_t *u_ptr,
+ const uint8_t *v_ptr,
+ int32_t width,
+ int32_t height,
+ int32_t y_span,
+ int32_t uv_span,
+ int32_t dst_span,
+ const uint32_t *tables,
+ int32_t dither);
+
+#endif /* YUV2RGB_H */
Please sign in to comment.
Something went wrong with that request. Please try again.