Skip to content

Commit

Permalink
Add support for GameBoy Camera
Browse files Browse the repository at this point in the history
All the reverse-engineering work comes from AntonioND [1].
A new video backend API has been added to grab video images.
By default, a dummy backend is provided.
However, an OpenCV based backend is also provided (if enabled at
compile-time with OPENCV=1 in Makefile. Disabled for MSVC).
Other implementation should be possible (GStreamer for instance ?) in
the future.

With the OpenCV backend, the video device selection can be done using
the Core parameter:

[Core]
GbCameraVideoDevice=<my_device>

Where <my_device> can be either an integer which represent the device
number (0 for default) or a string which specify the video device path.

Tested with 64DD Mario Talent Studio (Japan), a transfer pak plugged
in the first controller with a Japanese GameBoy camera. Also since the
core currently requires a cart ROM (even if should strictly be required)
I used Perfect Dark (Japan) to allow using the Transfer Pak. This is a
core/ui limitation not related to this PR.

[1] https://github.com/AntonioND/gbcam-rev-engineer
  • Loading branch information
bsmiles32 committed May 21, 2018
1 parent 462a40a commit 0f42915
Show file tree
Hide file tree
Showing 15 changed files with 822 additions and 66 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ addons:
- liblircclient-dev
- binutils-dev
- nasm
- libopencv-dev
env:
- OSD=0
- OSD=1
Expand Down Expand Up @@ -51,6 +52,7 @@ matrix:
- mxe-i686-w64-mingw32.shared-freetype
- mxe-i686-w64-mingw32.shared-libpng
- mxe-i686-w64-mingw32.shared-zlib
- mxe-i686-w64-mingw32.shared-opencv
- mxe-i686-w64-mingw32.shared-pkgconf
- nasm
script:
Expand All @@ -72,6 +74,7 @@ matrix:
- mxe-x86-64-w64-mingw32.shared-freetype
- mxe-x86-64-w64-mingw32.shared-libpng
- mxe-x86-64-w64-mingw32.shared-zlib
- mxe-x86-64-w64-mingw32.shared-opencv
- mxe-i686-w64-mingw32.shared-pkgconf
- nasm
script:
Expand Down
55 changes: 39 additions & 16 deletions projects/VisualStudio2013/mupen64plus-core.vcxproj

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions projects/VisualStudio2013/mupen64plus-core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@
<ClCompile Include="..\..\src\backends\clock_ctime_plus_delta.c">
<Filter>backends</Filter>
</ClCompile>
<ClCompile Include="..\..\src\backends\dummy_video_backend.c">
<Filter>backends</Filter>
</ClCompile>
<ClCompile Include="..\..\src\backends\opencv_video_backend.cpp">
<Filter>backends</Filter>
</ClCompile>
<ClCompile Include="..\..\src\device\dd\dd_controller.c">
<Filter>device\dd</Filter>
</ClCompile>
Expand Down Expand Up @@ -485,6 +491,12 @@
<ClInclude Include="..\..\src\backends\clock_ctime_plus_delta.h">
<Filter>backends</Filter>
</ClInclude>
<ClInclude Include="..\..\src\backends\dummy_video_backend.h">
<Filter>backends</Filter>
</ClInclude>
<ClInclude Include="..\..\src\backends\opencv_video_backend.h">
<Filter>backends</Filter>
</ClInclude>
<ClInclude Include="..\..\src\device\dd\dd_controller.h">
<Filter>device\dd</Filter>
</ClInclude>
Expand Down Expand Up @@ -620,6 +632,9 @@
<ClInclude Include="..\..\src\backends\api\storage_backend.h">
<Filter>backends\api</Filter>
</ClInclude>
<ClInclude Include="..\..\src\backends\api\video_backend.h">
<Filter>backends\api</Filter>
</ClInclude>
<ClInclude Include="..\..\src\backends\plugins_compat\plugins_compat.h">
<Filter>backends\plugins_compat</Filter>
</ClInclude>
Expand Down
21 changes: 21 additions & 0 deletions projects/unix/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,19 @@ endif
CFLAGS += $(LIBPNG_CFLAGS)
LDLIBS += $(LIBPNG_LDLIBS)

