Commit 8c29aac1 authored by Olivier FAURE's avatar Olivier FAURE
Browse files

Add UI overlay to video canvas

Resolves #13
parent 96e6bc22
......@@ -7,5 +7,5 @@ experimental.data
experimental.html
experimental.js
experimental.wasm
experimental.wasm.map
experimental.worker.js
......@@ -4,7 +4,17 @@ var spinnerElement = document.getElementById('spinner');
var Module = {
preRun: [],
postRun: [],
postRun: [ function() {
// This should run after the wasm module is instantiated
// before, the Pthread object won't be available
PThread.receiveObjectTransfer = function (data) {
let event = new CustomEvent('worker_message', {
detail: data.msg
});
window.dispatchEvent(event);
};
}],
print: (function() {
var element = document.getElementById('output');
if (element) element.value = ''; // clear browser cache
......@@ -26,12 +36,42 @@ var Module = {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
},
onRuntimeInitialized: function () {
this.play_video = this._play_video;
this.pause_video = this._pause_video;
this.is_paused = this._is_paused;
this.get_position = this._get_position;
this.set_position = this._set_position;
this.get_volume = this._get_volume;
this.set_volume = this._set_volume;
this.toggle_mute = this._toggle_mute;
this.get_mute = this._get_mute;
this.set_mute = this._set_mute;
},
canvas: (function() {
var canvas = document.getElementById('canvas')
var overlay = document.getElementById('overlay')
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false)
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);
});
// Create a global window.display_overlay variable to track whether
// the UI should be visible - see update_overlay function
window.display_overlay = true,
overlay.addEventListener('mouseenter', e => {
window.display_overlay = true;
});
overlay.addEventListener('mouseleave', e => {
window.display_overlay = false;
});
return canvas;
})(),
setStatus: function(text) {
......@@ -70,4 +110,4 @@ window.onerror = function(event) {
Module.setStatus = function(text) {
if (text) Module.printErr('[post-exception status] ' + text);
};
};
\ No newline at end of file
};
......@@ -37,4 +37,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}
mergeInto(LibraryManager.library, {
update_overlay: function() {
update_overlay();
},
// Worker functions
on_position_changed: function() {
postMessage({ cmd: "objectTransfer", msg: "on_position_changed" });
}
});
#include <stdio.h>
#include <vlc/vlc.h>
#include <vlc_common.h>
#include <assert.h>
#include <errno.h>
#include <emscripten.h>
#include <emscripten/html5.h>
libvlc_media_player_t *mp;
libvlc_instance_t *libvlc;
libvlc_instance_t *libvlc;
libvlc_time_t t = -1;
char flag = 1;
static void iter()
{
......@@ -22,23 +22,8 @@ static void iter()
t = libvlc_media_player_get_time(mp);
}
static EM_BOOL play_pause_handler(int eventType, const EmscriptenMouseEvent *e, void *userData)
{
(void) e;
(void) userData; // To use when mp won't be a global.
if (eventType == EMSCRIPTEN_EVENT_CLICK)
{
if (flag == 1)
{
libvlc_media_player_play(mp);
flag = 0;
}
// Don't do that until you implement a/v synchro.
// libvlc_media_player_pause(mp);
// flag = 1;
}
return 0;
}
extern void update_overlay();
extern void on_position_changed(const libvlc_event_t *p_event, void *p_data);
int main() {
......@@ -55,9 +40,9 @@ int main() {
"-Idummy",
"--ignore-config",
};
libvlc = libvlc_new( ARRAY_SIZE( vlc_argv ), vlc_argv );
if (libvlc == NULL)
{
fprintf( stderr, "unable to create libvlc instance" );
......@@ -76,18 +61,76 @@ int main() {
fprintf(stderr, "unable to create media player");
return -1;
}
libvlc_media_release( m );
m = libvlc_media_player_get_media(mp);
// libvlc_audio_set_volume (mp, 100);
// int res = libvlc_media_player_play (mp);
/* if (res != 0) {
fprintf( stderr, "unable to play media" );
return -1;
}
*/
emscripten_set_click_callback("#canvas", 0, 1, play_pause_handler);
libvlc_event_manager_t* event_manager = libvlc_media_player_event_manager(mp);
int res;
res = libvlc_event_attach(
event_manager,
libvlc_MediaPlayerPositionChanged,
on_position_changed,
NULL
);
assert(res == 0);
res = libvlc_event_attach(
event_manager,
libvlc_MediaPlayerPaused,
on_position_changed,
NULL
);
assert(res == 0);
emscripten_set_main_loop(iter, 1, 1);
return 0;
}
int g_is_started = 0;
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();
}
EM_BOOL EMSCRIPTEN_KEEPALIVE is_paused() {
return !libvlc_media_player_is_playing(mp);
}
float EMSCRIPTEN_KEEPALIVE get_position() {
return libvlc_media_player_get_position(mp);
}
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 get_volume() {
return libvlc_audio_get_volume(mp);
}
int EMSCRIPTEN_KEEPALIVE set_volume(int i_volume) {
return libvlc_audio_set_volume(mp, i_volume);
}
void EMSCRIPTEN_KEEPALIVE toggle_mute() {
libvlc_audio_toggle_mute(mp);
}
EM_BOOL EMSCRIPTEN_KEEPALIVE get_mute() {
return libvlc_audio_get_mute(mp);
}
void EMSCRIPTEN_KEEPALIVE set_mute(int i_status) {
libvlc_audio_set_mute(mp, i_status);
}
......@@ -11,12 +11,24 @@
body {
background-color: #343a40;
}
canvas.emscripten {
#canvas {
border:0 !important;
display: flex;
margin: 0 auto;
background-color: #000;
}
#overlay {
border:0 !important;
display: flex;
margin: 0 auto;
}
#stack {
display: grid;
}
#stack > canvas {
grid-column: 1;
grid-row: 1;
}
#spinner {
display: none;
}
......@@ -57,22 +69,210 @@
</head>
<body>
<div class="emscripten_border">
<div class="emscripten">
<div class="emscripten">
<div class="banner">
<img src="./assets/emscripten.svg" id ="em_logo">
<div class="vlc_head">
<img src="./assets/VLC_Icon.svg" id="logo">
<span id="point">.</span>
<span id="js">JS</span>
<span id="js">JS</span>
</div>
</div>
<progress id="progress" value="0" max="100"></progress>
<div class="spinner" id='spinner'></div>
<div class="emscripten" id="status">Downloading...</div>
</div>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" width=1280 height=720 tabindex=-1></canvas>
</div>
<div id="stack">
<canvas
class="emscripten" id="canvas"
oncontextmenu="event.preventDefault()" tabindex=-1
width=1280 height=720
></canvas>
<canvas
class="emscripten hidden" id="overlay"
oncontextmenu="event.preventDefault()" tabindex=-2
width=1280 height=720
></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,
);
};
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;
ctx.fillRect(
VOLUME_BAR_X, y + (MENU_BAR_HEIGHT - VOLUME_BAR_HEIGHT) / 2,
VOLUME_BAR_WIDTH * volume, VOLUME_BAR_HEIGHT
);
}
ctx.restore();
}
function on_overlay_click(mouse_event) {
let canvas_rect = mouse_event.target.getBoundingClientRect();
let x = mouse_event.clientX - canvas_rect.left;
let y = mouse_event.clientY - canvas_rect.top;
// 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) {
Module.play_video();
update_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;
Module.set_position(progress);
update_overlay();
}
// User clicked on volume/mute button
if (x > VOLUME_BUTTON_X && x < VOLUME_BUTTON_X + BUTTON_SIZE) {
Module.toggle_mute();
update_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;
Module.set_volume(new_volume * 100);
// Unmute
Module.set_mute(0);
update_overlay();
}
}
// User clicked outside the menu bar
else {
if (Module.play_video)
Module.play_video();
}
}
addEventListener('worker_message', function(msg) {
if (msg.detail === "on_position_changed") {
update_overlay();
}
});
</script>
</body>
</html>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment