libobs: Add services API, reduce repeated code
Add API for streaming services. The services API simplifies the creation of custom service features and user interface. Custom streaming services later on will be able to do things such as: - Be able to use service-specific APIs via modules, allowing a more direct means of communicating with the service and requesting or setting service-specific information - Get URL/stream key via other means of authentication such as OAuth, or be able to build custom URLs for services that require that sort of thing. - Query information (such as viewer count, chat, follower notifications, and other information) - Set channel information (such as current game, current channel title, activating commercials) Also, I reduce some repeated code that was used for all libobs objects. This includes the name of the object, the private data, settings, as well as the signal and procedure handlers. I also switched to using linked lists for the global object lists, rather than using an array of pointers (you could say it was.. pointless.) ..Anyway, the linked list info is also stored in the shared context data structure.
This commit is contained in:
parent
8225a0697a
commit
4a6d19f206
@ -167,6 +167,7 @@ set(libobs_libobs_SOURCES
|
||||
${libobs_PLATFORM_SOURCES}
|
||||
obs-avc.c
|
||||
obs-encoder.c
|
||||
obs-service.c
|
||||
obs-source.c
|
||||
obs-output.c
|
||||
obs.c
|
||||
|
@ -58,7 +58,11 @@ obs_display_t obs_display_create(struct gs_init_data *graphics_data)
|
||||
display = NULL;
|
||||
} else {
|
||||
pthread_mutex_lock(&obs->data.displays_mutex);
|
||||
da_push_back(obs->data.displays, &display);
|
||||
display->prev_next = &obs->data.first_display;
|
||||
display->next = obs->data.first_display;
|
||||
obs->data.first_display = display;
|
||||
if (display->next)
|
||||
display->next->prev_next = &display->next;
|
||||
pthread_mutex_unlock(&obs->data.displays_mutex);
|
||||
}
|
||||
|
||||
@ -82,7 +86,9 @@ void obs_display_destroy(obs_display_t display)
|
||||
{
|
||||
if (display) {
|
||||
pthread_mutex_lock(&obs->data.displays_mutex);
|
||||
da_erase_item(obs->data.displays, &display);
|
||||
*display->prev_next = display->next;
|
||||
if (display->next)
|
||||
display->next->prev_next = display->prev_next;
|
||||
pthread_mutex_unlock(&obs->data.displays_mutex);
|
||||
|
||||
gs_entercontext(obs_graphics());
|
||||
|
@ -42,20 +42,16 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name,
|
||||
pthread_mutex_init_value(&encoder->callbacks_mutex);
|
||||
pthread_mutex_init_value(&encoder->outputs_mutex);
|
||||
|
||||
if (!obs_context_data_init(&encoder->context, settings, name))
|
||||
return false;
|
||||
if (pthread_mutex_init(&encoder->callbacks_mutex, NULL) != 0)
|
||||
return false;
|
||||
if (pthread_mutex_init(&encoder->outputs_mutex, NULL) != 0)
|
||||
return false;
|
||||
|
||||
encoder->settings = obs_data_newref(settings);
|
||||
if (encoder->info.defaults)
|
||||
encoder->info.defaults(encoder->settings);
|
||||
encoder->info.defaults(encoder->context.settings);
|
||||
|
||||
pthread_mutex_lock(&obs->data.encoders_mutex);
|
||||
da_push_back(obs->data.encoders, &encoder);
|
||||
pthread_mutex_unlock(&obs->data.encoders_mutex);
|
||||
|
||||
encoder->name = bstrdup(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -79,10 +75,14 @@ static struct obs_encoder *create_encoder(const char *id,
|
||||
|
||||
success = init_encoder(encoder, name, settings);
|
||||
if (!success) {
|
||||
bfree(encoder);
|
||||
obs_encoder_destroy(encoder);
|
||||
encoder = NULL;
|
||||
}
|
||||
|
||||
obs_context_data_insert(&encoder->context,
|
||||
&obs->data.encoders_mutex,
|
||||
&obs->data.first_encoder);
|
||||
|
||||
return encoder;
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ static inline struct audio_convert_info *get_audio_info(
|
||||
memset(info, 0, sizeof(struct audio_convert_info));
|
||||
|
||||
if (encoder->info.audio_info)
|
||||
encoder->info.audio_info(encoder->data, info);
|
||||
encoder->info.audio_info(encoder->context.data, info);
|
||||
|
||||
if (info->format == AUDIO_FORMAT_UNKNOWN)
|
||||
info->format = aoi->format;
|
||||
@ -139,7 +139,7 @@ static inline struct video_scale_info *get_video_info(
|
||||
struct obs_encoder *encoder, struct video_scale_info *info)
|
||||
{
|
||||
if (encoder->info.video_info)
|
||||
if (encoder->info.video_info(encoder->data, info))
|
||||
if (encoder->info.video_info(encoder->context.data, info))
|
||||
return info;
|
||||
|
||||
return NULL;
|
||||
@ -199,13 +199,12 @@ static void obs_encoder_actually_destroy(obs_encoder_t encoder)
|
||||
|
||||
free_audio_buffers(encoder);
|
||||
|
||||
if (encoder->data)
|
||||
encoder->info.destroy(encoder->data);
|
||||
if (encoder->context.data)
|
||||
encoder->info.destroy(encoder->context.data);
|
||||
da_free(encoder->callbacks);
|
||||
obs_data_release(encoder->settings);
|
||||
pthread_mutex_destroy(&encoder->callbacks_mutex);
|
||||
pthread_mutex_destroy(&encoder->outputs_mutex);
|
||||
bfree(encoder->name);
|
||||
obs_context_data_free(&encoder->context);
|
||||
bfree(encoder);
|
||||
}
|
||||
}
|
||||
@ -217,9 +216,7 @@ void obs_encoder_destroy(obs_encoder_t encoder)
|
||||
if (encoder) {
|
||||
bool destroy;
|
||||
|
||||
pthread_mutex_lock(&obs->data.encoders_mutex);
|
||||
da_erase_item(obs->data.encoders, &encoder);
|
||||
pthread_mutex_unlock(&obs->data.encoders_mutex);
|
||||
obs_context_data_remove(&encoder->context);
|
||||
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
destroy = encoder->callbacks.num == 0;
|
||||
@ -267,7 +264,7 @@ obs_properties_t obs_encoder_properties(obs_encoder_t encoder,
|
||||
if (encoder && encoder->info.properties) {
|
||||
obs_properties_t props;
|
||||
props = encoder->info.properties(locale);
|
||||
obs_properties_apply_settings(props, encoder->settings);
|
||||
obs_properties_apply_settings(props, encoder->context.settings);
|
||||
return props;
|
||||
}
|
||||
return NULL;
|
||||
@ -277,17 +274,19 @@ void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings)
|
||||
{
|
||||
if (!encoder) return;
|
||||
|
||||
obs_data_apply(encoder->settings, settings);
|
||||
if (encoder->info.update && encoder->data)
|
||||
encoder->info.update(encoder->data, encoder->settings);
|
||||
obs_data_apply(encoder->context.settings, settings);
|
||||
|
||||
if (encoder->info.update && encoder->context.data)
|
||||
encoder->info.update(encoder->context.data,
|
||||
encoder->context.settings);
|
||||
}
|
||||
|
||||
bool obs_encoder_get_extra_data(obs_encoder_t encoder, uint8_t **extra_data,
|
||||
size_t *size)
|
||||
{
|
||||
if (encoder && encoder->info.extra_data && encoder->data)
|
||||
return encoder->info.extra_data(encoder->data, extra_data,
|
||||
size);
|
||||
if (encoder && encoder->info.extra_data && encoder->context.data)
|
||||
return encoder->info.extra_data(encoder->context.data,
|
||||
extra_data, size);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -296,8 +295,8 @@ obs_data_t obs_encoder_get_settings(obs_encoder_t encoder)
|
||||
{
|
||||
if (!encoder) return NULL;
|
||||
|
||||
obs_data_addref(encoder->settings);
|
||||
return encoder->settings;
|
||||
obs_data_addref(encoder->context.settings);
|
||||
return encoder->context.settings;
|
||||
}
|
||||
|
||||
static inline void reset_audio_buffers(struct obs_encoder *encoder)
|
||||
@ -317,7 +316,7 @@ static void intitialize_audio_encoder(struct obs_encoder *encoder)
|
||||
encoder->samplerate = info.samples_per_sec;
|
||||
encoder->planes = get_audio_planes(info.format, info.speakers);
|
||||
encoder->blocksize = get_audio_size(info.format, info.speakers, 1);
|
||||
encoder->framesize = encoder->info.frame_size(encoder->data);
|
||||
encoder->framesize = encoder->info.frame_size(encoder->context.data);
|
||||
|
||||
encoder->framesize_bytes = encoder->blocksize * encoder->framesize;
|
||||
reset_audio_buffers(encoder);
|
||||
@ -330,11 +329,12 @@ bool obs_encoder_initialize(obs_encoder_t encoder)
|
||||
if (encoder->active)
|
||||
return true;
|
||||
|
||||
if (encoder->data)
|
||||
encoder->info.destroy(encoder->data);
|
||||
if (encoder->context.data)
|
||||
encoder->info.destroy(encoder->context.data);
|
||||
|
||||
encoder->data = encoder->info.create(encoder->settings, encoder);
|
||||
if (!encoder->data)
|
||||
encoder->context.data = encoder->info.create(encoder->context.settings,
|
||||
encoder);
|
||||
if (!encoder->context.data)
|
||||
return false;
|
||||
|
||||
encoder->paired_encoder = NULL;
|
||||
@ -368,7 +368,7 @@ void obs_encoder_start(obs_encoder_t encoder,
|
||||
struct encoder_callback cb = {false, new_packet, param};
|
||||
bool first = false;
|
||||
|
||||
if (!encoder || !new_packet || !encoder->data) return;
|
||||
if (!encoder || !new_packet || !encoder->context.data) return;
|
||||
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
|
||||
@ -434,7 +434,7 @@ static inline bool get_sei(struct obs_encoder *encoder,
|
||||
uint8_t **sei, size_t *size)
|
||||
{
|
||||
if (encoder->info.sei_data)
|
||||
return encoder->info.sei_data(encoder->data, sei, size);
|
||||
return encoder->info.sei_data(encoder->context.data, sei, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -500,11 +500,12 @@ static inline void do_encode(struct obs_encoder *encoder,
|
||||
pkt.timebase_num = encoder->timebase_num;
|
||||
pkt.timebase_den = encoder->timebase_den;
|
||||
|
||||
success = encoder->info.encode(encoder->data, frame, &pkt, &received);
|
||||
success = encoder->info.encode(encoder->context.data, frame, &pkt,
|
||||
&received);
|
||||
if (!success) {
|
||||
full_stop(encoder);
|
||||
blog(LOG_ERROR, "Error encoding with encoder '%s'",
|
||||
encoder->name);
|
||||
encoder->context.name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,9 @@ struct obs_display {
|
||||
swapchain_t swap;
|
||||
pthread_mutex_t draw_callbacks_mutex;
|
||||
DARRAY(struct draw_callback) draw_callbacks;
|
||||
|
||||
struct obs_display *next;
|
||||
struct obs_display **prev_next;
|
||||
};
|
||||
|
||||
extern bool obs_display_init(struct obs_display *display,
|
||||
@ -133,17 +136,20 @@ struct obs_core_audio {
|
||||
|
||||
/* user sources, output channels, and displays */
|
||||
struct obs_core_data {
|
||||
/* arrays of pointers jim? you should really stop being lazy and use
|
||||
* linked lists. */
|
||||
DARRAY(struct obs_display*) displays;
|
||||
DARRAY(struct obs_source*) sources;
|
||||
DARRAY(struct obs_output*) outputs;
|
||||
DARRAY(struct obs_encoder*) encoders;
|
||||
pthread_mutex_t user_sources_mutex;
|
||||
DARRAY(struct obs_source*) user_sources;
|
||||
|
||||
struct obs_source *first_source;
|
||||
struct obs_display *first_display;
|
||||
struct obs_output *first_output;
|
||||
struct obs_encoder *first_encoder;
|
||||
struct obs_service *first_service;
|
||||
|
||||
pthread_mutex_t sources_mutex;
|
||||
pthread_mutex_t displays_mutex;
|
||||
pthread_mutex_t outputs_mutex;
|
||||
pthread_mutex_t encoders_mutex;
|
||||
pthread_mutex_t services_mutex;
|
||||
|
||||
struct obs_view main_view;
|
||||
|
||||
@ -178,20 +184,42 @@ extern struct obs_core *obs;
|
||||
extern void *obs_video_thread(void *param);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* obs shared context data */
|
||||
|
||||
struct obs_context_data {
|
||||
char *name;
|
||||
void *data;
|
||||
obs_data_t settings;
|
||||
signal_handler_t signals;
|
||||
proc_handler_t procs;
|
||||
|
||||
pthread_mutex_t *mutex;
|
||||
struct obs_context_data *next;
|
||||
struct obs_context_data **prev_next;
|
||||
};
|
||||
|
||||
extern bool obs_context_data_init(
|
||||
struct obs_context_data *context,
|
||||
obs_data_t settings,
|
||||
const char *name);
|
||||
extern void obs_context_data_free(struct obs_context_data *context);
|
||||
|
||||
extern void obs_context_data_insert(struct obs_context_data *context,
|
||||
pthread_mutex_t *mutex, void *first);
|
||||
extern void obs_context_data_remove(struct obs_context_data *context);
|
||||
|
||||
extern void obs_context_data_setname(struct obs_context_data *context,
|
||||
const char *name);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* sources */
|
||||
|
||||
struct obs_source {
|
||||
volatile long refs;
|
||||
struct obs_context_data context;
|
||||
struct obs_source_info info;
|
||||
|
||||
/* source-specific data */
|
||||
char *name; /* user-defined name */
|
||||
obs_data_t settings;
|
||||
void *data;
|
||||
|
||||
signal_handler_t signals;
|
||||
proc_handler_t procs;
|
||||
volatile long refs;
|
||||
|
||||
/* signals to call the source update in the video thread */
|
||||
bool defer_update;
|
||||
@ -256,10 +284,13 @@ struct obs_source {
|
||||
bool rendering_filter;
|
||||
};
|
||||
|
||||
bool obs_source_init_handlers(struct obs_source *source);
|
||||
extern bool obs_source_init_context(struct obs_source *source,
|
||||
obs_data_t settings, const char *name);
|
||||
extern bool obs_source_init(struct obs_source *source,
|
||||
const struct obs_source_info *info);
|
||||
|
||||
extern void obs_source_destroy(struct obs_source *source);
|
||||
|
||||
enum view_type {
|
||||
MAIN_VIEW,
|
||||
AUX_VIEW
|
||||
@ -274,13 +305,8 @@ extern void obs_source_video_tick(obs_source_t source, float seconds);
|
||||
/* outputs */
|
||||
|
||||
struct obs_output {
|
||||
char *name;
|
||||
void *data;
|
||||
struct obs_context_data context;
|
||||
struct obs_output_info info;
|
||||
obs_data_t settings;
|
||||
|
||||
signal_handler_t signals;
|
||||
proc_handler_t procs;
|
||||
|
||||
bool received_video;
|
||||
bool received_audio;
|
||||
@ -319,10 +345,8 @@ struct encoder_callback {
|
||||
};
|
||||
|
||||
struct obs_encoder {
|
||||
char *name;
|
||||
void *data;
|
||||
struct obs_context_data context;
|
||||
struct obs_encoder_info info;
|
||||
obs_data_t settings;
|
||||
|
||||
uint32_t samplerate;
|
||||
size_t planes;
|
||||
@ -373,3 +397,11 @@ extern void obs_encoder_add_output(struct obs_encoder *encoder,
|
||||
struct obs_output *output);
|
||||
extern void obs_encoder_remove_output(struct obs_encoder *encoder,
|
||||
struct obs_output *output);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* services */
|
||||
|
||||
struct obs_service {
|
||||
struct obs_context_data context;
|
||||
struct obs_service_info info;
|
||||
};
|
||||
|
@ -124,9 +124,9 @@ void obs_register_source_s(const struct obs_source_info *info, size_t size)
|
||||
struct obs_source_info data = {0};
|
||||
struct darray *array;
|
||||
|
||||
CHECK_REQUIRED_VAL(info, getname, obs_register_source);
|
||||
CHECK_REQUIRED_VAL(info, create, obs_register_source);
|
||||
CHECK_REQUIRED_VAL(info, destroy, obs_register_source);
|
||||
CHECK_REQUIRED_VAL(info, getname, obs_register_source);
|
||||
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) != 0 &&
|
||||
@ -177,10 +177,10 @@ void obs_register_output_s(const struct obs_output_info *info, size_t size)
|
||||
|
||||
void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size)
|
||||
{
|
||||
CHECK_REQUIRED_VAL(info, getname, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, create, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, destroy, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, encode, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, getname, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, create, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, destroy, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, encode, obs_register_encoder);
|
||||
|
||||
if (info->type == OBS_ENCODER_AUDIO)
|
||||
CHECK_REQUIRED_VAL(info, frame_size, obs_register_encoder);
|
||||
@ -190,9 +190,11 @@ void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size)
|
||||
|
||||
void obs_register_service_s(const struct obs_service_info *info, size_t size)
|
||||
{
|
||||
/* TODO */
|
||||
UNUSED_PARAMETER(size);
|
||||
UNUSED_PARAMETER(info);
|
||||
CHECK_REQUIRED_VAL(info, getname, obs_register_service);
|
||||
CHECK_REQUIRED_VAL(info, create, obs_register_service);
|
||||
CHECK_REQUIRED_VAL(info, destroy, obs_register_service);
|
||||
|
||||
REGISTER_OBS_DEF(size, obs_service_info, obs->service_types, info);
|
||||
}
|
||||
|
||||
void obs_regsiter_modal_ui_s(const struct obs_modal_ui *info, size_t size)
|
||||
|
@ -30,23 +30,25 @@ static inline const struct obs_output_info *find_output(const char *id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *obs_output_getdisplayname(const char *id, const char *locale)
|
||||
{
|
||||
const struct obs_output_info *info = find_output(id);
|
||||
return (info != NULL) ? info->getname(locale) : NULL;
|
||||
}
|
||||
|
||||
static const char *output_signals[] = {
|
||||
"void start(ptr output, int errorcode)",
|
||||
"void stop(ptr output)",
|
||||
NULL
|
||||
};
|
||||
|
||||
static bool init_output_handlers(struct obs_output *output)
|
||||
static bool init_output_handlers(struct obs_output *output, const char *name,
|
||||
obs_data_t settings)
|
||||
{
|
||||
output->signals = signal_handler_create();
|
||||
if (!output->signals)
|
||||
if (!obs_context_data_init(&output->context, settings, name))
|
||||
return false;
|
||||
|
||||
output->procs = proc_handler_create();
|
||||
if (!output->procs)
|
||||
return false;
|
||||
|
||||
signal_handler_add_array(output->signals, output_signals);
|
||||
signal_handler_add_array(output->context.signals, output_signals);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -66,28 +68,25 @@ obs_output_t obs_output_create(const char *id, const char *name,
|
||||
|
||||
if (pthread_mutex_init(&output->interleaved_mutex, NULL) != 0)
|
||||
goto fail;
|
||||
if (!init_output_handlers(output))
|
||||
if (!init_output_handlers(output, name, settings))
|
||||
goto fail;
|
||||
|
||||
output->info = *info;
|
||||
output->video = obs_video();
|
||||
output->audio = obs_audio();
|
||||
output->settings = obs_data_newref(settings);
|
||||
if (output->info.defaults)
|
||||
output->info.defaults(output->settings);
|
||||
output->info.defaults(output->context.settings);
|
||||
|
||||
output->data = info->create(output->settings, output);
|
||||
if (!output->data)
|
||||
output->context.data = info->create(output->context.settings, output);
|
||||
if (!output->context.data)
|
||||
goto fail;
|
||||
|
||||
output->name = bstrdup(name);
|
||||
|
||||
pthread_mutex_lock(&obs->data.outputs_mutex);
|
||||
da_push_back(obs->data.outputs, &output);
|
||||
pthread_mutex_unlock(&obs->data.outputs_mutex);
|
||||
|
||||
output->valid = true;
|
||||
|
||||
obs_context_data_insert(&output->context,
|
||||
&obs->data.outputs_mutex,
|
||||
&obs->data.first_output);
|
||||
|
||||
return output;
|
||||
|
||||
fail:
|
||||
@ -105,39 +104,32 @@ static inline void free_packets(struct obs_output *output)
|
||||
void obs_output_destroy(obs_output_t output)
|
||||
{
|
||||
if (output) {
|
||||
if (output->valid) {
|
||||
if (output->active)
|
||||
output->info.stop(output->data);
|
||||
obs_context_data_remove(&output->context);
|
||||
|
||||
pthread_mutex_lock(&obs->data.outputs_mutex);
|
||||
da_erase_item(obs->data.outputs, &output);
|
||||
pthread_mutex_unlock(&obs->data.outputs_mutex);
|
||||
}
|
||||
if (output->valid && output->active)
|
||||
output->info.stop(output->context.data);
|
||||
|
||||
free_packets(output);
|
||||
|
||||
if (output->data)
|
||||
output->info.destroy(output->data);
|
||||
if (output->context.data)
|
||||
output->info.destroy(output->context.data);
|
||||
|
||||
signal_handler_destroy(output->signals);
|
||||
proc_handler_destroy(output->procs);
|
||||
|
||||
obs_data_release(output->settings);
|
||||
pthread_mutex_destroy(&output->interleaved_mutex);
|
||||
bfree(output->name);
|
||||
obs_context_data_free(&output->context);
|
||||
bfree(output);
|
||||
}
|
||||
}
|
||||
|
||||
bool obs_output_start(obs_output_t output)
|
||||
{
|
||||
return (output != NULL) ? output->info.start(output->data) : false;
|
||||
return (output != NULL) ?
|
||||
output->info.start(output->context.data) : false;
|
||||
}
|
||||
|
||||
void obs_output_stop(obs_output_t output)
|
||||
{
|
||||
if (output) {
|
||||
output->info.stop(output->data);
|
||||
output->info.stop(output->context.data);
|
||||
signal_stop(output, OBS_OUTPUT_SUCCESS);
|
||||
}
|
||||
}
|
||||
@ -181,7 +173,7 @@ obs_properties_t obs_output_properties(obs_output_t output, const char *locale)
|
||||
if (output && output->info.properties) {
|
||||
obs_properties_t props;
|
||||
props = output->info.properties(locale);
|
||||
obs_properties_apply_settings(props, output->settings);
|
||||
obs_properties_apply_settings(props, output->context.settings);
|
||||
return props;
|
||||
}
|
||||
|
||||
@ -192,10 +184,11 @@ void obs_output_update(obs_output_t output, obs_data_t settings)
|
||||
{
|
||||
if (!output) return;
|
||||
|
||||
obs_data_apply(output->settings, settings);
|
||||
obs_data_apply(output->context.settings, settings);
|
||||
|
||||
if (output->info.update)
|
||||
output->info.update(output->data, output->settings);
|
||||
output->info.update(output->context.data,
|
||||
output->context.settings);
|
||||
}
|
||||
|
||||
obs_data_t obs_output_get_settings(obs_output_t output)
|
||||
@ -203,8 +196,8 @@ obs_data_t obs_output_get_settings(obs_output_t output)
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
obs_data_addref(output->settings);
|
||||
return output->settings;
|
||||
obs_data_addref(output->context.settings);
|
||||
return output->context.settings;
|
||||
}
|
||||
|
||||
bool obs_output_canpause(obs_output_t output)
|
||||
@ -215,17 +208,17 @@ bool obs_output_canpause(obs_output_t output)
|
||||
void obs_output_pause(obs_output_t output)
|
||||
{
|
||||
if (output && output->info.pause)
|
||||
output->info.pause(output->data);
|
||||
output->info.pause(output->context.data);
|
||||
}
|
||||
|
||||
signal_handler_t obs_output_signalhandler(obs_output_t output)
|
||||
{
|
||||
return output ? output->signals : NULL;
|
||||
return output ? output->context.signals : NULL;
|
||||
}
|
||||
|
||||
proc_handler_t obs_output_prochandler(obs_output_t output)
|
||||
{
|
||||
return output ? output->procs : NULL;
|
||||
return output ? output->context.procs : NULL;
|
||||
}
|
||||
|
||||
void obs_output_set_media(obs_output_t output, video_t video, audio_t audio)
|
||||
@ -396,7 +389,7 @@ static inline void send_interleaved(struct obs_output *output)
|
||||
struct encoder_packet out = output->interleaved_packets.array[0];
|
||||
da_erase(output->interleaved_packets, 0);
|
||||
|
||||
output->info.encoded_packet(output->data, &out);
|
||||
output->info.encoded_packet(output->context.data, &out);
|
||||
obs_free_encoder_packet(&out);
|
||||
}
|
||||
|
||||
@ -446,7 +439,8 @@ static void hook_data_capture(struct obs_output *output, bool encoded,
|
||||
|
||||
encoded_callback = (has_video && has_audio) ?
|
||||
interleave_packets : output->info.encoded_packet;
|
||||
param = (has_video && has_audio) ? output : output->data;
|
||||
param = (has_video && has_audio) ?
|
||||
output : output->context.data;
|
||||
|
||||
if (has_video)
|
||||
obs_encoder_start(output->video_encoder,
|
||||
@ -458,11 +452,13 @@ static void hook_data_capture(struct obs_output *output, bool encoded,
|
||||
if (has_video)
|
||||
video_output_connect(output->video,
|
||||
get_video_conversion(output),
|
||||
output->info.raw_video, output->data);
|
||||
output->info.raw_video,
|
||||
output->context.data);
|
||||
if (has_audio)
|
||||
audio_output_connect(output->audio,
|
||||
get_audio_conversion(output),
|
||||
output->info.raw_audio, output->data);
|
||||
output->info.raw_audio,
|
||||
output->context.data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -470,7 +466,7 @@ static inline void signal_start(struct obs_output *output)
|
||||
{
|
||||
struct calldata params = {0};
|
||||
calldata_setptr(¶ms, "output", output);
|
||||
signal_handler_signal(output->signals, "start", ¶ms);
|
||||
signal_handler_signal(output->context.signals, "start", ¶ms);
|
||||
calldata_free(¶ms);
|
||||
}
|
||||
|
||||
@ -479,7 +475,7 @@ static inline void signal_stop(struct obs_output *output, int code)
|
||||
struct calldata params = {0};
|
||||
calldata_setint(¶ms, "errorcode", code);
|
||||
calldata_setptr(¶ms, "output", output);
|
||||
signal_handler_signal(output->signals, "stop", ¶ms);
|
||||
signal_handler_signal(output->context.signals, "stop", ¶ms);
|
||||
calldata_free(¶ms);
|
||||
}
|
||||
|
||||
@ -566,7 +562,8 @@ void obs_output_end_data_capture(obs_output_t output)
|
||||
if (encoded) {
|
||||
encoded_callback = (has_video && has_audio) ?
|
||||
interleave_packets : output->info.encoded_packet;
|
||||
param = (has_video && has_audio) ? output : output->data;
|
||||
param = (has_video && has_audio) ?
|
||||
output : output->context.data;
|
||||
|
||||
if (has_video)
|
||||
obs_encoder_stop(output->video_encoder,
|
||||
@ -577,10 +574,12 @@ void obs_output_end_data_capture(obs_output_t output)
|
||||
} else {
|
||||
if (has_video)
|
||||
video_output_disconnect(output->video,
|
||||
output->info.raw_video, output->data);
|
||||
output->info.raw_video,
|
||||
output->context.data);
|
||||
if (has_audio)
|
||||
audio_output_disconnect(output->audio,
|
||||
output->info.raw_audio, output->data);
|
||||
output->info.raw_audio,
|
||||
output->context.data);
|
||||
}
|
||||
|
||||
output->active = false;
|
||||
|
@ -25,8 +25,8 @@ static inline void signal_item_remove(struct obs_scene_item *item)
|
||||
calldata_setptr(¶ms, "scene", item->parent);
|
||||
calldata_setptr(¶ms, "item", item);
|
||||
|
||||
signal_handler_signal(item->parent->source->signals, "item_remove",
|
||||
¶ms);
|
||||
signal_handler_signal(item->parent->source->context.signals,
|
||||
"item_remove", ¶ms);
|
||||
calldata_free(¶ms);
|
||||
}
|
||||
|
||||
@ -205,35 +205,28 @@ static const char *obs_scene_signals[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
void source_init_name(struct obs_source *source, const char *name);
|
||||
|
||||
obs_scene_t obs_scene_create(const char *name)
|
||||
{
|
||||
struct obs_source *source = bzalloc(sizeof(struct obs_source));
|
||||
struct obs_scene *scene;
|
||||
|
||||
if (!obs_source_init_handlers(source)) {
|
||||
if (!obs_source_init_context(source, NULL, name)) {
|
||||
bfree(source);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
signal_handler_add_array(source->signals, obs_scene_signals);
|
||||
signal_handler_add_array(source->context.signals, obs_scene_signals);
|
||||
|
||||
source->settings = obs_data_create();
|
||||
scene = scene_create(source->settings, source);
|
||||
source->data = scene;
|
||||
scene = scene_create(source->context.settings, source);
|
||||
source->context.data = scene;
|
||||
|
||||
assert(scene);
|
||||
if (!scene) {
|
||||
obs_data_release(source->settings);
|
||||
proc_handler_destroy(source->procs);
|
||||
signal_handler_destroy(source->signals);
|
||||
obs_context_data_free(&source->context);
|
||||
bfree(source);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
source_init_name(source, name);
|
||||
|
||||
scene->source = source;
|
||||
obs_source_init(source, &scene_info);
|
||||
memcpy(&source->info, &scene_info, sizeof(struct obs_source_info));
|
||||
@ -262,7 +255,7 @@ obs_scene_t obs_scene_fromsource(obs_source_t source)
|
||||
if (!source || source->info.type != OBS_SOURCE_TYPE_SCENE)
|
||||
return NULL;
|
||||
|
||||
return source->data;
|
||||
return source->context.data;
|
||||
}
|
||||
|
||||
obs_sceneitem_t obs_scene_findsource(obs_scene_t scene, const char *name)
|
||||
@ -276,7 +269,7 @@ obs_sceneitem_t obs_scene_findsource(obs_scene_t scene, const char *name)
|
||||
|
||||
item = scene->first_item;
|
||||
while (item) {
|
||||
if (strcmp(item->source->name, name) == 0)
|
||||
if (strcmp(item->source->context.name, name) == 0)
|
||||
break;
|
||||
|
||||
item = item->next;
|
||||
@ -358,7 +351,8 @@ obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source)
|
||||
|
||||
calldata_setptr(¶ms, "scene", scene);
|
||||
calldata_setptr(¶ms, "item", item);
|
||||
signal_handler_signal(scene->source->signals, "item_add", ¶ms);
|
||||
signal_handler_signal(scene->source->context.signals, "item_add",
|
||||
¶ms);
|
||||
calldata_free(¶ms);
|
||||
|
||||
return item;
|
||||
|
158
libobs/obs-service.c
Normal file
158
libobs/obs-service.c
Normal file
@ -0,0 +1,158 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "obs-internal.h"
|
||||
|
||||
static inline const struct obs_service_info *find_service(const char *id)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < obs->service_types.num; i++)
|
||||
if (strcmp(obs->service_types.array[i].id, id) == 0)
|
||||
return obs->service_types.array+i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *obs_service_getdisplayname(const char *id, const char *locale)
|
||||
{
|
||||
const struct obs_service_info *info = find_service(id);
|
||||
return (info != NULL) ? info->getname(locale) : NULL;
|
||||
}
|
||||
|
||||
obs_service_t obs_service_create(const char *id, const char *name,
|
||||
obs_data_t settings)
|
||||
{
|
||||
const struct obs_service_info *info = find_service(id);
|
||||
struct obs_service *service;
|
||||
|
||||
if (!info) {
|
||||
blog(LOG_ERROR, "Service '%s' not found", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
service = bzalloc(sizeof(struct obs_service));
|
||||
|
||||
if (!obs_context_data_init(&service->context, settings, name)) {
|
||||
bfree(service);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
service->info = *info;
|
||||
|
||||
obs_context_data_insert(&service->context,
|
||||
&obs->data.services_mutex,
|
||||
&obs->data.first_service);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
void obs_service_destroy(obs_service_t service)
|
||||
{
|
||||
if (service) {
|
||||
obs_context_data_remove(&service->context);
|
||||
|
||||
if (service->context.data)
|
||||
service->info.destroy(service->context.data);
|
||||
|
||||
obs_context_data_free(&service->context);
|
||||
bfree(service);
|
||||
}
|
||||
}
|
||||
|
||||
static inline obs_data_t get_defaults(const struct obs_service_info *info)
|
||||
{
|
||||
obs_data_t settings = obs_data_create();
|
||||
if (info->defaults)
|
||||
info->defaults(settings);
|
||||
return settings;
|
||||
}
|
||||
|
||||
obs_data_t obs_service_defaults(const char *id)
|
||||
{
|
||||
const struct obs_service_info *info = find_service(id);
|
||||
return (info) ? get_defaults(info) : NULL;
|
||||
}
|
||||
|
||||
obs_properties_t obs_get_service_properties(const char *id, const char *locale)
|
||||
{
|
||||
const struct obs_service_info *info = find_service(id);
|
||||
if (info && info->properties) {
|
||||
obs_data_t defaults = get_defaults(info);
|
||||
obs_properties_t properties;
|
||||
|
||||
properties = info->properties(locale);
|
||||
obs_properties_apply_settings(properties, defaults);
|
||||
obs_data_release(defaults);
|
||||
return properties;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obs_properties_t obs_service_properties(obs_service_t service,
|
||||
const char *locale)
|
||||
{
|
||||
if (service && service->info.properties) {
|
||||
obs_properties_t props;
|
||||
props = service->info.properties(locale);
|
||||
obs_properties_apply_settings(props, service->context.settings);
|
||||
return props;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void obs_service_update(obs_service_t service, obs_data_t settings)
|
||||
{
|
||||
if (!service) return;
|
||||
|
||||
obs_data_apply(service->context.settings, settings);
|
||||
|
||||
if (service->info.update)
|
||||
service->info.update(service->context.data,
|
||||
service->context.settings);
|
||||
}
|
||||
|
||||
obs_data_t obs_service_get_settings(obs_service_t service)
|
||||
{
|
||||
if (!service)
|
||||
return NULL;
|
||||
|
||||
obs_data_addref(service->context.settings);
|
||||
return service->context.settings;
|
||||
}
|
||||
|
||||
signal_handler_t obs_service_signalhandler(obs_service_t service)
|
||||
{
|
||||
return service ? service->context.signals : NULL;
|
||||
}
|
||||
|
||||
proc_handler_t obs_service_prochandler(obs_service_t service)
|
||||
{
|
||||
return service ? service->context.procs : NULL;
|
||||
}
|
||||
|
||||
const char *obs_service_get_url(obs_service_t service)
|
||||
{
|
||||
if (!service || !service->info.get_url) return NULL;
|
||||
return service->info.get_url(service->context.data);
|
||||
}
|
||||
|
||||
const char *obs_service_get_key(obs_service_t service)
|
||||
{
|
||||
if (!service || !service->info.get_key) return NULL;
|
||||
return service->info.get_key(service->context.data);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
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
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
struct obs_service_info {
|
||||
/* required */
|
||||
char *id;
|
||||
const char *id;
|
||||
|
||||
const char *(*getname)(const char *locale);
|
||||
void *(*create)(obs_data_t settings, obs_service_t service);
|
||||
@ -28,9 +28,18 @@ struct obs_service_info {
|
||||
/* optional */
|
||||
void (*update)(void *data, obs_data_t settings);
|
||||
|
||||
/* get stream url/key */
|
||||
void (*defaults)(obs_data_t settings);
|
||||
|
||||
obs_properties_t (*properties)(const char *locale);
|
||||
|
||||
const char *(*get_url)(void *data);
|
||||
const char *(*get_key)(void *data);
|
||||
|
||||
/* send (current game/title/activate commercial/etc) */
|
||||
/* TODO: more stuff later */
|
||||
};
|
||||
|
||||
EXPORT void obs_register_service_s(const struct obs_service_info *info,
|
||||
size_t size);
|
||||
|
||||
#define obs_register_service(info) \
|
||||
obs_register_service_s(info, sizeof(struct obs_service_info))
|
||||
|
@ -28,8 +28,6 @@
|
||||
#include "obs.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
static void obs_source_destroy(obs_source_t source);
|
||||
|
||||
static inline const struct obs_source_info *find_source(struct darray *list,
|
||||
const char *id)
|
||||
{
|
||||
@ -84,17 +82,14 @@ static const char *source_signals[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
bool obs_source_init_handlers(struct obs_source *source)
|
||||
bool obs_source_init_context(struct obs_source *source,
|
||||
obs_data_t settings, const char *name)
|
||||
{
|
||||
source->signals = signal_handler_create();
|
||||
if (!source->signals)
|
||||
if (!obs_context_data_init(&source->context, settings, name))
|
||||
return false;
|
||||
|
||||
source->procs = proc_handler_create();
|
||||
if (!source->procs)
|
||||
return false;
|
||||
|
||||
return signal_handler_add_array(source->signals, source_signals);
|
||||
return signal_handler_add_array(source->context.signals,
|
||||
source_signals);
|
||||
}
|
||||
|
||||
const char *obs_source_getdisplayname(enum obs_source_type type,
|
||||
@ -127,14 +122,17 @@ bool obs_source_init(struct obs_source *source,
|
||||
|
||||
if (info->output_flags & OBS_SOURCE_AUDIO) {
|
||||
source->audio_line = audio_output_createline(obs->audio.audio,
|
||||
source->name);
|
||||
source->context.name);
|
||||
if (!source->audio_line) {
|
||||
blog(LOG_ERROR, "Failed to create audio line for "
|
||||
"source '%s'", source->name);
|
||||
"source '%s'", source->context.name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
obs_context_data_insert(&source->context,
|
||||
&obs->data.sources_mutex,
|
||||
&obs->data.first_source);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -148,22 +146,11 @@ static inline void obs_source_dosignal(struct obs_source *source,
|
||||
if (signal_obs)
|
||||
signal_handler_signal(obs->signals, signal_obs, &data);
|
||||
if (signal_source)
|
||||
signal_handler_signal(source->signals, signal_source, &data);
|
||||
signal_handler_signal(source->context.signals, signal_source,
|
||||
&data);
|
||||
calldata_free(&data);
|
||||
}
|
||||
|
||||
void source_init_name(struct obs_source *source, const char *name)
|
||||
{
|
||||
if (!name || !*name) {
|
||||
struct dstr unnamed = {0};
|
||||
dstr_printf(&unnamed, "__unnamed%004lld",
|
||||
obs->data.unnamed_index++);
|
||||
source->name = unnamed.array;
|
||||
} else {
|
||||
source->name = bstrdup(name);
|
||||
}
|
||||
}
|
||||
|
||||
obs_source_t obs_source_create(enum obs_source_type type, const char *id,
|
||||
const char *name, obs_data_t settings)
|
||||
{
|
||||
@ -177,17 +164,14 @@ obs_source_t obs_source_create(enum obs_source_type type, const char *id,
|
||||
|
||||
source = bzalloc(sizeof(struct obs_source));
|
||||
|
||||
if (!obs_source_init_handlers(source))
|
||||
if (!obs_source_init_context(source, settings, name))
|
||||
goto fail;
|
||||
|
||||
source_init_name(source, name);
|
||||
|
||||
source->settings = obs_data_newref(settings);
|
||||
if (info->defaults)
|
||||
info->defaults(source->settings);
|
||||
info->defaults(source->context.settings);
|
||||
|
||||
source->data = info->create(source->settings, source);
|
||||
if (!source->data)
|
||||
source->context.data = info->create(source->context.settings, source);
|
||||
if (!source->context.data)
|
||||
goto fail;
|
||||
|
||||
if (!obs_source_init(source, info))
|
||||
@ -221,13 +205,15 @@ void source_frame_init(struct source_frame *frame, enum video_format format,
|
||||
}
|
||||
}
|
||||
|
||||
static void obs_source_destroy(struct obs_source *source)
|
||||
void obs_source_destroy(struct obs_source *source)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
obs_context_data_remove(&source->context);
|
||||
|
||||
obs_source_dosignal(source, "source_destroy", "destroy");
|
||||
|
||||
if (source->filter_parent)
|
||||
@ -243,8 +229,8 @@ static void obs_source_destroy(struct obs_source *source)
|
||||
texture_destroy(source->async_texture);
|
||||
gs_leavecontext();
|
||||
|
||||
if (source->data)
|
||||
source->info.destroy(source->data);
|
||||
if (source->context.data)
|
||||
source->info.destroy(source->context.data);
|
||||
|
||||
for (i = 0; i < MAX_AV_PLANES; i++)
|
||||
bfree(source->audio_data.data[i]);
|
||||
@ -252,17 +238,13 @@ static void obs_source_destroy(struct obs_source *source)
|
||||
audio_line_destroy(source->audio_line);
|
||||
audio_resampler_destroy(source->resampler);
|
||||
|
||||
proc_handler_destroy(source->procs);
|
||||
signal_handler_destroy(source->signals);
|
||||
|
||||
texrender_destroy(source->filter_texrender);
|
||||
da_free(source->video_frames);
|
||||
da_free(source->filters);
|
||||
pthread_mutex_destroy(&source->filter_mutex);
|
||||
pthread_mutex_destroy(&source->audio_mutex);
|
||||
pthread_mutex_destroy(&source->video_mutex);
|
||||
obs_data_release(source->settings);
|
||||
bfree(source->name);
|
||||
obs_context_data_free(&source->context);
|
||||
bfree(source);
|
||||
}
|
||||
|
||||
@ -285,6 +267,7 @@ void obs_source_remove(obs_source_t source)
|
||||
{
|
||||
struct obs_core_data *data = &obs->data;
|
||||
size_t id;
|
||||
bool exists;
|
||||
|
||||
pthread_mutex_lock(&data->sources_mutex);
|
||||
|
||||
@ -297,15 +280,18 @@ void obs_source_remove(obs_source_t source)
|
||||
|
||||
obs_source_addref(source);
|
||||
|
||||
id = da_find(data->sources, &source, 0);
|
||||
if (id != DARRAY_INVALID) {
|
||||
da_erase_item(data->sources, &source);
|
||||
id = da_find(data->user_sources, &source, 0);
|
||||
exists = (id != DARRAY_INVALID);
|
||||
if (exists) {
|
||||
da_erase(data->user_sources, id);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&data->sources_mutex);
|
||||
|
||||
obs_source_dosignal(source, "source_remove", "remove");
|
||||
if (exists)
|
||||
obs_source_dosignal(source, "source_remove", "remove");
|
||||
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
@ -349,7 +335,7 @@ obs_properties_t obs_source_properties(obs_source_t source, const char *locale)
|
||||
if (source && source->info.properties) {
|
||||
obs_properties_t props;
|
||||
props = source->info.properties(locale);
|
||||
obs_properties_apply_settings(props, source->settings);
|
||||
obs_properties_apply_settings(props, source->context.settings);
|
||||
return props;
|
||||
}
|
||||
|
||||
@ -363,7 +349,7 @@ uint32_t obs_source_get_output_flags(obs_source_t source)
|
||||
|
||||
static void obs_source_deferred_update(obs_source_t source)
|
||||
{
|
||||
source->info.update(source->data, source->settings);
|
||||
source->info.update(source->context.data, source->context.settings);
|
||||
source->defer_update = false;
|
||||
}
|
||||
|
||||
@ -371,41 +357,42 @@ void obs_source_update(obs_source_t source, obs_data_t settings)
|
||||
{
|
||||
if (!source) return;
|
||||
|
||||
obs_data_apply(source->settings, settings);
|
||||
obs_data_apply(source->context.settings, settings);
|
||||
|
||||
if (source->info.update) {
|
||||
if (source->info.output_flags & OBS_SOURCE_VIDEO)
|
||||
source->defer_update = true;
|
||||
else
|
||||
source->info.update(source->data, source->settings);
|
||||
source->info.update(source->context.data,
|
||||
source->context.settings);
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_source(obs_source_t source)
|
||||
{
|
||||
if (source->info.activate)
|
||||
source->info.activate(source->data);
|
||||
source->info.activate(source->context.data);
|
||||
obs_source_dosignal(source, "source_activate", "activate");
|
||||
}
|
||||
|
||||
static void deactivate_source(obs_source_t source)
|
||||
{
|
||||
if (source->info.deactivate)
|
||||
source->info.deactivate(source->data);
|
||||
source->info.deactivate(source->context.data);
|
||||
obs_source_dosignal(source, "source_deactivate", "deactivate");
|
||||
}
|
||||
|
||||
static void show_source(obs_source_t source)
|
||||
{
|
||||
if (source->info.show)
|
||||
source->info.show(source->data);
|
||||
source->info.show(source->context.data);
|
||||
obs_source_dosignal(source, "source_show", "show");
|
||||
}
|
||||
|
||||
static void hide_source(obs_source_t source)
|
||||
{
|
||||
if (source->info.hide)
|
||||
source->info.hide(source->data);
|
||||
source->info.hide(source->context.data);
|
||||
obs_source_dosignal(source, "source_hide", "hide");
|
||||
}
|
||||
|
||||
@ -494,7 +481,7 @@ void obs_source_video_tick(obs_source_t source, float seconds)
|
||||
texrender_reset(source->filter_texrender);
|
||||
|
||||
if (source->info.video_tick)
|
||||
source->info.video_tick(source->data, seconds);
|
||||
source->info.video_tick(source->context.data, seconds);
|
||||
}
|
||||
|
||||
/* unless the value is 3+ hours worth of frames, this won't overflow */
|
||||
@ -526,7 +513,7 @@ static inline void handle_ts_jump(obs_source_t source, uint64_t expected,
|
||||
{
|
||||
blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', "
|
||||
"expected value %"PRIu64", input value %"PRIu64,
|
||||
source->name, diff, expected, ts);
|
||||
source->context.name, diff, expected, ts);
|
||||
|
||||
/* if has video, ignore audio data until reset */
|
||||
if (source->info.output_flags & OBS_SOURCE_ASYNC)
|
||||
@ -749,7 +736,7 @@ static inline void obs_source_default_render(obs_source_t source,
|
||||
passes = technique_begin(tech);
|
||||
for (i = 0; i < passes; i++) {
|
||||
technique_beginpass(tech, i);
|
||||
source->info.video_render(source->data, effect);
|
||||
source->info.video_render(source->context.data, effect);
|
||||
technique_endpass(tech);
|
||||
}
|
||||
technique_end(tech);
|
||||
@ -767,7 +754,7 @@ static inline void obs_source_main_render(obs_source_t source)
|
||||
if (default_effect)
|
||||
obs_source_default_render(source, color_matrix);
|
||||
else
|
||||
source->info.video_render(source->data,
|
||||
source->info.video_render(source->context.data,
|
||||
custom_draw ? NULL : gs_geteffect());
|
||||
}
|
||||
|
||||
@ -793,7 +780,7 @@ uint32_t obs_source_getwidth(obs_source_t source)
|
||||
if (!source) return 0;
|
||||
|
||||
if (source->info.getwidth)
|
||||
return source->info.getwidth(source->data);
|
||||
return source->info.getwidth(source->context.data);
|
||||
return source->async_width;
|
||||
}
|
||||
|
||||
@ -802,7 +789,7 @@ uint32_t obs_source_getheight(obs_source_t source)
|
||||
if (!source) return 0;
|
||||
|
||||
if (source->info.getheight)
|
||||
return source->info.getheight(source->data);
|
||||
return source->info.getheight(source->context.data);
|
||||
return source->async_height;
|
||||
}
|
||||
|
||||
@ -913,8 +900,8 @@ obs_data_t obs_source_getsettings(obs_source_t source)
|
||||
{
|
||||
if (!source) return NULL;
|
||||
|
||||
obs_data_addref(source->settings);
|
||||
return source->settings;
|
||||
obs_data_addref(source->context.settings);
|
||||
return source->context.settings;
|
||||
}
|
||||
|
||||
static inline struct source_frame *filter_async_video(obs_source_t source,
|
||||
@ -924,7 +911,8 @@ static inline struct source_frame *filter_async_video(obs_source_t source,
|
||||
for (i = source->filters.num; i > 0; i--) {
|
||||
struct obs_source *filter = source->filters.array[i-1];
|
||||
if (filter->info.filter_video) {
|
||||
in = filter->info.filter_video(filter->data, in);
|
||||
in = filter->info.filter_video(filter->context.data,
|
||||
in);
|
||||
if (!in)
|
||||
return NULL;
|
||||
}
|
||||
@ -1021,7 +1009,8 @@ static inline struct filtered_audio *filter_async_audio(obs_source_t source,
|
||||
for (i = source->filters.num; i > 0; i--) {
|
||||
struct obs_source *filter = source->filters.array[i-1];
|
||||
if (filter->info.filter_audio) {
|
||||
in = filter->info.filter_audio(filter->data, in);
|
||||
in = filter->info.filter_audio(filter->context.data,
|
||||
in);
|
||||
if (!in)
|
||||
return NULL;
|
||||
}
|
||||
@ -1263,15 +1252,13 @@ void obs_source_releaseframe(obs_source_t source, struct source_frame *frame)
|
||||
|
||||
const char *obs_source_getname(obs_source_t source)
|
||||
{
|
||||
return source ? source->name : NULL;
|
||||
return source ? source->context.name : NULL;
|
||||
}
|
||||
|
||||
void obs_source_setname(obs_source_t source, const char *name)
|
||||
{
|
||||
if (!source) return;
|
||||
|
||||
bfree(source->name);
|
||||
source->name = bstrdup(name);
|
||||
obs_context_data_setname(&source->context, name);
|
||||
}
|
||||
|
||||
void obs_source_gettype(obs_source_t source, enum obs_source_type *type,
|
||||
@ -1369,12 +1356,12 @@ void obs_source_process_filter(obs_source_t filter, effect_t effect,
|
||||
|
||||
signal_handler_t obs_source_signalhandler(obs_source_t source)
|
||||
{
|
||||
return source ? source->signals : NULL;
|
||||
return source ? source->context.signals : NULL;
|
||||
}
|
||||
|
||||
proc_handler_t obs_source_prochandler(obs_source_t source)
|
||||
{
|
||||
return source ? source->procs : NULL;
|
||||
return source ? source->context.procs : NULL;
|
||||
}
|
||||
|
||||
void obs_source_setvolume(obs_source_t source, float volume)
|
||||
@ -1384,7 +1371,7 @@ void obs_source_setvolume(obs_source_t source, float volume)
|
||||
calldata_setptr(&data, "source", source);
|
||||
calldata_setfloat(&data, "volume", volume);
|
||||
|
||||
signal_handler_signal(source->signals, "volume", &data);
|
||||
signal_handler_signal(source->context.signals, "volume", &data);
|
||||
signal_handler_signal(obs->signals, "source_volume", &data);
|
||||
|
||||
volume = (float)calldata_float(&data, "volume");
|
||||
@ -1451,7 +1438,7 @@ static void enum_source_tree_callback(obs_source_t parent, obs_source_t child,
|
||||
if (child->info.enum_sources && !child->enum_refs) {
|
||||
os_atomic_inc_long(&child->enum_refs);
|
||||
|
||||
child->info.enum_sources(child->data,
|
||||
child->info.enum_sources(child->context.data,
|
||||
enum_source_tree_callback, data);
|
||||
|
||||
os_atomic_dec_long(&child->enum_refs);
|
||||
@ -1470,7 +1457,7 @@ void obs_source_enum_sources(obs_source_t source,
|
||||
obs_source_addref(source);
|
||||
|
||||
os_atomic_inc_long(&source->enum_refs);
|
||||
source->info.enum_sources(source->data, enum_callback, param);
|
||||
source->info.enum_sources(source->context.data, enum_callback, param);
|
||||
os_atomic_dec_long(&source->enum_refs);
|
||||
|
||||
obs_source_release(source);
|
||||
@ -1488,7 +1475,8 @@ void obs_source_enum_tree(obs_source_t source,
|
||||
obs_source_addref(source);
|
||||
|
||||
os_atomic_inc_long(&source->enum_refs);
|
||||
source->info.enum_sources(source->data, enum_source_tree_callback,
|
||||
source->info.enum_sources(source->context.data,
|
||||
enum_source_tree_callback,
|
||||
&data);
|
||||
os_atomic_dec_long(&source->enum_refs);
|
||||
|
||||
|
@ -22,20 +22,28 @@
|
||||
|
||||
static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time)
|
||||
{
|
||||
size_t i;
|
||||
uint64_t delta_time;
|
||||
float seconds;
|
||||
struct obs_core_data *data = &obs->data;
|
||||
struct obs_source *source;
|
||||
uint64_t delta_time;
|
||||
float seconds;
|
||||
|
||||
if (!last_time)
|
||||
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->data.sources.num; i++)
|
||||
obs_source_video_tick(obs->data.sources.array[i], seconds);
|
||||
pthread_mutex_lock(&data->sources_mutex);
|
||||
|
||||
last_time = cur_time;
|
||||
return last_time;
|
||||
source = data->first_source;
|
||||
while (source) {
|
||||
if (source->refs)
|
||||
obs_source_video_tick(source, seconds);
|
||||
source = (struct obs_source*)source->context.next;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&data->sources_mutex);
|
||||
|
||||
return cur_time;
|
||||
}
|
||||
|
||||
/* in obs-display.c */
|
||||
@ -43,6 +51,8 @@ extern void render_display(struct obs_display *display);
|
||||
|
||||
static inline void render_displays(void)
|
||||
{
|
||||
struct obs_display *display;
|
||||
|
||||
if (!obs->data.valid)
|
||||
return;
|
||||
|
||||
@ -51,8 +61,11 @@ static inline void render_displays(void)
|
||||
/* render extra displays/swaps */
|
||||
pthread_mutex_lock(&obs->data.displays_mutex);
|
||||
|
||||
for (size_t i = 0; i < obs->data.displays.num; i++)
|
||||
render_display(obs->data.displays.array[i]);
|
||||
display = obs->data.first_display;
|
||||
while (display) {
|
||||
render_display(display);
|
||||
display = display->next;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&obs->data.displays_mutex);
|
||||
|
||||
|
251
libobs/obs.c
251
libobs/obs.c
@ -395,6 +395,8 @@ static bool obs_init_data(void)
|
||||
return false;
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&data->user_sources_mutex, &attr) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&data->sources_mutex, &attr) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&data->displays_mutex, &attr) != 0)
|
||||
@ -403,6 +405,8 @@ static bool obs_init_data(void)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&data->encoders_mutex, &attr) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&data->services_mutex, &attr) != 0)
|
||||
goto fail;
|
||||
if (!obs_view_init(&data->main_view))
|
||||
goto fail;
|
||||
|
||||
@ -413,32 +417,48 @@ fail:
|
||||
return data->valid;
|
||||
}
|
||||
|
||||
#define FREE_OBS_LINKED_LIST(type) \
|
||||
do { \
|
||||
int unfreed = 0; \
|
||||
while (data->first_ ## type ) { \
|
||||
obs_ ## type ## _destroy(data->first_ ## type ); \
|
||||
unfreed++; \
|
||||
} \
|
||||
if (unfreed) \
|
||||
blog(LOG_INFO, "\t%d " #type "(s) were remaining", \
|
||||
unfreed); \
|
||||
} while (false)
|
||||
|
||||
static void obs_free_data(void)
|
||||
{
|
||||
struct obs_core_data *data = &obs->data;
|
||||
uint32_t i;
|
||||
|
||||
data->valid = false;
|
||||
|
||||
obs_view_free(&data->main_view);
|
||||
|
||||
while (data->outputs.num)
|
||||
obs_output_destroy(data->outputs.array[0]);
|
||||
while (data->encoders.num)
|
||||
obs_encoder_destroy(data->encoders.array[0]);
|
||||
while (data->displays.num)
|
||||
obs_display_destroy(data->displays.array[0]);
|
||||
blog(LOG_INFO, "Freeing OBS context data");
|
||||
|
||||
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);
|
||||
if (data->user_sources.num)
|
||||
blog(LOG_INFO, "\t%d user source(s) were remaining",
|
||||
(int)data->user_sources.num);
|
||||
|
||||
while (data->user_sources.num)
|
||||
obs_source_remove(data->user_sources.array[0]);
|
||||
da_free(data->user_sources);
|
||||
|
||||
FREE_OBS_LINKED_LIST(source);
|
||||
FREE_OBS_LINKED_LIST(output);
|
||||
FREE_OBS_LINKED_LIST(encoder);
|
||||
FREE_OBS_LINKED_LIST(display);
|
||||
FREE_OBS_LINKED_LIST(service);
|
||||
|
||||
pthread_mutex_destroy(&data->user_sources_mutex);
|
||||
pthread_mutex_destroy(&data->sources_mutex);
|
||||
pthread_mutex_destroy(&data->displays_mutex);
|
||||
pthread_mutex_destroy(&data->outputs_mutex);
|
||||
pthread_mutex_destroy(&data->encoders_mutex);
|
||||
pthread_mutex_destroy(&data->services_mutex);
|
||||
}
|
||||
|
||||
static const char *obs_signals[] = {
|
||||
@ -522,11 +542,6 @@ void obs_shutdown(void)
|
||||
free_module(obs->modules.array+i);
|
||||
da_free(obs->modules);
|
||||
|
||||
da_free(obs->data.sources);
|
||||
da_free(obs->data.outputs);
|
||||
da_free(obs->data.encoders);
|
||||
da_free(obs->data.displays);
|
||||
|
||||
bfree(obs);
|
||||
obs = NULL;
|
||||
}
|
||||
@ -655,6 +670,26 @@ bool obs_enum_output_types(size_t idx, const char **id)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obs_enum_encoder_types(size_t idx, const char **id)
|
||||
{
|
||||
if (!obs) return false;
|
||||
|
||||
if (idx >= obs->encoder_types.num)
|
||||
return false;
|
||||
*id = obs->encoder_types.array[idx].id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obs_enum_service_types(size_t idx, const char **id)
|
||||
{
|
||||
if (!obs) return false;
|
||||
|
||||
if (idx >= obs->service_types.num)
|
||||
return false;
|
||||
*id = obs->service_types.array[idx].id;
|
||||
return true;
|
||||
}
|
||||
|
||||
graphics_t obs_graphics(void)
|
||||
{
|
||||
return (obs != NULL) ? obs->video.graphics : NULL;
|
||||
@ -737,7 +772,7 @@ bool obs_add_source(obs_source_t source)
|
||||
if (!obs) return false;
|
||||
|
||||
pthread_mutex_lock(&obs->data.sources_mutex);
|
||||
da_push_back(obs->data.sources, &source);
|
||||
da_push_back(obs->data.user_sources, &source);
|
||||
obs_source_addref(source);
|
||||
pthread_mutex_unlock(&obs->data.sources_mutex);
|
||||
|
||||
@ -791,49 +826,63 @@ void obs_set_output_source(uint32_t channel, obs_source_t source)
|
||||
}
|
||||
}
|
||||
|
||||
void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t), void *param)
|
||||
void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t), void *param)
|
||||
{
|
||||
struct obs_core_data *data = &obs->data;
|
||||
|
||||
if (!obs) return;
|
||||
|
||||
pthread_mutex_lock(&data->outputs_mutex);
|
||||
pthread_mutex_lock(&obs->data.user_sources_mutex);
|
||||
|
||||
for (size_t i = 0; i < data->outputs.num; i++)
|
||||
if (!enum_proc(param, data->outputs.array[i]))
|
||||
for (size_t i = 0; i < obs->data.user_sources.num; i++) {
|
||||
struct obs_source *source = obs->data.user_sources.array[i];
|
||||
if (!enum_proc(param, source))
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&obs->data.user_sources_mutex);
|
||||
}
|
||||
|
||||
static inline void obs_enum(void *pstart, pthread_mutex_t *mutex, void *proc,
|
||||
void *param)
|
||||
{
|
||||
struct obs_context_data **start = pstart, *context;
|
||||
bool (*enum_proc)(void*, void*) = proc;
|
||||
|
||||
assert(start);
|
||||
assert(mutex);
|
||||
assert(enum_proc);
|
||||
|
||||
pthread_mutex_lock(mutex);
|
||||
|
||||
context = *start;
|
||||
while (context) {
|
||||
if (!enum_proc(param, context))
|
||||
break;
|
||||
|
||||
pthread_mutex_unlock(&data->outputs_mutex);
|
||||
context = context->next;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t), void *param)
|
||||
{
|
||||
if (!obs) return;
|
||||
obs_enum(&obs->data.first_output, &obs->data.outputs_mutex,
|
||||
enum_proc, param);
|
||||
}
|
||||
|
||||
void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t), void *param)
|
||||
{
|
||||
struct obs_core_data *data = &obs->data;
|
||||
|
||||
if (!obs) return;
|
||||
|
||||
pthread_mutex_lock(&data->encoders_mutex);
|
||||
|
||||
for (size_t i = 0; i < data->encoders.num; i++)
|
||||
if (!enum_proc(param, data->encoders.array[i]))
|
||||
break;
|
||||
|
||||
pthread_mutex_unlock(&data->encoders_mutex);
|
||||
obs_enum(&obs->data.first_encoder, &obs->data.encoders_mutex,
|
||||
enum_proc, param);
|
||||
}
|
||||
|
||||
void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t), void *param)
|
||||
void obs_enum_services(bool (*enum_proc)(void*, obs_service_t), void *param)
|
||||
{
|
||||
struct obs_core_data *data = &obs->data;
|
||||
|
||||
if (!obs) return;
|
||||
|
||||
pthread_mutex_lock(&data->sources_mutex);
|
||||
|
||||
for (size_t i = 0; i < data->sources.num; i++)
|
||||
if (!enum_proc(param, data->sources.array[i]))
|
||||
break;
|
||||
|
||||
pthread_mutex_unlock(&data->sources_mutex);
|
||||
obs_enum(&obs->data.first_service, &obs->data.services_mutex,
|
||||
enum_proc, param);
|
||||
}
|
||||
|
||||
obs_source_t obs_get_source_by_name(const char *name)
|
||||
@ -844,18 +893,18 @@ obs_source_t obs_get_source_by_name(const char *name)
|
||||
|
||||
if (!obs) return NULL;
|
||||
|
||||
pthread_mutex_lock(&data->sources_mutex);
|
||||
pthread_mutex_lock(&data->user_sources_mutex);
|
||||
|
||||
for (i = 0; i < data->sources.num; i++) {
|
||||
struct obs_source *cur_source = data->sources.array[i];
|
||||
if (strcmp(cur_source->name, name) == 0) {
|
||||
for (i = 0; i < data->user_sources.num; i++) {
|
||||
struct obs_source *cur_source = data->user_sources.array[i];
|
||||
if (strcmp(cur_source->context.name, name) == 0) {
|
||||
source = cur_source;
|
||||
obs_source_addref(source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&data->sources_mutex);
|
||||
pthread_mutex_unlock(&data->user_sources_mutex);
|
||||
return source;
|
||||
}
|
||||
|
||||
@ -936,3 +985,103 @@ float obs_get_present_volume(void)
|
||||
{
|
||||
return obs ? obs->audio.present_volume : 0.0f;
|
||||
}
|
||||
|
||||
/* ensures that names are never blank */
|
||||
static inline char *dup_name(const char *name)
|
||||
{
|
||||
if (!name || !*name) {
|
||||
struct dstr unnamed = {0};
|
||||
dstr_printf(&unnamed, "__unnamed%004lld",
|
||||
obs->data.unnamed_index++);
|
||||
|
||||
return unnamed.array;
|
||||
} else {
|
||||
return bstrdup(name);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool obs_context_data_init_wrap(
|
||||
struct obs_context_data *context,
|
||||
obs_data_t settings,
|
||||
const char *name)
|
||||
{
|
||||
assert(context);
|
||||
|
||||
obs_context_data_free(context);
|
||||
|
||||
context->signals = signal_handler_create();
|
||||
if (!context)
|
||||
return false;
|
||||
|
||||
context->procs = proc_handler_create();
|
||||
if (!context->procs)
|
||||
return false;
|
||||
|
||||
context->name = dup_name(name);
|
||||
context->settings = obs_data_newref(settings);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obs_context_data_init(
|
||||
struct obs_context_data *context,
|
||||
obs_data_t settings,
|
||||
const char *name)
|
||||
{
|
||||
if (obs_context_data_init_wrap(context, settings, name)) {
|
||||
return true;
|
||||
} else {
|
||||
obs_context_data_free(context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void obs_context_data_free(struct obs_context_data *context)
|
||||
{
|
||||
signal_handler_destroy(context->signals);
|
||||
proc_handler_destroy(context->procs);
|
||||
obs_data_release(context->settings);
|
||||
obs_context_data_remove(context);
|
||||
bfree(context->name);
|
||||
|
||||
memset(context, 0, sizeof(*context));
|
||||
}
|
||||
|
||||
void obs_context_data_insert(struct obs_context_data *context,
|
||||
pthread_mutex_t *mutex, void *pfirst)
|
||||
{
|
||||
struct obs_context_data **first = pfirst;
|
||||
|
||||
assert(context);
|
||||
assert(mutex);
|
||||
assert(first);
|
||||
|
||||
context->mutex = mutex;
|
||||
|
||||
pthread_mutex_lock(mutex);
|
||||
context->prev_next = first;
|
||||
context->next = *first;
|
||||
*first = context;
|
||||
if (context->next)
|
||||
context->next->prev_next = &context->next;
|
||||
pthread_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
void obs_context_data_remove(struct obs_context_data *context)
|
||||
{
|
||||
if (context && context->mutex) {
|
||||
pthread_mutex_lock(context->mutex);
|
||||
*context->prev_next = context->next;
|
||||
if (context->next)
|
||||
context->next->prev_next = context->prev_next;
|
||||
pthread_mutex_unlock(context->mutex);
|
||||
|
||||
context->mutex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void obs_context_data_setname(struct obs_context_data *context,
|
||||
const char *name)
|
||||
{
|
||||
bfree(context->name);
|
||||
context->name = dup_name(name);
|
||||
}
|
||||
|
36
libobs/obs.h
36
libobs/obs.h
@ -234,14 +234,15 @@ EXPORT bool obs_enum_filter_types(size_t idx, const char **id);
|
||||
*/
|
||||
EXPORT bool obs_enum_transition_types(size_t idx, const char **id);
|
||||
|
||||
/**
|
||||
* Enumerates all available ouput types.
|
||||
*
|
||||
* Outputs handle color space conversion, encoding, and output to file or
|
||||
* streams.
|
||||
*/
|
||||
/** Enumerates all available output types. */
|
||||
EXPORT bool obs_enum_output_types(size_t idx, const char **id);
|
||||
|
||||
/** Enumerates all available encoder types. */
|
||||
EXPORT bool obs_enum_encoder_types(size_t idx, const char **id);
|
||||
|
||||
/** Enumerates all available service types. */
|
||||
EXPORT bool obs_enum_service_types(size_t idx, const char **id);
|
||||
|
||||
/** Gets the main graphics context for this OBS context */
|
||||
EXPORT graphics_t obs_graphics(void);
|
||||
|
||||
@ -864,13 +865,34 @@ EXPORT void obs_free_encoder_packet(struct encoder_packet *packet);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Stream Services */
|
||||
|
||||
EXPORT const char *obs_service_getdisplayname(const char *id,
|
||||
const char *locale);
|
||||
|
||||
EXPORT obs_service_t obs_service_create(const char *service,
|
||||
EXPORT obs_service_t obs_service_create(const char *id, const char *name,
|
||||
obs_data_t settings);
|
||||
EXPORT void obs_service_destroy(obs_service_t service);
|
||||
|
||||
/** Gets the default settings for a service */
|
||||
EXPORT obs_data_t obs_service_defaults(const char *id);
|
||||
|
||||
/** Returns the property list, if any. Free with obs_properties_destroy */
|
||||
EXPORT obs_properties_t obs_get_service_properties(const char *id,
|
||||
const char *locale);
|
||||
|
||||
/**
|
||||
* Returns the property list of an existing service context, if any. Free with
|
||||
* obs_properties_destroy
|
||||
*/
|
||||
EXPORT obs_properties_t obs_service_properties(obs_service_t service,
|
||||
const char *locale);
|
||||
|
||||
/** Returns the URL for this service context */
|
||||
const char *obs_service_get_url(obs_service_t service);
|
||||
|
||||
/** Returns the stream key (if any) for this service context */
|
||||
const char *obs_service_get_key(obs_service_t service);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Source frame allocation functions */
|
||||
|
@ -109,6 +109,7 @@
|
||||
<ClCompile Include="..\..\..\libobs\obs-output.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-properties.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-scene.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-service.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-source.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-video.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-view.c" />
|
||||
@ -210,7 +211,7 @@
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
@ -231,7 +232,7 @@
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
@ -256,7 +257,7 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
@ -281,7 +282,7 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
|
@ -380,5 +380,8 @@
|
||||
<ClCompile Include="..\..\..\libobs\obs-avc.c">
|
||||
<Filter>libobs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\libobs\obs-service.c">
|
||||
<Filter>libobs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user