[obs] use thread to handle frame advance when obs is behind

This commit is contained in:
Geoffrey McRae 2020-04-24 21:30:28 +10:00
parent fdb9a9cca8
commit 00a41be413
2 changed files with 101 additions and 26 deletions

View file

@ -1 +1 @@
B1-181-g243efcd51a+1 B1-182-g041a6c3cf6+1

125
obs/lg.c
View file

@ -1,4 +1,5 @@
#include <obs/obs-module.h> #include <obs/obs-module.h>
#include <obs/util/threading.h>
#include <common/ivshmem.h> #include <common/ivshmem.h>
#include <common/KVMFR.h> #include <common/KVMFR.h>
@ -6,11 +7,22 @@
#include <lgmp/client.h> #include <lgmp/client.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
typedef enum
{
STATE_STOPPED,
STATE_OPEN,
STATE_STARTING,
STATE_RUNNING,
STATE_STOPPING
}
LGState;
typedef struct typedef struct
{ {
obs_source_t * context; obs_source_t * context;
bool valid; LGState state;
char * shmFile; char * shmFile;
uint32_t width, height; uint32_t width, height;
FrameType type; FrameType type;
@ -18,6 +30,9 @@ typedef struct
PLGMPClient lgmp; PLGMPClient lgmp;
PLGMPClientQueue frameQueue; PLGMPClientQueue frameQueue;
gs_texture_t * texture; gs_texture_t * texture;
pthread_t frameThread;
os_sem_t * frameSem;
} }
LGPlugin; LGPlugin;
@ -32,13 +47,37 @@ static void * lgCreate(obs_data_t * settings, obs_source_t * context)
{ {
LGPlugin * this = bzalloc(sizeof(LGPlugin)); LGPlugin * this = bzalloc(sizeof(LGPlugin));
this->context = context; this->context = context;
os_sem_init(&this->frameSem, 0);
lgUpdate(this, settings); lgUpdate(this, settings);
return this; return this;
} }
static void deinit(LGPlugin * this) static void deinit(LGPlugin * this)
{ {
lgmpClientFree(&this->lgmp); switch(this->state)
{
case STATE_STARTING:
/* wait for startup to finish */
while(this->state == STATE_STARTING)
usleep(1);
/* fallthrough */
case STATE_RUNNING:
case STATE_STOPPING:
this->state = STATE_STOPPING;
pthread_join(this->frameThread, NULL);
this->state = STATE_STOPPED;
/* fallthrough */
case STATE_OPEN:
lgmpClientFree(&this->lgmp);
ivshmemClose(&this->shmDev);
break;
case STATE_STOPPED:
break;
}
if (this->shmFile) if (this->shmFile)
{ {
@ -46,16 +85,22 @@ static void deinit(LGPlugin * this)
this->shmFile = NULL; this->shmFile = NULL;
} }
if (this->shmDev.mem) if (this->texture)
ivshmemClose(&this->shmDev); {
obs_enter_graphics();
gs_texture_destroy(this->texture);
this->texture = NULL;
obs_leave_graphics();
}
this->valid = false; this->state = STATE_STOPPED;
} }
static void lgDestroy(void * data) static void lgDestroy(void * data)
{ {
LGPlugin * this = (LGPlugin *)data; LGPlugin * this = (LGPlugin *)data;
deinit(this); deinit(this);
os_sem_destroy(this->frameSem);
bfree(this); bfree(this);
} }
@ -73,6 +118,43 @@ static obs_properties_t * lgGetProperties(void * data)
return props; return props;
} }
static void * frameThread(void * data)
{
LGPlugin * this = (LGPlugin *)data;
if (lgmpClientSubscribe(this->lgmp, LGMP_Q_FRAME, &this->frameQueue) != LGMP_OK)
{
this->state = STATE_STOPPING;
return NULL;
}
this->state = STATE_RUNNING;
os_sem_post(this->frameSem);
while(this->state == STATE_RUNNING)
{
LGMP_STATUS status;
os_sem_wait(this->frameSem);
if ((status = lgmpClientAdvanceToLast(this->frameQueue)) != LGMP_OK)
{
if (status != LGMP_ERR_QUEUE_EMPTY)
{
printf("lgmpClientAdvanceToLast: %s\n", lgmpStatusString(status));
os_sem_post(this->frameSem);
break;
}
}
os_sem_post(this->frameSem);
usleep(100);
}
printf("unsubscribe\n");
lgmpClientUnsubscribe(&this->frameQueue);
this->state = STATE_STOPPING;
return NULL;
}
static void lgUpdate(void * data, obs_data_t * settings) static void lgUpdate(void * data, obs_data_t * settings)
{ {
LGPlugin * this = (LGPlugin *)data; LGPlugin * this = (LGPlugin *)data;
@ -82,42 +164,42 @@ static void lgUpdate(void * data, obs_data_t * settings)
if (!ivshmemOpenDev(&this->shmDev, this->shmFile)) if (!ivshmemOpenDev(&this->shmDev, this->shmFile))
return; return;
this->state = STATE_OPEN;
if (lgmpClientInit(this->shmDev.mem, this->shmDev.size, &this->lgmp) != LGMP_OK) if (lgmpClientInit(this->shmDev.mem, this->shmDev.size, &this->lgmp) != LGMP_OK)
return; return;
if (lgmpClientSubscribe(this->lgmp, LGMP_Q_FRAME, &this->frameQueue) != LGMP_OK) this->state = STATE_STARTING;
return; pthread_create(&this->frameThread, NULL, frameThread, this);
this->valid = true;
} }
static void lgVideoTick(void * data, float seconds) static void lgVideoTick(void * data, float seconds)
{ {
LGPlugin * this = (LGPlugin *)data; LGPlugin * this = (LGPlugin *)data;
if (!this->valid) if (this->state != STATE_RUNNING)
return; return;
LGMP_STATUS status; LGMP_STATUS status;
LGMPMessage msg; LGMPMessage msg;
if ((status = lgmpClientAdvanceToLast(this->frameQueue)) != LGMP_OK) os_sem_wait(this->frameSem);
if (this->state != STATE_RUNNING)
{ {
if (status == LGMP_ERR_QUEUE_EMPTY) os_sem_post(this->frameSem);
return;
printf("lgmpClientAdvanceToLast: %s\n", lgmpStatusString(status));
this->valid = false;
return; return;
} }
if ((status = lgmpClientProcess(this->frameQueue, &msg)) != LGMP_OK) if ((status = lgmpClientProcess(this->frameQueue, &msg)) != LGMP_OK)
{ {
if (status == LGMP_ERR_QUEUE_EMPTY) if (status == LGMP_ERR_QUEUE_EMPTY)
{
os_sem_post(this->frameSem);
return; return;
}
printf("lgmpClientProcess: %s\n", lgmpStatusString(status)); printf("lgmpClientProcess: %s\n", lgmpStatusString(status));
this->valid = false; this->state = STATE_STOPPING;
return; return;
} }
@ -147,7 +229,6 @@ static void lgVideoTick(void * data, float seconds)
case FRAME_TYPE_RGBA10: format = GS_R10G10B10A2; break; case FRAME_TYPE_RGBA10: format = GS_R10G10B10A2; break;
default: default:
printf("invalid type %d\n", this->type); printf("invalid type %d\n", this->type);
this->valid = false;
obs_leave_graphics(); obs_leave_graphics();
return; return;
} }
@ -158,7 +239,6 @@ static void lgVideoTick(void * data, float seconds)
if (!this->texture) if (!this->texture)
{ {
printf("create texture failed\n"); printf("create texture failed\n");
this->valid = false;
obs_leave_graphics(); obs_leave_graphics();
return; return;
} }
@ -183,6 +263,7 @@ static void lgVideoTick(void * data, float seconds)
// gs_texture_set_image(this->texture, frameData, frame->pitch, false); // gs_texture_set_image(this->texture, frameData, frame->pitch, false);
lgmpClientMessageDone(this->frameQueue); lgmpClientMessageDone(this->frameQueue);
os_sem_post(this->frameSem);
obs_leave_graphics(); obs_leave_graphics();
} }
@ -205,18 +286,12 @@ static void lgVideoRender(void * data, gs_effect_t * effect)
static uint32_t lgGetWidth(void * data) static uint32_t lgGetWidth(void * data)
{ {
LGPlugin * this = (LGPlugin *)data; LGPlugin * this = (LGPlugin *)data;
if (!this->valid)
return 0;
return this->width; return this->width;
} }
static uint32_t lgGetHeight(void * data) static uint32_t lgGetHeight(void * data)
{ {
LGPlugin * this = (LGPlugin *)data; LGPlugin * this = (LGPlugin *)data;
if (!this->valid)
return 0;
return this->height; return this->height;
} }