Revamp API and start using doxygen

The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc.  You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.

The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used.  It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:

 1.) Requiring exports to create sources/outputs/encoders/etc meant that
     you could not create them by any other means, which meant that
     things like faruton's .net plugin would become difficult.

 2.) Export function declarations could not be checked, therefore if you
     created a function with the wrong parameters and parameter types,
     the compiler wouldn't know how to check for that.

 3.) Required overly complex load functions in libobs just to handle it.
     It makes much more sense to just have a load function that you call
     manually.  Complexity is the bane of all good programs.

 4.) It required that you have functions of specific names, which looked
     and felt somewhat unsightly.

So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction.  You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.

It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.

The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.

Also, started writing some doxygen documentation in to the main library
headers.  Will add more detailed documentation as I go.
This commit is contained in:
jp9000 2014-02-12 08:04:50 -07:00
parent 524ff94912
commit 8e81d8be56
42 changed files with 1120 additions and 1409 deletions

View file

@ -1,6 +1,6 @@
uniform float4x4 ViewProj;
uniform float4x4 color_matrix;
uniform texture2d diffuse;
uniform texture2d image;
sampler_state def_sampler {
Filter = Linear;
@ -23,12 +23,12 @@ VertInOut VSDefault(VertInOut vert_in)
float4 PSDrawBare(VertInOut vert_in) : TARGET
{
return diffuse.Sample(def_sampler, vert_in.uv);
return image.Sample(def_sampler, vert_in.uv);
}
float4 PSDrawMatrix(VertInOut vert_in) : TARGET
{
float4 yuv = diffuse.Sample(def_sampler, vert_in.uv);
float4 yuv = image.Sample(def_sampler, vert_in.uv);
return saturate(mul(float4(yuv.xyz, 1.0), color_matrix));
}

View file

@ -253,8 +253,24 @@ eparam_t effect_getworldmatrix(effect_t effect)
static inline void effect_setval_inline(effect_t effect, eparam_t param,
const void *data, size_t size)
{
bool size_changed = param->cur_val.num != size;
bool size_changed;
if (!effect) {
blog(LOG_WARNING, "effect_setval_inline: invalid effect");
return;
}
if (!param) {
blog(LOG_WARNING, "effect_setval_inline: invalid param");
return;
}
if (!data) {
blog(LOG_WARNING, "effect_setval_inline: invalid data");
return;
}
size_changed = param->cur_val.num != size;
if (!matching_effect(effect, param))
return;

View file

@ -19,15 +19,8 @@
#include <xmmintrin.h>
#include <emmintrin.h>
static FORCE_INLINE uint32_t get_m128_32_0(const __m128i val)
{
return *(uint32_t* const)&val;
}
static FORCE_INLINE uint32_t get_m128_32_1(const __m128i val)
{
return *(((uint32_t* const)&val)+1);
}
#define get_m128_32_0(val) (*((uint32_t*)&val))
#define get_m128_32_1(val) (*(((uint32_t*)&val)+1))
static FORCE_INLINE void pack_lum(uint8_t *lum_plane,
uint32_t lum_pos0, uint32_t lum_pos1,

View file

@ -17,17 +17,11 @@
#pragma once
/* Maximum number of source channels for output and per display */
/** Maximum number of source channels for output and per display */
#define MAX_CHANNELS 32
#define MODULE_SUCCESS 0
#define MODULE_ERROR -1
#define MODULE_FILENOTFOUND -2
#define MODULE_FUNCTIONNOTFOUND -3
#define MODULE_INCOMPATIBLE_VER -4
#define SOURCE_VIDEO (1<<0) /* Source has video */
#define SOURCE_AUDIO (1<<1) /* Source has audio */
#define SOURCE_ASYNC_VIDEO (1<<2) /* Async video (use with SOURCE_VIDEO) */
#define SOURCE_DEFAULT_EFFECT (1<<3) /* Source uses default/filter effect */
#define SOURCE_YUV (1<<4) /* Source is in YUV color space */
#define MODULE_SUCCESS 0
#define MODULE_ERROR -1
#define MODULE_FILE_NOT_FOUND -2
#define MODULE_FUNCTION_NOT_FOUND -3
#define MODULE_INCOMPATIBLE_VER -4

View file

@ -18,29 +18,10 @@
#include "obs.h"
#include "obs-internal.h"
bool load_encoder_info(void *module, const char *module_name,
const char *id, struct encoder_info *info)
{
LOAD_MODULE_SUBFUNC(getname, true);
LOAD_MODULE_SUBFUNC(create, true);
LOAD_MODULE_SUBFUNC(destroy, true);
LOAD_MODULE_SUBFUNC(update, true);
LOAD_MODULE_SUBFUNC(reset, true);
LOAD_MODULE_SUBFUNC(encode, true);
LOAD_MODULE_SUBFUNC(getheader, true);
LOAD_MODULE_SUBFUNC(properties, false);
LOAD_MODULE_SUBFUNC(setbitrate, false);
LOAD_MODULE_SUBFUNC(request_keyframe, false);
info->id = id;
return true;
}
static inline struct encoder_info *get_encoder_info(const char *id)
static inline struct obs_encoder_info *get_encoder_info(const char *id)
{
for (size_t i = 0; i < obs->encoder_types.num; i++) {
struct encoder_info *info = obs->encoder_types.array+i;
struct obs_encoder_info *info = obs->encoder_types.array+i;
if (strcmp(info->id, id) == 0)
return info;
@ -51,7 +32,7 @@ static inline struct encoder_info *get_encoder_info(const char *id)
const char *obs_encoder_getdisplayname(const char *id, const char *locale)
{
struct encoder_info *ei = get_encoder_info(id);
struct obs_encoder_info *ei = get_encoder_info(id);
if (!ei)
return NULL;
@ -62,13 +43,13 @@ obs_encoder_t obs_encoder_create(const char *id, const char *name,
obs_data_t settings)
{
struct obs_encoder *encoder;
struct encoder_info *ei = get_encoder_info(id);
struct obs_encoder_info *ei = get_encoder_info(id);
if (!ei)
return NULL;
encoder = bzalloc(sizeof(struct obs_encoder));
encoder->callbacks = *ei;
encoder->info = *ei;
if (pthread_mutex_init(&encoder->data_callbacks_mutex, NULL) != 0) {
bfree(encoder);
@ -98,7 +79,7 @@ void obs_encoder_destroy(obs_encoder_t encoder)
da_erase_item(obs->data.encoders, &encoder);
pthread_mutex_unlock(&obs->data.encoders_mutex);
encoder->callbacks.destroy(encoder->data);
encoder->info.destroy(encoder->data);
obs_data_release(encoder->settings);
bfree(encoder);
}
@ -106,7 +87,7 @@ void obs_encoder_destroy(obs_encoder_t encoder)
obs_properties_t obs_encoder_properties(const char *id, const char *locale)
{
const struct encoder_info *ei = get_encoder_info(id);
const struct obs_encoder_info *ei = get_encoder_info(id);
if (ei && ei->properties)
return ei->properties(locale);
return NULL;
@ -115,40 +96,40 @@ obs_properties_t obs_encoder_properties(const char *id, const char *locale)
void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings)
{
obs_data_replace(&encoder->settings, settings);
encoder->callbacks.update(encoder->data, encoder->settings);
encoder->info.update(encoder->data, encoder->settings);
}
bool obs_encoder_reset(obs_encoder_t encoder)
{
return encoder->callbacks.reset(encoder->data);
return encoder->info.reset(encoder->data);
}
bool obs_encoder_encode(obs_encoder_t encoder, void *frames, size_t size)
{
/* TODO */
//encoder->callbacks.encode(encoder->data, frames, size, packets);
//encoder->info.encode(encoder->data, frames, size, packets);
return false;
}
int obs_encoder_getheader(obs_encoder_t encoder,
struct encoder_packet **packets)
{
return encoder->callbacks.getheader(encoder, packets);
return encoder->info.getheader(encoder, packets);
}
bool obs_encoder_setbitrate(obs_encoder_t encoder, uint32_t bitrate,
uint32_t buffersize)
{
if (encoder->callbacks.setbitrate)
return encoder->callbacks.setbitrate(encoder->data, bitrate,
if (encoder->info.setbitrate)
return encoder->info.setbitrate(encoder->data, bitrate,
buffersize);
return false;
}
bool obs_encoder_request_keyframe(obs_encoder_t encoder)
{
if (encoder->callbacks.request_keyframe)
return encoder->callbacks.request_keyframe(encoder->data);
if (encoder->info.request_keyframe)
return encoder->info.request_keyframe(encoder->data);
return false;
}

View file

@ -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
@ -17,125 +17,14 @@
#pragma once
#include "util/c99defs.h"
#include "util/dstr.h"
/*
* ===========================================
* Encoders
* ===========================================
*
* An encoder context allows data to be encoded from raw output, and allow
* it to be used to output contexts (such as outputting to stream).
*
* A module with encoders needs to export these functions:
* + enum_encoders
*
* Each individual encoder is then exported by it's name. For example, an
* encoder named "myencoder" would have the following exports:
* + myencoder_getname
* + myencoder_create
* + myencoder_destroy
* + myencoder_update
* + myencoder_reset
* + myencoder_encode
* + myencoder_getheader
*
* [and optionally]
* + myencoder_properties
* + myencoder_setbitrate
* + myencoder_request_keyframe
*
* ===========================================
* Primary Exports
* ===========================================
* const char *enum_encoders(size_t idx);
* idx: index of the encoder.
* Return value: Encoder identifier name. NULL when no more available.
*
* ===========================================
* Encoder Exports
* ===========================================
* const char *[name]_getname(const char *locale);
* Returns the full translated name of the encoder type
* (seen by the user).
*
* ---------------------------------------------------------
* void *[name]_create(obs_data_t settings, const char *name,
* obs_encoder_t encoder);
* Creates an encoder.
*
* settings: Settings of the encoder.
* name: Name of the encoder.
* encoder: Pointer to encoder context.
* Return value: Internal encoder pointer, or NULL if failed.
*
* ---------------------------------------------------------
* void [name]_destroy(void *data);
* Destroys the encoder.
*
* ---------------------------------------------------------
* void [name]_update(void *data, obs_data_t settings)
* Updates the encoder's settings
*
* settings: New settings of the encoder
*
* ---------------------------------------------------------
* bool [name]_reset(void *data)
* Restarts encoder
*
* Return value: true if successful
*
* ---------------------------------------------------------
* int [name]_encode(void *data, void *frames, size_t size,
* struct encoder_packet **packets)
* Encodes data.
*
* frames: frame data
* size: size of data pointed to by the frame parameter
* packets: returned packets, or NULL if none
* Return value: number of encoder frames
*
* ---------------------------------------------------------
* int [name]_getheader(void *data, struct encoder_packet **packets)
* Returns the header packets for this encoder.
*
* packets: returned packets, or NULL if none
* Return value: number of encoder frames
*
* ===========================================
* Optional Encoder Exports
* ===========================================
* obs_properties_t [name]_properties(const char *locale);
* Returns the properties of this particular encoder type, if any.
*
* ---------------------------------------------------------
* bool [name]_setbitrate(void *data, uint32_t bitrate, uint32_t buffersize);
* Sets the bitrate of the encoder
*
* bitrate: Bitrate
* buffersize: Buffer size
* Returns true if successful/compatible
*
* ---------------------------------------------------------
* bool [name]_request_keyframe(void *data)
* Requests a keyframe from the encoder
*
* Returns true if successful/compatible.
*/
struct obs_encoder;
struct encoder_info {
struct obs_encoder_info {
const char *id;
const char *(*getname)(const char *locale);
void *(*create)(obs_data_t settings, struct obs_encoder *encoder);
void *(*create)(obs_data_t settings, obs_encoder_t encoder);
void (*destroy)(void *data);
void (*update)(void *data, obs_data_t settings);
bool (*reset)(void *data);
int (*encode)(void *data, void *frames, size_t size,
@ -143,26 +32,12 @@ struct encoder_info {
int (*getheader)(void *data, struct encoder_packet **packets);
/* optional */
void (*update)(void *data, obs_data_t settings);
obs_properties_t (*properties)(const char *locale);
bool (*setbitrate)(void *data, uint32_t bitrate, uint32_t buffersize);
bool (*request_keyframe)(void *data);
};
struct obs_encoder_callback {
void (*new_packet)(void *param, struct encoder_packet *packet);
void *param;
};
struct obs_encoder {
char *name;
void *data;
struct encoder_info callbacks;
obs_data_t settings;
pthread_mutex_t data_callbacks_mutex;
DARRAY(struct obs_encoder_callback) data_callbacks;
};
extern bool load_encoder_info(void *module, const char *module_name,
const char *encoder_name, struct encoder_info *info);
EXPORT void obs_register_encoder(const struct obs_encoder_info *info);

View file

@ -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
@ -17,39 +17,37 @@
#pragma once
#include "util/c99defs.h"
#include "util/darray.h"
#include "util/dstr.h"
#include "util/threading.h"
#include "callback/signal.h"
#include "callback/proc.h"
#include "graphics/graphics.h"
#include "media-io/audio-resampler.h"
#include "media-io/video-io.h"
#include "media-io/audio-io.h"
#include "obs.h"
#include "obs-module.h"
#include "obs-source.h"
#include "obs-output.h"
#include "obs-service.h"
#include "obs-encoder.h"
#define LOAD_MODULE_SUBFUNC(name, required) \
do { \
info->name = load_module_subfunc(module, module_name, \
id, #name, required); \
if (required && !info->name) \
return false; \
} while (false)
#define NUM_TEXTURES 2
struct obs_display {
swapchain_t swap; /* can be NULL if just sound */
obs_source_t channels[MAX_CHANNELS];
/* TODO: sound output target */
};
/* ------------------------------------------------------------------------- */
/* core */
struct obs_module {
char *name;
void *module;
};
extern void free_module(struct obs_module *mod);
/* ------------------------------------------------------------------------- */
/* core */
struct obs_core_video {
graphics_t graphics;
@ -96,26 +94,129 @@ struct obs_core_data {
};
struct obs_core {
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 encoder_info) encoder_types;
DARRAY(struct service_info) service_types;
DARRAY(struct obs_modal_ui) ui_modal_callbacks;
DARRAY(struct obs_modeless_ui) ui_modeless_callbacks;
DARRAY(struct obs_module) modules;
DARRAY(struct obs_source_info) input_types;
DARRAY(struct obs_source_info) filter_types;
DARRAY(struct obs_source_info) transition_types;
DARRAY(struct obs_output_info) output_types;
DARRAY(struct obs_encoder_info) encoder_types;
DARRAY(struct obs_service_info) service_types;
DARRAY(struct obs_modal_ui) modal_ui_callbacks;
DARRAY(struct obs_modeless_ui) modeless_ui_callbacks;
signal_handler_t signals;
proc_handler_t procs;
signal_handler_t signals;
proc_handler_t procs;
/* segmented into multiple sub-structures to keep things a bit more
* clean and organized */
struct obs_core_video video;
struct obs_core_audio audio;
struct obs_core_data data;
struct obs_core_video video;
struct obs_core_audio audio;
struct obs_core_data data;
};
extern struct obs_core *obs;
extern void *obs_video_thread(void *param);
/* ------------------------------------------------------------------------- */
/* displays */
struct obs_display {
swapchain_t swap; /* can be NULL if just sound */
obs_source_t channels[MAX_CHANNELS];
/* TODO: sound output target */
};
/* ------------------------------------------------------------------------- */
/* sources */
struct obs_source {
volatile int refs;
struct obs_source_info info;
/* source-specific data */
char *name; /* user-defined name */
enum obs_source_type type;
obs_data_t settings;
void *data;
signal_handler_t signals;
proc_handler_t procs;
/* 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;
/* timing (if video is present, is based upon video) */
volatile bool timing_set;
volatile uint64_t timing_adjust;
volatile int audio_reset_ref;
uint64_t next_audio_ts_min;
uint64_t last_frame_ts;
uint64_t last_sys_timestamp;
/* audio */
bool audio_failed;
struct resample_info sample_info;
audio_resampler_t resampler;
audio_line_t audio_line;
pthread_mutex_t audio_mutex;
struct filtered_audio audio_data;
size_t audio_storage_size;
float volume;
/* async video data */
texture_t output_texture;
DARRAY(struct source_frame*) video_frames;
pthread_mutex_t video_mutex;
/* filters */
struct obs_source *filter_parent;
struct obs_source *filter_target;
DARRAY(struct obs_source*) filters;
pthread_mutex_t filter_mutex;
texrender_t filter_texrender;
bool rendering_filter;
};
bool obs_source_init_handlers(struct obs_source *source);
extern bool obs_source_init(struct obs_source *source,
const struct obs_source_info *info);
extern void obs_source_activate(obs_source_t source);
extern void obs_source_deactivate(obs_source_t source);
extern void obs_source_video_tick(obs_source_t source, float seconds);
/* ------------------------------------------------------------------------- */
/* outputs */
struct obs_output {
char *name;
void *data;
struct obs_output_info info;
obs_data_t settings;
};
/* ------------------------------------------------------------------------- */
/* encoders */
struct obs_encoder_callback {
void (*new_packet)(void *param, struct encoder_packet *packet);
void *param;
};
struct obs_encoder {
char *name;
void *data;
struct obs_encoder_info info;
obs_data_t settings;
pthread_mutex_t data_callbacks_mutex;
DARRAY(struct obs_encoder_callback) data_callbacks;
};

View file

@ -22,133 +22,71 @@
#include "obs-internal.h"
#include "obs-module.h"
void *load_module_subfunc(void *module, const char *module_name,
const char *name, const char *func, bool required)
{
struct dstr func_name;
void *func_addr = NULL;
dstr_init_copy(&func_name, name);
dstr_cat(&func_name, "_");
dstr_cat(&func_name, func);
func_addr = os_dlsym(module, func_name.array);
if (required && !func_addr)
blog(LOG_ERROR, "Could not load function '%s' from module '%s'",
func_name.array, module_name);
dstr_free(&func_name);
return func_addr;
}
static void module_load_exports(struct obs_module *mod,
struct darray *output_array, const char *type,
const size_t data_size, void *callback_ptr)
{
bool (*enum_func)(size_t idx, const char **name);
bool (*callback)(void*, const char*, const char*, void*);
struct dstr enum_name;
const char *name;
size_t i = 0;
callback = callback_ptr;
dstr_init_copy(&enum_name, "enum_");
dstr_cat(&enum_name, type);
enum_func = os_dlsym(mod->module, enum_name.array);
if (!enum_func)
goto complete;
while (enum_func(i++, &name)) {
void *info = bmalloc(data_size);
if (!callback(mod->module, mod->name, name, info))
blog(LOG_ERROR, "Couldn't load '%s' because it "
"was missing required functions",
name);
else
darray_push_back(data_size, output_array, info);
bfree(info);
}
complete:
dstr_free(&enum_name);
}
static void module_load_modal_ui_exports(struct obs_module *mod)
{
bool (*enum_func)(size_t idx, struct obs_modal_ui *info);
struct obs_modal_ui ui_info;
size_t i = 0;
enum_func = os_dlsym(mod->module, "enum_modal_ui");
if (enum_func)
while (enum_func(i++, &ui_info))
da_push_back(obs->ui_modal_callbacks, &ui_info);
}
static void module_load_modeless_ui_exports(struct obs_module *mod)
{
bool (*enum_func)(size_t idx, struct obs_modeless_ui *info);
struct obs_modeless_ui ui_info;
size_t i = 0;
enum_func = os_dlsym(mod->module, "enum_modeless_ui");
if (enum_func)
while (enum_func(i++, &ui_info))
da_push_back(obs->ui_modeless_callbacks, &ui_info);
}
extern char *find_plugin(const char *plugin);
/* checks API version of module and calls module_load if it exists.
* if the API version used by the module is incompatible, fails. */
/* These variables get the current size of the info structures. Used to
* automatically prevent API breakage in case functions have to be added */
static size_t cur_source_info_size = 0;
static size_t cur_output_info_size = 0;
static size_t cur_encoder_info_size = 0;
static size_t cur_service_info_size = 0;
static size_t cur_modal_ui_size = 0;
static size_t cur_modeless_ui_size = 0;
static inline int req_func_not_found(const char *name, const char *path)
{
blog(LOG_WARNING, "Required module function '%s' in module '%s' not "
"found, loading of module failed",
name, path);
return MODULE_FUNCTION_NOT_FOUND;
}
#define LOAD_REQ_SIZE_FUNC(func, module, path) \
func = os_dlsym(module, #func); \
if (!func) \
return req_func_not_found(#func, path)
static int call_module_load(void *module, const char *path)
{
uint32_t (*module_version)(uint32_t obs_ver) = NULL;
bool (*module_load)(void) = NULL;
uint32_t version, major, minor;
bool (*obs_module_load)(uint32_t obs_ver) = NULL;
size_t (*obs_module_source_info_size)(void) = NULL;
size_t (*obs_module_output_info_size)(void) = NULL;
size_t (*obs_module_encoder_info_size)(void) = NULL;
size_t (*obs_module_service_info_size)(void) = NULL;
size_t (*obs_module_modal_ui_size)(void) = NULL;
size_t (*obs_module_modeless_ui_size)(void) = NULL;
module_load = os_dlsym(module, "module_load");
obs_module_load = os_dlsym(module, "obs_module_load");
if (!obs_module_load)
return req_func_not_found("obs_module_load", path);
module_version = os_dlsym(module, "module_version");
if (!module_version) {
LOAD_REQ_SIZE_FUNC(obs_module_source_info_size, module, path);
LOAD_REQ_SIZE_FUNC(obs_module_output_info_size, module, path);
LOAD_REQ_SIZE_FUNC(obs_module_encoder_info_size, module, path);
LOAD_REQ_SIZE_FUNC(obs_module_service_info_size, module, path);
LOAD_REQ_SIZE_FUNC(obs_module_modal_ui_size, module, path);
LOAD_REQ_SIZE_FUNC(obs_module_modeless_ui_size, module, path);
cur_source_info_size = obs_module_source_info_size();
cur_output_info_size = obs_module_output_info_size();
cur_encoder_info_size = obs_module_encoder_info_size();
cur_service_info_size = obs_module_service_info_size();
cur_modal_ui_size = obs_module_modal_ui_size();
cur_modeless_ui_size = obs_module_modeless_ui_size();
if (!obs_module_load(LIBOBS_API_VER)) {
blog(LOG_WARNING, "Module '%s' failed to load: "
"module_version not found.", path);
return MODULE_FUNCTIONNOTFOUND;
}
version = module_version(LIBOBS_API_VER);
major = (version >> 16);
minor = (version & 0xFF);
if (major != LIBOBS_API_MAJOR_VER) {
blog(LOG_WARNING, "Module '%s' failed to load: "
"incompatible major version "
"(current API: %u.%u, module version: %u.%u)",
path,
LIBOBS_API_MAJOR_VER, LIBOBS_API_MINOR_VER,
major, minor);
return MODULE_INCOMPATIBLE_VER;
}
if (minor > LIBOBS_API_MINOR_VER) {
blog(LOG_WARNING, "Module '%s' failed to load: "
"incompatible minor version "
"(current API: %u.%u, module version: %u.%u)",
path,
LIBOBS_API_MAJOR_VER, LIBOBS_API_MINOR_VER,
major, minor);
return MODULE_INCOMPATIBLE_VER;
}
if (module_load && !module_load()) {
blog(LOG_WARNING, "Module '%s' failed to load: "
"module_load failed", path);
"obs_module_load failed", path);
return MODULE_ERROR;
}
cur_source_info_size = 0;
cur_output_info_size = 0;
cur_encoder_info_size = 0;
cur_service_info_size = 0;
cur_modal_ui_size = 0;
cur_modeless_ui_size = 0;
return MODULE_SUCCESS;
}
@ -161,7 +99,7 @@ int obs_load_module(const char *path)
mod.module = os_dlopen(plugin_path);
bfree(plugin_path);
if (!mod.module)
return MODULE_FILENOTFOUND;
return MODULE_FILE_NOT_FOUND;
errorcode = call_module_load(mod.module, path);
if (errorcode != MODULE_SUCCESS) {
@ -170,20 +108,6 @@ int obs_load_module(const char *path)
}
mod.name = bstrdup(path);
module_load_exports(&mod, &obs->input_types.da, "inputs",
sizeof(struct source_info), load_source_info);
module_load_exports(&mod, &obs->filter_types.da, "filters",
sizeof(struct source_info), load_source_info);
module_load_exports(&mod, &obs->transition_types.da, "transitions",
sizeof(struct source_info), load_source_info);
module_load_exports(&mod, &obs->output_types.da, "outputs",
sizeof(struct output_info), load_output_info);
module_load_exports(&mod, &obs->encoder_types.da, "encoders",
sizeof(struct encoder_info), load_encoder_info);
module_load_modal_ui_exports(&mod);
module_load_modeless_ui_exports(&mod);
da_push_back(obs->modules, &mod);
return MODULE_SUCCESS;
}
@ -196,8 +120,7 @@ void free_module(struct obs_module *mod)
if (mod->module) {
void (*module_unload)(void);
module_unload = os_dlsym(mod->module,
"module_unload");
module_unload = os_dlsym(mod->module, "module_unload");
if (module_unload)
module_unload();
@ -206,3 +129,111 @@ void free_module(struct obs_module *mod)
bfree(mod->name);
}
void obs_register_source(const struct obs_source_info *info)
{
struct obs_source_info data = {0};
struct darray *array;
if (!cur_source_info_size) {
blog(LOG_WARNING, "Tried to register obs_source_info"
" outside of obs_module_load");
return;
}
memcpy(&data, info, cur_source_info_size);
if (info->type == OBS_SOURCE_TYPE_INPUT) {
array = &obs->input_types.da;
} else if (info->type == OBS_SOURCE_TYPE_FILTER) {
array = &obs->filter_types.da;
} else if (info->type == OBS_SOURCE_TYPE_TRANSITION) {
array = &obs->transition_types.da;
} else {
blog(LOG_WARNING, "Tried to register unknown source type: %u",
info->type);
return;
}
darray_push_back(sizeof(struct obs_source_info), array, &data);
}
#define REGISTER_OBS_DEF(size_var, structure, dest, info) \
do { \
struct structure data = {0}; \
if (!size_var) { \
blog(LOG_WARNING, "Tried to register " #structure \
" outside of obs_module_load"); \
return; \
} \
\
memcpy(&data, info, size_var); \
da_push_back(dest, &data); \
} while (false)
#define CHECK_REQUIRED_VAL(info, val, func) \
do { \
if (!info->val) {\
blog(LOG_WARNING, "Required value '" #val " for" \
"'%s' not found. " #func \
" failed.", \
info->id);\
return; \
} \
} while (false)
void obs_register_output(const struct obs_output_info *info)
{
CHECK_REQUIRED_VAL(info, getname, obs_register_output);
CHECK_REQUIRED_VAL(info, create, obs_register_output);
CHECK_REQUIRED_VAL(info, destroy, obs_register_output);
CHECK_REQUIRED_VAL(info, start, obs_register_output);
CHECK_REQUIRED_VAL(info, stop, obs_register_output);
CHECK_REQUIRED_VAL(info, active, obs_register_output);
REGISTER_OBS_DEF(cur_output_info_size, obs_output_info,
obs->output_types, info);
}
void obs_register_encoder(const struct obs_encoder_info *info)
{
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, reset, obs_register_encoder);
CHECK_REQUIRED_VAL(info, encode, obs_register_encoder);
CHECK_REQUIRED_VAL(info, getheader, obs_register_encoder);
REGISTER_OBS_DEF(cur_encoder_info_size, obs_encoder_info,
obs->encoder_types, info);
}
void obs_register_service(const struct obs_service_info *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(cur_service_info_size, obs_service_info,
obs->service_types, info);
}
void obs_regsiter_modal_ui(const struct obs_modal_ui *info)
{
CHECK_REQUIRED_VAL(info, task, obs_regsiter_modal_ui);
CHECK_REQUIRED_VAL(info, target, obs_regsiter_modal_ui);
CHECK_REQUIRED_VAL(info, exec, obs_regsiter_modal_ui);
REGISTER_OBS_DEF(cur_modal_ui_size, obs_modal_ui,
obs->modal_ui_callbacks, info);
}
void obs_regsiter_modeless_ui(const struct obs_modeless_ui *info)
{
CHECK_REQUIRED_VAL(info, task, obs_regsiter_modeless_ui);
CHECK_REQUIRED_VAL(info, target, obs_regsiter_modeless_ui);
CHECK_REQUIRED_VAL(info, create, obs_regsiter_modeless_ui);
REGISTER_OBS_DEF(cur_modeless_ui_size, obs_modeless_ui,
obs->modeless_ui_callbacks, info);
}

View file

@ -1,5 +1,5 @@
/******************************************************************************
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
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
@ -17,13 +17,61 @@
#pragma once
#include "util/darray.h"
#include "obs.h"
struct obs_module {
char *name;
void *module;
};
#ifdef __cplusplus
#define MODULE_EXPORT extern "C" EXPORT
#else
#define MODULE_EXPORT EXPORT
#endif
extern void *load_module_subfunc(void *module, const char *module_name,
const char *name, const char *func, bool required);
extern void free_module(struct obs_module *mod);
#define OBS_SIZE_FUNC(structure, func) \
MODULE_EXPORT size_t func(void); \
size_t func(void) {return sizeof(struct structure);}
/**
* @file
*
* This file is used by modules for module declaration and module exports.
*/
/** Required: Declares a libobs module. */
#define OBS_DECLARE_MODULE() \
MODULE_EXPORT uint32_t obs_module_ver(void); \
uint32_t obs_module_ver(void) {return LIBOBS_API_VER;} \
OBS_SIZE_FUNC(obs_source_info, obs_module_source_info_size) \
OBS_SIZE_FUNC(obs_output_info, obs_module_output_info_size) \
OBS_SIZE_FUNC(obs_encoder_info, obs_module_encoder_info_size) \
OBS_SIZE_FUNC(obs_encoder_info, obs_module_service_info_size) \
OBS_SIZE_FUNC(obs_modal_ui, obs_module_modal_ui_size) \
OBS_SIZE_FUNC(obs_modeless_ui, obs_module_modeless_ui_size)
/**
* Required: Called when the module is loaded. Use this function to load all
* the sources/encoders/outputs/services for your module, or anything else that
* may need loading.
*
* @param libobs_ver The version of libobs.
* @return Return true to continue loading the module, otherwise
* false to indcate failure and unload the module
*/
MODULE_EXPORT bool obs_module_load(uint32_t libobs_version);
/** Optional: Called when the module is unloaded. */
MODULE_EXPORT void obs_module_unload(void);
/**
* Optional: Declares the author(s) of the module
*
* @param name Author name(s)
*/
#define OBS_MODULE_AUTHOR(name) \
MODULE_EXPORT const char *obs_module_author(void); \
const char *obs_module_author(void) {return name;}
/**
* Optional: Declares the author of the module
*
* @param locale Locale to look up the description for.
*/
MODULE_EXPORT const char *obs_module_description(const char *locale);

View file

@ -18,25 +18,7 @@
#include "obs.h"
#include "obs-internal.h"
bool load_output_info(void *module, const char *module_name,
const char *id, struct output_info *info)
{
LOAD_MODULE_SUBFUNC(getname, true);
LOAD_MODULE_SUBFUNC(create, true);
LOAD_MODULE_SUBFUNC(destroy, true);
LOAD_MODULE_SUBFUNC(update, true);
LOAD_MODULE_SUBFUNC(start, true);
LOAD_MODULE_SUBFUNC(stop, true);
LOAD_MODULE_SUBFUNC(active, true);
LOAD_MODULE_SUBFUNC(properties, false);
LOAD_MODULE_SUBFUNC(pause, false);
info->id = id;
return true;
}
static inline const struct output_info *find_output(const char *id)
static inline const struct obs_output_info *find_output(const char *id)
{
size_t i;
for (i = 0; i < obs->output_types.num; i++)
@ -49,7 +31,7 @@ static inline const struct output_info *find_output(const char *id)
obs_output_t obs_output_create(const char *id, const char *name,
obs_data_t settings)
{
const struct output_info *info = find_output(id);
const struct obs_output_info *info = find_output(id);
struct obs_output *output;
if (!info) {
@ -58,7 +40,7 @@ obs_output_t obs_output_create(const char *id, const char *name,
}
output = bmalloc(sizeof(struct obs_output));
output->callbacks = *info;
output->info = *info;
output->settings = obs_data_newref(settings);
output->data = info->create(output->settings, output);
@ -79,16 +61,16 @@ obs_output_t obs_output_create(const char *id, const char *name,
void obs_output_destroy(obs_output_t output)
{
if (output) {
if (output->callbacks.active) {
if (output->callbacks.active(output->data))
output->callbacks.stop(output->data);
if (output->info.active) {
if (output->info.active(output->data))
output->info.stop(output->data);
}
pthread_mutex_lock(&obs->data.outputs_mutex);
da_erase_item(obs->data.outputs, &output);
pthread_mutex_unlock(&obs->data.outputs_mutex);
output->callbacks.destroy(output->data);
output->info.destroy(output->data);
obs_data_release(output->settings);
bfree(output->name);
bfree(output);
@ -97,22 +79,22 @@ void obs_output_destroy(obs_output_t output)
bool obs_output_start(obs_output_t output)
{
return output->callbacks.start(output->data);
return output->info.start(output->data);
}
void obs_output_stop(obs_output_t output)
{
output->callbacks.stop(output->data);
output->info.stop(output->data);
}
bool obs_output_active(obs_output_t output)
{
return output->callbacks.active(output);
return output->info.active(output);
}
obs_properties_t obs_output_properties(const char *id, const char *locale)
{
const struct output_info *info = find_output(id);
const struct obs_output_info *info = find_output(id);
if (info && info->properties)
return info->properties(locale);
return NULL;
@ -122,8 +104,8 @@ void obs_output_update(obs_output_t output, obs_data_t settings)
{
obs_data_replace(&output->settings, settings);
if (output->callbacks.update)
output->callbacks.update(output->data, output->settings);
if (output->info.update)
output->info.update(output->data, output->settings);
}
obs_data_t obs_output_get_settings(obs_output_t output)
@ -137,11 +119,11 @@ obs_data_t obs_output_get_settings(obs_output_t output)
bool obs_output_canpause(obs_output_t output)
{
return output->callbacks.pause != NULL;
return output->info.pause != NULL;
}
void obs_output_pause(obs_output_t output)
{
if (output->callbacks.pause)
output->callbacks.pause(output->data);
if (output->info.pause)
output->info.pause(output->data);
}

View file

@ -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
@ -17,119 +17,26 @@
#pragma once
#include "util/c99defs.h"
#include "util/dstr.h"
/*
* ===========================================
* Outputs
* ===========================================
*
* An output takes raw audio and/or video and processes and/or outputs it
* to a destination, whether that destination be a file, network, or other.
*
* A module with outputs needs to export these functions:
* + enum_outputs
*
* Each individual output is then exported by it's name. For example, an
* output named "myoutput" would have the following exports:
* + myoutput_getname
* + myoutput_create
* + myoutput_destroy
* + myoutput_update
* + myoutput_start
* + myoutput_stop
* + myoutput_active
*
* [and optionally]
* + myoutput_properties
* + myoutput_pause
*
* ===========================================
* Primary Exports
* ===========================================
* const char *enum_outputs(size_t idx);
* idx: index of the output.
* Return value: Output identifier name. NULL when no more available.
*
* ===========================================
* Output Exports
* ===========================================
* const char *[name]_getname(const char *locale);
* Returns the full translated name of the output type (seen by the user).
*
* ---------------------------------------------------------
* void *[name]_create(obs_data_t settings, obs_output_t output);
* Creates an output.
*
* settings: Settings of the output.
* output: pointer to main output
* Return value: Internal output pointer, or NULL if failed.
*
* ---------------------------------------------------------
* void [name]_destroy(void *data);
* Destroys the output.
*
* ---------------------------------------------------------
* void [name]_update(void *data, obs_data_t settings)
* Updates the output's settings
*
* settings: New settings of the output
*
* ---------------------------------------------------------
* bool [name]_start(void *data)
* Starts output
*
* Return value: true if successful
*
* ---------------------------------------------------------
* void [name]_stop(void *data)
* Stops output
*
* ---------------------------------------------------------
* bool [name]_active(void *data)
* Returns whether currently active or not
*
* ===========================================
* Optional Output Exports
* ===========================================
* obs_properties_t [name]_properties(const char *locale);
* Returns the properties of this particular source type, if any.
*
* ---------------------------------------------------------
* void [name]_pause(void *data)
* Pauses output. Typically only usable for local recordings.
*/
struct obs_output;
struct output_info {
struct obs_output_info {
/* required */
const char *id;
const char *(*getname)(const char *locale);
void *(*create)(obs_data_t settings, struct obs_output *output);
void *(*create)(obs_data_t settings, obs_output_t output);
void (*destroy)(void *data);
void (*update)(void *data, obs_data_t settings);
bool (*start)(void *data);
void (*stop)(void *data);
bool (*active)(void *data);
/* optional */
void (*update)(void *data, obs_data_t settings);
obs_properties_t (*properties)(const char *locale);
void (*pause)(void *data);
};
struct obs_output {
char *name;
void *data;
struct output_info callbacks;
obs_data_t settings;
};
extern bool load_output_info(void *module, const char *module_name,
const char *output_name, struct output_info *info);
EXPORT void obs_register_output(const struct obs_output_info *info);

View file

@ -81,11 +81,6 @@ static void scene_destroy(void *data)
bfree(scene);
}
static uint32_t scene_get_output_flags(void *data)
{
return SOURCE_VIDEO;
}
static inline void detach_sceneitem(struct obs_scene_item *item)
{
if (item->prev)
@ -115,7 +110,7 @@ static inline void attach_sceneitem(struct obs_scene_item *item,
}
}
static void scene_video_render(void *data)
static void scene_video_render(void *data, effect_t effect)
{
struct obs_scene *scene = data;
struct obs_scene_item *item;
@ -154,16 +149,17 @@ static uint32_t scene_getsize(void *data)
return 0;
}
static const struct source_info scene_info =
static const struct obs_source_info scene_info =
{
.id = "scene",
.getname = scene_getname,
.create = scene_create,
.destroy = scene_destroy,
.get_output_flags = scene_get_output_flags,
.video_render = scene_video_render,
.getwidth = scene_getsize,
.getheight = scene_getsize,
.id = "scene",
.type = OBS_SOURCE_TYPE_SCENE,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
.getname = scene_getname,
.create = scene_create,
.destroy = scene_destroy,
.video_render = scene_video_render,
.getwidth = scene_getsize,
.getheight = scene_getsize,
};
obs_scene_t obs_scene_create(const char *name)
@ -188,11 +184,11 @@ obs_scene_t obs_scene_create(const char *name)
}
source->name = bstrdup(name);
source->type = SOURCE_SCENE;
source->type = OBS_SOURCE_TYPE_SCENE;
scene->source = source;
obs_source_init(source, &scene_info);
memcpy(&source->callbacks, &scene_info, sizeof(struct source_info));
memcpy(&source->info, &scene_info, sizeof(struct obs_source_info));
return scene;
}
@ -215,7 +211,7 @@ obs_source_t obs_scene_getsource(obs_scene_t scene)
obs_scene_t obs_scene_fromsource(obs_source_t source)
{
if (source->type != SOURCE_SCENE)
if (source->type != OBS_SOURCE_TYPE_SCENE)
return NULL;
return source->data;

View file

@ -18,7 +18,7 @@
#pragma once
#include "obs.h"
#include "obs-source.h"
#include "obs-internal.h"
/* how obs scene! */

View file

@ -17,15 +17,16 @@
#pragma once
struct service_data;
struct service_info {
struct obs_service_info {
/* required */
char *id;
const char *(*getname)(const char *locale);
void *(*create)(obs_data_t settings, struct service_data *service);
void (*destroy)(void *data);
/* optional */
void (*update)(void *data, obs_data_t settings);
/* get stream url/key */

View file

@ -26,37 +26,14 @@
static void obs_source_destroy(obs_source_t source);
bool load_source_info(void *module, const char *module_name,
const char *id, struct source_info *info)
{
LOAD_MODULE_SUBFUNC(getname, true);
LOAD_MODULE_SUBFUNC(create, true);
LOAD_MODULE_SUBFUNC(destroy, true);
LOAD_MODULE_SUBFUNC(get_output_flags, true);
LOAD_MODULE_SUBFUNC(properties, false);
LOAD_MODULE_SUBFUNC(update, false);
LOAD_MODULE_SUBFUNC(activate, false);
LOAD_MODULE_SUBFUNC(deactivate, false);
LOAD_MODULE_SUBFUNC(video_tick, false);
LOAD_MODULE_SUBFUNC(video_render, false);
LOAD_MODULE_SUBFUNC(getwidth, false);
LOAD_MODULE_SUBFUNC(getheight, false);
LOAD_MODULE_SUBFUNC(filter_video, false);
LOAD_MODULE_SUBFUNC(filter_audio, false);
info->id = id;
return true;
}
static inline const struct source_info *find_source(struct darray *list,
static inline const struct obs_source_info *find_source(struct darray *list,
const char *id)
{
size_t i;
struct source_info *array = list->array;
struct obs_source_info *array = list->array;
for (i = 0; i < list->num; i++) {
struct source_info *info = array+i;
struct obs_source_info *info = array+i;
if (strcmp(info->id, id) == 0)
return info;
}
@ -64,16 +41,25 @@ static inline const struct source_info *find_source(struct darray *list,
return NULL;
}
static const struct source_info *get_source_info(enum obs_source_type type,
static const struct obs_source_info *get_source_info(enum obs_source_type type,
const char *id)
{
struct darray *list = NULL;
switch (type) {
case SOURCE_INPUT: list = &obs->input_types.da; break;
case SOURCE_FILTER: list = &obs->filter_types.da; break;
case SOURCE_TRANSITION: list = &obs->transition_types.da; break;
case SOURCE_SCENE:
case OBS_SOURCE_TYPE_INPUT:
list = &obs->input_types.da;
break;
case OBS_SOURCE_TYPE_FILTER:
list = &obs->filter_types.da;
break;
case OBS_SOURCE_TYPE_TRANSITION:
list = &obs->transition_types.da;
break;
case OBS_SOURCE_TYPE_SCENE:
default:
blog(LOG_WARNING, "get_source_info: invalid source type");
return NULL;
@ -95,22 +81,21 @@ bool obs_source_init_handlers(struct obs_source *source)
const char *obs_source_getdisplayname(enum obs_source_type type,
const char *id, const char *locale)
{
const struct source_info *info = get_source_info(type, id);
const struct obs_source_info *info = get_source_info(type, id);
return (info != NULL) ? info->getname(locale) : NULL;
}
/* internal initialization */
bool obs_source_init(struct obs_source *source, const struct source_info *info)
bool obs_source_init(struct obs_source *source,
const struct obs_source_info *info)
{
uint32_t flags = info->get_output_flags(source->data);
source->refs = 1;
source->volume = 1.0f;
pthread_mutex_init_value(&source->filter_mutex);
pthread_mutex_init_value(&source->video_mutex);
pthread_mutex_init_value(&source->audio_mutex);
memcpy(&source->callbacks, info, sizeof(struct source_info));
memcpy(&source->info, info, sizeof(struct obs_source_info));
if (pthread_mutex_init(&source->filter_mutex, NULL) != 0)
return false;
@ -119,7 +104,7 @@ bool obs_source_init(struct obs_source *source, const struct source_info *info)
if (pthread_mutex_init(&source->video_mutex, NULL) != 0)
return false;
if (flags & SOURCE_AUDIO) {
if (info->output_flags & OBS_SOURCE_AUDIO) {
source->audio_line = audio_output_createline(obs->audio.audio,
source->name);
if (!source->audio_line) {
@ -148,7 +133,7 @@ obs_source_t obs_source_create(enum obs_source_type type, const char *id,
{
struct obs_source *source;
const struct source_info *info = get_source_info(type, id);
const struct obs_source_info *info = get_source_info(type, id);
if (!info) {
blog(LOG_WARNING, "Source '%s' not found", id);
return NULL;
@ -268,7 +253,7 @@ static void obs_source_destroy(obs_source_t source)
gs_leavecontext();
if (source->data)
source->callbacks.destroy(source->data);
source->info.destroy(source->data);
for (i = 0; i < MAX_AUDIO_PLANES; i++)
bfree(source->audio_data.data[i]);
@ -279,6 +264,7 @@ static void obs_source_destroy(obs_source_t source)
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);
@ -338,41 +324,45 @@ bool obs_source_removed(obs_source_t source)
obs_properties_t obs_source_properties(enum obs_source_type type,
const char *id, const char *locale)
{
const struct source_info *info = get_source_info(type, id);
if (info && info->properties)
return info->properties(locale);
const struct obs_source_info *info = get_source_info(type, id);
if (info && info->get_properties)
return info->get_properties(locale);
return NULL;
}
uint32_t obs_source_get_output_flags(obs_source_t source)
{
return source->callbacks.get_output_flags(source->data);
return source->info.output_flags;
}
void obs_source_update(obs_source_t source, obs_data_t settings)
{
obs_data_replace(&source->settings, settings);
if (source->callbacks.update)
source->callbacks.update(source->data, source->settings);
if (source->info.update)
source->info.update(source->data, source->settings);
}
void obs_source_activate(obs_source_t source)
{
if (source->callbacks.activate)
source->callbacks.activate(source->data);
if (source->info.activate)
source->info.activate(source->data);
}
void obs_source_deactivate(obs_source_t source)
{
if (source->callbacks.deactivate)
source->callbacks.deactivate(source->data);
if (source->info.deactivate)
source->info.deactivate(source->data);
}
void obs_source_video_tick(obs_source_t source, float seconds)
{
if (source->callbacks.video_tick)
source->callbacks.video_tick(source->data, seconds);
/* reset the filter render texture information once every frame */
if (source->filter_texrender)
texrender_reset(source->filter_texrender);
if (source->info.video_tick)
source->info.video_tick(source->data, seconds);
}
/* unless the value is 3+ hours worth of frames, this won't overflow */
@ -399,13 +389,11 @@ static inline void reset_audio_timing(obs_source_t source, uint64_t timetamp)
static inline void handle_ts_jump(obs_source_t source, uint64_t ts,
uint64_t diff)
{
uint32_t flags = source->callbacks.get_output_flags(source->data);
blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%lld', "
"resetting audio timing", source->name, diff);
/* if has video, ignore audio data until reset */
if (flags & SOURCE_ASYNC_VIDEO)
if (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO)
source->audio_reset_ref--;
else
reset_audio_timing(source, ts);
@ -555,7 +543,7 @@ static void obs_source_draw_texture(texture_t tex, struct source_frame *frame)
sizeof(float) * 16);
}
param = effect_getparambyname(effect, "diffuse");
param = effect_getparambyname(effect, "image");
effect_settexture(effect, param, tex);
gs_draw_sprite(tex, frame->flip ? GS_FLIP_V : 0, 0, 0);
@ -583,17 +571,18 @@ static inline void obs_source_render_filters(obs_source_t source)
source->rendering_filter = false;
}
static inline void obs_source_default_render(obs_source_t source, bool yuv)
static inline void obs_source_default_render(obs_source_t source,
bool color_matrix)
{
effect_t effect = obs->video.default_effect;
const char *tech_name = yuv ? "DrawMatrix" : "Draw";
const char *tech_name = color_matrix ? "DrawMatrix" : "Draw";
technique_t tech = effect_gettechnique(effect, tech_name);
size_t passes, i;
passes = technique_begin(tech);
for (i = 0; i < passes; i++) {
technique_beginpass(tech, i);
source->callbacks.video_render(source->data);
source->info.video_render(source->data, effect);
technique_endpass(tech);
}
technique_end(tech);
@ -601,20 +590,21 @@ static inline void obs_source_default_render(obs_source_t source, bool yuv)
static inline void obs_source_main_render(obs_source_t source)
{
uint32_t flags = source->callbacks.get_output_flags(source->data);
uint32_t flags = source->info.output_flags;
bool color_matrix = (flags & OBS_SOURCE_COLOR_MATRIX) != 0;
bool default_effect = !source->filter_parent &&
source->filters.num == 0 &&
(flags & SOURCE_DEFAULT_EFFECT) != 0;
(flags & OBS_SOURCE_CUSTOM_DRAW) == 0;
if (default_effect)
obs_source_default_render(source, (flags & SOURCE_YUV) != 0);
obs_source_default_render(source, color_matrix);
else
source->callbacks.video_render(source->data);
source->info.video_render(source->data, NULL);
}
void obs_source_video_render(obs_source_t source)
{
if (source->callbacks.video_render) {
if (source->info.video_render) {
if (source->filters.num && !source->rendering_filter)
obs_source_render_filters(source);
else
@ -630,15 +620,15 @@ void obs_source_video_render(obs_source_t source)
uint32_t obs_source_getwidth(obs_source_t source)
{
if (source->callbacks.getwidth)
return source->callbacks.getwidth(source->data);
if (source->info.getwidth)
return source->info.getwidth(source->data);
return 0;
}
uint32_t obs_source_getheight(obs_source_t source)
{
if (source->callbacks.getheight)
return source->callbacks.getheight(source->data);
if (source->info.getheight)
return source->info.getheight(source->data);
return 0;
}
@ -747,8 +737,8 @@ static inline struct source_frame *filter_async_video(obs_source_t source,
size_t i;
for (i = source->filters.num; i > 0; i--) {
struct obs_source *filter = source->filters.array[i-1];
if (filter->callbacks.filter_video) {
in = filter->callbacks.filter_video(filter->data, in);
if (filter->info.filter_video) {
in = filter->info.filter_video(filter->data, in);
if (!in)
return NULL;
}
@ -842,8 +832,8 @@ static inline struct filtered_audio *filter_async_audio(obs_source_t source,
size_t i;
for (i = source->filters.num; i > 0; i--) {
struct obs_source *filter = source->filters.array[i-1];
if (filter->callbacks.filter_audio) {
in = filter->callbacks.filter_audio(filter->data, in);
if (filter->info.filter_audio) {
in = filter->info.filter_audio(filter->data, in);
if (!in)
return NULL;
}
@ -952,11 +942,13 @@ void obs_source_output_audio(obs_source_t source,
output = filter_async_audio(source, &source->audio_data);
if (output) {
bool async = (flags & OBS_SOURCE_ASYNC_VIDEO) == 0;
pthread_mutex_lock(&source->audio_mutex);
/* wait for video to start before outputting any audio so we
* have a base for sync */
if (source->timing_set || (flags & SOURCE_ASYNC_VIDEO) == 0) {
if (source->timing_set || async) {
struct audio_data data;
for (int i = 0; i < MAX_AUDIO_PLANES; i++)
@ -1091,15 +1083,15 @@ void obs_source_gettype(obs_source_t source, enum obs_source_type *type,
const char **id)
{
if (type) *type = source->type;
if (id) *id = source->callbacks.id;
if (id) *id = source->info.id;
}
static inline void render_filter_bypass(obs_source_t target, effect_t effect,
uint32_t width, uint32_t height, bool yuv)
uint32_t width, uint32_t height, bool use_matrix)
{
const char *tech_name = yuv ? "DrawMatrix" : "Draw";
const char *tech_name = use_matrix ? "DrawMatrix" : "Draw";
technique_t tech = effect_gettechnique(effect, tech_name);
eparam_t diffuse = effect_getparambyname(effect, "diffuse");
eparam_t image = effect_getparambyname(effect, "image");
size_t passes, i;
passes = technique_begin(tech);
@ -1112,14 +1104,14 @@ static inline void render_filter_bypass(obs_source_t target, effect_t effect,
}
static inline void render_filter_tex(texture_t tex, effect_t effect,
uint32_t width, uint32_t height, bool yuv)
uint32_t width, uint32_t height, bool use_matrix)
{
const char *tech_name = yuv ? "DrawMatrix" : "Draw";
const char *tech_name = use_matrix ? "DrawMatrix" : "Draw";
technique_t tech = effect_gettechnique(effect, tech_name);
eparam_t diffuse = effect_getparambyname(effect, "diffuse");
eparam_t image = effect_getparambyname(effect, "image");
size_t passes, i;
effect_settexture(effect, diffuse, tex);
effect_settexture(effect, image, tex);
passes = technique_begin(tech);
for (i = 0; i < passes; i++) {
@ -1130,8 +1122,8 @@ static inline void render_filter_tex(texture_t tex, effect_t effect,
technique_end(tech);
}
void obs_source_process_filter(obs_source_t filter, texrender_t texrender,
effect_t effect, uint32_t width, uint32_t height,
void obs_source_process_filter(obs_source_t filter, effect_t effect,
uint32_t width, uint32_t height, enum gs_color_format format,
enum allow_direct_render allow_direct)
{
obs_source_t target = obs_filter_gettarget(filter);
@ -1140,8 +1132,8 @@ void obs_source_process_filter(obs_source_t filter, texrender_t texrender,
uint32_t parent_flags = obs_source_get_output_flags(parent);
int cx = obs_source_getwidth(target);
int cy = obs_source_getheight(target);
bool yuv = (target_flags & SOURCE_YUV) != 0;
bool expects_def = (parent_flags & SOURCE_DEFAULT_EFFECT) != 0;
bool use_matrix = !!(target_flags & OBS_SOURCE_COLOR_MATRIX);
bool expects_def = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW);
bool can_directly = allow_direct == ALLOW_DIRECT_RENDERING;
/* if the parent does not use any custom effects, and this is the last
@ -1149,23 +1141,27 @@ void obs_source_process_filter(obs_source_t filter, texrender_t texrender,
* using the filter effect instead of rendering to texture to reduce
* the total number of passes */
if (can_directly && expects_def && target == parent) {
render_filter_bypass(target, effect, width, height, yuv);
render_filter_bypass(target, effect, width, height, use_matrix);
return;
}
if (texrender_begin(texrender, cx, cy)) {
if (!filter->filter_texrender)
filter->filter_texrender = texrender_create(format,
GS_ZS_NONE);
if (texrender_begin(filter->filter_texrender, cx, cy)) {
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
if (expects_def && parent == target)
obs_source_default_render(parent, yuv);
obs_source_default_render(parent, use_matrix);
else
obs_source_video_render(target);
texrender_end(texrender);
texrender_end(filter->filter_texrender);
}
/* --------------------------- */
render_filter_tex(texrender_gettexture(texrender), effect,
width, height, yuv);
render_filter_tex(texrender_gettexture(filter->filter_texrender),
effect, width, height, use_matrix);
}
signal_handler_t obs_source_signalhandler(obs_source_t source)

View file

@ -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
@ -17,253 +17,218 @@
#pragma once
#include "util/c99defs.h"
#include "util/darray.h"
#include "util/dstr.h"
#include "util/threading.h"
#include "media-io/audio-resampler.h"
#include "callback/signal.h"
#include "callback/proc.h"
#include "obs.h"
/*
* ===========================================
* Sources
* ===========================================
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Source output flags
*
* A source is literally a "source" of audio and/or video.
*
* A module with sources needs to export these functions:
* + enum_[type]
*
* [type] can be one of the following:
* + input
* + filter
* + transition
*
* input: A source used for directly playing video and/or sound.
* filter: A source that is used for filtering other sources, modifying
* the audio/video data before it is actually played.
* transition: A source used for transitioning between two different sources
* on screen.
*
* Each individual source is then exported by it's name. For example, a
* source named "mysource" would have the following exports:
* + mysource_getname
* + mysource_create
* + mysource_destroy
* + mysource_get_output_flags
*
* [and optionally]
* + mysource_properties
* + mysource_update
* + mysource_activate
* + mysource_deactivate
* + mysource_video_tick
* + mysource_video_render
* + mysource_getwidth
* + mysource_getheight
* + mysource_getparam
* + mysource_setparam
* + mysource_filtervideo
* + mysource_filteraudio
*
* ===========================================
* Primary Exports
* ===========================================
* bool enum_[type](size_t idx, const char **name);
* idx: index of the enumeration.
* name: pointer to variable that receives the type of the source
* Return value: false when no more available.
*
* ===========================================
* Source Exports
* ===========================================
* const char *[name]_getname(const char *locale);
* Returns the full name of the source type (seen by the user).
*
* ---------------------------------------------------------
* void *[name]_create(obs_data_t settings, obs_source_t source);
* Creates a source.
*
* settings: Settings of the source.
* source: pointer to main source
* Return value: Internal source pointer, or NULL if failed.
*
* ---------------------------------------------------------
* void [name]_destroy(void *data);
* Destroys the source.
*
* ---------------------------------------------------------
* uint32_t [name]_get_output_flags(void *data);
* Returns a combination of one of the following values:
* + SOURCE_VIDEO: source has video
* + SOURCE_AUDIO: source has audio
* + SOURCE_ASYNC_VIDEO: video is sent asynchronously via RAM
* + SOURCE_DEFAULT_EFFECT: source uses default effect
* + SOURCE_YUV: source is in YUV color space
*
* ===========================================
* Optional Source Exports
* ===========================================
* obs_properties_t [name]_properties(const char *locale);
* Returns the properties of this particular source type, if any.
*
* ---------------------------------------------------------
* void [name]_update(void *data, obs_data_t settings);
* Called to update the settings of the source.
*
* ---------------------------------------------------------
* void [name]_video_activate(void *data);
* Called when the source is being displayed.
*
* ---------------------------------------------------------
* void [name]_video_deactivate(void *data);
* Called when the source is no longer being displayed.
*
* ---------------------------------------------------------
* void [name]_video_tick(void *data, float seconds);
* Called each video frame with the time taken between the last and
* current frame, in seconds.
*
* ---------------------------------------------------------
* void [name]_video_render(void *data);
* Called to render the source.
*
* ---------------------------------------------------------
* uint32_t [name]_getwidth(void *data);
* Returns the width of a source, or -1 for maximum width. If you render
* video, this is required.
*
* ---------------------------------------------------------
* uint32_t [name]_getheight(void *data);
* Returns the height of a source, or -1 for maximum height. If you
* render video, this is required.
*
* ---------------------------------------------------------
* void [name]_getparam(void *data, const char *param, void *buf,
* size_t buf_size);
* Gets a source parameter value.
*
* param: Name of parameter.
* Return value: Value of parameter.
*
* ---------------------------------------------------------
* void [name]_setparam(void *data, const char *param, const void *buf,
* size_t size);
* Sets a source parameter value.
*
* param: Name of parameter.
* val: Value of parameter to set.
*
* ---------------------------------------------------------
* struct source_frame *[name]_filter_video(void *data,
* const struct source_frame *frame);
* Filters audio data. Used with audio filters.
*
* frame: Video frame data.
* returns: New video frame data (or NULL if pending)
*
* ---------------------------------------------------------
* struct filter_audio [name]_filter_audio(void *data,
* struct filter_audio *audio);
* Filters video data. Used with async video data.
*
* audio: Audio data.
* reutrns New audio data (or NULL if pending)
* These flags determine what type of data the source outputs and expects.
* @{
*/
struct obs_source;
/**
* Source has video.
*
* Unless SOURCE_ASYNC_VIDEO is specified, the source must include the
* video_render callback in the source definition structure.
*/
#define OBS_SOURCE_VIDEO (1<<0)
struct source_info {
/**
* Source has audio.
*
* Use the obs_source_output_audio function to pass raw audio data, which will
* be automatically converted and uploaded. If used with SOURCE_ASYNC_VIDEO,
* audio will automatically be synced up to the video output.
*/
#define OBS_SOURCE_AUDIO (1<<1)
/**
* Source passes raw video data via RAM.
*
* Use the obs_source_output_video function to pass raw video data, which will
* be automatically uploaded at the specified timestamp.
*
* If this flag is specified, it is not necessary to include the video_render
* callback. However, if you wish to use that function as well, you must call
* obs_source_getframe to get the current frame data, and
* obs_source_releaseframe to release the data when complete.
*/
#define OBS_SOURCE_ASYNC_VIDEO ((1<<2) | OBS_SOURCE_VIDEO)
/**
* Source uses custom drawing, rather than a default effect.
*
* If this flag is specified, the video_render callback will pass a NULL
* effect, and effect-based filters will not use direct rendering.
*/
#define OBS_SOURCE_CUSTOM_DRAW (1<<3)
/**
* Source uses a color matrix (usually YUV sources).
*
* When this is used, the video_render callback will automatically assign a
* 4x4 YUV->RGB matrix to the "color_matrix" parameter of the effect, or it can
* be overwritten with a custom value.
*/
#define OBS_SOURCE_COLOR_MATRIX (1<<4)
/** @} */
/**
* Source definition structure
*/
struct obs_source_info {
/* ----------------------------------------------------------------- */
/* Required implementation*/
/** Unique string identifier for the source */
const char *id;
/* ----------------------------------------------------------------- */
/* required implementations */
/**
* Type of source.
*
* OBS_SOURCE_INPUT for input sources,
* OBS_SOURCE_FILTER for filter sources, and
* OBS_SOURCE_TRANSITION for transition sources.
*/
enum obs_source_type type;
/** Source output flags */
uint32_t output_flags;
/**
* Get the translated name of the source type
*
* @param locale The locale to translate with
* @return The translated name of the source type
*/
const char *(*getname)(const char *locale);
/**
* Creates the source data for the source
*
* @param settings Settings to initialize the source with
* @param source Source that this data is assoicated with
* @return The data associated with this source
*/
void *(*create)(obs_data_t settings, obs_source_t source);
/** Destroys the private data for the source */
void (*destroy)(void *data);
uint32_t (*get_output_flags)(void *data);
/* ----------------------------------------------------------------- */
/* optional implementations */
/* Optional implementation */
obs_properties_t (*properties)(const char *locale);
/**
* Gets the property information of this source
*
* @param locale The locale to translate with
* @return The properties data. Caller is responsible for
* freeing the data with obs_properties_destroy
*/
obs_properties_t (*get_properties)(const char *locale);
/**
* Updates the settings for this source
*
* @param data Source data
* @param settings New settings for this source
*/
void (*update)(void *data, obs_data_t settings);
/** Called when the source has been activated */
void (*activate)(void *data);
/**
* Called when the source has been deactivated (no longer being
* played/displayed)
*/
void (*deactivate)(void *data);
/**
* Called each video frame with the time elapsed
*
* @param data Source data
* @param seconds Seconds elapsed since the last frame
*/
void (*video_tick)(void *data, float seconds);
void (*video_render)(void *data);
/**
* Called when rendering the source with the graphics subsystem.
*
* If this is an input/transition source, this is called to draw the
* source texture with the graphics subsystem using the specified
* effect.
*
* If this is a filter source, it wraps source draw calls (for
* example applying a custom effect with custom parameters to a
* source). In this case, it's highly recommended to use the
* obs_source_process_filter function to automatically handle
* effect-based filter processing. However, you can implement custom
* draw handling as desired as well.
*
* If the source output flags do not include SOURCE_CUSTOM_DRAW, all
* a source needs to do is set the "output" parameter of the effect to
* the desired texture, and then draw. If the output flags include
* SOURCE_COLOR_MATRIX, you may optionally set the the "color_matrix"
* parameter of the effect to a custom 4x4 conversion matrix (by
* default it will be set to an YUV->RGB conversion matrix)
*
* @param data Source data
* @param effect Effect to be used with this source. If the source
* output flags include SOURCE_CUSTOM_DRAW, this will
* be NULL, and the source is expected to process with
* an effect manually.
*/
void (*video_render)(void *data, effect_t effect);
/** @return The width of the source */
uint32_t (*getwidth)(void *data);
/** @return The height of the source */
uint32_t (*getheight)(void *data);
/**
* Called to filter raw async video data.
*
* @note This function is only used with filter sources.
*
* @param data Source data
* @param frame Video frame to filter
* @return New video frame data. This can defer video data to
* be drawn later if time is needed for processing
*/
struct source_frame *(*filter_video)(void *data,
const struct source_frame *frame);
/**
* Called to filter raw audio data.
*
* @note This function is only used with filter sources.
*
* @param data Source data
* @param audio Audio data to filter.
* @return Modified or new audio data. You can directly modify
* the data passed and return it, or you can defer audio
* data for later if time is needed for processing.
*/
struct filtered_audio *(*filter_audio)(void *data,
struct filtered_audio *audio);
};
struct obs_source {
volatile int refs;
/**
* Regsiters a source definition to the current obs context. This should be
* used in obs_module_load.
*
* @param info Pointer to the source definition structure
*/
EXPORT void obs_register_source(const struct obs_source_info *info);
/* source-specific data */
char *name; /* user-defined name */
enum obs_source_type type;
obs_data_t settings;
void *data;
struct source_info callbacks;
signal_handler_t signals;
proc_handler_t procs;
/* 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;
/* timing (if video is present, is based upon video) */
volatile bool timing_set;
volatile uint64_t timing_adjust;
volatile int audio_reset_ref;
uint64_t next_audio_ts_min;
uint64_t last_frame_ts;
uint64_t last_sys_timestamp;
/* audio */
bool audio_failed;
struct resample_info sample_info;
audio_resampler_t resampler;
audio_line_t audio_line;
pthread_mutex_t audio_mutex;
struct filtered_audio audio_data;
size_t audio_storage_size;
float volume;
/* async video data */
texture_t output_texture;
DARRAY(struct source_frame*) video_frames;
pthread_mutex_t video_mutex;
/* filters */
struct obs_source *filter_parent;
struct obs_source *filter_target;
DARRAY(struct obs_source*) filters;
pthread_mutex_t filter_mutex;
bool rendering_filter;
};
extern bool load_source_info(void *module, const char *module_name,
const char *source_name, struct source_info *info);
bool obs_source_init_handlers(struct obs_source *source);
extern bool obs_source_init(struct obs_source *source,
const struct source_info *info);
extern void obs_source_activate(obs_source_t source);
extern void obs_source_deactivate(obs_source_t source);
extern void obs_source_video_tick(obs_source_t source, float seconds);
#ifdef __cplusplus
}
#endif

View file

@ -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
@ -23,138 +23,132 @@
extern "C" {
#endif
struct obs_modal_ui {
const char *name;
const char *task;
const char *target;
bool (*callback)(void *object, void *ui_data);
};
struct obs_modeless_ui {
const char *name;
const char *task;
const char *target;
void *(*callback)(void *object, void *ui_data);
};
/*
* ===========================================
* Module UI calls
* ===========================================
/**
* @file
*
* Modules can specify custom user-interface-specific exports. UI exports
* Modules can specify custom user-interface-specific exports. UI functions
* can be within the same library as the actual core logic, or separated in to
* different modules to split up UI logic and core module logic.
*
* The reasoning for this is to allow for custom user interface of differing
* toolkits or for automatically generated user interface, or to simply allow
* separation of UI code from core code (which may often be in differing
* languages)
*
* A module with UI calls needs to export one or both of these functions:
* + enum_modal_ui
* + enum_modeless_ui
*
* The enum_ui function provides an obs_ui_info structure for each
* input/output/etc. Modeless UI should be exported enum_modeless_ui. For
* example, to export Qt-specific configuration functions, the values given to
* enum_modal_ui might look something like this:
*
* struct obs_modal_ui ui_list[] = {
* {"mysource", "config", "qt", mysource_config},
* {"myoutput", "config", "qt", myoutput_config},
* {"myencoder", "config", "qt", myenoder_config}
* };
*
* 'qt' could be replaced with 'wx' or something similar if using wxWidgets,
* or 'win32' if using bare windows API. It could also specify a custom name
* if desired (use with discretion).
*
* ===========================================
* Primary Exports
* ===========================================
* bool enum_modal_ui(size_t idx, struct obs_modal_ui *ui_info);
*
* idx: index of the enumeration
* ui_info: pointer to the ui data for this enumeration
* Return value: false when no more available.
*
* ---------------------------------------------------------
* bool enum_modeless_ui(size_t idx, struct obs_modeless_ui *ui_info);
*
* idx: index of the enumeration
* ui_info: pointer to the ui data for this enumeration
* Return value: false when no more available.
*
* ===========================================
* Modal UI Callback
* ===========================================
* bool modal_callback(void *object, void *ui_data);
*
* The 'object' variable points to the input/output/encoder/etc. The
* 'ui_data' varaible points to the UI parent or UI-specific data to be used
* with the custom user interface.
*
* What 'ui_data' points to differs depending on the target, and you should
* use discretion and consistency when using this variable to relay
* information to the UI function. For example, it would be ideal to have
* 'ui_data' point to a parent, QWidget for Qt, or a wxWindow for wxWidgets,
* etc., though it's up to the discretion of the developer to define that
* value. Because of the nature of void pointers, discretion and consistency
* is advised.
*
* ===========================================
* Modeless UI Callback
* ===========================================
* void *modeless_callback(void *data, void *ui_data);
*
* Modeless UI calls return immediately, and typically are supposed to return
* a pointer or handle to the specific UI object that was created. For
* example, a Qt object would ideally return a pointer to a QWidget. Again,
* discretion and consistency is advised for the return value.
* languages).
*/
/** Modal UI definition structure */
struct obs_modal_ui {
const char *id; /**< Identifier associated with this UI */
const char *task; /**< Task of the UI */
const char *target; /**< UI target (UI toolkit or program name) */
/**
* Callback to execute modal interface.
*
* The @b object variable points to the input/output/encoder/etc. The
* @b ui_data varaible points to the UI parent or UI-specific data to
* be used with the custom user interface.
*
* What @b ui_data points to differs depending on the target, and you
* should use discretion and consistency when using this variable to
* relay information to the UI function. For example, it would be
* ideal to have @b ui_data point to a parent, QWidget for Qt, or a
* wxWindow for wxWidgets, etc., though it's up to the discretion of
* the developer to define that value. Because of the nature of void
* pointers, discretion and consistency is advised.
*
* @param object Pointer/handle to the data associated with this
* call.
* @param ui_data UI data to pass associated with this specific
* target, if any.
* @return @b true if user completed the task, or
* @b false if user cancelled the task.
*/
bool (*exec)(void *object, void *ui_data);
};
/**
* ===========================================
* obs_exec_ui
* ===========================================
* Requests modal UI to be displayed. Returns when user is complete.
* Regsiters a modal UI definition to the current obs context. This should be
* used in obs_module_load.
*
* name: Name of the input/output/etc type that UI was requested for
* task: Task of the user interface (usually "config")
* target: Desired target (i.e. "qt", "wx", "gtk3", "win32", etc)
* data: Pointer to the obs input/output/etc
* ui_data: UI-specific data, usually a parent pointer/handle (if any)
*
* Return value: OBS_UI_SUCCESS if the UI was successful
* OBS_UI_CANCEL if the UI was cancelled by the user
* OBS_UI_NOTFOUND if the UI callback was not found
* @param info Pointer to the modal definition structure
*/
EXPORT void obs_register_modal_ui(const struct obs_modal_ui *info);
/* ------------------------------------------------------------------------- */
/** Modeless UI definition structure */
struct obs_modeless_ui {
const char *id; /**< Identifier associated with this UI */
const char *task; /**< Task of the UI */
const char *target; /**< UI target (UI toolkit or program name) */
/**
* Callback to create modeless interface.
*
* This function is almost identical to the modal exec function,
* except modeless UI calls return immediately, and typically are
* supposed to return a pointer or handle to the specific UI object
* that was created. For example, a Qt object would ideally return a
* pointer to a QWidget. Again, discretion and consistency is advised
* for the return value.
*
* @param object Pointer/handle to the data associated with this
* call.
* @param ui_data UI data to pass associated with this specific
* target, if any.
* @return Pointer/handle to the modeless UI associated with
* the specific target.
*/
void *(*create)(void *object, void *ui_data);
};
/**
* Registers a modeless UI definition to the current obs context. This should
* be used in obs_module_load.
*
* @param info Pointer to the modal definition structure
*/
EXPORT void obs_register_modeless_ui(const struct obs_modeless_ui *info);
/* ------------------------------------------------------------------------- */
#define OBS_UI_SUCCESS 0
#define OBS_UI_CANCEL -1
#define OBS_UI_NOTFOUND -2
EXPORT int obs_exec_ui(const char *name, const char *task, const char *target,
/**
* Requests modal UI to be displayed. Returns when user is complete.
*
* @param name Name of the input/output/etc type that UI was requested for
* @param task Task of the user interface (usually "config")
* @param target Desired target (i.e. "qt", "wx", "gtk3", "win32", etc)
* @param data Pointer to the obs input/output/etc
* @param ui_data UI-specific data, usually a parent pointer/handle (if any)
*
* @return OBS_UI_SUCCESS if the UI was successful,
* OBS_UI_CANCEL if the UI was cancelled by the user, or
* OBS_UI_NOTFOUND if the UI callback was not found
*/
EXPORT int obs_exec_ui(const char *id, const char *task, const char *target,
void *data, void *ui_data);
/**
* ===========================================
* obs_create_ui
* ===========================================
* Requests modeless UI to be created. Returns immediately.
*
* name: Name of the input/output/etc type that UI was requested for
* task: Task of the user interface
* target: Desired target (i.e. "qt", "wx", "gtk3", "win32", etc)
* data: Pointer to the obs input/output/etc
* ui_data: UI-specific data, usually a parent pointer/handle (if any)
* @param name Name of the input/output/etc type that UI was requested for
* @param task Task of the user interface
* @param target Desired target (i.e. "qt", "wx", "gtk3", "win32", etc)
* @param data Pointer to the obs input/output/etc
* @param ui_data UI-specific data, usually a parent pointer/handle (if any)
*
* Return value: Pointer to the target-specific modeless object, or NULL if
* not found or failed.
* @return Pointer/handle to the target-specific modeless object, or
* NULL if not found or failed.
*/
EXPORT void *obs_create_ui(const char *name, const char *task,
EXPORT void *obs_create_ui(const char *id, const char *task,
const char *target, void *data, void *ui_data);
#ifdef __cplusplus
}
#endif

View file

@ -164,7 +164,7 @@ static inline void render_output_texture(struct obs_core_video *video,
/* TODO: replace with actual downscalers or unpackers */
effect_t effect = video->default_effect;
technique_t tech = effect_gettechnique(effect, "DrawMatrix");
eparam_t diffuse = effect_getparambyname(effect, "diffuse");
eparam_t image = effect_getparambyname(effect, "image");
eparam_t matrix = effect_getparambyname(effect, "color_matrix");
size_t passes, i;
@ -184,7 +184,7 @@ static inline void render_output_texture(struct obs_core_video *video,
};
effect_setval(effect, matrix, mat_val, sizeof(mat_val));
effect_settexture(effect, diffuse, texture);
effect_settexture(effect, image, texture);
passes = technique_begin(tech);
for (i = 0; i < passes; i++) {

View file

@ -19,7 +19,6 @@
#include "obs.h"
#include "obs-internal.h"
#include "obs-module.h"
struct obs_core *obs = NULL;
@ -332,8 +331,8 @@ void obs_shutdown(void)
da_free(obs->transition_types);
da_free(obs->output_types);
da_free(obs->service_types);
da_free(obs->ui_modal_callbacks);
da_free(obs->ui_modeless_callbacks);
da_free(obs->modal_ui_callbacks);
da_free(obs->modeless_ui_callbacks);
obs_free_data();
obs_free_video();
@ -463,13 +462,13 @@ video_t obs_video(void)
}
/* TODO: optimize this later so it's not just O(N) string lookups */
static inline struct obs_modal_ui *get_modal_ui_callback(const char *name,
static inline struct obs_modal_ui *get_modal_ui_callback(const char *id,
const char *task, const char *target)
{
for (size_t i = 0; i < obs->ui_modal_callbacks.num; i++) {
struct obs_modal_ui *callback = obs->ui_modal_callbacks.array+i;
for (size_t i = 0; i < obs->modal_ui_callbacks.num; i++) {
struct obs_modal_ui *callback = obs->modal_ui_callbacks.array+i;
if (strcmp(callback->name, name) == 0 &&
if (strcmp(callback->id, id) == 0 &&
strcmp(callback->task, task) == 0 &&
strcmp(callback->target, target) == 0)
return callback;
@ -478,14 +477,14 @@ static inline struct obs_modal_ui *get_modal_ui_callback(const char *name,
return NULL;
}
static inline struct obs_modeless_ui *get_modeless_ui_callback(const char *name,
static inline struct obs_modeless_ui *get_modeless_ui_callback(const char *id,
const char *task, const char *target)
{
for (size_t i = 0; i < obs->ui_modeless_callbacks.num; i++) {
for (size_t i = 0; i < obs->modeless_ui_callbacks.num; i++) {
struct obs_modeless_ui *callback;
callback = obs->ui_modeless_callbacks.array+i;
callback = obs->modeless_ui_callbacks.array+i;
if (strcmp(callback->name, name) == 0 &&
if (strcmp(callback->id, id) == 0 &&
strcmp(callback->task, task) == 0 &&
strcmp(callback->target, target) == 0)
return callback;
@ -502,7 +501,7 @@ int obs_exec_ui(const char *name, const char *task, const char *target,
callback = get_modal_ui_callback(name, task, target);
if (callback) {
bool success = callback->callback(data, ui_data);
bool success = callback->exec(data, ui_data);
errorcode = success ? OBS_UI_SUCCESS : OBS_UI_CANCEL;
}
@ -515,7 +514,7 @@ void *obs_create_ui(const char *name, const char *task, const char *target,
struct obs_modeless_ui *callback;
callback = get_modeless_ui_callback(name, task, target);
return callback ? callback->callback(data, ui_data) : NULL;
return callback ? callback->create(data, ui_data) : NULL;
}
bool obs_add_source(obs_source_t source)

View file

@ -26,10 +26,31 @@
#include "callback/signal.h"
#include "callback/proc.h"
/* opaque types */
struct obs_display;
struct obs_source;
struct obs_scene;
struct obs_scene_item;
struct obs_output;
struct obs_encoder;
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_encoder *obs_encoder_t;
typedef struct obs_service *obs_service_t;
#include "obs-defs.h"
#include "obs-data.h"
#include "obs-ui.h"
#include "obs-properties.h"
#include "obs-source.h"
#include "obs-encoder.h"
#include "obs-output.h"
#include "obs-service.h"
/*
* Main libobs header used by applications.
@ -47,10 +68,11 @@ extern "C" {
LIBOBS_API_MINOR_VER)
enum obs_source_type {
SOURCE_INPUT,
SOURCE_FILTER,
SOURCE_TRANSITION,
SOURCE_SCENE
OBS_SOURCE_TYPE_INPUT,
OBS_SOURCE_TYPE_FILTER,
OBS_SOURCE_TYPE_TRANSITION,
OBS_SOURCE_TYPE_SCENE
};
/* used for changing the order of items (for example, filters in a source,
@ -142,23 +164,6 @@ struct encoder_packet {
enum packet_priority priority;
};
/* opaque types */
struct obs_display;
struct obs_source;
struct obs_scene;
struct obs_scene_item;
struct obs_output;
struct obs_encoder;
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_encoder *obs_encoder_t;
typedef struct obs_service *obs_service_t;
/* ------------------------------------------------------------------------- */
/* OBS context */
@ -433,9 +438,8 @@ EXPORT void obs_source_releaseframe(obs_source_t source,
struct source_frame *frame);
/** Default RGB filter handler for generic effect filters */
EXPORT void obs_source_process_filter(obs_source_t filter,
texrender_t texrender, effect_t effect,
uint32_t width, uint32_t height,
EXPORT void obs_source_process_filter(obs_source_t filter, effect_t effect,
uint32_t width, uint32_t height, enum gs_color_format format,
enum allow_direct_render allow_direct);

View file

@ -184,7 +184,7 @@ void OBSBasic::UpdateSceneSelection(OBSSource source)
obs_source_type type;
obs_source_gettype(source, &type, NULL);
if (type != SOURCE_SCENE)
if (type != OBS_SOURCE_TYPE_SCENE)
return;
obs_scene_t scene = obs_scene_fromsource(source);
@ -232,7 +232,7 @@ void OBSBasic::SourceAdded(void *data, calldata_t params)
obs_source_type type;
obs_source_gettype(source, &type, NULL);
if (type == SOURCE_SCENE)
if (type == OBS_SOURCE_TYPE_SCENE)
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
"AddScene",
Q_ARG(OBSSource, OBSSource(source)));
@ -245,7 +245,7 @@ void OBSBasic::SourceRemoved(void *data, calldata_t params)
obs_source_type type;
obs_source_gettype(source, &type, NULL);
if (type == SOURCE_SCENE)
if (type == OBS_SOURCE_TYPE_SCENE)
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
"RemoveScene",
Q_ARG(OBSSource, OBSSource(source)));
@ -474,8 +474,8 @@ void OBSBasic::AddSource(obs_scene_t scene, const char *id)
}
if (success) {
obs_source_t source = obs_source_create(SOURCE_INPUT, id,
name.c_str(), NULL);
obs_source_t source = obs_source_create(OBS_SOURCE_TYPE_INPUT,
id, name.c_str(), NULL);
sourceSceneRefs[source] = 0;
@ -497,7 +497,8 @@ void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
QMenu popup;
while (obs_enum_input_types(idx++, &type)) {
const char *name = obs_source_getdisplayname(SOURCE_INPUT,
const char *name = obs_source_getdisplayname(
OBS_SOURCE_TYPE_INPUT,
type, App()->GetLocale());
QAction *popupItem = new QAction(QT_UTF8(name), this);

View file

@ -19,13 +19,9 @@ add_definitions(${Libswresample_DEFINITIONS})
set(obs-ffmpeg_SOURCES
obs-ffmpeg.c
obs-ffmpeg-output.c)
set(obs-ffmpeg_HEADERS
obs-ffmpeg-output.h)
add_library(obs-ffmpeg MODULE
${obs-ffmpeg_SOURCES}
${obs-ffmpeg_HEADERS})
${obs-ffmpeg_SOURCES})
target_link_libraries(obs-ffmpeg
libobs
${Libavcodec_LIBRARIES}

View file

@ -16,7 +16,41 @@
******************************************************************************/
#include <obs.h>
#include "obs-ffmpeg-output.h"
#include <util/circlebuf.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
struct ffmpeg_data {
AVStream *video;
AVStream *audio;
AVCodec *acodec;
AVCodec *vcodec;
AVFormatContext *output;
struct SwsContext *swscale;
AVPicture dst_picture;
AVFrame *vframe;
int frame_size;
int total_frames;
struct circlebuf excess_frames[MAX_AUDIO_PLANES];
uint8_t *samples[MAX_AUDIO_PLANES];
AVFrame *aframe;
int total_samples;
const char *filename_test;
bool initialized;
};
struct ffmpeg_output {
obs_output_t output;
volatile bool active;
struct ffmpeg_data ff_data;
};
/* ------------------------------------------------------------------------- */
/* TODO: remove these later */
#define SPS_TODO 44100
@ -319,29 +353,29 @@ fail:
/* ------------------------------------------------------------------------- */
const char *ffmpeg_output_getname(const char *locale)
static const char *ffmpeg_output_getname(const char *locale)
{
/* TODO: translation */
return "FFmpeg file output";
}
void test_callback(void *param, int bla, const char *format, va_list args)
static void ffmpeg_log_callback(void *param, int bla, const char *format,
va_list args)
{
blogva(LOG_INFO, format, args);
}
struct ffmpeg_output *ffmpeg_output_create(obs_data_t settings,
static struct ffmpeg_output *ffmpeg_output_create(obs_data_t settings,
obs_output_t output)
{
struct ffmpeg_output *data = bzalloc(sizeof(struct ffmpeg_output));
data->output = output;
av_log_set_callback(test_callback);
av_log_set_callback(ffmpeg_log_callback);
return data;
}
void ffmpeg_output_destroy(struct ffmpeg_output *data)
static void ffmpeg_output_destroy(struct ffmpeg_output *data)
{
if (data) {
if (data->active)
@ -350,7 +384,8 @@ void ffmpeg_output_destroy(struct ffmpeg_output *data)
}
}
void ffmpeg_output_update(struct ffmpeg_output *data, obs_data_t settings)
static void ffmpeg_output_update(struct ffmpeg_output *data,
obs_data_t settings)
{
}
@ -457,7 +492,7 @@ static inline void encode_audio(struct ffmpeg_data *data,
ret = avcodec_fill_audio_frame(data->aframe, context->channels,
context->sample_fmt, data->samples[0],
total_size, 1);
(int)total_size, 1);
if (ret < 0) {
blog(LOG_ERROR, "receive_audio: avcodec_fill_audio_frame "
"failed: %s", av_err2str(ret));
@ -513,7 +548,7 @@ static void receive_audio(void *param, const struct audio_data *frame)
}
}
bool ffmpeg_output_start(struct ffmpeg_output *data)
static bool ffmpeg_output_start(struct ffmpeg_output *data)
{
video_t video = obs_video();
audio_t audio = obs_audio();
@ -552,7 +587,7 @@ bool ffmpeg_output_start(struct ffmpeg_output *data)
return true;
}
void ffmpeg_output_stop(struct ffmpeg_output *data)
static void ffmpeg_output_stop(struct ffmpeg_output *data)
{
if (data->active) {
data->active = false;
@ -562,7 +597,18 @@ void ffmpeg_output_stop(struct ffmpeg_output *data)
}
}
bool ffmpeg_output_active(struct ffmpeg_output *data)
static bool ffmpeg_output_active(struct ffmpeg_output *data)
{
return data->active;
}
struct obs_output_info ffmpeg_output = {
.id = "ffmpeg_output",
.getname = ffmpeg_output_getname,
.create = ffmpeg_output_create,
.destroy = ffmpeg_output_destroy,
.update = ffmpeg_output_update,
.start = ffmpeg_output_start,
.stop = ffmpeg_output_stop,
.active = ffmpeg_output_active
};

View file

@ -1,69 +0,0 @@
/******************************************************************************
Copyright (C) 2013 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/>.
******************************************************************************/
#pragma once
#include <util/c99defs.h>
#include <util/circlebuf.h>
#include <media-io/audio-io.h>
#include <media-io/video-io.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
struct ffmpeg_data {
AVStream *video;
AVStream *audio;
AVCodec *acodec;
AVCodec *vcodec;
AVFormatContext *output;
struct SwsContext *swscale;
AVPicture dst_picture;
AVFrame *vframe;
int frame_size;
int total_frames;
struct circlebuf excess_frames[MAX_AUDIO_PLANES];
uint8_t *samples[MAX_AUDIO_PLANES];
AVFrame *aframe;
int total_samples;
const char *filename_test;
bool initialized;
};
struct ffmpeg_output {
obs_output_t output;
volatile bool active;
struct ffmpeg_data ff_data;
};
EXPORT const char *ffmpeg_output_getname(const char *locale);
EXPORT struct ffmpeg_output *ffmpeg_output_create(obs_data_t settings,
obs_output_t output);
EXPORT void ffmpeg_output_destroy(struct ffmpeg_output *data);
EXPORT void ffmpeg_output_update(struct ffmpeg_output *data,
obs_data_t settings);
EXPORT bool ffmpeg_output_start(struct ffmpeg_output *data);
EXPORT void ffmpeg_output_stop(struct ffmpeg_output *data);
EXPORT bool ffmpeg_output_active(struct ffmpeg_output *data);

View file

@ -1,21 +1,11 @@
#include <string.h>
#include <obs.h>
#include <obs-module.h>
EXPORT bool enum_outputs(size_t idx, const char **name);
EXPORT uint32_t module_version(uint32_t in_version);
OBS_DECLARE_MODULE()
static const char *outputs[] = {"ffmpeg_output"};
extern struct obs_output_info ffmpeg_output;
uint32_t module_version(uint32_t in_version)
bool obs_module_load(uint32_t obs_version)
{
return LIBOBS_API_VER;
}
bool enum_outputs(size_t idx, const char **name)
{
if (idx >= sizeof(outputs)/sizeof(const char*))
return false;
*name = outputs[idx];
obs_register_output(&ffmpeg_output);
return true;
}

View file

@ -148,14 +148,14 @@ static void test()
/* ------------------------------------------------------ */
/* create source */
SourceContext source{obs_source_create(SOURCE_INPUT,
SourceContext source{obs_source_create(OBS_SOURCE_INPUT,
"random", "a test source", NULL)};
if (!source)
throw "Couldn't create random test source";
/* ------------------------------------------------------ */
/* create filter */
SourceContext filter{obs_source_create(SOURCE_FILTER,
SourceContext filter{obs_source_create(OBS_SOURCE_FILTER,
"test", "a test filter", NULL)};
if (!filter)
throw "Couldn't create test filter";

View file

@ -30,15 +30,8 @@ set(test-input_SOURCES
test-sinewave.c
test-random.c)
set(test-input_HEADERS
test-filter.h
test-input-exports.h
test-random.h
test-sinewave.h)
add_library(test-input MODULE
${test-input_SOURCES}
${test-input_HEADERS})
${test-input_SOURCES})
target_link_libraries(test-input
${test-input_PLATFORM_DEPS}

View file

@ -1,41 +0,0 @@
#pragma once
#include "obs.h"
#import <CoreGraphics/CGDisplayStream.h>
#include <util/darray.h>
#ifdef __cplusplus
extern "C" {
#endif
struct desktop_tex {
samplerstate_t sampler;
effect_t whatever;
CGDisplayStreamRef disp;
uint32_t width, height;
texture_t tex;
pthread_mutex_t mutex;
IOSurfaceRef current, prev;
};
EXPORT const char *osx_desktop_test_getname(const char *locale);
EXPORT struct desktop_tex *osx_desktop_test_create(const char *settings,
obs_source_t source);
EXPORT void osx_desktop_test_destroy(struct desktop_tex *rt);
EXPORT uint32_t osx_desktop_test_get_output_flags(struct desktop_tex *rt);
EXPORT void osx_desktop_test_video_tick(struct desktop_tex *rt, float dt);
EXPORT void osx_desktop_test_video_render(struct desktop_tex *rt,
obs_source_t filter_target);
EXPORT uint32_t osx_desktop_test_getwidth(struct desktop_tex *rt);
EXPORT uint32_t osx_desktop_test_getheight(struct desktop_tex *rt);
#ifdef __cplusplus
}
#endif

View file

@ -1,25 +1,60 @@
#include <stdlib.h>
#include "test-desktop.h"
#include <obs.h>
#include <pthread.h>
#import <CoreGraphics/CGDisplayStream.h>
#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>
#import <OpenGL/OpenGL.h>
#import <OpenGL/CGLIOSurface.h>
#import <OpenGL/CGLMacro.h>
#import <CoreGraphics/CGDisplayStream.h>
#include <pthread.h>
struct desktop_tex {
samplerstate_t sampler;
effect_t whatever;
const char *osx_desktop_test_getname(const char *locale)
{
return "OSX Monitor Capture";
}
CGDisplayStreamRef disp;
uint32_t width, height;
texture_t tex;
pthread_mutex_t mutex;
IOSurfaceRef current, prev;
};
static IOSurfaceRef current = NULL,
prev = NULL;
static pthread_mutex_t c_mutex;
struct desktop_tex *osx_desktop_test_create(const char *settings,
static const char *osx_desktop_test_getname(const char *locale)
{
return "OSX Monitor Capture";
}
static void osx_desktop_test_destroy(struct desktop_tex *rt)
{
if (rt) {
pthread_mutex_lock(&rt->mutex);
gs_entercontext(obs_graphics());
if (current) {
IOSurfaceDecrementUseCount(rt->current);
CFRelease(rt->current);
}
if (rt->sampler)
samplerstate_destroy(rt->sampler);
if (rt->tex)
texture_destroy(rt->tex);
CGDisplayStreamStop(rt->disp);
effect_destroy(rt->whatever);
bfree(rt);
gs_leavecontext();
pthread_mutex_unlock(&rt->mutex);
}
}
static struct desktop_tex *osx_desktop_test_create(const char *settings,
obs_source_t source)
{
struct desktop_tex *rt = bzalloc(sizeof(struct desktop_tex));
@ -101,35 +136,7 @@ struct desktop_tex *osx_desktop_test_create(const char *settings,
return rt;
}
void osx_desktop_test_destroy(struct desktop_tex *rt)
{
if (rt) {
pthread_mutex_lock(&rt->mutex);
gs_entercontext(obs_graphics());
if (current) {
IOSurfaceDecrementUseCount(rt->current);
CFRelease(rt->current);
}
if (rt->sampler)
samplerstate_destroy(rt->sampler);
if (rt->tex)
texture_destroy(rt->tex);
CGDisplayStreamStop(rt->disp);
effect_destroy(rt->whatever);
bfree(rt);
gs_leavecontext();
pthread_mutex_unlock(&rt->mutex);
}
}
uint32_t osx_desktop_test_get_output_flags(struct desktop_tex *rt)
{
return SOURCE_VIDEO;
}
void osx_desktop_test_video_render(struct desktop_tex *rt,
static void osx_desktop_test_video_render(struct desktop_tex *rt,
obs_source_t filter_target)
{
pthread_mutex_lock(&rt->mutex);
@ -161,12 +168,24 @@ fail:
pthread_mutex_unlock(&rt->mutex);
}
uint32_t osx_desktop_test_getwidth(struct desktop_tex *rt)
static uint32_t osx_desktop_test_getwidth(struct desktop_tex *rt)
{
return rt->width;
}
uint32_t osx_desktop_test_getheight(struct desktop_tex *rt)
static uint32_t osx_desktop_test_getheight(struct desktop_tex *rt)
{
return rt->height;
}
struct obs_source_info osx_desktop = {
.id = "osx_desktop",
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
.getname = osx_desktop_test_getname,
.create = osx_desktop_test_create,
.destroy = osx_desktop_test_destroy,
.video_render = osx_desktop_video_render,
.getwidth = osx_desktop_test_getwidth,
.getheight = osx_desktop_test_getheight,
};

View file

@ -1,11 +1,29 @@
#include "test-filter.h"
#include <obs.h>
const char *test_getname(const char *locale)
struct test_filter {
obs_source_t source;
effect_t whatever;
};
static const char *filter_getname(const char *locale)
{
return "Test";
}
struct test_filter *test_create(const char *settings, obs_source_t source)
static void filter_destroy(struct test_filter *tf)
{
if (tf) {
gs_entercontext(obs_graphics());
effect_destroy(tf->whatever);
bfree(tf);
gs_leavecontext();
}
}
static struct test_filter *filter_create(obs_data_t settings,
obs_source_t source)
{
struct test_filter *tf = bzalloc(sizeof(struct test_filter));
char *effect_file;
@ -18,42 +36,27 @@ struct test_filter *test_create(const char *settings, obs_source_t source)
tf->whatever = gs_create_effect_from_file(effect_file, NULL);
bfree(effect_file);
if (!tf->whatever) {
test_destroy(tf);
filter_destroy(tf);
return NULL;
}
tf->texrender = texrender_create(GS_RGBA, GS_ZS_NONE);
gs_leavecontext();
return tf;
}
void test_destroy(struct test_filter *tf)
static void filter_render(struct test_filter *tf, effect_t effect)
{
if (tf) {
gs_entercontext(obs_graphics());
effect_destroy(tf->whatever);
texrender_destroy(tf->texrender);
bfree(tf);
gs_leavecontext();
}
obs_source_process_filter(tf->source, tf->whatever, 0, 0, GS_RGBA,
ALLOW_DIRECT_RENDERING);
}
uint32_t test_get_output_flags(struct test_filter *tf)
{
return SOURCE_VIDEO;
}
void test_video_tick(struct test_filter *tf, float seconds)
{
texrender_reset(tf->texrender);
}
void test_video_render(struct test_filter *tf)
{
obs_source_process_filter(tf->source, tf->texrender, tf->whatever,
0, 0, ALLOW_DIRECT_RENDERING);
}
struct obs_source_info test_filter = {
.id = "test_filter",
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.getname = filter_getname,
.create = filter_create,
.destroy = filter_destroy,
.video_render = filter_render
};

View file

@ -1,26 +0,0 @@
#pragma once
#include <obs.h>
#ifdef __cplusplus
extern "C" {
#endif
struct test_filter {
obs_source_t source;
effect_t whatever;
texrender_t texrender;
};
EXPORT const char *test_getname(const char *locale);
EXPORT struct test_filter *test_create(const char *settings, obs_source_t source);
EXPORT void test_destroy(struct test_filter *rt);
EXPORT uint32_t test_get_output_flags(struct test_filter *rt);
EXPORT void test_video_tick(struct test_filter *rt, float seconds);
EXPORT void test_video_render(struct test_filter *rt);
#ifdef __cplusplus
}
#endif

View file

@ -1,15 +0,0 @@
#pragma once
#include <util/c99defs.h>
#ifdef __cplusplus
extern "C" {
#endif
EXPORT uint32_t module_version(uint32_t in_version);
EXPORT bool enum_inputs(size_t idx, const char **name);
EXPORT bool enum_filters(size_t idx, const char **name);
#ifdef __cplusplus
}
#endif

View file

@ -1,35 +1,24 @@
#include <obs.h>
#include "test-input-exports.h"
#include <obs-module.h>
OBS_DECLARE_MODULE()
extern struct obs_source_info test_random;
extern struct obs_source_info test_sinewave;
extern struct obs_source_info test_filter;
const char *inputs[] = {
#ifdef __APPLE__
"osx_desktop_test",
extern struct obs_source_info osx_desktop;
#endif
"random",
"sinewave"
};
const char *filters[] = {"test"};
uint32_t module_version(uint32_t in_version)
bool obs_module_load(uint32_t libobs_version)
{
return LIBOBS_API_VER;
}
obs_register_source(&test_random);
obs_register_source(&test_sinewave);
obs_register_source(&test_filter);
bool enum_inputs(size_t idx, const char **name)
{
if (idx >= (sizeof(inputs)/sizeof(const char*)))
return false;
#ifdef __APPLE__
obs_register_source(&osx_desktop);
#endif
*name = inputs[idx];
return true;
}
bool enum_filters(size_t idx, const char **name)
{
if (idx >= (sizeof(filters)/sizeof(const char*)))
return false;
*name = filters[idx];
return true;
}

View file

@ -1,12 +1,29 @@
#include <stdlib.h>
#include "test-random.h"
#include <obs.h>
const char *random_getname(const char *locale)
struct random_tex {
texture_t texture;
};
static const char *random_getname(const char *locale)
{
return "20x20 Random Pixel Texture Source (Test)";
}
struct random_tex *random_create(const char *settings, obs_source_t source)
static void random_destroy(struct random_tex *rt)
{
if (rt) {
gs_entercontext(obs_graphics());
texture_destroy(rt->texture);
bfree(rt);
gs_leavecontext();
}
}
static struct random_tex *random_create(obs_data_t settings,
obs_source_t source)
{
struct random_tex *rt = bzalloc(sizeof(struct random_tex));
uint32_t *pixels = bmalloc(20*20*4);
@ -39,38 +56,31 @@ struct random_tex *random_create(const char *settings, obs_source_t source)
return rt;
}
void random_destroy(struct random_tex *rt)
static void random_video_render(struct random_tex *rt, effect_t effect)
{
if (rt) {
gs_entercontext(obs_graphics());
texture_destroy(rt->texture);
bfree(rt);
gs_leavecontext();
}
}
uint32_t random_get_output_flags(struct random_tex *rt)
{
return SOURCE_VIDEO | SOURCE_DEFAULT_EFFECT;
}
void random_video_render(struct random_tex *rt, obs_source_t filter_target)
{
effect_t effect = gs_geteffect();
eparam_t diffuse = effect_getparambyname(effect, "diffuse");
effect_settexture(effect, diffuse, rt->texture);
eparam_t image = effect_getparambyname(effect, "image");
effect_settexture(effect, image, rt->texture);
gs_draw_sprite(rt->texture, 0, 0, 0);
}
uint32_t random_getwidth(struct random_tex *rt)
static uint32_t random_getwidth(struct random_tex *rt)
{
return texture_getwidth(rt->texture);
}
uint32_t random_getheight(struct random_tex *rt)
static uint32_t random_getheight(struct random_tex *rt)
{
return texture_getheight(rt->texture);
}
struct obs_source_info test_random = {
.id = "random",
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_VIDEO,
.getname = random_getname,
.create = random_create,
.destroy = random_destroy,
.video_render = random_video_render,
.getwidth = random_getwidth,
.getheight = random_getheight
};

View file

@ -1,28 +0,0 @@
#pragma once
#include <obs.h>
#ifdef __cplusplus
extern "C" {
#endif
struct random_tex {
texture_t texture;
};
EXPORT const char *random_getname(const char *locale);
EXPORT struct random_tex *random_create(const char *settings,
obs_source_t source);
EXPORT void random_destroy(struct random_tex *rt);
EXPORT uint32_t random_get_output_flags(struct random_tex *rt);
EXPORT void random_video_render(struct random_tex *rt,
obs_source_t filter_target);
EXPORT uint32_t random_getwidth(struct random_tex *rt);
EXPORT uint32_t random_getheight(struct random_tex *rt);
#ifdef __cplusplus
}
#endif

View file

@ -1,8 +1,18 @@
#include <math.h>
#include "test-sinewave.h"
#include <util/bmem.h>
#include <util/threading.h>
#include <util/platform.h>
#include <obs.h>
struct sinewave_data {
bool initialized_thread;
pthread_t thread;
event_t event;
obs_source_t source;
};
/* middle C */
const double rate = 261.63/48000.0;
static const double rate = 261.63/48000.0;
#define M_PI 3.1415926535897932384626433832795
#define M_PI_X2 M_PI*2
@ -45,12 +55,27 @@ static void *sinewave_thread(void *pdata)
/* ------------------------------------------------------------------------- */
const char *sinewave_getname(const char *locale)
static const char *sinewave_getname(const char *locale)
{
return "Sinewave Sound Source (Test)";
}
struct sinewave_data *sinewave_create(const char *settings, obs_source_t source)
static void sinewave_destroy(struct sinewave_data *swd)
{
if (swd) {
if (swd->initialized_thread) {
void *ret;
event_signal(&swd->event);
pthread_join(swd->thread, &ret);
}
event_destroy(&swd->event);
bfree(swd);
}
}
static struct sinewave_data *sinewave_create(obs_data_t settings,
obs_source_t source)
{
struct sinewave_data *swd = bzalloc(sizeof(struct sinewave_data));
swd->source = source;
@ -68,21 +93,11 @@ fail:
return NULL;
}
void sinewave_destroy(struct sinewave_data *swd)
{
if (swd) {
if (swd->initialized_thread) {
void *ret;
event_signal(&swd->event);
pthread_join(swd->thread, &ret);
}
event_destroy(&swd->event);
bfree(swd);
}
}
uint32_t sinewave_get_output_flags(struct sinewave_data *swd)
{
return SOURCE_AUDIO;
}
struct obs_source_info test_sinewave = {
.id = "test_sinewave",
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_AUDIO,
.getname = sinewave_getname,
.create = sinewave_create,
.destroy = sinewave_destroy,
};

View file

@ -1,28 +0,0 @@
#pragma once
#include <util/bmem.h>
#include <util/threading.h>
#include <util/platform.h>
#include <obs.h>
#ifdef __cplusplus
extern "C" {
#endif
struct sinewave_data {
bool initialized_thread;
pthread_t thread;
event_t event;
obs_source_t source;
};
EXPORT const char *sinewave_getname(const char *locale);
EXPORT struct sinewave_data *sinewave_create(const char *settings,
obs_source_t source);
EXPORT void sinewave_destroy(struct sinewave_data *swd);
EXPORT uint32_t sinewave_get_output_flags(struct sinewave_data *swd);
#ifdef __cplusplus
}
#endif

View file

@ -110,7 +110,8 @@ static HWND CreateTestWindow(HINSTANCE instance)
if (!RegisterClass(&wc))
return 0;
return CreateWindow(TEXT("bla"), TEXT("bla"), WS_OVERLAPPEDWINDOW|WS_VISIBLE,
return CreateWindow(TEXT("bla"), TEXT("bla"),
WS_OVERLAPPEDWINDOW|WS_VISIBLE,
1920/2 - cx/2, 1080/2 - cy/2, cx, cy,
NULL, NULL, instance, NULL);
}
@ -139,14 +140,14 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine,
/* ------------------------------------------------------ */
/* create source */
SourceContext source = obs_source_create(SOURCE_INPUT,
SourceContext source = obs_source_create(OBS_SOURCE_TYPE_INPUT,
"random", "some randon source", NULL);
if (!source)
throw "Couldn't create random test source";
/* ------------------------------------------------------ */
/* create filter */
SourceContext filter = obs_source_create(SOURCE_FILTER,
SourceContext filter = obs_source_create(OBS_SOURCE_TYPE_FILTER,
"test", "a nice little green filter", NULL);
if (!filter)
throw "Couldn't create test filter";

View file

@ -174,9 +174,6 @@
<ClCompile Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg-output.c" />
<ClCompile Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg-output.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View file

@ -22,9 +22,4 @@
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg-output.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -18,12 +18,6 @@
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\test\test-input\test-filter.h" />
<ClInclude Include="..\..\..\test\test-input\test-input-exports.h" />
<ClInclude Include="..\..\..\test\test-input\test-random.h" />
<ClInclude Include="..\..\..\test\test-input\test-sinewave.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\test\test-input\test-filter.c" />
<ClCompile Include="..\..\..\test\test-input\test-input.c" />

View file

@ -14,20 +14,6 @@
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\test\test-input\test-filter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\test\test-input\test-random.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\test\test-input\test-input-exports.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\test\test-input\test-sinewave.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\test\test-input\test-random.c">
<Filter>Source Files</Filter>