Fix drawing bug with async video sources

Before, async video sources would flicker because they were only being
drawn when they were updated.  So when updated, they'd draw that frame,
then it would stop drawing it until it updated again.  This fixes that
issue and they should now draw properly.

Also, fix a few other minor bugs and issues relating to async video,
and make it so that non-async video filters can be properly applied to
them.

For the purposes of testing, change the 'test-random' source to an async
video source that updates every quarter of a second with a new random
face.

Also fix a bug where non-async video sources wouldn't have filter
effects applied properly.
This commit is contained in:
jp9000 2014-04-13 02:22:28 -07:00
parent bc33b09ba9
commit 2451b80ef6
5 changed files with 156 additions and 98 deletions

View File

@ -193,6 +193,7 @@ struct obs_source {
signal_handler_t signals;
proc_handler_t procs;
/* signals to call the source update in the video thread */
bool defer_update;
/* ensures show/hide are only called once */
@ -237,9 +238,14 @@ struct obs_source {
float transition_volume;
/* async video data */
texture_t output_texture;
texture_t async_texture;
enum video_format async_format;
float async_color_matrix[16];
bool async_flip;
DARRAY(struct source_frame*) video_frames;
pthread_mutex_t video_mutex;
uint32_t async_width;
uint32_t async_height;
/* filters */
struct obs_source *filter_parent;

View File

@ -128,8 +128,9 @@ void obs_register_source_s(const struct obs_source_info *info, size_t size)
CHECK_REQUIRED_VAL(info, create, obs_register_source);
CHECK_REQUIRED_VAL(info, destroy, obs_register_source);
if (info->type == OBS_SOURCE_TYPE_INPUT &&
info->output_flags & OBS_SOURCE_VIDEO) {
if (info->type == OBS_SOURCE_TYPE_INPUT &&
(info->output_flags & OBS_SOURCE_VIDEO) != 0 &&
(info->output_flags & OBS_SOURCE_ASYNC) == 0) {
CHECK_REQUIRED_VAL(info, getwidth, obs_register_source);
CHECK_REQUIRED_VAL(info, getheight, obs_register_source);
}

View File

@ -240,7 +240,7 @@ static void obs_source_destroy(struct obs_source *source)
source_frame_destroy(source->video_frames.array[i]);
gs_entercontext(obs->video.graphics);
texture_destroy(source->output_texture);
texture_destroy(source->async_texture);
gs_leavecontext();
if (source->data)
@ -529,7 +529,7 @@ static inline void handle_ts_jump(obs_source_t source, uint64_t expected,
source->name, diff, expected, ts);
/* if has video, ignore audio data until reset */
if (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO)
if (source->info.output_flags & OBS_SOURCE_ASYNC)
os_atomic_dec_long(&source->audio_reset_ref);
else
reset_audio_timing(source, ts);
@ -577,21 +577,25 @@ static void source_output_audio_line(obs_source_t source,
audio_line_output(source->audio_line, &in);
}
static bool set_texture_size(obs_source_t source, struct source_frame *frame)
static inline bool set_async_texture_size(struct obs_source *source,
struct source_frame *frame)
{
if (source->output_texture) {
uint32_t width = texture_getwidth(source->output_texture);
uint32_t height = texture_getheight(source->output_texture);
if (width == frame->width && height == frame->height)
if (source->async_texture) {
if (source->async_width == frame->width &&
source->async_height == frame->height)
return true;
}
texture_destroy(source->output_texture);
source->output_texture = gs_create_texture(frame->width, frame->height,
texture_destroy(source->async_texture);
source->async_texture = gs_create_texture(frame->width, frame->height,
GS_RGBA, 1, NULL, GS_DYNAMIC);
return source->output_texture != NULL;
if (!source->async_texture)
return false;
source->async_width = frame->width;
source->async_height = frame->height;
return true;
}
enum convert_type {
@ -626,11 +630,18 @@ static inline enum convert_type get_convert_type(enum video_format format)
return CONVERT_NONE;
}
static bool upload_frame(texture_t tex, const struct source_frame *frame)
static bool update_async_texture(struct obs_source *source,
const struct source_frame *frame)
{
void *ptr;
uint32_t linesize;
texture_t tex = source->async_texture;
enum convert_type type = get_convert_type(frame->format);
void *ptr;
uint32_t linesize;
source->async_format = frame->format;
source->async_flip = frame->flip;
memcpy(source->async_color_matrix, frame->color_matrix,
sizeof(frame->color_matrix));
if (type == CONVERT_NONE) {
texture_setimage(tex, frame->data[0], frame->linesize[0],
@ -663,44 +674,59 @@ static bool upload_frame(texture_t tex, const struct source_frame *frame)
return true;
}
static void obs_source_draw_texture(texture_t tex, struct source_frame *frame)
static inline void obs_source_draw_texture(struct obs_source *source,
effect_t effect, float *color_matrix)
{
effect_t effect = obs->video.default_effect;
bool yuv = format_is_yuv(frame->format);
const char *type = yuv ? "DrawMatrix" : "Draw";
technique_t tech;
eparam_t param;
texture_t tex = source->async_texture;
eparam_t param;
if (!upload_frame(tex, frame))
return;
tech = effect_gettechnique(effect, type);
technique_begin(tech);
technique_beginpass(tech, 0);
if (yuv) {
if (color_matrix) {
param = effect_getparambyname(effect, "color_matrix");
effect_setval(effect, param, frame->color_matrix,
sizeof(float) * 16);
effect_setval(effect, param, color_matrix, sizeof(float) * 16);
}
param = effect_getparambyname(effect, "image");
effect_settexture(effect, param, tex);
gs_draw_sprite(tex, frame->flip ? GS_FLIP_V : 0, 0, 0);
gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);
}
technique_endpass(tech);
technique_end(tech);
static void obs_source_draw_async_texture(struct obs_source *source)
{
effect_t effect = gs_geteffect();
bool yuv = format_is_yuv(source->async_format);
const char *type = yuv ? "DrawMatrix" : "Draw";
bool def_draw = (!effect);
technique_t tech;
if (def_draw) {
effect = obs_get_default_effect();
tech = effect_gettechnique(effect, type);
technique_begin(tech);
technique_beginpass(tech, 0);
}
obs_source_draw_texture(source, effect,
yuv ? source->async_color_matrix : NULL);
if (def_draw) {
technique_endpass(tech);
technique_end(tech);
}
}
static void obs_source_render_async_video(obs_source_t source)
{
struct source_frame *frame = obs_source_getframe(source);
if (!frame)
return;
if (frame) {
if (!set_async_texture_size(source, frame))
return;
if (!update_async_texture(source, frame))
return;
}
if (set_texture_size(source, frame))
obs_source_draw_texture(source->output_texture, frame);
if (source->async_texture)
obs_source_draw_async_texture(source);
obs_source_releaseframe(source, frame);
}
@ -731,48 +757,53 @@ static inline void obs_source_default_render(obs_source_t source,
static inline void obs_source_main_render(obs_source_t source)
{
uint32_t flags = source->info.output_flags;
bool color_matrix = (flags & OBS_SOURCE_COLOR_MATRIX) != 0;
uint32_t flags = source->info.output_flags;
bool color_matrix = (flags & OBS_SOURCE_COLOR_MATRIX) != 0;
bool custom_draw = (flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
bool default_effect = !source->filter_parent &&
source->filters.num == 0 &&
(flags & OBS_SOURCE_CUSTOM_DRAW) == 0;
!custom_draw;
if (default_effect)
obs_source_default_render(source, color_matrix);
else
source->info.video_render(source->data, NULL);
source->info.video_render(source->data,
custom_draw ? NULL : gs_geteffect());
}
void obs_source_video_render(obs_source_t source)
{
if (!source) return;
if (source->info.video_render) {
if (source->filters.num && !source->rendering_filter)
obs_source_render_filters(source);
else
obs_source_main_render(source);
if (source->filters.num && !source->rendering_filter)
obs_source_render_filters(source);
} else if (source->filter_target) {
else if (source->info.video_render)
obs_source_main_render(source);
else if (source->filter_target)
obs_source_video_render(source->filter_target);
} else {
else
obs_source_render_async_video(source);
}
}
uint32_t obs_source_getwidth(obs_source_t source)
{
if (source && source->info.getwidth)
if (!source) return 0;
if (source->info.getwidth)
return source->info.getwidth(source->data);
return 0;
return source->async_width;
}
uint32_t obs_source_getheight(obs_source_t source)
{
if (source && source->info.getheight)
if (!source) return 0;
if (source->info.getheight)
return source->info.getheight(source->data);
return 0;
return source->async_height;
}
obs_source_t obs_filter_getparent(obs_source_t filter)
@ -1102,7 +1133,7 @@ void obs_source_output_audio(obs_source_t source,
output = filter_async_audio(source, &source->audio_data);
if (output) {
bool async = (flags & OBS_SOURCE_ASYNC_VIDEO) != 0;
bool async = (flags & OBS_SOURCE_ASYNC) != 0;
pthread_mutex_lock(&source->audio_mutex);

View File

@ -23,6 +23,7 @@
extern "C" {
#endif
enum obs_source_type {
OBS_SOURCE_TYPE_INPUT,
OBS_SOURCE_TYPE_FILTER,
@ -31,6 +32,7 @@ enum obs_source_type {
OBS_SOURCE_TYPE_SCENE = 0x80000000
};
/**
* @name Source output flags
*
@ -55,6 +57,9 @@ enum obs_source_type {
*/
#define OBS_SOURCE_AUDIO (1<<1)
/** Async video flag (use OBS_SOURCE_ASYNC_VIDEO) */
#define OBS_SOURCE_ASYNC (1<<2)
/**
* Source passes raw video data via RAM.
*
@ -66,7 +71,7 @@ enum obs_source_type {
* obs_source_getframe to get the current frame data, and
* obs_source_releaseframe to release the data when complete.
*/
#define OBS_SOURCE_ASYNC_VIDEO ((1<<2) | OBS_SOURCE_VIDEO)
#define OBS_SOURCE_ASYNC_VIDEO (OBS_SOURCE_ASYNC | OBS_SOURCE_VIDEO)
/**
* Source uses custom drawing, rather than a default effect.
@ -132,10 +137,12 @@ struct obs_source_info {
/** Destroys the private data for the source */
void (*destroy)(void *data);
/** Returns the width of the source. Required if input and video */
/** Returns the width of the source. Required if this is an input
* source and has non-async video */
uint32_t (*getwidth)(void *data);
/** Returns the height of the source. Required if input and video */
/** Returns the height of the source. Required if this is an input
* source and has non-async video */
uint32_t (*getheight)(void *data);
/* ----------------------------------------------------------------- */

View File

@ -1,8 +1,13 @@
#include <stdlib.h>
#include <util/threading.h>
#include <util/platform.h>
#include <obs.h>
struct random_tex {
texture_t texture;
obs_source_t source;
os_event_t stop_signal;
pthread_t thread;
bool initialized;
};
static const char *random_getname(const char *locale)
@ -16,19 +21,18 @@ static void random_destroy(void *data)
struct random_tex *rt = data;
if (rt) {
gs_entercontext(obs_graphics());
if (rt->initialized) {
os_event_signal(rt->stop_signal);
pthread_join(rt->thread, NULL);
}
texture_destroy(rt->texture);
os_event_destroy(rt->stop_signal);
bfree(rt);
gs_leavecontext();
}
}
static void *random_create(obs_data_t settings, obs_source_t source)
static inline void fill_texture(uint32_t *pixels)
{
struct random_tex *rt = bzalloc(sizeof(struct random_tex));
uint32_t *pixels = bmalloc(20*20*4);
size_t x, y;
for (y = 0; y < 20; y++) {
@ -41,53 +45,62 @@ static void *random_create(obs_data_t settings, obs_source_t source)
pixels[y*20 + x] = pixel;
}
}
}
gs_entercontext(obs_graphics());
static void *video_thread(void *data)
{
struct random_tex *rt = data;
uint32_t pixels[20*20];
uint64_t cur_time = os_gettime_ns();
rt->texture = gs_create_texture(20, 20, GS_RGBA, 1,
(const void**)&pixels, 0);
bfree(pixels);
struct source_frame frame = {
.data = {[0] = (uint8_t*)pixels},
.linesize = {[0] = 20*4},
.width = 20,
.height = 20,
.format = VIDEO_FORMAT_BGRX
};
if (!rt->texture) {
while (os_event_try(rt->stop_signal) == EAGAIN) {
fill_texture(pixels);
frame.timestamp = cur_time;
obs_source_output_video(rt->source, &frame);
os_sleepto_ns(cur_time += 250000000);
}
return NULL;
}
static void *random_create(obs_data_t settings, obs_source_t source)
{
struct random_tex *rt = bzalloc(sizeof(struct random_tex));
rt->source = source;
if (os_event_init(&rt->stop_signal, OS_EVENT_TYPE_MANUAL) != 0) {
random_destroy(rt);
return NULL;
}
gs_leavecontext();
if (pthread_create(&rt->thread, NULL, video_thread, rt) != 0) {
random_destroy(rt);
return NULL;
}
rt->initialized = true;
UNUSED_PARAMETER(settings);
UNUSED_PARAMETER(source);
return rt;
}
static void random_video_render(void *data, effect_t effect)
{
struct random_tex *rt = data;
eparam_t image = effect_getparambyname(effect, "image");
effect_settexture(effect, image, rt->texture);
gs_draw_sprite(rt->texture, 0, 0, 0);
}
static uint32_t random_getwidth(void *data)
{
struct random_tex *rt = data;
return texture_getwidth(rt->texture);
}
static uint32_t random_getheight(void *data)
{
struct random_tex *rt = data;
return texture_getheight(rt->texture);
}
struct obs_source_info test_random = {
.id = "random",
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_VIDEO,
.output_flags = OBS_SOURCE_ASYNC_VIDEO,
.getname = random_getname,
.create = random_create,
.destroy = random_destroy,
.video_render = random_video_render,
.getwidth = random_getwidth,
.getheight = random_getheight
};