ifeq ($(OPENCV), 1)
# OpenCV lib
ifeq ($(origin OPENCV_CFLAGS) $(origin OPENCV_LDLIBS), undefined undefined)
ifeq ($(shell $(PKG_CONFIG) --modversion opencv 2>/dev/null),)
$(error No OpenCV development libraries found!)
endif
OPENCV_CFLAGS += $(shell $(PKG_CONFIG) --cflags opencv)
OPENCV_LDLIBS += $(shell $(PKG_CONFIG) --libs opencv)
endif
CFLAGS += $(OPENCV_CFLAGS)
LDLIBS += $(OPENCV_LDLIBS)
endif

# test for presence of SDL
ifeq ($(origin SDL_CFLAGS) $(origin SDL_LDLIBS), undefined undefined)
SDL_CONFIG = $(CROSS_COMPILE)sdl2-config
Expand Down Expand Up @@ -446,6 +459,7 @@ SOURCE = \
$(SRCDIR)/backends/plugins_compat/input_plugin_compat.c \
$(SRCDIR)/backends/clock_ctime_plus_delta.c \
$(SRCDIR)/backends/file_storage.c \
$(SRCDIR)/backends/dummy_video_backend.c \
$(SRCDIR)/device/cart/cart.c \
$(SRCDIR)/device/cart/af_rtc.c \
$(SRCDIR)/device/cart/cart_rom.c \
Expand Down Expand Up @@ -613,6 +627,12 @@ ifeq ($(DEBUGGER), 1)
endif
endif

ifeq ($(OPENCV), 1)
SOURCE += $(SRCDIR)/backends/opencv_video_backend.cpp
CFLAGS += -DM64P_OPENCV
endif


# generate a list of object files to build, make a temporary directory for them
OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(filter %.c, $(SOURCE)))
OBJECTS += $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(SRCDIR)/asm_defines/asm_defines.c)
Expand Down Expand Up @@ -647,6 +667,7 @@ targets:
@echo " PIC=(1|0) == Force enable/disable of position independent code"
@echo " OSD=(1|0) == Enable/disable build of OpenGL On-screen display"
@echo " NEW_DYNAREC=1 == Replace dynamic recompiler with Ari64's experimental dynarec"
@echo " OPENCV=1 == Enable OpenCV support"
@echo " POSTFIX=name == String added to the name of the the build (default: '')"
@echo " Install Options:"
@echo " PREFIX=path == install/uninstall prefix (default: /usr/local/)"
Expand Down
44 changes: 44 additions & 0 deletions src/backends/api/video_backend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus - video_backend.h *
* Mupen64Plus homepage: https://mupen64plus.org/ *
* Copyright (C) 2017 Bobby Smiles *
* *
* This program 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 Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef M64P_BACKENDS_API_VIDEO_BACKEND_H
#define M64P_BACKENDS_API_VIDEO_BACKEND_H

#include "api/m64p_types.h"

struct video_input_backend_interface
{
/* Open a video stream with following properties (width, height)
* Returns M64ERR_SUCCESS on success.
*/
m64p_error (*open)(void* vin, unsigned int width, unsigned int height);

/* Close a previsouly opened video stream.
*/
void (*close)(void* vin);

/* Grab a BGR image on an open stream
* Returns M64ERR_SUCCESS on success.
*/
m64p_error (*grab_image)(void* vin, void* data);
};

#endif
46 changes: 46 additions & 0 deletions src/backends/dummy_video_backend.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus - dummy_video_backend.c *
* Mupen64Plus homepage: https://mupen64plus.org/ *
* Copyright (C) 2018 Bobby Smiles *
* *
* This program 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 Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "backends/dummy_video_backend.h"
#include "backends/api/video_backend.h"

#include "api/m64p_types.h"

static m64p_error dummy_video_open(void* vin, unsigned int width, unsigned int height)
{
return M64ERR_SUCCESS;
}

static void dummy_video_close(void* vin)
{
}

static m64p_error dummy_grab_image(void* vin, void* data)
{
return M64ERR_UNSUPPORTED;
}

const struct video_input_backend_interface g_idummy_video_input_backend =
{
dummy_video_open,
dummy_video_close,
dummy_grab_image
};
29 changes: 29 additions & 0 deletions src/backends/dummy_video_backend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus - dummy_video_backend.h *
* Mupen64Plus homepage: https://mupen64plus.org/ *
* Copyright (C) 2018 Bobby Smiles *
* *
* This program 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 Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef M64P_BACKENDS_DUMMY_VIDEO_BACKEND_H
#define M64P_BACKENDS_DUMMY_VIDEO_BACKEND_H

#include "api/video_backend.h"

extern const struct video_input_backend_interface g_idummy_video_input_backend;

