diff --git a/Makefile.emscripten b/Makefile.emscripten
index 2b6383a3d4..7622aefa4c 100644
--- a/Makefile.emscripten
+++ b/Makefile.emscripten
@@ -39,7 +39,8 @@ OBJ = frontend/frontend_emscripten.o \
audio/sinc.o \
audio/null.o \
performance.o \
- core_info.o
+ core_info.o \
+ camera/rwebcam.o
HAVE_OPENGL = 1
HAVE_RGUI = 1
@@ -58,8 +59,8 @@ endif
libretro = libretro_emscripten.bc
LIBS = -lm
-DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE
-LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) -s OUTLINING_LIMIT=50000 --js-library emscripten/library_rwebaudio.js --js-library emscripten/library_rwebinput.js --no-heap-copy
+DEFINES = -DHAVE_SCREENSHOTS -DHAVE_CAMERA -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE
+LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) -s OUTLINING_LIMIT=50000 --js-library emscripten/library_rwebaudio.js --js-library emscripten/library_rwebinput.js --js-library emscripten/library_rwebcam.js --no-heap-copy
ifeq ($(PERF_TEST), 1)
DEFINES += -DPERF_TEST
diff --git a/camera/rwebcam.c b/camera/rwebcam.c
new file mode 100644
index 0000000000..a8a3c2a503
--- /dev/null
+++ b/camera/rwebcam.c
@@ -0,0 +1,53 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2013 - Michael Lelli
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include "../driver.h"
+#include "../emscripten/RWebCam.h"
+
+static void *rwebcam_init(const char *device, uint64_t caps, unsigned width, unsigned height)
+{
+ (void)device;
+ return RWebCamInit(caps, width, height);
+}
+
+static void rwebcam_free(void *data)
+{
+ RWebCamFree(data);
+}
+
+static bool rwebcam_start(void *data)
+{
+ return RWebCamStart(data);
+}
+
+static void rwebcam_stop(void *data)
+{
+ RWebCamStop(data);
+}
+
+static bool rwebcam_poll(void *data, retro_camera_frame_raw_framebuffer_t frame_raw_cb,
+ retro_camera_frame_opengl_texture_t frame_gl_cb)
+{
+ return RWebCamPoll(data, frame_raw_cb, frame_gl_cb);
+}
+
+const camera_driver_t camera_rwebcam = {
+ rwebcam_init,
+ rwebcam_free,
+ rwebcam_start,
+ rwebcam_stop,
+ rwebcam_poll,
+ "rwebcam",
+};
diff --git a/config.def.h b/config.def.h
index 4d731e481a..bb889ee8a3 100644
--- a/config.def.h
+++ b/config.def.h
@@ -82,6 +82,7 @@ enum
INPUT_NULL,
CAMERA_V4L2,
+ CAMERA_RWEBCAM,
CAMERA_NULL,
};
@@ -183,6 +184,8 @@ enum
#if defined(HAVE_V4L2)
#define CAMERA_DEFAULT_DRIVER CAMERA_V4L2
+#elif defined(EMSCRIPTEN)
+#define CAMERA_DEFAULT_DRIVER CAMERA_RWEBCAM
#else
#define CAMERA_DEFAULT_DRIVER CAMERA_NULL
#endif
diff --git a/driver.c b/driver.c
index ce624bd5a6..2ab1834752 100644
--- a/driver.c
+++ b/driver.c
@@ -183,6 +183,9 @@ static const input_driver_t *input_drivers[] = {
static const camera_driver_t *camera_drivers[] = {
#ifdef HAVE_V4L2
&camera_v4l2,
+#endif
+#ifdef EMSCRIPTEN
+ &camera_rwebcam,
#endif
NULL,
};
diff --git a/driver.h b/driver.h
index 1e497505bd..1b2cda5dfc 100644
--- a/driver.h
+++ b/driver.h
@@ -612,6 +612,7 @@ extern const input_driver_t input_qnx;
extern const input_driver_t input_rwebinput;
extern const input_driver_t input_null;
extern const camera_driver_t camera_v4l2;
+extern const camera_driver_t camera_rwebcam;
extern const input_osk_driver_t input_ps3_osk;
#include "driver_funcs.h"
diff --git a/emscripten/RWebCam.h b/emscripten/RWebCam.h
new file mode 100644
index 0000000000..6556a26f56
--- /dev/null
+++ b/emscripten/RWebCam.h
@@ -0,0 +1,24 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2013 - Michael Lelli
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include
+#include
+#include "../driver.h"
+
+void *RWebCamInit(uint64_t caps, unsigned width, unsigned height);
+void RWebCamFree(void *data);
+bool RWebCamStart(void *data);
+void RWebCamStop(void *data);
+bool RWebCamPoll(void *data, retro_camera_frame_raw_framebuffer_t frame_raw_cb, retro_camera_frame_opengl_texture_t frame_gl_cb);
diff --git a/emscripten/library_rwebcam.js b/emscripten/library_rwebcam.js
index 42192f51a5..77216d4381 100644
--- a/emscripten/library_rwebcam.js
+++ b/emscripten/library_rwebcam.js
@@ -2,19 +2,20 @@
var LibraryRWebCam = {
$RWC: {
- /*
- run modes:
- 0: not running
- 1: waiting for user to confirm webcam
- 2: running
- */
- runMode: 0,
- videoElement: null
+ RETRO_CAMERA_BUFFER_OPENGL_TEXTURE: 0,
+ RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER: 1,
+ tmp: null,
+
+ contexts: [],
+ counter: 0,
+
+ ready: function(data) {
+ return RWC.contexts[data].runMode == 2 && !RWC.contexts[data].videoElement.paused && RWC.contexts[data].videoElement.videoWidth != 0 && RWC.contexts[data].videoElement.videoHeight != 0;
+ }
},
- RWebCamInit: function() {
- RWC.runMode = 0;
-
+ RWebCamInit__deps: ['malloc'],
+ RWebCamInit: function(caps1, caps2, width, height) {
if (!navigator) return 0;
navigator.getMedia = navigator.getUserMedia ||
@@ -24,47 +25,93 @@ var LibraryRWebCam = {
if (!navigator.getMedia) return 0;
- RWC.videoElement = document.createElement("video");
+ var c = ++RWC.counter;
- RWC.runMode = 1;
+ RWC.contexts[c] = [];
+ RWC.contexts[c].videoElement = document.createElement("video");
+ if (width !== 0 && height !== 0) {
+ RWC.contexts[c].videoElement.width = width;
+ RWC.contexts[c].videoElement.height = height;
+ }
+ RWC.contexts[c].runMode = 1;
+ RWC.contexts[c].glTex = caps1 & (1 << RWC.RETRO_CAMERA_BUFFER_OPENGL_TEXTURE);
+ RWC.contexts[c].rawFb = caps1 & (1 << RWC.RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER);
navigator.getMedia({video: true, audio: false}, function(stream) {
- RWC.videoElement.autoplay = true;
- RWC.videoElement.src = URL.createObjectURL(stream);
- RWC.runMode = 2;
+ RWC.contexts[c].videoElement.autoplay = true;
+ RWC.contexts[c].videoElement.src = URL.createObjectURL(stream);
+ RWC.contexts[c].runMode = 2;
}, function (err) {
console.log("webcam request failed", err);
RWC.runMode = 0;
});
- return 1;
+ // for getting/storing texture id in GL mode
+ if (!RWC.tmp) RWC.tmp = _malloc(4);
+ return c;
},
- RWebCamTexImage2D__deps: ['RWebCamReady'],
- RWebCamTexImage2D: function(width, height) {
- if (!_RWebCamReady()) return 0;
-
- Module.ctx.texImage2D(Module.ctx.TEXTURE_2D, 0, Module.ctx.RGB, Module.ctx.RGB, Module.ctx.UNSIGNED_BYTE, RWC.videoElement);
-
- if (width) {{{ makeSetValue('width', '0', 'RWC.videoElement.videoWidth', 'i32') }}};
- if (height) {{{ makeSetValue('height', '0', 'RWC.videoElement.videoHeight', 'i32') }}};
+ RWebCamFree: function(data) {
+ RWC.contexts[data].videoElement.pause();
+ URL.revokeObjectURL(RWC.contexts[data].videoElement.src);
+ RWC.contexts[data].videoElement = null;
+ RWC.contexts[data] = null;
},
- RWebCamTexSubImage2D__deps: ['RWebCamReady'],
- RWebCamTexSubImage2D: function(x, y) {
- if (!_RWebCamReady()) return 0;
+ RWebCamStart__deps: ['glGenTextures', 'glBindTexture', 'glGetIntegerv', 'glTexParameteri'],
+ RWebCamStart: function(data) {
+ var ret = 0;
+ if (RWC.contexts[data].glTex) {
+ _glGenTextures(1, RWC.tmp);
+ RWC.contexts[data].glTexId = {{{ makeGetValue('RWC.tmp', '0', 'i32') }}};
+ if (RWC.contexts[data].glTexId !== 0) {
+ // save previous texture
+ _glGetIntegerv(0x8069 /* GL_TEXTURE_BINDING_2D */, RWC.tmp);
+ var prev = {{{ makeGetValue('RWC.tmp', '0', 'i32') }}};
+ _glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, RWC.contexts[data].glTexId);
+ /* NPOT textures in WebGL must have these filters and clamping settings */
+ _glTexParameteri(0x0DE1 /* GL_TEXTURE_2D */, 0x2800 /* GL_TEXTURE_MAG_FILTER */, 0x2601 /* GL_LINEAR */);
+ _glTexParameteri(0x0DE1 /* GL_TEXTURE_2D */, 0x2801 /* GL_TEXTURE_MIN_FILTER */, 0x2601 /* GL_LINEAR */);
+ _glTexParameteri(0x0DE1 /* GL_TEXTURE_2D */, 0x2802 /* GL_TEXTURE_WRAP_S */, 0x812F /* GL_CLAMP_TO_EDGE */);
+ _glTexParameteri(0x0DE1 /* GL_TEXTURE_2D */, 0x2803 /*GL_TEXTURE_WRAP_T */, 0x812F /* GL_CLAMP_TO_EDGE */);
+ _glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, prev);
+ RWC.contexts[data].glFirstFrame = true;
+ ret = 1;
+ }
+ }
- Module.ctx.texSubImage2D(Module.ctx.TEXTURE_2D, 0, x, y, Module.ctx.RGB, Module.ctx.UNSIGNED_BYTE, RWC.videoElement);
+ return ret;
},
- RWebCamReady: function() {
- return (RWC.runMode == 2 && !RWC.videoElement.paused && RWC.videoElement.videoWidth != 0 && RWC.videoElement.videoHeight != 0) ? 1 : 0;
+ RWebCamStop__deps: ['glDeleteTextures'],
+ RWebCamStop: function(data) {
+ if (RWC.contexts[data].glTexId) {
+ _glDeleteTextures(1, RWC.contexts[data].glTexId);
+ }
},
- RWebCamFree: function() {
- RWC.videoElement.pause();
- RWC.videoElement = null;
- RWC.runMode = 0;
+ RWebCamPoll__deps: ['glBindTexture', 'glGetIntegerv'],
+ RWebCamPoll: function(data, frame_raw_cb, frame_gl_cb) {
+ if (!RWC.ready(data)) return 0;
+ var ret = 0;
+
+ if (RWC.contexts[data].glTexId !== 0 && frame_gl_cb !== 0) {
+ _glGetIntegerv(0x8069 /* GL_TEXTURE_BINDING_2D */, RWC.tmp);
+ var prev = {{{ makeGetValue('RWC.tmp', '0', 'i32') }}};
+ _glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, RWC.contexts[data].glTexId);
+ if (RWC.contexts[data].glFirstFrame) {
+ Module.ctx.texImage2D(Module.ctx.TEXTURE_2D, 0, Module.ctx.RGB, Module.ctx.RGB, Module.ctx.UNSIGNED_BYTE, RWC.contexts[data].videoElement);
+ RWC.contexts[data].glFirstFrame = false;
+ } else {
+ Module.ctx.texSubImage2D(Module.ctx.TEXTURE_2D, 0, 0, 0, Module.ctx.RGB, Module.ctx.UNSIGNED_BYTE, RWC.contexts[data].videoElement);
+ }
+ _glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, prev);
+ Runtime.dynCall('viii', frame_gl_cb, [RWC.contexts[data].glTexId, 0x0DE1 /* GL_TEXTURE_2D */, 0]);
+
+ ret = 1;
+ }
+
+ return ret;
}
};
diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c
index be836075b7..5c2a044180 100644
--- a/frontend/frontend_emscripten.c
+++ b/frontend/frontend_emscripten.c
@@ -21,8 +21,8 @@
#include "../file.h"
#include "../emscripten/RWebAudio.h"
-#ifdef HAVE_RGUI
-#include "../frontend/menu/rgui.h"
+#ifdef HAVE_MENU
+#include "../frontend/menu/menu_common.h"
#endif
static bool menuloop;
diff --git a/settings.c b/settings.c
index ced3899fe0..0f99876ccc 100644
--- a/settings.c
+++ b/settings.c
@@ -155,6 +155,8 @@ const char *config_get_default_camera(void)
{
case CAMERA_V4L2:
return "video4linux2";
+ case CAMERA_RWEBCAM:
+ return "rwebcam";
case CAMERA_NULL:
return "null";
default: