Commit dbff0ca7 authored by Olivier FAURE's avatar Olivier FAURE
Browse files

Modularize JS scaffholding

parent 8c29aac1
......@@ -30,6 +30,7 @@ PROJECT_DIR=${PROJECT_DIR:=./vlc/extras/package/wasm-emscripten/build}
# for release, remove profiling-funcs and add -Os
emcc --bind -s USE_PTHREADS=1 -s TOTAL_MEMORY=1GB -s PTHREAD_POOL_SIZE=15 \
-s OFFSCREEN_FRAMEBUFFER=1 -s USE_WEBGL2=1 --profiling-funcs \
-s MODULARIZE=1 -s EXPORT_NAME="VlcModule" \
-I $PATH_VLC/include/ -I $PROJECT_DIR/wasm32-unknown-emscripten/include/ main.c \
$PROJECT_DIR/build-emscripten/lib/.libs/libvlc.a \
$PROJECT_DIR/build-emscripten/vlc-modules.bc \
......@@ -37,5 +38,5 @@ emcc --bind -s USE_PTHREADS=1 -s TOTAL_MEMORY=1GB -s PTHREAD_POOL_SIZE=15 \
$PROJECT_DIR/wasm32-unknown-emscripten/lib/*.a \
$PROJECT_DIR/build-emscripten/src/.libs/libvlccore.a \
$PROJECT_DIR/build-emscripten/compat/.libs/libcompat.a \
--js-library library.js \
-o experimental.html --preload-file ${SAMPLE_DIR}
--js-library lib/wasm-imports.js \
-o experimental.js --preload-file ${SAMPLE_DIR}
export class MediaPlayer {
constructor(module, path) {
this.module = module;
//this.media_player_ptr = module._libvlc_media_player_new_from_path(path);
this.media_player_ptr = module._get_media_player_buck_bunny();
}
toggle_play() {
if (!this.is_playing()) {
this.play();
}
else {
this.pause();
}
}
play() {
return this.module._play(this.media_player_ptr);
}
pause() {
return this.module._pause_video(this.media_player_ptr);
}
is_playing() {
return this.module._is_playing(this.media_player_ptr);
}
get_position() {
return this.module._get_position(this.media_player_ptr);
}
set_position(position, fast = 0) {
// TODO - what does "fast" argument do?
return this.module._set_position(this.media_player_ptr, position, fast);
}
get_volume() {
return this.module._get_volume(this.media_player_ptr);
}
set_volume(volume) {
return this.module._set_volume(this.media_player_ptr, volume);
}
toggle_mute() {
return this.module._toggle_mute(this.media_player_ptr);
}
get_mute() {
return this.module._get_mute(this.media_player_ptr);
}
set_mute(mute) {
return this.module._set_mute(this.media_player_ptr, mute);
}
}
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
var spinnerElement = document.getElementById('spinner');
var Module = {
var VlcModuleExt = {
preRun: [],
postRun: [ function() {
// This should run after the wasm module is instantiated
// before, the Pthread object won't be available
PThread.receiveObjectTransfer = function (data) {
VlcModuleExt.PThread.receiveObjectTransfer = function (data) {
let event = new CustomEvent('worker_message', {
detail: data.msg
});
......@@ -59,7 +60,7 @@ var Module = {
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
overlay.addEventListener('click', (event) => {
on_overlay_click(event);
window.on_overlay_click(overlay, event);
});
// Create a global window.display_overlay variable to track whether
......@@ -75,13 +76,13 @@ var Module = {
return canvas;
})(),
setStatus: function(text) {
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
if (text === Module.setStatus.last.text) return;
if (!VlcModuleExt.setStatus.last) VlcModuleExt.setStatus.last = { time: Date.now(), text: '' };
if (text === VlcModuleExt.setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
Module.setStatus.last.time = now;
Module.setStatus.last.text = text;
if (m && now - VlcModuleExt.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
VlcModuleExt.setStatus.last.time = now;
VlcModuleExt.setStatus.last.text = text;
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
......@@ -99,15 +100,6 @@ var Module = {
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
VlcModuleExt.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
}
};
Module.setStatus('Downloading...');
window.onerror = function(event) {
// TODO: do not warn on ok events like simulating an infinite loop or exitStatus
Module.setStatus('Exception thrown, see JavaScript console');
spinnerElement.style.display = 'none';
Module.setStatus = function(text) {
if (text) Module.printErr('[post-exception status] ' + text);
};
};
const PROGRESS_BAR_COLOR = "#2a81d4";
const PROGRESS_BAR_BG_COLOR = "#2d2d2d";
const VOLUME_BAR_COLOR = "#4ad24a";
const VOLUME_BAR_MUTE_COLOR = "#4d704e";
const VOLUME_BAR_BG_COLOR = "#2d2d2d";
const BUTTON_BG_COLOR = "#464646";
const GENERAL_BG_COLOR = "#353535";
const TEXT_COLOR = "#8d8d8c";
const MENU_BAR_HEIGHT = 30;
const BUTTON_SIZE = 20;
const GAP_SIZE = 5;
function get_layout(canvas) {
const VOLUME_BAR_WIDTH = 100;
const VOLUME_BAR_HEIGHT = 20;
const PROGRESS_BAR_WIDTH = canvas.width -
(GAP_SIZE + BUTTON_SIZE + GAP_SIZE + GAP_SIZE + BUTTON_SIZE + GAP_SIZE + VOLUME_BAR_WIDTH + GAP_SIZE);
const PROGRESS_BAR_HEIGHT = 10;
const PLAY_BUTTON_X = GAP_SIZE;
const PROGRESS_BAR_X = PLAY_BUTTON_X + BUTTON_SIZE + GAP_SIZE;
const VOLUME_BUTTON_X = PROGRESS_BAR_X + PROGRESS_BAR_WIDTH + GAP_SIZE;
const VOLUME_BAR_X = VOLUME_BUTTON_X + BUTTON_SIZE + GAP_SIZE;
return {
VOLUME_BAR_WIDTH,
VOLUME_BAR_HEIGHT,
PROGRESS_BAR_WIDTH,
PROGRESS_BAR_HEIGHT,
PLAY_BUTTON_X,
PROGRESS_BAR_X,
VOLUME_BUTTON_X,
VOLUME_BAR_X,
};
}
const central_play_icon = new Image(50, 50);
central_play_icon.src = "vlc/modules/gui/qt/pixmaps/play_button.svg";
const play_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
play_button.src = "vlc/modules/gui/qt/pixmaps/play.png";
const pause_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
pause_button.src = "vlc/modules/gui/qt/pixmaps/pause.png";
const volume_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
volume_button.src = "vlc/modules/gui/qt/pixmaps/toolbar/volume-medium.png";
const muted_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
muted_button.src = "vlc/modules/gui/qt/pixmaps/toolbar/volume-muted.png";
export function update_overlay(overlay) {
const ctx = overlay.getContext("2d");
const media_player = window.media_player;
if (media_player == null) {
return;
}
const {
VOLUME_BAR_WIDTH,
VOLUME_BAR_HEIGHT,
PROGRESS_BAR_WIDTH,
PROGRESS_BAR_HEIGHT,
PLAY_BUTTON_X,
PROGRESS_BAR_X,
VOLUME_BUTTON_X,
VOLUME_BAR_X,
} = get_layout(overlay);
ctx.save();
ctx.clearRect(0, 0, overlay.width, overlay.height);
let is_paused = !media_player.is_playing();
if (is_paused) {
ctx.drawImage(
central_play_icon,
overlay.width / 2 - central_play_icon.width / 2,
overlay.height / 2 - central_play_icon.height / 2,
central_play_icon.width,
central_play_icon.height,
);
}
if (window.display_overlay) {
// -- PAINT BACKGROUND --
ctx.fillStyle = GENERAL_BG_COLOR;
ctx.fillRect(
0, overlay.height - MENU_BAR_HEIGHT,
overlay.width, MENU_BAR_HEIGHT
);
let y = overlay.height - MENU_BAR_HEIGHT;
// -- DRAW PLAY/PAUSE BUTTON --
ctx.drawImage(is_paused ? play_button : pause_button,
GAP_SIZE, y + (MENU_BAR_HEIGHT - BUTTON_SIZE) / 2,
BUTTON_SIZE, BUTTON_SIZE
);
// -- DRAW PROGRESS BAR --
let position = media_player.get_position();
ctx.fillStyle = PROGRESS_BAR_BG_COLOR;
ctx.fillRect(
PROGRESS_BAR_X, y + (MENU_BAR_HEIGHT - PROGRESS_BAR_HEIGHT) / 2,
PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT
);
ctx.fillStyle = PROGRESS_BAR_COLOR;
ctx.fillRect(
PROGRESS_BAR_X, y + (MENU_BAR_HEIGHT - PROGRESS_BAR_HEIGHT) / 2,
PROGRESS_BAR_WIDTH * position, PROGRESS_BAR_HEIGHT
);
// -- DRAW VOLUME/MUTE BUTTON --
let is_muted = media_player.get_mute();
ctx.drawImage(is_muted ? muted_button : volume_button,
VOLUME_BUTTON_X, y + (MENU_BAR_HEIGHT - BUTTON_SIZE) / 2,
BUTTON_SIZE, BUTTON_SIZE
);
// -- DRAW VOLUME BAR --
let volume = media_player.get_volume() / 100;
ctx.fillStyle = VOLUME_BAR_BG_COLOR;
ctx.fillRect(
VOLUME_BAR_X, y + (MENU_BAR_HEIGHT - VOLUME_BAR_HEIGHT) / 2,
VOLUME_BAR_WIDTH, VOLUME_BAR_HEIGHT
);
ctx.fillStyle = is_muted ? VOLUME_BAR_MUTE_COLOR : VOLUME_BAR_COLOR;
ctx.fillRect(
VOLUME_BAR_X, y + (MENU_BAR_HEIGHT - VOLUME_BAR_HEIGHT) / 2,
VOLUME_BAR_WIDTH * volume, VOLUME_BAR_HEIGHT
);
}
ctx.restore();
}
export function on_overlay_click(overlay, mouse_event) {
const ctx = overlay.getContext("2d");
const media_player = window.media_player;
if (media_player == null) {
return;
}
const {
VOLUME_BAR_WIDTH,
VOLUME_BAR_HEIGHT,
PROGRESS_BAR_WIDTH,
PROGRESS_BAR_HEIGHT,
PLAY_BUTTON_X,
PROGRESS_BAR_X,
VOLUME_BUTTON_X,
VOLUME_BAR_X,
} = get_layout(overlay);
let canvas_rect = mouse_event.target.getBoundingClientRect();
let x = mouse_event.clientX - canvas_rect.left;
let y = mouse_event.clientY - canvas_rect.top;
// TODO - initial state
// User clicked in menu bar
if (y > overlay.height - MENU_BAR_HEIGHT) {
// User clicked on play/pause button
if (x > PLAY_BUTTON_X && x < PLAY_BUTTON_X + BUTTON_SIZE) {
media_player.toggle_play();
update_overlay(overlay);
}
// User clicked on progress bar
if (x > PROGRESS_BAR_X && x < PROGRESS_BAR_X + PROGRESS_BAR_WIDTH) {
let progress = (x - PROGRESS_BAR_X) / PROGRESS_BAR_WIDTH;
media_player.set_position(progress);
update_overlay(overlay);
}
// User clicked on volume/mute button
if (x > VOLUME_BUTTON_X && x < VOLUME_BUTTON_X + BUTTON_SIZE) {
media_player.toggle_mute();
update_overlay(overlay);
}
// User clicked on volume bar
if (x > VOLUME_BAR_X && x < VOLUME_BAR_X + VOLUME_BAR_WIDTH) {
let new_volume = (x - VOLUME_BAR_X) / VOLUME_BAR_WIDTH;
media_player.set_volume(new_volume * 100);
// Unmute
media_player.set_mute(0);
update_overlay(overlay);
}
}
// User clicked outside the menu bar
else {
media_player.toggle_play();
}
}
mergeInto(LibraryManager.library, {
update_overlay: function() {
update_overlay();
const overlay = document.getElementById("overlay");
update_overlay(overlay);
},
// Worker functions
......
......@@ -31,7 +31,6 @@ int main() {
* If this thread stop, all proxyfied functions wont be called.
*/
EM_ASM(Module['noExitRuntime']=true);
libvlc_media_t *m;
char const *vlc_argv[] = {
"-vvv",
"--no-spu",
......@@ -42,30 +41,39 @@ int main() {
};
libvlc = libvlc_new( ARRAY_SIZE( vlc_argv ), vlc_argv );
if (libvlc == NULL)
{
fprintf( stderr, "unable to create libvlc instance" );
return -1;
}
emscripten_set_main_loop(iter, 1, 1);
return 0;
}
void* EMSCRIPTEN_KEEPALIVE get_media_player_buck_bunny() {
libvlc_media_player_t *media_player;
libvlc_media_t *m;
m = libvlc_media_new_path( libvlc, "./samples/BigBuckBunny.mp4" );
if (m == NULL)
{
fprintf(stderr, "unable to create media");
return -1;
return NULL;
}
mp = libvlc_media_player_new_from_media( m );
if (mp == NULL)
media_player = libvlc_media_player_new_from_media( m );
if (media_player == NULL)
{
fprintf(stderr, "unable to create media player");
return -1;
return NULL;
}
libvlc_media_release( m );
m = libvlc_media_player_get_media(mp);
m = libvlc_media_player_get_media(media_player);
libvlc_event_manager_t* event_manager = libvlc_media_player_event_manager(mp);
libvlc_event_manager_t* event_manager = libvlc_media_player_event_manager(media_player);
int res;
res = libvlc_event_attach(
event_manager,
......@@ -82,55 +90,49 @@ int main() {
);
assert(res == 0);
emscripten_set_main_loop(iter, 1, 1);
assert(media_player != NULL);
mp = media_player;
return 0;
return media_player;
}
int g_is_started = 0;
int EMSCRIPTEN_KEEPALIVE play(libvlc_media_player_t *media_player) {
return libvlc_media_player_play(media_player);
}
void EMSCRIPTEN_KEEPALIVE play_video() {
if (g_is_started == 0)
{
libvlc_media_player_play(mp);
g_is_started = 1;
}
else {
libvlc_media_player_pause(mp);
}
update_overlay();
void EMSCRIPTEN_KEEPALIVE pause_video(libvlc_media_player_t *media_player) {
libvlc_media_player_pause(media_player);
}
EM_BOOL EMSCRIPTEN_KEEPALIVE is_paused() {
return !libvlc_media_player_is_playing(mp);
EM_BOOL EMSCRIPTEN_KEEPALIVE is_playing(libvlc_media_player_t *media_player) {
return libvlc_media_player_is_playing(media_player);
}
float EMSCRIPTEN_KEEPALIVE get_position() {
return libvlc_media_player_get_position(mp);
float EMSCRIPTEN_KEEPALIVE get_position(libvlc_media_player_t *media_player) {
return libvlc_media_player_get_position(media_player);
}
int EMSCRIPTEN_KEEPALIVE set_position(float position) {
// TODO - what does "fast" argument do?
return libvlc_media_player_set_position(mp, position, false);
int EMSCRIPTEN_KEEPALIVE set_position(libvlc_media_player_t *media_player, float position, EM_BOOL fast) {
return libvlc_media_player_set_position(media_player, position, fast);
}
int EMSCRIPTEN_KEEPALIVE get_volume() {
return libvlc_audio_get_volume(mp);
int EMSCRIPTEN_KEEPALIVE get_volume(libvlc_media_player_t *media_player) {
return libvlc_audio_get_volume(media_player);
}
int EMSCRIPTEN_KEEPALIVE set_volume(int i_volume) {
return libvlc_audio_set_volume(mp, i_volume);
int EMSCRIPTEN_KEEPALIVE set_volume(libvlc_media_player_t *media_player, int i_volume) {
return libvlc_audio_set_volume(media_player, i_volume);
}
void EMSCRIPTEN_KEEPALIVE toggle_mute() {
libvlc_audio_toggle_mute(mp);
void EMSCRIPTEN_KEEPALIVE toggle_mute(libvlc_media_player_t *media_player) {
libvlc_audio_toggle_mute(media_player);
}
EM_BOOL EMSCRIPTEN_KEEPALIVE get_mute() {
return libvlc_audio_get_mute(mp);
EM_BOOL EMSCRIPTEN_KEEPALIVE get_mute(libvlc_media_player_t *media_player) {
return libvlc_audio_get_mute(media_player);
}
void EMSCRIPTEN_KEEPALIVE set_mute(int i_status) {
libvlc_audio_set_mute(mp, i_status);
void EMSCRIPTEN_KEEPALIVE set_mute(libvlc_media_player_t *media_player, int i_status) {
libvlc_audio_set_mute(media_player, i_status);
}
......@@ -97,178 +97,37 @@
></canvas>
</div>
<script type="text/javascript" src="./assets/module-loader.js"></script>
<script async type="text/javascript" src="./experimental.js"></script>
<script>
const central_play_icon = new Image(50, 50);
const overlay = document.getElementById("overlay");
const ctx = overlay.getContext("2d");
central_play_icon.onload = function() {
ctx.drawImage(
central_play_icon,
overlay.width / 2 - central_play_icon.width / 2,
overlay.height / 2 - central_play_icon.height / 2,
central_play_icon.width,
central_play_icon.height,
);
<script src="./lib/module-loader.js"></script>
<script src="./experimental.js"></script>
<script type="module">
import { update_overlay, on_overlay_click } from "./lib/overlay.js";
import { MediaPlayer } from "./lib/libvlc.js";
const Module = await VlcModule(VlcModuleExt);
Module.setStatus('Downloading...');
window.onerror = function(event) {
// TODO: do not warn on ok events like simulating an infinite loop or exitStatus
Module.setStatus('Exception thrown, see JavaScript console');
spinnerElement.style.display = 'none';
Module.setStatus = function(text) {
if (text) Module.printErr('[post-exception status] ' + text);
};
};
central_play_icon.src = "vlc/modules/gui/qt/pixmaps/play_button.svg";
const PROGRESS_BAR_COLOR = "#2a81d4";
const PROGRESS_BAR_BG_COLOR = "#2d2d2d";
const VOLUME_BAR_COLOR = "#4ad24a";
const VOLUME_BAR_MUTE_COLOR = "#4d704e";
const VOLUME_BAR_BG_COLOR = "#2d2d2d";
const BUTTON_BG_COLOR = "#464646";
const GENERAL_BG_COLOR = "#353535";
const TEXT_COLOR = "#8d8d8c";
const MENU_BAR_HEIGHT = 30;
const BUTTON_SIZE = 20;
const GAP_SIZE = 5;
const VOLUME_BAR_WIDTH = 100;
const VOLUME_BAR_HEIGHT = 20;
const PROGRESS_BAR_WIDTH = overlay.width -
(GAP_SIZE + BUTTON_SIZE + GAP_SIZE + GAP_SIZE + BUTTON_SIZE + GAP_SIZE + VOLUME_BAR_WIDTH + GAP_SIZE);
const PROGRESS_BAR_HEIGHT = 10;
const PLAY_BUTTON_X = GAP_SIZE;
const PROGRESS_BAR_X = PLAY_BUTTON_X + BUTTON_SIZE + GAP_SIZE;
const VOLUME_BUTTON_X = PROGRESS_BAR_X + PROGRESS_BAR_WIDTH + GAP_SIZE;
const VOLUME_BAR_X = VOLUME_BUTTON_X + BUTTON_SIZE + GAP_SIZE;
const play_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
play_button.src = "vlc/modules/gui/qt/pixmaps/play.png";
const pause_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
pause_button.src = "vlc/modules/gui/qt/pixmaps/pause.png";
const volume_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
volume_button.src = "vlc/modules/gui/qt/pixmaps/toolbar/volume-medium.png";
const muted_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
muted_button.src = "vlc/modules/gui/qt/pixmaps/toolbar/volume-muted.png";
function update_overlay() {
ctx.save();
ctx.clearRect(0, 0, overlay.width, overlay.height);
let is_paused = Module.is_paused();
if (is_paused) {
ctx.drawImage(
central_play_icon,
overlay.width / 2 - central_play_icon.width / 2,
overlay.height / 2 - central_play_icon.height / 2,
central_play_icon.width,
central_play_icon.height,
);
}
if (window.display_overlay) {
// -- PAINT BACKGROUND --
ctx.fillStyle = GENERAL_BG_COLOR;
ctx.fillRect(
0, overlay.height - MENU_BAR_HEIGHT,
overlay.width, MENU_BAR_HEIGHT
);
let y = overlay.height - MENU_BAR_HEIGHT;
// -- DRAW PLAY/PAUSE BUTTON --
ctx.drawImage(is_paused ? play_button : pause_button,
GAP_SIZE, y + (MENU_BAR_HEIGHT - BUTTON_SIZE) / 2,
BUTTON_SIZE, BUTTON_SIZE
);
// -- DRAW PROGRESS BAR --
let position = Module.get_position();
ctx.fillStyle = PROGRESS_BAR_BG_COLOR;
ctx.fillRect(
PROGRESS_BAR_X, y + (MENU_BAR_HEIGHT - PROGRESS_BAR_HEIGHT) / 2,
PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT
);
ctx.fillStyle = PROGRESS_BAR_COLOR;
ctx.fillRect(
PROGRESS_BAR_X, y + (MENU_BAR_HEIGHT - PROGRESS_BAR_HEIGHT) / 2,
PROGRESS_BAR_WIDTH * position, PROGRESS_BAR_HEIGHT
);
// -- DRAW VOLUME/MUTE BUTTON --
let is_muted = Module.get_mute();
ctx.drawImage(is_muted ? muted_button : volume_button,
VOLUME_BUTTON_X, y + (MENU_BAR_HEIGHT - BUTTON_SIZE) / 2,
BUTTON_SIZE, BUTTON_SIZE
);
// -- DRAW VOLUME BAR --
let volume = Module.get_volume() / 100;
ctx.fillStyle = VOLUME_BAR_BG_COLOR;
ctx.fillRect(
VOLUME_BAR_X, y + (MENU_BAR_HEIGHT - VOLUME_BAR_HEIGHT) / 2,
VOLUME_BAR_WIDTH, VOLUME_BAR_HEIGHT
);
ctx.fillStyle = is_muted ? VOLUME_BAR_MUTE_COLOR : VOLUME_BAR_COLOR;