#endif
168 changes: 168 additions & 0 deletions src/backends/opencv_video_backend.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus - opencv_video_backend.h *
* Mupen64Plus homepage: https://mupen64plus.org/ *
* Copyright (C) 2017 Bobby Smiles *
* *
* This program 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 Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "opencv2/core/version.hpp"

#if CV_MAJOR_VERSION >= 3
/* this is for opencv >= 3.0 (new style headers + videoio/highgui split) */
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#elif CV_MAJOR_VERSION >= 2
/* otherwise go the safe way and let opencv include all its headers */
#include <opencv2/opencv.hpp>
#else
#error "Unsupported version of OpenCV"
#endif

#include <cstdlib>

extern "C"
{
#include "backends/opencv_video_backend.h"
#include "backends/api/video_backend.h"

#define M64P_CORE_PROTOTYPES 1
#include "api/callbacks.h"
#include "api/m64p_types.h"
#include "main/util.h"

/* Implements video_input_backend.
* Needs device field to be set before calling.
*/
static m64p_error opencv_video_open(void* vin, unsigned int width, unsigned int height)
{
try {
int dev_num;
struct opencv_video_backend* b = static_cast<struct opencv_video_backend*>(vin);

/* allocate memory for cv::VideoCapture */
cv::VideoCapture* cap = static_cast<cv::VideoCapture*>(std::malloc(sizeof(cv::VideoCapture)));
if (cap == NULL) {
DebugMessage(M64MSG_ERROR, "Failed to allocated memory for video device %s", b->device);
return M64ERR_NO_MEMORY;
}

/* placement new to call cv::VideoCapture constructor */
new(cap)cv::VideoCapture();


/* open device (we support both device number or path */
if (string_to_int(b->device, &dev_num)) {
cap->open(dev_num);
}
else {
cap->open(b->device);
}

if (!cap->isOpened()) {
DebugMessage(M64MSG_ERROR, "Failed to open video device %s", b->device);
std::free(cap);
return M64ERR_SYSTEM_FAIL;
}

/* TODO: adapt capture resolution to the desired resolution */

DebugMessage(M64MSG_INFO, "Video successfully opened: %s", b->device);

b->cap = cap;
b->width = width;
b->height = height;

return M64ERR_SUCCESS;
}
/* C++ exception must not cross C-API boundaries */
catch(...) { return M64ERR_INTERNAL; }
}

static void opencv_video_close(void* vin)
{
try {
struct opencv_video_backend* b = static_cast<struct opencv_video_backend*>(vin);

if (b->cap != NULL) {
/* explicit call to cv::VideoCapture destructor */
static_cast<cv::VideoCapture*>(b->cap)->~VideoCapture();

/* free allocated memory */
std::free(b->cap);
b->cap = NULL;
}

if (b->device != NULL) {
std::free(b->device);
b->device = NULL;
}

DebugMessage(M64MSG_INFO, "Video closed");
}
/* C++ exception must not cross C-API boundaries */
catch(...) { return; }
}

static m64p_error opencv_grab_image(void* vin, void* data)
{
try {
struct opencv_video_backend* b = static_cast<struct opencv_video_backend*>(vin);
cv::VideoCapture* cap = static_cast<cv::VideoCapture*>(b->cap);

/* read next frame */
cv::Mat frame;
if (cap == NULL || !cap->read(frame)) {
DebugMessage(M64MSG_ERROR, "Failed to grab frame !");
return M64ERR_SYSTEM_FAIL;
}

/* resize image to desired resolution */
cv::Mat output = cv::Mat(b->height, b->width, CV_8UC3, data);
cv::resize(frame, output, output.size(), 0, 0, cv::INTER_AREA);

return M64ERR_SUCCESS;
}
/* C++ exception must not cross C-API boundaries */
catch(...) { return M64ERR_INTERNAL; }
}


#if 0
void cv_imshow(const char* name, unsigned int width, unsigned int height, int channels, void* data)
{
try {
int type = (channels == 1) ? CV_8UC1 : CV_8UC3;

cv::Mat frame = cv::Mat(height, width, type, data);
cv::imshow(name, frame);
cv::waitKey(1);
}
/* C++ exception must not cross C-API boundaries */
catch(...) { return; }
}
#endif

const struct video_input_backend_interface g_iopencv_video_input_backend =
{
opencv_video_open,
opencv_video_close,
opencv_grab_image
};

}
Loading

0 comments on commit 0f42915

Please sign in to comment.