cleaned up main internal data structure design, changed to reference counting for sources to ensure safe destruction of source objects from all parts of the system, added some service-related stuff for testing

This commit is contained in:
jp9000
2013-11-20 15:00:16 -07:00
parent 5e33707f6a
commit 409b011a8e
18 changed files with 3155 additions and 327 deletions

View File

@@ -405,18 +405,23 @@ EXPORT texture_t texrender_gettexture(texrender_t texrender);
/* ---------------- */
/* global functions */
#define GS_SUCCESS 0
#define GS_SUCCESS 0
#define GS_ERROR_MODULENOTFOUND -1
#define GS_ERROR_FAIL -2
struct gs_init_data {
struct gs_window {
#if defined(_WIN32)
void *hwnd;
#elif defined(__APPLE__)
__unsafe_unretained id view;
#elif defined(__posix__)
int bla;
/* TODO */
#endif
};
struct gs_init_data {
struct gs_window window;
uint32_t cx, cy;
uint32_t num_backbuffers;
enum gs_color_format format;

View File

@@ -33,52 +33,64 @@
/*#include "obs-service.h"*/
#define NUM_TEXTURES 2
#define MAX_CHANNELS 32
struct obs_display {
swapchain_t swap; /* can be NULL if just sound */
obs_source_t source;
swapchain_t swap; /* can be NULL if just sound */
obs_source_t channels[MAX_CHANNELS];
/* TODO: sound output target */
};
struct obs_data {
DARRAY(struct obs_module) modules;
/* ------------------------------------------------------------------------- */
DARRAY(struct source_info) input_types;
DARRAY(struct source_info) filter_types;
DARRAY(struct source_info) transition_types;
DARRAY(struct output_info) output_types;
/*DARRAY(struct service_info) service_types;*/
struct obs_video {
graphics_t graphics;
stagesurf_t copy_surfaces[NUM_TEXTURES];
effect_t default_effect;
bool textures_copied[NUM_TEXTURES];
bool copy_mapped;
int cur_texture;
DARRAY(struct obs_display*) displays;
DARRAY(struct obs_source*) sources;
video_t video;
pthread_t video_thread;
bool thread_initialized;
/* graphics */
graphics_t graphics;
stagesurf_t copy_surfaces[NUM_TEXTURES];
effect_t default_effect;
bool textures_copied[NUM_TEXTURES];
bool copy_mapped;
int cur_texture;
/* TODO: sound output stuff */
/* media */
media_t media;
video_t video;
audio_t audio;
uint32_t output_width;
uint32_t output_height;
/* threading */
pthread_t video_thread;
bool thread_initialized;
pthread_mutex_t source_list_mutex;
pthread_mutex_t display_list_mutex;
obs_source_t primary_source;
uint32_t output_width;
uint32_t output_height;
};
extern struct obs_data *obs;
struct obs_audio {
/* TODO: audio subsystem */
audio_t audio;
};
struct obs_data {
DARRAY(struct obs_display*) displays;
DARRAY(struct obs_source*) sources;
obs_source_t channels[MAX_CHANNELS];
pthread_mutex_t sources_mutex;
pthread_mutex_t displays_mutex;
};
struct obs_subsystem {
DARRAY(struct obs_module) modules;
DARRAY(struct source_info) input_types;
DARRAY(struct source_info) filter_types;
DARRAY(struct source_info) transition_types;
DARRAY(struct output_info) output_types;
DARRAY(struct service_info) service_types;
media_t media;
/* segmented into multiple sub-structures to keep things a bit more
* clean and organized */
struct obs_video video;
struct obs_audio audio;
struct obs_data data;
};
extern struct obs_subsystem *obs;
extern void *obs_video_thread(void *param);

View File

@@ -37,17 +37,37 @@ obs_display_t obs_display_create(struct gs_init_data *graphics_data)
void obs_display_destroy(obs_display_t display)
{
if (display) {
size_t i;
pthread_mutex_lock(&obs->data.displays_mutex);
da_erase_item(obs->data.displays, &display);
pthread_mutex_unlock(&obs->data.displays_mutex);
for (i = 0; i < MAX_CHANNELS; i++)
obs_source_release(display->channels[i]);
swapchain_destroy(display->swap);
bfree(display);
}
}
obs_source_t obs_display_getsource(obs_display_t display)
obs_source_t obs_display_getsource(obs_display_t display, uint32_t channel)
{
return display->source;
assert(channel < MAX_CHANNELS);
return display->channels[channel];
}
void obs_display_setsource(obs_display_t display, obs_source_t source)
void obs_display_setsource(obs_display_t display, uint32_t channel,
obs_source_t source)
{
display->source = source;
struct obs_source *prev_source;
assert(channel < MAX_CHANNELS);
prev_source = display->channels[channel];
display->channels[channel] = source;
if (source)
obs_source_addref(source);
if (prev_source)
obs_source_release(prev_source);
}

View File

@@ -38,8 +38,12 @@ static void scene_destroy(void *data)
struct obs_scene *scene = data;
size_t i;
for (i = 0; i < scene->items.num; i++)
bfree(scene->items.array[i]);
for (i = 0; i < scene->items.num; i++) {
struct obs_scene_item *item = scene->items.array[i];
if (item->source)
obs_source_release(item->source);
bfree(item);
}
da_free(scene->items);
bfree(scene);
@@ -58,6 +62,12 @@ static void scene_video_render(void *data)
for (i = scene->items.num; i > 0; i--) {
struct obs_scene_item *item = scene->items.array[i-1];
if (obs_source_removed(item->source)) {
obs_source_release(item->source);
da_erase(scene->items, i--);
continue;
}
gs_matrix_push();
gs_matrix_translate3f(item->origin.x, item->origin.y, 0.0f);
gs_matrix_scale3f(item->scale.x, item->scale.y, 1.0f);
@@ -136,7 +146,7 @@ obs_scene_t obs_scene_create(void)
void obs_scene_destroy(obs_scene_t scene)
{
if (scene)
obs_source_destroy(scene->source);
obs_source_release(scene->source);
}
obs_source_t obs_scene_getsource(obs_scene_t scene)
@@ -153,6 +163,9 @@ obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source)
item->parent = scene;
vec2_set(&item->scale, 1.0f, 1.0f);
if (source)
obs_source_addref(source);
da_push_back(scene->items, &item);
return item;
}
@@ -160,6 +173,9 @@ obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source)
void obs_sceneitem_destroy(obs_sceneitem_t item)
{
if (item) {
if (item->source)
obs_source_release(item->source);
da_erase_item(item->parent->items, item);
bfree(item);
}

View File

@@ -20,10 +20,15 @@
struct service_data;
struct service_info {
const char *(*getname)(const char *locale);
void *(*create)(const char *settings, struct service_data *service);
void (*destroy)(void *data);
void (*config)(void *data, const char *settings);
/* optional */
const char *(*getdata)(const char *attribute);
/* get stream url/key */
/* get (viewers/etc) */
/* send (current game/title/activate commercial/etc) */

View File

@@ -21,6 +21,8 @@
#include "obs.h"
#include "obs-data.h"
static void obs_source_destroy(obs_source_t source);
bool get_source_info(void *module, const char *module_name,
const char *source_name, struct source_info *info)
{
@@ -88,6 +90,7 @@ bool obs_source_init(struct obs_source *source, const char *settings,
{
uint32_t flags = info->get_output_flags(source->data);
source->refs = 1;
pthread_mutex_init_value(&source->filter_mutex);
pthread_mutex_init_value(&source->video_mutex);
pthread_mutex_init_value(&source->audio_mutex);
@@ -102,7 +105,7 @@ bool obs_source_init(struct obs_source *source, const char *settings,
return false;
if (flags & SOURCE_AUDIO) {
source->audio_line = audio_output_createline(obs->audio);
source->audio_line = audio_output_createline(obs->audio.audio);
if (!source->audio_line) {
blog(LOG_ERROR, "Failed to create audio line for "
"source");
@@ -110,11 +113,6 @@ bool obs_source_init(struct obs_source *source, const char *settings,
}
}
source->valid = true;
pthread_mutex_lock(&obs->source_list_mutex);
da_push_back(obs->sources, &source);
pthread_mutex_unlock(&obs->source_list_mutex);
return true;
}
@@ -158,47 +156,64 @@ fail:
return NULL;
}
void obs_source_destroy(obs_source_t source)
static void obs_source_destroy(obs_source_t source)
{
if (source) {
size_t i;
if (source->filter_parent)
obs_source_filter_remove(source->filter_parent, source);
size_t i;
for (i = 0; i < source->filters.num; i++)
obs_source_destroy(source->filters.array[i]);
if (source->filter_parent)
obs_source_filter_remove(source->filter_parent, source);
if (source->valid) {
pthread_mutex_lock(&obs->source_list_mutex);
da_erase_item(obs->sources, &source);
pthread_mutex_unlock(&obs->source_list_mutex);
}
for (i = 0; i < source->filters.num; i++)
obs_source_release(source->filters.array[i]);
for (i = 0; i < source->audio_wait_buffer.num; i++)
audiobuf_free(source->audio_wait_buffer.array+i);
for (i = 0; i < source->video_frames.num; i++)
source_frame_destroy(source->video_frames.array[i]);
for (i = 0; i < source->audio_wait_buffer.num; i++)
audiobuf_free(source->audio_wait_buffer.array+i);
for (i = 0; i < source->video_frames.num; i++)
source_frame_destroy(source->video_frames.array[i]);
gs_entercontext(obs->graphics);
texture_destroy(source->output_texture);
gs_leavecontext();
gs_entercontext(obs->video.graphics);
texture_destroy(source->output_texture);
gs_leavecontext();
if (source->data)
source->callbacks.destroy(source->data);
if (source->data)
source->callbacks.destroy(source->data);
bfree(source->audio_data.data);
audio_line_destroy(source->audio_line);
audio_resampler_destroy(source->resampler);
bfree(source->audio_data.data);
audio_line_destroy(source->audio_line);
audio_resampler_destroy(source->resampler);
da_free(source->video_frames);
da_free(source->audio_wait_buffer);
da_free(source->filters);
pthread_mutex_destroy(&source->filter_mutex);
pthread_mutex_destroy(&source->audio_mutex);
pthread_mutex_destroy(&source->video_mutex);
dstr_free(&source->settings);
bfree(source);
}
da_free(source->video_frames);
da_free(source->audio_wait_buffer);
da_free(source->filters);
pthread_mutex_destroy(&source->filter_mutex);
pthread_mutex_destroy(&source->audio_mutex);
pthread_mutex_destroy(&source->video_mutex);
dstr_free(&source->settings);
bfree(source);
}
void obs_source_addref(obs_source_t source)
{
assert(source != NULL);
if (!source)
return;
++source->refs;
}
void obs_source_release(obs_source_t source)
{
assert(source != NULL);
if (!source)
return;
if (--source->refs == 0)
obs_source_destroy(source);
}
bool obs_source_removed(obs_source_t source)
{
return source->removed;
}
uint32_t obs_source_get_output_flags(obs_source_t source)
@@ -394,7 +409,7 @@ static bool upload_frame(texture_t tex, const struct source_frame *frame)
static void obs_source_draw_texture(texture_t tex, struct source_frame *frame)
{
effect_t effect = obs->default_effect;
effect_t effect = obs->video.default_effect;
bool yuv = is_yuv(frame->format);
const char *type = yuv ? "DrawYUVToRGB" : "DrawRGB";
technique_t tech;
@@ -655,9 +670,11 @@ static inline struct filtered_audio *filter_async_audio(obs_source_t source,
static inline void reset_resampler(obs_source_t source,
const struct source_audio *audio)
{
const struct audio_info *obs_info = audio_output_getinfo(obs->audio);
const struct audio_info *obs_info;
struct resample_info output_info;
obs_info = audio_output_getinfo(obs->audio.audio);
output_info.format = obs_info->format;
output_info.samples_per_sec = obs_info->samples_per_sec;
output_info.speakers = obs_info->speakers;
@@ -685,7 +702,7 @@ static inline void reset_resampler(obs_source_t source,
static inline void copy_audio_data(obs_source_t source,
const void *data, uint32_t frames, uint64_t timestamp)
{
size_t blocksize = audio_output_blocksize(obs->audio);
size_t blocksize = audio_output_blocksize(obs->audio.audio);
size_t size = (size_t)frames * blocksize;
/* ensure audio storage capacity */
@@ -731,7 +748,7 @@ void obs_source_output_audio(obs_source_t source,
const struct source_audio *audio)
{
uint32_t flags = obs_source_get_output_flags(source);
size_t blocksize = audio_output_blocksize(obs->audio);
size_t blocksize = audio_output_blocksize(obs->audio.audio);
struct filtered_audio *output;
process_audio(source, audio);

View File

@@ -211,10 +211,17 @@ static inline void audiobuf_free(struct audiobuf *buf)
}
struct obs_source {
void *data; /* source-specific data */
volatile int refs;
/* source-specific data */
void *data;
struct source_info callbacks;
struct dstr settings;
bool valid;
/* used to indicate that the source has been removed and all
* references to it should be released (not exactly how I would prefer
* to handle things but it's the best option) */
bool removed;
/* async video and audio */
bool timing_set;

View File

@@ -26,12 +26,12 @@ static void tick_sources(uint64_t cur_time, uint64_t *last_time)
float seconds;
if (!last_time)
*last_time = cur_time - video_getframetime(obs->video);
*last_time = cur_time - video_getframetime(obs->video.video);
delta_time = cur_time - *last_time;
seconds = (float)((double)delta_time / 1000000000.0);
for (i = 0; i < obs->sources.num; i++)
obs_source_video_tick(obs->sources.array[i], seconds);
for (i = 0; i < obs->data.sources.num; i++)
obs_source_video_tick(obs->data.sources.array[i], seconds);
*last_time = cur_time;
}
@@ -40,6 +40,7 @@ static inline void render_display(struct obs_display *display)
{
struct vec4 clear_color;
uint32_t width, height;
size_t i;
gs_load_swapchain(display ? display->swap : NULL);
@@ -51,14 +52,27 @@ static inline void render_display(struct obs_display *display)
&clear_color, 1.0f, 0);
gs_enable_depthtest(false);
gs_enable_blending(false);
/* gs_enable_blending(false); */
gs_setcullmode(GS_NEITHER);
gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f);
gs_setviewport(0, 0, width, height);
if (obs->primary_source)
obs_source_video_render(obs->primary_source);
for (i = 0; i < MAX_CHANNELS; i++) {
struct obs_source **p_source;
p_source = (display) ? display->channels+i :
obs->data.channels+i;
if (*p_source) {
if ((*p_source)->removed) {
obs_source_release(*p_source);
*p_source = NULL;
} else {
obs_source_video_render(*p_source);
}
}
}
gs_endscene();
gs_present();
@@ -68,56 +82,58 @@ static inline void render_displays(void)
{
size_t i;
pthread_mutex_lock(&obs->display_list_mutex);
/* render extra displays/swaps */
pthread_mutex_lock(&obs->data.displays_mutex);
for (i = 0; i < obs->displays.num; i++) {
struct obs_display *display = obs->displays.array[i];
for (i = 0; i < obs->data.displays.num; i++)
render_display(obs->data.displays.array[i]);
render_display(display);
}
pthread_mutex_unlock(&obs->display_list_mutex);
pthread_mutex_unlock(&obs->data.displays_mutex);
/* render main display */
render_display(NULL);
}
static bool swap_frame(uint64_t timestamp)
{
stagesurf_t last_surface = obs->copy_surfaces[obs->cur_texture];
struct obs_video *video = &obs->video;
stagesurf_t last_surface = video->copy_surfaces[video->cur_texture];
stagesurf_t surface;
struct video_frame frame;
if (obs->copy_mapped) {
/* the last frame stays mapped until rendering starts with the next */
if (video->copy_mapped) {
stagesurface_unmap(last_surface);
obs->copy_mapped = false;
video->copy_mapped = false;
}
obs->textures_copied[obs->cur_texture] = true;
//gs_stage_texture(last_surface, NULL);
video->textures_copied[video->cur_texture] = true;
/* TODO: texture staging */
//gs_stage_texture(last_surface, );
if (++obs->cur_texture == NUM_TEXTURES)
obs->cur_texture = 0;
if (++video->cur_texture == NUM_TEXTURES)
video->cur_texture = 0;
if (obs->textures_copied[obs->cur_texture]) {
surface = obs->copy_surfaces[obs->cur_texture];
obs->copy_mapped = stagesurface_map(surface, &frame.data,
if (video->textures_copied[video->cur_texture]) {
surface = video->copy_surfaces[video->cur_texture];
video->copy_mapped = stagesurface_map(surface, &frame.data,
&frame.row_size);
if (obs->copy_mapped) {
if (video->copy_mapped) {
frame.timestamp = timestamp;
video_output_frame(obs->video, &frame);
video_output_frame(video->video, &frame);
}
}
return obs->copy_mapped;
return video->copy_mapped;
}
void *obs_video_thread(void *param)
{
uint64_t last_time = 0;
while (video_output_wait(obs->video)) {
uint64_t cur_time = video_gettime(obs->video);
while (video_output_wait(obs->video.video)) {
uint64_t cur_time = video_gettime(obs->video.video);
gs_entercontext(obs_graphics());

View File

@@ -19,40 +19,71 @@
#include "obs-data.h"
#include "obs-module.h"
struct obs_data *obs = NULL;
struct obs_subsystem *obs = NULL;
extern char *find_libobs_data_file(const char *file);
static bool obs_init_graphics(const char *graphics_module,
struct gs_init_data *graphics_data, struct video_info *vi)
static inline void make_gs_init_data(struct gs_init_data *gid,
struct obs_video_info *ovi)
{
int errorcode;
memcpy(&gid->window, &ovi->window, sizeof(struct gs_window));
gid->cx = ovi->base_width;
gid->cy = ovi->base_height;
gid->num_backbuffers = 2;
gid->format = GS_RGBA;
gid->zsformat = GS_ZS_NONE;
gid->adapter = ovi->adapter;
}
static inline void make_video_info(struct video_info *vi,
struct obs_video_info *ovi)
{
vi->name = "video";
vi->type = ovi->output_format;
vi->fps_num = ovi->fps_num;
vi->fps_den = ovi->fps_den;
vi->width = ovi->output_width;
vi->height = ovi->output_height;
}
static bool obs_init_graphics(struct obs_video_info *ovi)
{
struct obs_video *video = &obs->video;
struct gs_init_data graphics_data;
bool success = true;
int errorcode;
size_t i;
errorcode = gs_create(&obs->graphics, graphics_module, graphics_data);
make_gs_init_data(&graphics_data, ovi);
video->output_width = ovi->output_width;
video->output_height = ovi->output_height;
errorcode = gs_create(&video->graphics, ovi->graphics_module,
&graphics_data);
if (errorcode != GS_SUCCESS) {
if (errorcode == GS_ERROR_MODULENOTFOUND)
blog(LOG_ERROR, "Could not find graphics module '%s'",
graphics_module);
ovi->graphics_module);
return false;
}
gs_entercontext(obs->graphics);
gs_entercontext(video->graphics);
for (i = 0; i < NUM_TEXTURES; i++) {
obs->copy_surfaces[i] = gs_create_stagesurface(vi->width,
vi->height, graphics_data->format);
if (!obs->copy_surfaces[i])
video->copy_surfaces[i] = gs_create_stagesurface(
ovi->output_width, ovi->output_height,
graphics_data.format);
if (!video->copy_surfaces[i])
success = false;
}
if (success) {
char *filename = find_libobs_data_file("default.effect");
obs->default_effect = gs_create_effect_from_file( filename,
video->default_effect = gs_create_effect_from_file(filename,
NULL);
bfree(filename);
if (!obs->default_effect)
if (!video->default_effect)
success = false;
}
@@ -60,114 +91,179 @@ static bool obs_init_graphics(const char *graphics_module,
return success;
}
static bool obs_init_media(struct video_info *vi, struct audio_info *ai)
static bool obs_init_video(struct obs_video_info *ovi)
{
struct obs_video *video = &obs->video;
struct video_info vi;
int errorcode;
memset(video, 0, sizeof(struct obs_video));
if (!obs_init_graphics(ovi))
return false;
make_video_info(&vi, ovi);
errorcode = video_output_open(&video->video, obs->media, &vi);
if (errorcode != VIDEO_OUTPUT_SUCCESS) {
if (errorcode == VIDEO_OUTPUT_INVALIDPARAM)
blog(LOG_ERROR, "Invalid video parameters specified");
else
blog(LOG_ERROR, "Could not open video output");
return false;
}
errorcode = pthread_create(&video->video_thread, NULL,
obs_video_thread, obs);
if (errorcode != 0)
return false;
video->thread_initialized = true;
return true;
}
static void obs_free_video(void)
{
struct obs_video *video = &obs->video;
size_t i;
if (video->video) {
void *thread_retval;
video_output_stop(video->video);
if (video->thread_initialized)
pthread_join(video->video_thread, &thread_retval);
video_output_close(video->video);
}
if (video->graphics) {
int cur_texture = video->cur_texture;
gs_entercontext(video->graphics);
if (video->copy_mapped)
stagesurface_unmap(video->copy_surfaces[cur_texture]);
for (i = 0; i < NUM_TEXTURES; i++)
stagesurface_destroy(video->copy_surfaces[i]);
effect_destroy(video->default_effect);
gs_leavecontext();
gs_destroy(video->graphics);
}
memset(video, 0, sizeof(struct obs_video));
}
static bool obs_init_audio(struct audio_info *ai)
{
struct obs_audio *audio = &obs->audio;
int errorcode;
/* TODO: sound subsystem */
errorcode = audio_output_open(&audio->audio, obs->media, ai);
if (errorcode == AUDIO_OUTPUT_SUCCESS)
return true;
else if (errorcode= AUDIO_OUTPUT_INVALIDPARAM)
blog(LOG_ERROR, "Invalid audio parameters specified");
else
blog(LOG_ERROR, "Could not open audio output");
return false;
}
static void obs_free_audio(void)
{
struct obs_audio *audio = &obs->audio;
if (audio->audio)
audio_output_close(audio->audio);
memset(audio, 0, sizeof(struct obs_audio));
}
static bool obs_init_data(void)
{
struct obs_data *data = &obs->data;
pthread_mutex_init_value(&obs->data.displays_mutex);
if (pthread_mutex_init(&data->sources_mutex, NULL) != 0)
return false;
if (pthread_mutex_init(&data->displays_mutex, NULL) != 0)
return false;
return true;
}
static void obs_free_data(void)
{
struct obs_data *data = &obs->data;
size_t i;
for (i = 0; i < MAX_CHANNELS; i++)
obs_set_output_source(i, NULL);
while (data->displays.num)
obs_display_destroy(data->displays.array[0]);
pthread_mutex_lock(&obs->data.sources_mutex);
for (i = 0; i < data->sources.num; i++)
obs_source_release(data->sources.array[i]);
da_free(data->sources);
pthread_mutex_unlock(&obs->data.sources_mutex);
}
static bool obs_init(void)
{
obs = bmalloc(sizeof(struct obs_subsystem));
memset(obs, 0, sizeof(struct obs_subsystem));
obs_init_data();
obs->media = media_open();
if (!obs->media)
return false;
if (!obs_reset_video(vi))
return false;
if (!obs_reset_audio(ai))
return false;
return true;
}
static bool obs_init_threading(void)
bool obs_startup()
{
if (pthread_mutex_init(&obs->source_list_mutex, NULL) != 0)
return false;
if (pthread_mutex_init(&obs->display_list_mutex, NULL) != 0)
return false;
if (pthread_create(&obs->video_thread, NULL, obs_video_thread,
obs) != 0)
return false;
bool success;
obs->thread_initialized = true;
return true;
if (obs) {
blog(LOG_ERROR, "Tried to call obs_startup more than once");
return false;
}
success = obs_init();
if (!success)
obs_shutdown();
return success;
}
static bool obs_init(const char *graphics_module,
struct gs_init_data *graphics_data,
struct video_info *vi, struct audio_info *ai)
{
obs = bmalloc(sizeof(struct obs_data));
memset(obs, 0, sizeof(struct obs_data));
pthread_mutex_init_value(&obs->source_list_mutex);
pthread_mutex_init_value(&obs->display_list_mutex);
if (!obs_init_graphics(graphics_module, graphics_data, vi))
return false;
if (!obs_init_media(vi, ai))
return false;
if (!obs_init_threading())
return false;
return true;
}
static inline void obs_free_graphics(void)
{
size_t i;
if (!obs->graphics)
return;
gs_entercontext(obs->graphics);
if (obs->copy_mapped)
stagesurface_unmap(obs->copy_surfaces[obs->cur_texture]);
for (i = 0; i < NUM_TEXTURES; i++)
stagesurface_destroy(obs->copy_surfaces[i]);
effect_destroy(obs->default_effect);
gs_leavecontext();
gs_destroy(obs->graphics);
}
static inline void obs_free_media(void)
{
video_output_close(obs->video);
audio_output_close(obs->audio);
media_close(obs->media);
}
static inline void obs_free_threading(void)
{
void *thread_ret;
video_output_stop(obs->video);
if (obs->thread_initialized)
pthread_join(obs->video_thread, &thread_ret);
pthread_mutex_destroy(&obs->source_list_mutex);
pthread_mutex_destroy(&obs->display_list_mutex);
}
static void obs_destroy(void)
void obs_shutdown(void)
{
size_t i;
if (!obs)
return;
for (i = 0; i < obs->displays.num; i++)
obs_display_destroy(obs->displays.array[i]);
da_free(obs->input_types);
da_free(obs->filter_types);
da_free(obs->transition_types);
da_free(obs->output_types);
/*da_free(obs->services);*/
da_free(obs->service_types);
da_free(obs->displays);
da_free(obs->sources);
obs_free_threading();
obs_free_media();
obs_free_graphics();
obs_free_data();
obs_free_video();
obs_free_audio();
media_close(obs->media);
for (i = 0; i < obs->modules.num; i++)
free_module(obs->modules.array+i);
@@ -177,53 +273,24 @@ static void obs_destroy(void)
obs = NULL;
}
bool obs_startup(const char *graphics_module,
struct gs_init_data *graphics_data,
struct video_info *vi, struct audio_info *ai)
bool obs_reset_video(struct obs_video_info *ovi)
{
if (!obs_init(graphics_module, graphics_data, vi, ai)) {
obs_destroy();
return false;
}
obs_free_video();
if (ovi)
return obs_init_video(ovi);
return true;
}
void obs_shutdown(void)
{
obs_destroy();
}
bool obs_reset_video(struct video_info *vi)
{
int errorcode;
if (obs->video) {
video_output_close(obs->video);
obs->video = NULL;
}
obs->output_width = vi->width;
obs->output_height = vi->height;
errorcode = video_output_open(&obs->video, obs->media, vi);
if (errorcode == VIDEO_OUTPUT_SUCCESS)
return true;
else if (errorcode == VIDEO_OUTPUT_INVALIDPARAM)
blog(LOG_ERROR, "Invalid video parameters specified");
else
blog(LOG_ERROR, "Could not open video output");
return false;
}
bool obs_reset_audio(struct audio_info *ai)
{
/* TODO */
obs_free_audio();
if(ai)
return obs_init_audio(ai);
return true;
}
bool obs_enum_inputs(size_t idx, const char **name)
{
if (idx >= obs->input_types.num)
@@ -256,10 +323,9 @@ bool obs_enum_outputs(size_t idx, const char **name)
return true;
}
graphics_t obs_graphics(void)
{
return obs->graphics;
return obs->video.graphics;
}
media_t obs_media(void)
@@ -267,12 +333,49 @@ media_t obs_media(void)
return obs->media;
}
obs_source_t obs_get_primary_source(void)
bool obs_add_source(obs_source_t source)
{
return obs->primary_source;
pthread_mutex_lock(&obs->data.sources_mutex);
da_push_back(obs->data.sources, &source);
obs_source_addref(source);
pthread_mutex_unlock(&obs->data.sources_mutex);
return true;
}
void obs_set_primary_source(obs_source_t source)
void obs_delete_source(obs_source_t source)
{
obs->primary_source = source;
struct obs_data *data = &obs->data;
size_t id;
pthread_mutex_lock(&data->sources_mutex);
id = da_find(data->sources, &source, 0);
if (id != DARRAY_INVALID) {
source->removed = true;
da_erase_item(data->sources, &source);
obs_source_release(source);
}
pthread_mutex_unlock(&data->sources_mutex);
}
obs_source_t obs_get_output_source(uint32_t channel)
{
assert(channel < MAX_CHANNELS);
return obs->data.channels[channel];
}
void obs_set_output_source(uint32_t channel, obs_source_t source)
{
struct obs_source *prev_source;
assert(channel < MAX_CHANNELS);
prev_source = obs->data.channels[channel];
obs->data.channels[channel] = source;
if (source)
obs_source_addref(source);
if (prev_source)
obs_source_release(prev_source);
}

View File

@@ -46,6 +46,11 @@ enum obs_source_type {
SOURCE_SCENE
};
enum obs_video_type {
OBS_VIDEO_YUV,
OBS_VIDEO_RGB
};
/* used for changing the order of items (for example, filters in a source,
* or items in a scene) */
enum order_movement {
@@ -55,6 +60,19 @@ enum order_movement {
ORDER_MOVE_BOTTOM
};
struct obs_video_info {
const char *graphics_module;
uint32_t fps_num;
uint32_t fps_den;
uint32_t base_width;
uint32_t base_height;
uint32_t output_width;
uint32_t output_height;
enum video_format output_format;
uint32_t adapter;
struct gs_window window;
};
struct filtered_audio {
void *data;
uint32_t frames;
@@ -100,12 +118,14 @@ struct obs_source;
struct obs_scene;
struct obs_scene_item;
struct obs_output;
struct obs_service;
typedef struct obs_display *obs_display_t;
typedef struct obs_source *obs_source_t;
typedef struct obs_scene *obs_scene_t;
typedef struct obs_scene_item *obs_sceneitem_t;
typedef struct obs_output *obs_output_t;
typedef struct obs_service *obs_service_t;
/* ------------------------------------------------------------------------- */
/* OBS context */
@@ -113,21 +133,17 @@ typedef struct obs_output *obs_output_t;
/**
* Starts up and shuts down OBS
*
* Using the graphics module specified, creates an OBS context and sets the
* primary video/audio output information.
* Creates the OBS context.
*/
EXPORT bool obs_startup(const char *graphics_module,
struct gs_init_data *graphics_data,
struct video_info *vi, struct audio_info *ai);
EXPORT bool obs_startup(void);
EXPORT void obs_shutdown(void);
/**
* Sets base video ouput base resolution/fps/format
*
* NOTE: Cannot reset base video if currently streaming/recording.
* NOTE: Cannot set base video if currently streaming/recording.
*/
EXPORT bool obs_reset_video(struct video_info *vi);
EXPORT bool obs_reset_video(struct obs_video_info *ovi);
/**
* Sets base audio output format/channels/samples/etc
@@ -184,18 +200,24 @@ EXPORT graphics_t obs_graphics(void);
EXPORT media_t obs_media(void);
/**
* Sets/gets the primary output source.
* Adds/removes a source to/from the user source list.
*
* The primary source is the source that's presented.
* The user source list is the list of sources that are accessible by a user.
* Typically when a transition is active, it is not meant to be accessible by
* users, so there's no reason for a user to see such a source.
*/
EXPORT void obs_set_primary_source(obs_source_t source);
EXPORT obs_source_t obs_get_primary_source(void);
EXPORT bool obs_add_source(obs_source_t source);
EXPORT void obs_delete_source(obs_source_t source);
/** Sets/gets the primary output source for a channel. */
EXPORT void obs_set_output_source(uint32_t channel, obs_source_t source);
EXPORT obs_source_t obs_get_output_source(uint32_t channel);
/**
* Returns the location of a plugin data file.
*
* file: Name of file to locate. For example, "myplugin/mydata.data"
* returns: Output string, or NULL if not found. Use bfree to free string.
* returns: Path string, or NULL if not found. Use bfree to free string.
*/
EXPORT char *obs_find_plugin_file(const char *file);
@@ -214,8 +236,10 @@ EXPORT obs_display_t obs_display_create(struct gs_init_data *graphics_data);
EXPORT void obs_display_destroy(obs_display_t display);
/** Sets the source to be used for a display context. */
EXPORT void obs_display_setsource(obs_display_t display, obs_source_t source);
EXPORT obs_source_t obs_display_getsource(obs_display_t display);
EXPORT void obs_display_setsource(obs_display_t display, uint32_t channel,
obs_source_t source);
EXPORT obs_source_t obs_display_getsource(obs_display_t display,
uint32_t channel);
/* ------------------------------------------------------------------------- */
@@ -235,7 +259,11 @@ EXPORT const char *obs_source_getdisplayname(enum obs_source_type type,
*/
EXPORT obs_source_t obs_source_create(enum obs_source_type type,
const char *name, const char *settings);
EXPORT void obs_source_destroy(obs_source_t source);
EXPORT void obs_source_addref(obs_source_t source);
EXPORT void obs_source_release(obs_source_t source);
/** Returns true if the source should be released */
EXPORT bool obs_source_removed(obs_source_t source);
/**
* Retrieves flags that specify what type of data the source presents/modifies.
@@ -285,7 +313,7 @@ EXPORT bool obs_source_enum_children(obs_source_t source, size_t idx,
EXPORT obs_source_t obs_filter_gettarget(obs_source_t filter);
/** Adds a filter to the source (which is used whenever the source is used) */
EXPORT void obs_source_filter_add(obs_source_t source,obs_source_t filter);
EXPORT void obs_source_filter_add(obs_source_t source, obs_source_t filter);
/** Removes a filter from the source */
EXPORT void obs_source_filter_remove(obs_source_t source, obs_source_t filter);
@@ -393,6 +421,19 @@ EXPORT const char *obs_output_get_settings(obs_output_t output);
EXPORT void obs_output_save_settings(obs_output_t output,
const char *settings);
/* ------------------------------------------------------------------------- */
/* Stream Services */
EXPORT obs_service_t obs_service_create(const char *service,
const char *settings);
EXPORT void obs_service_destroy(obs_service_t service);
EXPORT void obs_service_setdata(obs_service_t service, const char *attribute,
const char *data);
EXPORT const char *obs_service_getdata(obs_service_t service,
const char *attribute);
#ifdef __cplusplus
}
#endif