[obs] implemented intial OBS Looking Glass Client plugin

Yes, it works! but no cursor support yet
This commit is contained in:
Geoffrey McRae 2020-01-10 18:14:08 +11:00
parent 3253e7fd10
commit c92312a6c6
5 changed files with 340 additions and 1 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ module/modules.order
*.a *.a
*.o *.o
*.exe *.exe
*/build

View File

@ -1 +1 @@
B1-71-ge5178793b3+1 B1-72-g3253e7fd10+1

72
obs/CMakeLists.txt Normal file
View File

@ -0,0 +1,72 @@
cmake_minimum_required(VERSION 3.0)
project(looking-glass-obs C)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
include(GNUInstallDirs)
include(CheckCCompilerFlag)
include(FeatureSummary)
option(OPTIMIZE_FOR_NATIVE "Build with -march=native" ON)
if(OPTIMIZE_FOR_NATIVE)
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
add_compile_options("-march=native")
endif()
endif()
option(ENABLE_BACKTRACE "Enable backtrace support on crash" ON)
add_feature_info(ENABLE_BACKTRACE ENABLE_BACKTRACE "Backtrace support.")
add_compile_options(
"-Wall"
"-Werror"
"-Wfatal-errors"
"-ffast-math"
"-fdata-sections"
"-ffunction-sections"
"-fpic"
"$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
)
set(EXE_FLAGS "-Wl,--gc-sections")
set(CMAKE_C_STANDARD 11)
execute_process(
COMMAND cat ../VERSION
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions(-D BUILD_VERSION='"${BUILD_VERSION}"')
add_definitions(-D ATOMIC_LOCKING)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
include_directories(
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
)
link_libraries(
${CMAKE_DL_LIBS}
rt
m
)
set(SOURCES
main.c
lg.c
)
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common")
add_subdirectory("${PROJECT_TOP}/LGMP/lgmp" "${CMAKE_BINARY_DIR}/lgmp")
add_library(looking-glass-obs SHARED ${SOURCES})
target_link_libraries(looking-glass-obs
${EXE_FLAGS}
lg_common
lgmp
)
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

232
obs/lg.c Normal file
View File

@ -0,0 +1,232 @@
#include <obs/obs-module.h>
#include <common/ivshmem.h>
#include <common/KVMFR.h>
#include <common/framebuffer.h>
#include <lgmp/client.h>
#include <stdio.h>
typedef struct
{
obs_source_t * context;
bool valid;
char * shmFile;
uint32_t width, height;
FrameType type;
struct IVSHMEM shmDev;
PLGMPClient lgmp;
PLGMPClientQueue frameQueue;
gs_texture_t * texture;
}
LGPlugin;
static void lgUpdate(void * data, obs_data_t * settings);
static const char * lgGetName(void * unused)
{
return obs_module_text("Looking Glass Client");
}
static void * lgCreate(obs_data_t * settings, obs_source_t * context)
{
LGPlugin * this = bzalloc(sizeof(LGPlugin));
this->context = context;
lgUpdate(this, settings);
return this;
}
static void deinit(LGPlugin * this)
{
lgmpClientFree(&this->lgmp);
if (this->shmFile)
{
bfree(this->shmFile);
this->shmFile = NULL;
}
if (this->shmDev.mem)
ivshmemClose(&this->shmDev);
this->valid = false;
}
static void lgDestroy(void * data)
{
LGPlugin * this = (LGPlugin *)data;
deinit(this);
bfree(this);
}
static void lgGetDefaults(obs_data_t * defaults)
{
obs_data_set_default_string(defaults, "shmFile", "/dev/shm/looking-glass");
}
static obs_properties_t * lgGetProperties(void * data)
{
obs_properties_t * props = obs_properties_create();
obs_properties_add_text(props, "shmFile", obs_module_text("SHM File"), OBS_TEXT_DEFAULT);
return props;
}
static void lgUpdate(void * data, obs_data_t * settings)
{
LGPlugin * this = (LGPlugin *)data;
deinit(this);
this->shmFile = bstrdup(obs_data_get_string(settings, "shmFile"));
if (!ivshmemOpenDev(&this->shmDev, this->shmFile))
return;
if (lgmpClientInit(this->shmDev.mem, this->shmDev.size, &this->lgmp) != LGMP_OK)
return;
if (lgmpClientSubscribe(this->lgmp, LGMP_Q_FRAME, &this->frameQueue) != LGMP_OK)
return;
this->valid = true;
}
static void lgVideoTick(void * data, float seconds)
{
LGPlugin * this = (LGPlugin *)data;
if (!this->valid)
return;
LGMP_STATUS status;
LGMPMessage msg;
if ((status = lgmpClientAdvanceToLast(this->frameQueue)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_EMPTY)
return;
printf("lgmpClientAdvanceToLast: %s\n", lgmpStatusString(status));
this->valid = false;
return;
}
if ((status = lgmpClientProcess(this->frameQueue, &msg)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_EMPTY)
return;
printf("lgmpClientProcess: %s\n", lgmpStatusString(status));
this->valid = false;
return;
}
obs_enter_graphics();
KVMFRFrame * frame = (KVMFRFrame *)msg.mem;
if (this->width != frame->width ||
this->height != frame->height ||
this->type != frame->type)
{
if (this->texture)
gs_texture_destroy(this->texture);
this->texture = NULL;
this->width = frame->width;
this->height = frame->height;
this->type = frame->type;
}
if (!this->texture)
{
enum gs_color_format format;
switch(this->type)
{
case FRAME_TYPE_BGRA : format = GS_BGRA ; break;
case FRAME_TYPE_RGBA : format = GS_RGBA ; break;
case FRAME_TYPE_RGBA10: format = GS_R10G10B10A2; break;
default:
printf("invalid type %d\n", this->type);
this->valid = false;
obs_leave_graphics();
return;
}
this->texture = gs_texture_create(
this->width, this->height, format, 1, NULL, GS_DYNAMIC);
if (!this->texture)
{
printf("create texture failed\n");
this->valid = false;
obs_leave_graphics();
return;
}
}
FrameBuffer fb = (FrameBuffer)(frame + 1);
uint8_t *texData;
uint32_t linesize;
gs_texture_map(this->texture, &texData, &linesize);
if (linesize == frame->pitch)
framebuffer_read(fb, texData, frame->height * frame->pitch);
gs_texture_unmap(this->texture);
// gs_texture_set_image(this->texture, frameData, frame->pitch, false);
lgmpClientMessageDone(this->frameQueue);
obs_leave_graphics();
}
static void lgVideoRender(void * data, gs_effect_t * effect)
{
LGPlugin * this = (LGPlugin *)data;
if (!this->texture)
return;
effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, this->texture);
while (gs_effect_loop(effect, "Draw")) {
gs_draw_sprite(this->texture, 0, 0, 0);
}
}
static uint32_t lgGetWidth(void * data)
{
LGPlugin * this = (LGPlugin *)data;
if (!this->valid)
return 0;
return this->width;
}
static uint32_t lgGetHeight(void * data)
{
LGPlugin * this = (LGPlugin *)data;
if (!this->valid)
return 0;
return this->height;
}
struct obs_source_info lg_source =
{
.id = "looking-glass-obs",
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
OBS_SOURCE_DO_NOT_DUPLICATE,
.get_name = lgGetName,
.create = lgCreate,
.destroy = lgDestroy,
.update = lgUpdate,
.get_defaults = lgGetDefaults,
.get_properties = lgGetProperties,
.video_tick = lgVideoTick,
.video_render = lgVideoRender,
.get_width = lgGetWidth,
.get_height = lgGetHeight,
// .icon_type = OBS_ICON_TYPE_DESKTOP_CAPTURE
};

34
obs/main.c Normal file
View File

@ -0,0 +1,34 @@
#include <obs/obs-module.h>
#include <stdio.h>
#ifdef _WIN32
#undef EXPORT
#define EXPORT __declspec(dllexport)
#endif
OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("looking-glass-obs", "en-US")
MODULE_EXPORT const char *obs_module_description(void)
{
return "Looking Glass Client";
}
extern struct obs_source_info lg_source;
MODULE_EXPORT bool obs_module_load(void)
{
obs_register_source(&lg_source);
return true;
}
#if defined(_WIN32) && defined(__GNUC__)
/* GCC requires a DLL entry point even without any standard library included. */
/* Types extracted from windows.h to avoid polluting the rest of the file. */
int __stdcall DallMainCRTStartup(void* instance, unsigned reason, void* reserved)
{
(void) instance;
(void) reason;
(void) reserved;
return 1;
}
#endif