New upstream version 22.0.3+dfsg1
This commit is contained in:
parent
665f64a933
commit
cdc9a9fc87
334 changed files with 14525 additions and 2639 deletions
|
|
@ -13,6 +13,13 @@ endif()
|
|||
|
||||
if(UNIX)
|
||||
if (NOT APPLE)
|
||||
find_package(X11_XCB REQUIRED)
|
||||
find_package(XCB OPTIONAL_COMPONENTS XINPUT)
|
||||
if (XCB_XINPUT_FOUND)
|
||||
set(USE_XINPUT "1")
|
||||
else()
|
||||
set(USE_XINPUT "0")
|
||||
endif()
|
||||
find_package(PulseAudio)
|
||||
if (NOT "${PULSEAUDIO_LIBRARY}" STREQUAL "")
|
||||
message(STATUS "Found PulseAudio - Audio Monitor enabled")
|
||||
|
|
@ -22,14 +29,13 @@ if(UNIX)
|
|||
endif()
|
||||
else()
|
||||
set(HAVE_PULSEAUDIO "0")
|
||||
set(USE_XINPUT "0")
|
||||
endif()
|
||||
find_package(DBus QUIET)
|
||||
if (NOT APPLE)
|
||||
find_package(X11_XCB REQUIRED)
|
||||
endif()
|
||||
else()
|
||||
set(HAVE_DBUS "0")
|
||||
set(HAVE_PULSEAUDIO "0")
|
||||
set(USE_XINPUT "0")
|
||||
endif()
|
||||
|
||||
find_package(ImageMagick QUIET COMPONENTS MagickCore)
|
||||
|
|
@ -199,6 +205,16 @@ elseif(UNIX)
|
|||
${libobs_PLATFORM_DEPS}
|
||||
${X11_XCB_LIBRARIES})
|
||||
|
||||
if(USE_XINPUT)
|
||||
include_directories(
|
||||
${XCB_XINPUT_INCLUDE_DIR})
|
||||
add_definitions(
|
||||
${XCB_DEFINITIONS})
|
||||
set(libobs_PLATFORM_DEPS
|
||||
${XCB_XINPUT_LIBRARY}
|
||||
${libobs_PLATFORM_DEPS})
|
||||
endif()
|
||||
|
||||
if(HAVE_PULSEAUDIO)
|
||||
set(libobs_PLATFORM_DEPS
|
||||
${libobs_PLATFORM_DEPS}
|
||||
|
|
|
|||
|
|
@ -25,23 +25,21 @@ static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb,
|
|||
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreams,
|
||||
kAudioDevicePropertyScopeInput,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
/* check to see if it's a mac input device */
|
||||
if (!allow_inputs) {
|
||||
AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
|
||||
if (!size)
|
||||
return true;
|
||||
}
|
||||
/* Check if the device is capable of audio output. */
|
||||
AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
|
||||
if (!allow_inputs && !size)
|
||||
return true;
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
|
||||
addr.mSelector = kAudioDevicePropertyDeviceUID;
|
||||
stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid);
|
||||
if (!success(stat, "get audio device UID"))
|
||||
return true;
|
||||
goto fail;
|
||||
|
||||
addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
|
||||
stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolBox/AudioQueue.h>
|
||||
#include <AudioToolbox/AudioQueue.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
|
||||
|
|
@ -193,14 +193,14 @@ static bool audio_monitor_init(struct audio_monitor *monitor,
|
|||
}
|
||||
|
||||
if (strcmp(uid, "default") != 0) {
|
||||
CFStringRef cf_uid = CFStringCreateWithBytesNoCopy(NULL,
|
||||
CFStringRef cf_uid = CFStringCreateWithBytes(NULL,
|
||||
(const UInt8*)uid, strlen(uid),
|
||||
kCFStringEncodingUTF8,
|
||||
false, NULL);
|
||||
false);
|
||||
|
||||
stat = AudioQueueSetProperty(monitor->queue,
|
||||
kAudioQueueProperty_CurrentDevice,
|
||||
cf_uid, sizeof(cf_uid));
|
||||
&cf_uid, sizeof(cf_uid));
|
||||
CFRelease(cf_uid);
|
||||
|
||||
if (!success(stat, "set current device")) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ struct signal_callback {
|
|||
signal_callback_t callback;
|
||||
void *data;
|
||||
bool remove;
|
||||
bool keep_ref;
|
||||
};
|
||||
|
||||
struct signal_info {
|
||||
|
|
@ -96,6 +97,7 @@ struct global_callback_info {
|
|||
struct signal_handler {
|
||||
struct signal_info *first;
|
||||
pthread_mutex_t mutex;
|
||||
volatile long refs;
|
||||
|
||||
DARRAY(struct global_callback_info) global_callbacks;
|
||||
pthread_mutex_t global_callbacks_mutex;
|
||||
|
|
@ -126,6 +128,7 @@ signal_handler_t *signal_handler_create(void)
|
|||
{
|
||||
struct signal_handler *handler = bzalloc(sizeof(struct signal_handler));
|
||||
handler->first = NULL;
|
||||
handler->refs = 1;
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
if (pthread_mutexattr_init(&attr) != 0)
|
||||
|
|
@ -149,20 +152,25 @@ signal_handler_t *signal_handler_create(void)
|
|||
return handler;
|
||||
}
|
||||
|
||||
static void signal_handler_actually_destroy(signal_handler_t *handler)
|
||||
{
|
||||
struct signal_info *sig = handler->first;
|
||||
while (sig != NULL) {
|
||||
struct signal_info *next = sig->next;
|
||||
signal_info_destroy(sig);
|
||||
sig = next;
|
||||
}
|
||||
|
||||
da_free(handler->global_callbacks);
|
||||
pthread_mutex_destroy(&handler->global_callbacks_mutex);
|
||||
pthread_mutex_destroy(&handler->mutex);
|
||||
bfree(handler);
|
||||
}
|
||||
|
||||
void signal_handler_destroy(signal_handler_t *handler)
|
||||
{
|
||||
if (handler) {
|
||||
struct signal_info *sig = handler->first;
|
||||
while (sig != NULL) {
|
||||
struct signal_info *next = sig->next;
|
||||
signal_info_destroy(sig);
|
||||
sig = next;
|
||||
}
|
||||
|
||||
da_free(handler->global_callbacks);
|
||||
pthread_mutex_destroy(&handler->global_callbacks_mutex);
|
||||
pthread_mutex_destroy(&handler->mutex);
|
||||
bfree(handler);
|
||||
if (handler && os_atomic_dec_long(&handler->refs) == 0) {
|
||||
signal_handler_actually_destroy(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -197,11 +205,12 @@ bool signal_handler_add(signal_handler_t *handler, const char *signal_decl)
|
|||
return success;
|
||||
}
|
||||
|
||||
void signal_handler_connect(signal_handler_t *handler, const char *signal,
|
||||
signal_callback_t callback, void *data)
|
||||
static void signal_handler_connect_internal(signal_handler_t *handler,
|
||||
const char *signal, signal_callback_t callback, void *data,
|
||||
bool keep_ref)
|
||||
{
|
||||
struct signal_info *sig, *last;
|
||||
struct signal_callback cb_data = {callback, data, false};
|
||||
struct signal_callback cb_data = {callback, data, false, keep_ref};
|
||||
size_t idx;
|
||||
|
||||
if (!handler)
|
||||
|
|
@ -221,13 +230,28 @@ void signal_handler_connect(signal_handler_t *handler, const char *signal,
|
|||
|
||||
pthread_mutex_lock(&sig->mutex);
|
||||
|
||||
if (keep_ref)
|
||||
os_atomic_inc_long(&handler->refs);
|
||||
|
||||
idx = signal_get_callback_idx(sig, callback, data);
|
||||
if (idx == DARRAY_INVALID)
|
||||
if (keep_ref || idx == DARRAY_INVALID)
|
||||
da_push_back(sig->callbacks, &cb_data);
|
||||
|
||||
pthread_mutex_unlock(&sig->mutex);
|
||||
}
|
||||
|
||||
void signal_handler_connect(signal_handler_t *handler, const char *signal,
|
||||
signal_callback_t callback, void *data)
|
||||
{
|
||||
signal_handler_connect_internal(handler, signal, callback, data, false);
|
||||
}
|
||||
|
||||
void signal_handler_connect_ref(signal_handler_t *handler, const char *signal,
|
||||
signal_callback_t callback, void *data)
|
||||
{
|
||||
signal_handler_connect_internal(handler, signal, callback, data, true);
|
||||
}
|
||||
|
||||
static inline struct signal_info *getsignal_locked(signal_handler_t *handler,
|
||||
const char *name)
|
||||
{
|
||||
|
|
@ -247,6 +271,7 @@ void signal_handler_disconnect(signal_handler_t *handler, const char *signal,
|
|||
signal_callback_t callback, void *data)
|
||||
{
|
||||
struct signal_info *sig = getsignal_locked(handler, signal);
|
||||
bool keep_ref = false;
|
||||
size_t idx;
|
||||
|
||||
if (!sig)
|
||||
|
|
@ -256,13 +281,19 @@ void signal_handler_disconnect(signal_handler_t *handler, const char *signal,
|
|||
|
||||
idx = signal_get_callback_idx(sig, callback, data);
|
||||
if (idx != DARRAY_INVALID) {
|
||||
if (sig->signalling)
|
||||
if (sig->signalling) {
|
||||
sig->callbacks.array[idx].remove = true;
|
||||
else
|
||||
} else {
|
||||
keep_ref = sig->callbacks.array[idx].keep_ref;
|
||||
da_erase(sig->callbacks, idx);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&sig->mutex);
|
||||
|
||||
if (keep_ref && os_atomic_dec_long(&handler->refs) == 0) {
|
||||
signal_handler_actually_destroy(handler);
|
||||
}
|
||||
}
|
||||
|
||||
static THREAD_LOCAL struct signal_callback *current_signal_cb = NULL;
|
||||
|
|
@ -280,6 +311,7 @@ void signal_handler_signal(signal_handler_t *handler, const char *signal,
|
|||
calldata_t *params)
|
||||
{
|
||||
struct signal_info *sig = getsignal_locked(handler, signal);
|
||||
long remove_refs = 0;
|
||||
|
||||
if (!sig)
|
||||
return;
|
||||
|
|
@ -298,8 +330,12 @@ void signal_handler_signal(signal_handler_t *handler, const char *signal,
|
|||
|
||||
for (size_t i = sig->callbacks.num; i > 0; i--) {
|
||||
struct signal_callback *cb = sig->callbacks.array+i-1;
|
||||
if (cb->remove)
|
||||
if (cb->remove) {
|
||||
if (cb->keep_ref)
|
||||
remove_refs++;
|
||||
|
||||
da_erase(sig->callbacks, i-1);
|
||||
}
|
||||
}
|
||||
|
||||
sig->signalling = false;
|
||||
|
|
@ -331,6 +367,12 @@ void signal_handler_signal(signal_handler_t *handler, const char *signal,
|
|||
}
|
||||
|
||||
pthread_mutex_unlock(&handler->global_callbacks_mutex);
|
||||
|
||||
if (remove_refs) {
|
||||
os_atomic_set_long(&handler->refs,
|
||||
os_atomic_load_long(&handler->refs) -
|
||||
remove_refs);
|
||||
}
|
||||
}
|
||||
|
||||
void signal_handler_connect_global(signal_handler_t *handler,
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ static inline bool signal_handler_add_array(signal_handler_t *handler,
|
|||
|
||||
EXPORT void signal_handler_connect(signal_handler_t *handler,
|
||||
const char *signal, signal_callback_t callback, void *data);
|
||||
EXPORT void signal_handler_connect_ref(signal_handler_t *handler,
|
||||
const char *signal, signal_callback_t callback, void *data);
|
||||
EXPORT void signal_handler_disconnect(signal_handler_t *handler,
|
||||
const char *signal, signal_callback_t callback, void *data);
|
||||
|
||||
|
|
|
|||
|
|
@ -491,13 +491,40 @@ static inline int ep_parse_func_param(struct effect_parser *ep,
|
|||
struct ep_func *func, struct ep_var *var)
|
||||
{
|
||||
int code;
|
||||
bool var_type_keyword = false;
|
||||
|
||||
if (!cf_next_valid_token(&ep->cfp))
|
||||
return PARSE_EOF;
|
||||
|
||||
code = ep_check_for_keyword(ep, "uniform", &var->uniform);
|
||||
code = ep_check_for_keyword(ep, "in", &var_type_keyword);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
else if (var_type_keyword)
|
||||
var->var_type = EP_VAR_IN;
|
||||
|
||||
if (!var_type_keyword) {
|
||||
code = ep_check_for_keyword(ep, "inout", &var_type_keyword);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
else if (var_type_keyword)
|
||||
var->var_type = EP_VAR_INOUT;
|
||||
}
|
||||
|
||||
if (!var_type_keyword) {
|
||||
code = ep_check_for_keyword(ep, "out", &var_type_keyword);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
else if (var_type_keyword)
|
||||
var->var_type = EP_VAR_OUT;
|
||||
}
|
||||
|
||||
if (!var_type_keyword) {
|
||||
code = ep_check_for_keyword(ep, "uniform", &var_type_keyword);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
else if (var_type_keyword)
|
||||
var->var_type = EP_VAR_UNIFORM;
|
||||
}
|
||||
|
||||
code = cf_get_name(&ep->cfp, &var->type, "type", ")");
|
||||
if (code != PARSE_SUCCESS)
|
||||
|
|
@ -1083,8 +1110,14 @@ static inline void ep_write_func_sampler_deps(struct effect_parser *ep,
|
|||
|
||||
static inline void ep_write_var(struct dstr *shader, struct ep_var *var)
|
||||
{
|
||||
if (var->uniform)
|
||||
if (var->var_type == EP_VAR_INOUT)
|
||||
dstr_cat(shader, "inout ");
|
||||
else if (var->var_type == EP_VAR_OUT)
|
||||
dstr_cat(shader, "out ");
|
||||
else if (var->var_type == EP_VAR_UNIFORM)
|
||||
dstr_cat(shader, "uniform ");
|
||||
// The "in" input modifier is implied by default, so leave it blank
|
||||
// in that case.
|
||||
|
||||
dstr_cat(shader, var->type);
|
||||
dstr_cat(shader, " ");
|
||||
|
|
|
|||
|
|
@ -37,9 +37,17 @@ struct dstr;
|
|||
/* ------------------------------------------------------------------------- */
|
||||
/* effect parser var data */
|
||||
|
||||
enum ep_var_type {
|
||||
EP_VAR_NONE,
|
||||
EP_VAR_IN = EP_VAR_NONE,
|
||||
EP_VAR_INOUT,
|
||||
EP_VAR_OUT,
|
||||
EP_VAR_UNIFORM
|
||||
};
|
||||
|
||||
struct ep_var {
|
||||
char *type, *name, *mapping;
|
||||
bool uniform;
|
||||
enum ep_var_type var_type;
|
||||
};
|
||||
|
||||
static inline void ep_var_init(struct ep_var *epv)
|
||||
|
|
|
|||
|
|
@ -334,16 +334,40 @@ static inline int sp_parse_func_param(struct shader_parser *sp,
|
|||
struct shader_var *var)
|
||||
{
|
||||
int code;
|
||||
bool is_uniform = false;
|
||||
bool var_type_keyword = false;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return PARSE_EOF;
|
||||
|
||||
code = sp_check_for_keyword(sp, "uniform", &is_uniform);
|
||||
code = sp_check_for_keyword(sp, "in", &var_type_keyword);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
else if (var_type_keyword)
|
||||
var->var_type = SHADER_VAR_IN;
|
||||
|
||||
var->var_type = is_uniform ? SHADER_VAR_UNIFORM : SHADER_VAR_NONE;
|
||||
if (!var_type_keyword) {
|
||||
code = sp_check_for_keyword(sp, "inout", &var_type_keyword);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
else if (var_type_keyword)
|
||||
var->var_type = SHADER_VAR_INOUT;
|
||||
}
|
||||
|
||||
if (!var_type_keyword) {
|
||||
code = sp_check_for_keyword(sp, "out", &var_type_keyword);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
else if (var_type_keyword)
|
||||
var->var_type = SHADER_VAR_OUT;
|
||||
}
|
||||
|
||||
if (!var_type_keyword) {
|
||||
code = sp_check_for_keyword(sp, "uniform", &var_type_keyword);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
else if (var_type_keyword)
|
||||
var->var_type = SHADER_VAR_UNIFORM;
|
||||
}
|
||||
|
||||
code = cf_get_name(&sp->cfp, &var->type, "type", ")");
|
||||
if (code != PARSE_SUCCESS)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ EXPORT enum gs_address_mode get_address_mode(const char *address_mode);
|
|||
|
||||
enum shader_var_type {
|
||||
SHADER_VAR_NONE,
|
||||
SHADER_VAR_IN = SHADER_VAR_NONE,
|
||||
SHADER_VAR_INOUT,
|
||||
SHADER_VAR_OUT,
|
||||
SHADER_VAR_UNIFORM,
|
||||
SHADER_VAR_CONST
|
||||
};
|
||||
|
|
|
|||
|
|
@ -109,6 +109,8 @@ static inline bool init_output(media_remux_job_t job, const char *out_filename)
|
|||
}
|
||||
out_stream->time_base = out_stream->codec->time_base;
|
||||
|
||||
av_dict_copy(&out_stream->metadata, in_stream->metadata, 0);
|
||||
|
||||
out_stream->codec->codec_tag = 0;
|
||||
if (job->ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||
out_stream->codec->flags |= CODEC_FLAG_GLOBAL_H;
|
||||
|
|
@ -141,6 +143,9 @@ bool media_remux_job_create(media_remux_job_t *job, const char *in_filename,
|
|||
if (!os_file_exists(in_filename))
|
||||
return false;
|
||||
|
||||
if (strcmp(in_filename, out_filename) == 0)
|
||||
return false;
|
||||
|
||||
*job = (media_remux_job_t)bzalloc(sizeof(struct media_remux_job));
|
||||
if (!*job)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include "util/threading.h"
|
||||
#include "util/bmem.h"
|
||||
|
|
@ -62,18 +63,20 @@ struct meter_cb {
|
|||
};
|
||||
|
||||
struct obs_volmeter {
|
||||
pthread_mutex_t mutex;
|
||||
obs_source_t *source;
|
||||
enum obs_fader_type type;
|
||||
float cur_db;
|
||||
pthread_mutex_t mutex;
|
||||
obs_source_t *source;
|
||||
enum obs_fader_type type;
|
||||
float cur_db;
|
||||
|
||||
pthread_mutex_t callback_mutex;
|
||||
DARRAY(struct meter_cb)callbacks;
|
||||
pthread_mutex_t callback_mutex;
|
||||
DARRAY(struct meter_cb) callbacks;
|
||||
|
||||
unsigned int update_ms;
|
||||
enum obs_peak_meter_type peak_meter_type;
|
||||
unsigned int update_ms;
|
||||
float prev_samples[MAX_AUDIO_CHANNELS][4];
|
||||
|
||||
float vol_magnitude[MAX_AUDIO_CHANNELS];
|
||||
float vol_peak[MAX_AUDIO_CHANNELS];
|
||||
float magnitude[MAX_AUDIO_CHANNELS];
|
||||
float peak[MAX_AUDIO_CHANNELS];
|
||||
};
|
||||
|
||||
static float cubic_def_to_db(const float def)
|
||||
|
|
@ -256,48 +259,257 @@ static void volmeter_source_destroyed(void *vptr, calldata_t *calldata)
|
|||
obs_volmeter_detach_source(volmeter);
|
||||
}
|
||||
|
||||
static void volmeter_process_audio_data(obs_volmeter_t *volmeter,
|
||||
const struct audio_data *data)
|
||||
static int get_nr_channels_from_audio_data(const struct audio_data *data)
|
||||
{
|
||||
int nr_channels = 0;
|
||||
for (int i = 0; i < MAX_AV_PLANES; i++) {
|
||||
if (data->data[i])
|
||||
nr_channels++;
|
||||
}
|
||||
return CLAMP(nr_channels, 0, MAX_AUDIO_CHANNELS);
|
||||
}
|
||||
|
||||
/* msb(h, g, f, e) lsb(d, c, b, a) --> msb(h, h, g, f) lsb(e, d, c, b)
|
||||
*/
|
||||
#define SHIFT_RIGHT_2PS(msb, lsb) {\
|
||||
__m128 tmp = _mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3));\
|
||||
lsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1));\
|
||||
msb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1));\
|
||||
}
|
||||
|
||||
/* x(d, c, b, a) --> (|d|, |c|, |b|, |a|)
|
||||
*/
|
||||
#define abs_ps(v) \
|
||||
_mm_andnot_ps(_mm_set1_ps(-0.f), v)
|
||||
|
||||
/* Take cross product of a vector with a matrix resulting in vector.
|
||||
*/
|
||||
#define VECTOR_MATRIX_CROSS_PS(out, v, m0, m1, m2, m3) \
|
||||
{\
|
||||
out = _mm_mul_ps(v, m0);\
|
||||
__m128 mul1 = _mm_mul_ps(v, m1);\
|
||||
__m128 mul2 = _mm_mul_ps(v, m2);\
|
||||
__m128 mul3 = _mm_mul_ps(v, m3);\
|
||||
\
|
||||
_MM_TRANSPOSE4_PS(out, mul1, mul2, mul3);\
|
||||
\
|
||||
out = _mm_add_ps(out, mul1);\
|
||||
out = _mm_add_ps(out, mul2);\
|
||||
out = _mm_add_ps(out, mul3);\
|
||||
}
|
||||
|
||||
/* x4(d, c, b, a) --> max(a, b, c, d)
|
||||
*/
|
||||
#define hmax_ps(r, x4) \
|
||||
do { \
|
||||
float x4_mem[4]; \
|
||||
_mm_storeu_ps(x4_mem, x4); \
|
||||
r = x4_mem[0]; \
|
||||
r = fmaxf(r, x4_mem[1]); \
|
||||
r = fmaxf(r, x4_mem[2]); \
|
||||
r = fmaxf(r, x4_mem[3]); \
|
||||
} while (false)
|
||||
|
||||
/* Calculate the true peak over a set of samples.
|
||||
* The algorithm implements 5x oversampling by using Whittaker–Shannon
|
||||
* interpolation over four samples.
|
||||
*
|
||||
* The four samples have location t=-1.5, -0.5, +0.5, +1.5
|
||||
* The oversamples are taken at locations t=-0.3, -0.1, +0.1, +0.3
|
||||
*
|
||||
* @param previous_samples Last 4 samples from the previous iteration.
|
||||
* @param samples The samples to find the peak in.
|
||||
* @param nr_samples Number of sets of 4 samples.
|
||||
* @returns 5 times oversampled true-peak from the set of samples.
|
||||
*/
|
||||
static float get_true_peak(__m128 previous_samples, const float *samples,
|
||||
size_t nr_samples)
|
||||
{
|
||||
/* These are normalized-sinc parameters for interpolating over sample
|
||||
* points which are located at x-coords: -1.5, -0.5, +0.5, +1.5.
|
||||
* And oversample points at x-coords: -0.3, -0.1, 0.1, 0.3. */
|
||||
const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f);
|
||||
const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);
|
||||
const __m128 p1 = _mm_set_ps(-0.189207f, 0.504551f, 0.756827f, -0.216236f);
|
||||
const __m128 p3 = _mm_set_ps(-0.103943f, 0.233872f, 0.935489f, -0.155915f);
|
||||
|
||||
__m128 work = previous_samples;
|
||||
__m128 peak = previous_samples;
|
||||
for (size_t i = 0; (i + 3) < nr_samples; i += 4) {
|
||||
__m128 new_work = _mm_load_ps(&samples[i]);
|
||||
__m128 intrp_samples;
|
||||
|
||||
/* Include the actual sample values in the peak. */
|
||||
__m128 abs_new_work = abs_ps(new_work);
|
||||
peak = _mm_max_ps(peak, abs_new_work);
|
||||
|
||||
/* Shift in the next point. */
|
||||
SHIFT_RIGHT_2PS(new_work, work);
|
||||
VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3);
|
||||
peak = _mm_max_ps(peak, abs_ps(intrp_samples));
|
||||
|
||||
SHIFT_RIGHT_2PS(new_work, work);
|
||||
VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3);
|
||||
peak = _mm_max_ps(peak, abs_ps(intrp_samples));
|
||||
|
||||
SHIFT_RIGHT_2PS(new_work, work);
|
||||
VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3);
|
||||
peak = _mm_max_ps(peak, abs_ps(intrp_samples));
|
||||
|
||||
SHIFT_RIGHT_2PS(new_work, work);
|
||||
VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3);
|
||||
peak = _mm_max_ps(peak, abs_ps(intrp_samples));
|
||||
}
|
||||
|
||||
float r;
|
||||
hmax_ps(r, peak);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* points contain the first four samples to calculate the sinc interpolation
|
||||
* over. They will have come from a previous iteration.
|
||||
*/
|
||||
static float get_sample_peak(__m128 previous_samples, const float *samples,
|
||||
size_t nr_samples)
|
||||
{
|
||||
__m128 peak = previous_samples;
|
||||
for (size_t i = 0; (i + 3) < nr_samples; i += 4) {
|
||||
__m128 new_work = _mm_load_ps(&samples[i]);
|
||||
peak = _mm_max_ps(peak, abs_ps(new_work));
|
||||
}
|
||||
|
||||
float r;
|
||||
hmax_ps(r, peak);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void volmeter_process_peak_last_samples(obs_volmeter_t *volmeter,
|
||||
int channel_nr, float *samples, size_t nr_samples)
|
||||
{
|
||||
/* Take the last 4 samples that need to be used for the next peak
|
||||
* calculation. If there are less than 4 samples in total the new
|
||||
* samples shift out the old samples. */
|
||||
|
||||
switch (nr_samples) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
volmeter->prev_samples[channel_nr][0] =
|
||||
volmeter->prev_samples[channel_nr][1];
|
||||
volmeter->prev_samples[channel_nr][1] =
|
||||
volmeter->prev_samples[channel_nr][2];
|
||||
volmeter->prev_samples[channel_nr][2] =
|
||||
volmeter->prev_samples[channel_nr][3];
|
||||
volmeter->prev_samples[channel_nr][3] = samples[nr_samples-1];
|
||||
break;
|
||||
case 2:
|
||||
volmeter->prev_samples[channel_nr][0] =
|
||||
volmeter->prev_samples[channel_nr][2];
|
||||
volmeter->prev_samples[channel_nr][1] =
|
||||
volmeter->prev_samples[channel_nr][3];
|
||||
volmeter->prev_samples[channel_nr][2] = samples[nr_samples-2];
|
||||
volmeter->prev_samples[channel_nr][3] = samples[nr_samples-1];
|
||||
break;
|
||||
case 3:
|
||||
volmeter->prev_samples[channel_nr][0] =
|
||||
volmeter->prev_samples[channel_nr][3];
|
||||
volmeter->prev_samples[channel_nr][1] = samples[nr_samples-3];
|
||||
volmeter->prev_samples[channel_nr][2] = samples[nr_samples-2];
|
||||
volmeter->prev_samples[channel_nr][3] = samples[nr_samples-1];
|
||||
break;
|
||||
default:
|
||||
volmeter->prev_samples[channel_nr][0] = samples[nr_samples-4];
|
||||
volmeter->prev_samples[channel_nr][1] = samples[nr_samples-3];
|
||||
volmeter->prev_samples[channel_nr][2] = samples[nr_samples-2];
|
||||
volmeter->prev_samples[channel_nr][3] = samples[nr_samples-1];
|
||||
}
|
||||
}
|
||||
|
||||
static void volmeter_process_peak(obs_volmeter_t *volmeter,
|
||||
const struct audio_data *data, int nr_channels)
|
||||
{
|
||||
int nr_samples = data->frames;
|
||||
int channel_nr = 0;
|
||||
|
||||
for (size_t plane_nr = 0; plane_nr < MAX_AV_PLANES; plane_nr++) {
|
||||
for (int plane_nr = 0; channel_nr < nr_channels; plane_nr++) {
|
||||
float *samples = (float *)data->data[plane_nr];
|
||||
if (!samples) {
|
||||
// This plane does not contain data.
|
||||
continue;
|
||||
}
|
||||
if (((uintptr_t)samples & 0xf) > 0) {
|
||||
printf("Audio plane %i is not aligned %p skipping "
|
||||
"peak volume measurement.\n",
|
||||
plane_nr, samples);
|
||||
volmeter->peak[channel_nr] = 1.0;
|
||||
channel_nr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// For each plane calculate:
|
||||
// * peak = the maximum-absolute of the sample values.
|
||||
// * magnitude = root-mean-square of the sample values.
|
||||
// A VU meter needs to integrate over 300ms, but this will
|
||||
// be handled by the ballistics of the meter itself,
|
||||
// reality. Which makes this calculation independent of
|
||||
// sample rate or update rate.
|
||||
float peak = 0.0;
|
||||
float sum_of_squares = 0.0;
|
||||
for (int sample_nr = 0; sample_nr < nr_samples; sample_nr++) {
|
||||
float sample = samples[sample_nr];
|
||||
/* volmeter->prev_samples may not be aligned to 16 bytes;
|
||||
* use unaligned load. */
|
||||
__m128 previous_samples = _mm_loadu_ps(
|
||||
volmeter->prev_samples[channel_nr]);
|
||||
|
||||
float peak;
|
||||
switch (volmeter->peak_meter_type) {
|
||||
case TRUE_PEAK_METER:
|
||||
peak = get_true_peak(previous_samples, samples,
|
||||
nr_samples);
|
||||
break;
|
||||
|
||||
case SAMPLE_PEAK_METER:
|
||||
default:
|
||||
peak = get_sample_peak(previous_samples, samples,
|
||||
nr_samples);
|
||||
break;
|
||||
|
||||
peak = fmaxf(peak, fabsf(sample));
|
||||
sum_of_squares += (sample * sample);
|
||||
}
|
||||
|
||||
volmeter->vol_magnitude[channel_nr] = sqrtf(sum_of_squares /
|
||||
volmeter_process_peak_last_samples(volmeter, channel_nr, samples,
|
||||
nr_samples);
|
||||
volmeter->vol_peak[channel_nr] = peak;
|
||||
|
||||
volmeter->peak[channel_nr] = peak;
|
||||
|
||||
channel_nr++;
|
||||
}
|
||||
|
||||
// Clear audio channels that are not in use.
|
||||
/* Clear the peak of the channels that have not been handled. */
|
||||
for (; channel_nr < MAX_AUDIO_CHANNELS; channel_nr++) {
|
||||
volmeter->vol_magnitude[channel_nr] = 0.0;
|
||||
volmeter->vol_peak[channel_nr] = 0.0;
|
||||
volmeter->peak[channel_nr] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static void volmeter_process_magnitude(obs_volmeter_t *volmeter,
|
||||
const struct audio_data *data, int nr_channels)
|
||||
{
|
||||
size_t nr_samples = data->frames;
|
||||
|
||||
int channel_nr = 0;
|
||||
for (int plane_nr = 0; channel_nr < nr_channels; plane_nr++) {
|
||||
float *samples = (float *)data->data[plane_nr];
|
||||
if (!samples) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float sum = 0.0;
|
||||
for (size_t i = 0; i < nr_samples; i++) {
|
||||
float sample = samples[i];
|
||||
sum += sample * sample;
|
||||
}
|
||||
volmeter->magnitude[channel_nr] = sqrtf(sum / nr_samples);
|
||||
|
||||
channel_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
static void volmeter_process_audio_data(obs_volmeter_t *volmeter,
|
||||
const struct audio_data *data)
|
||||
{
|
||||
int nr_channels = get_nr_channels_from_audio_data(data);
|
||||
|
||||
volmeter_process_peak(volmeter, data, nr_channels);
|
||||
volmeter_process_magnitude(volmeter, data, nr_channels);
|
||||
}
|
||||
|
||||
static void volmeter_source_data_received(void *vptr, obs_source_t *source,
|
||||
const struct audio_data *data, bool muted)
|
||||
{
|
||||
|
|
@ -317,15 +529,15 @@ static void volmeter_source_data_received(void *vptr, obs_source_t *source,
|
|||
for (int channel_nr = 0; channel_nr < MAX_AUDIO_CHANNELS;
|
||||
channel_nr++) {
|
||||
magnitude[channel_nr] = mul_to_db(
|
||||
volmeter->vol_magnitude[channel_nr] * mul);
|
||||
volmeter->magnitude[channel_nr] * mul);
|
||||
peak[channel_nr] = mul_to_db(
|
||||
volmeter->vol_peak[channel_nr] * mul);
|
||||
input_peak[channel_nr] = mul_to_db(
|
||||
volmeter->vol_peak[channel_nr]);
|
||||
}
|
||||
volmeter->peak[channel_nr] * mul);
|
||||
|
||||
// The input-peak is NOT adjusted with volume, so that the user
|
||||
// can check the input-gain.
|
||||
/* The input-peak is NOT adjusted with volume, so that the user
|
||||
* can check the input-gain. */
|
||||
input_peak[channel_nr] = mul_to_db(
|
||||
volmeter->peak[channel_nr]);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
|
|
@ -641,6 +853,14 @@ void obs_volmeter_detach_source(obs_volmeter_t *volmeter)
|
|||
volmeter_source_data_received, volmeter);
|
||||
}
|
||||
|
||||
void obs_volmeter_set_peak_meter_type(obs_volmeter_t *volmeter,
|
||||
enum obs_peak_meter_type peak_meter_type)
|
||||
{
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
volmeter->peak_meter_type = peak_meter_type;
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
}
|
||||
|
||||
void obs_volmeter_set_update_interval(obs_volmeter_t *volmeter,
|
||||
const unsigned int ms)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,6 +68,27 @@ enum obs_fader_type {
|
|||
OBS_FADER_LOG
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Peak meter types
|
||||
*/
|
||||
enum obs_peak_meter_type {
|
||||
/**
|
||||
* @brief A simple peak meter measuring the maximum of all samples.
|
||||
*
|
||||
* This was a very common type of peak meter used for audio, but
|
||||
* is not very accurate with regards to further audio processing.
|
||||
*/
|
||||
SAMPLE_PEAK_METER,
|
||||
|
||||
/**
|
||||
* @brief An accurate peak meter measure the maximum of inter-samples.
|
||||
*
|
||||
* This meter is more computational intensive due to 4x oversampling
|
||||
* to determine the true peak to an accuracy of +/- 0.5 dB.
|
||||
*/
|
||||
TRUE_PEAK_METER
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a fader
|
||||
* @param type the type of the fader
|
||||
|
|
@ -200,6 +221,14 @@ EXPORT bool obs_volmeter_attach_source(obs_volmeter_t *volmeter,
|
|||
*/
|
||||
EXPORT void obs_volmeter_detach_source(obs_volmeter_t *volmeter);
|
||||
|
||||
/**
|
||||
* @brief Set the peak meter type for the volume meter
|
||||
* @param volmeter pointer to the volume meter object
|
||||
* @param peak_meter_type set if true-peak needs to be measured.
|
||||
*/
|
||||
EXPORT void obs_volmeter_set_peak_meter_type(obs_volmeter_t *volmeter,
|
||||
enum obs_peak_meter_type peak_meter_type);
|
||||
|
||||
/**
|
||||
* @brief Set the update interval for the volume meter
|
||||
* @param volmeter pointer to the volume meter object
|
||||
|
|
|
|||
|
|
@ -27,21 +27,21 @@
|
|||
/*
|
||||
* Increment if major breaking API changes
|
||||
*/
|
||||
#define LIBOBS_API_MAJOR_VER 21
|
||||
#define LIBOBS_API_MAJOR_VER 22
|
||||
|
||||
/*
|
||||
* Increment if backward-compatible additions
|
||||
*
|
||||
* Reset to zero each major version
|
||||
*/
|
||||
#define LIBOBS_API_MINOR_VER 1
|
||||
#define LIBOBS_API_MINOR_VER 0
|
||||
|
||||
/*
|
||||
* Increment if backward-compatible bug fix
|
||||
*
|
||||
* Reset to zero each major or minor version
|
||||
*/
|
||||
#define LIBOBS_API_PATCH_VER 2
|
||||
#define LIBOBS_API_PATCH_VER 3
|
||||
|
||||
#define MAKE_SEMANTIC_VERSION(major, minor, patch) \
|
||||
((major << 24) | \
|
||||
|
|
@ -61,6 +61,11 @@
|
|||
# define OBS_INSTALL_PREFIX ""
|
||||
# define OBS_PLUGIN_DESTINATION "obs-plugins"
|
||||
# define OBS_RELATIVE_PREFIX "../../"
|
||||
# define OBS_RELEASE_CANDIDATE_MAJOR 0
|
||||
# define OBS_RELEASE_CANDIDATE_MINOR 0
|
||||
# define OBS_RELEASE_CANDIDATE_PATCH 0
|
||||
# define OBS_RELEASE_CANDIDATE_VER 0
|
||||
# define OBS_RELEASE_CANDIDATE 0
|
||||
#endif
|
||||
|
||||
#define OBS_INSTALL_DATA_PATH OBS_INSTALL_PREFIX OBS_DATA_PATH
|
||||
|
|
|
|||
|
|
@ -1319,6 +1319,19 @@ void obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj)
|
|||
da_insert(array->objects, idx, &obj);
|
||||
}
|
||||
|
||||
void obs_data_array_push_back_array(obs_data_array_t *array,
|
||||
obs_data_array_t *array2)
|
||||
{
|
||||
if (!array || !array2)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < array2->objects.num; i++) {
|
||||
obs_data_t *obj = array2->objects.array[i];
|
||||
obs_data_addref(obj);
|
||||
}
|
||||
da_push_back_da(array->objects, array2->objects);
|
||||
}
|
||||
|
||||
void obs_data_array_erase(obs_data_array_t *array, size_t idx)
|
||||
{
|
||||
if (array) {
|
||||
|
|
|
|||
|
|
@ -159,6 +159,8 @@ EXPORT obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx);
|
|||
EXPORT size_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj);
|
||||
EXPORT void obs_data_array_insert(obs_data_array_t *array, size_t idx,
|
||||
obs_data_t *obj);
|
||||
EXPORT void obs_data_array_push_back_array(obs_data_array_t *array,
|
||||
obs_data_array_t *array2);
|
||||
EXPORT void obs_data_array_erase(obs_data_array_t *array, size_t idx);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -189,8 +189,7 @@ static void add_connection(struct obs_encoder *encoder)
|
|||
struct video_scale_info info = {0};
|
||||
get_video_info(encoder, &info);
|
||||
|
||||
video_output_connect(encoder->media, &info, receive_video,
|
||||
encoder);
|
||||
start_raw_video(encoder->media, &info, receive_video, encoder);
|
||||
}
|
||||
|
||||
set_encoder_active(encoder, true);
|
||||
|
|
@ -202,8 +201,7 @@ static void remove_connection(struct obs_encoder *encoder)
|
|||
audio_output_disconnect(encoder->media, encoder->mixer_idx,
|
||||
receive_audio, encoder);
|
||||
else
|
||||
video_output_disconnect(encoder->media, receive_video,
|
||||
encoder);
|
||||
stop_raw_video(encoder->media, receive_video, encoder);
|
||||
|
||||
obs_encoder_shutdown(encoder);
|
||||
set_encoder_active(encoder, false);
|
||||
|
|
@ -295,6 +293,14 @@ obs_data_t *obs_encoder_defaults(const char *id)
|
|||
return (info) ? get_defaults(info) : NULL;
|
||||
}
|
||||
|
||||
obs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder)
|
||||
{
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_defaults"))
|
||||
return NULL;
|
||||
|
||||
return get_defaults(&encoder->info);
|
||||
}
|
||||
|
||||
obs_properties_t *obs_get_encoder_properties(const char *id)
|
||||
{
|
||||
const struct obs_encoder_info *ei = find_encoder(id);
|
||||
|
|
|
|||
|
|
@ -79,6 +79,62 @@ obs_hotkey_t *obs_hotkey_binding_get_hotkey(obs_hotkey_binding_t *binding)
|
|||
return binding->hotkey;
|
||||
}
|
||||
|
||||
static inline bool find_id(obs_hotkey_id id, size_t *idx);
|
||||
void obs_hotkey_set_name(obs_hotkey_id id, const char *name)
|
||||
{
|
||||
size_t idx;
|
||||
|
||||
if (!find_id(id, &idx))
|
||||
return;
|
||||
|
||||
obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx];
|
||||
bfree(hotkey->name);
|
||||
hotkey->name = bstrdup(name);
|
||||
}
|
||||
|
||||
void obs_hotkey_set_description(obs_hotkey_id id, const char *desc)
|
||||
{
|
||||
size_t idx;
|
||||
|
||||
if (!find_id(id, &idx))
|
||||
return;
|
||||
|
||||
obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx];
|
||||
bfree(hotkey->description);
|
||||
hotkey->description = bstrdup(desc);
|
||||
}
|
||||
|
||||
static inline bool find_pair_id(obs_hotkey_pair_id id, size_t *idx);
|
||||
void obs_hotkey_pair_set_names(obs_hotkey_pair_id id,
|
||||
const char *name0, const char *name1)
|
||||
{
|
||||
size_t idx;
|
||||
obs_hotkey_pair_t pair;
|
||||
|
||||
if (!find_pair_id(id, &idx))
|
||||
return;
|
||||
|
||||
pair = obs->hotkeys.hotkey_pairs.array[idx];
|
||||
|
||||
obs_hotkey_set_name(pair.id[0], name0);
|
||||
obs_hotkey_set_name(pair.id[1], name1);
|
||||
}
|
||||
|
||||
void obs_hotkey_pair_set_descriptions(obs_hotkey_pair_id id,
|
||||
const char *desc0, const char *desc1)
|
||||
{
|
||||
size_t idx;
|
||||
obs_hotkey_pair_t pair;
|
||||
|
||||
if (!find_pair_id(id, &idx))
|
||||
return;
|
||||
|
||||
pair = obs->hotkeys.hotkey_pairs.array[idx];
|
||||
|
||||
obs_hotkey_set_description(pair.id[0], desc0);
|
||||
obs_hotkey_set_description(pair.id[1], desc1);
|
||||
}
|
||||
|
||||
static void hotkey_signal(const char *signal, obs_hotkey_t *hotkey)
|
||||
{
|
||||
calldata_t data;
|
||||
|
|
@ -804,6 +860,30 @@ obs_data_array_t *obs_hotkey_save(obs_hotkey_id id)
|
|||
return result;
|
||||
}
|
||||
|
||||
void obs_hotkey_pair_save(obs_hotkey_pair_id id,
|
||||
obs_data_array_t **p_data0,
|
||||
obs_data_array_t **p_data1)
|
||||
{
|
||||
if ((!p_data0 && !p_data1) || !lock())
|
||||
return;
|
||||
|
||||
size_t idx;
|
||||
if (!find_pair_id(id, &idx))
|
||||
goto unlock;
|
||||
|
||||
obs_hotkey_pair_t *pair = &obs->hotkeys.hotkey_pairs.array[idx];
|
||||
|
||||
if (p_data0 && find_id(pair->id[0], &idx)) {
|
||||
*p_data0 = save_hotkey(&obs->hotkeys.hotkeys.array[idx]);
|
||||
}
|
||||
if (p_data1 && find_id(pair->id[1], &idx)) {
|
||||
*p_data1 = save_hotkey(&obs->hotkeys.hotkeys.array[idx]);
|
||||
}
|
||||
|
||||
unlock:
|
||||
unlock();
|
||||
}
|
||||
|
||||
static inline bool enum_save_hotkey(void *data,
|
||||
size_t idx, obs_hotkey_t *hotkey)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ const size_t OBS_INVALID_HOTKEY_ID = (size_t)-1;
|
|||
const size_t OBS_INVALID_HOTKEY_PAIR_ID = (size_t)-1;
|
||||
#endif
|
||||
|
||||
#define XINPUT_MOUSE_LEN 33
|
||||
|
||||
enum obs_key {
|
||||
#define OBS_HOTKEY(x) x,
|
||||
#include "obs-hotkeys.h"
|
||||
|
|
@ -58,6 +60,8 @@ enum obs_hotkey_registerer_type {
|
|||
};
|
||||
typedef enum obs_hotkey_registerer_type obs_hotkey_registerer_t;
|
||||
|
||||
/* getter functions */
|
||||
|
||||
EXPORT obs_hotkey_id obs_hotkey_get_id(const obs_hotkey_t *key);
|
||||
EXPORT const char *obs_hotkey_get_name(const obs_hotkey_t *key);
|
||||
EXPORT const char *obs_hotkey_get_description(const obs_hotkey_t *key);
|
||||
|
|
@ -74,6 +78,15 @@ EXPORT obs_hotkey_id obs_hotkey_binding_get_hotkey_id(
|
|||
EXPORT obs_hotkey_t *obs_hotkey_binding_get_hotkey(
|
||||
obs_hotkey_binding_t *binding);
|
||||
|
||||
/* setter functions */
|
||||
|
||||
EXPORT void obs_hotkey_set_name(obs_hotkey_id id, const char *name);
|
||||
EXPORT void obs_hotkey_set_description(obs_hotkey_id id, const char *desc);
|
||||
EXPORT void obs_hotkey_pair_set_names(obs_hotkey_pair_id id,
|
||||
const char *name0, const char *name1);
|
||||
EXPORT void obs_hotkey_pair_set_descriptions(obs_hotkey_pair_id id,
|
||||
const char *desc0, const char *desc1);
|
||||
|
||||
#ifndef SWIG
|
||||
struct obs_hotkeys_translations {
|
||||
const char *insert;
|
||||
|
|
@ -223,6 +236,10 @@ EXPORT void obs_hotkey_pair_load(obs_hotkey_pair_id id, obs_data_array_t *data0,
|
|||
|
||||
EXPORT obs_data_array_t *obs_hotkey_save(obs_hotkey_id id);
|
||||
|
||||
EXPORT void obs_hotkey_pair_save(obs_hotkey_pair_id id,
|
||||
obs_data_array_t **p_data0,
|
||||
obs_data_array_t **p_data1);
|
||||
|
||||
EXPORT obs_data_t *obs_hotkeys_save_encoder(obs_encoder_t *encoder);
|
||||
|
||||
EXPORT obs_data_t *obs_hotkeys_save_output(obs_output_t *output);
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ struct obs_core_video {
|
|||
gs_samplerstate_t *point_sampler;
|
||||
gs_stagesurf_t *mapped_surface;
|
||||
int cur_texture;
|
||||
long raw_active;
|
||||
|
||||
uint64_t video_time;
|
||||
uint64_t video_avg_frame_time_ns;
|
||||
|
|
@ -330,6 +331,8 @@ struct obs_core_data {
|
|||
|
||||
long long unnamed_index;
|
||||
|
||||
obs_data_t *private_data;
|
||||
|
||||
volatile bool valid;
|
||||
};
|
||||
|
||||
|
|
@ -408,6 +411,13 @@ extern bool audio_callback(void *param,
|
|||
uint64_t start_ts_in, uint64_t end_ts_in, uint64_t *out_ts,
|
||||
uint32_t mixers, struct audio_output_data *mixes);
|
||||
|
||||
extern void start_raw_video(video_t *video,
|
||||
const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
extern void stop_raw_video(video_t *video,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* obs shared context data */
|
||||
|
|
@ -696,9 +706,6 @@ extern bool obs_source_init_context(struct obs_source *source,
|
|||
obs_data_t *settings, const char *name,
|
||||
obs_data_t *hotkey_data, bool private);
|
||||
|
||||
extern void obs_source_save(obs_source_t *source);
|
||||
extern void obs_source_load(obs_source_t *source);
|
||||
|
||||
extern bool obs_transition_init(obs_source_t *transition);
|
||||
extern void obs_transition_free(obs_source_t *transition);
|
||||
extern void obs_transition_tick(obs_source_t *transition);
|
||||
|
|
|
|||
|
|
@ -85,6 +85,17 @@ int obs_open_module(obs_module_t **module, const char *path,
|
|||
if (!module || !path || !obs)
|
||||
return MODULE_ERROR;
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* HACK: Do not load obsolete obs-browser build on macOS; the
|
||||
* obs-browser plugin used to live in the Application Support
|
||||
* directory. */
|
||||
if (astrstri(path, "Library/Application Support") != NULL &&
|
||||
astrstri(path, "obs-browser") != NULL) {
|
||||
blog(LOG_WARNING, "Ignoring old obs-browser.so version");
|
||||
return MODULE_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
blog(LOG_DEBUG, "---------------------------------");
|
||||
|
||||
mod.module = os_dlopen(path);
|
||||
|
|
@ -725,13 +736,13 @@ error:
|
|||
HANDLE_ERROR(size, obs_service_info, info);
|
||||
}
|
||||
|
||||
void obs_regsiter_modal_ui_s(const struct obs_modal_ui *info, size_t size)
|
||||
void obs_register_modal_ui_s(const struct obs_modal_ui *info, size_t size)
|
||||
{
|
||||
#define CHECK_REQUIRED_VAL_(info, val, func) \
|
||||
CHECK_REQUIRED_VAL(struct obs_modal_ui, info, val, func)
|
||||
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);
|
||||
CHECK_REQUIRED_VAL_(info, task, obs_register_modal_ui);
|
||||
CHECK_REQUIRED_VAL_(info, target, obs_register_modal_ui);
|
||||
CHECK_REQUIRED_VAL_(info, exec, obs_register_modal_ui);
|
||||
#undef CHECK_REQUIRED_VAL_
|
||||
|
||||
REGISTER_OBS_DEF(size, obs_modal_ui, obs->modal_ui_callbacks, info);
|
||||
|
|
@ -741,13 +752,13 @@ error:
|
|||
HANDLE_ERROR(size, obs_modal_ui, info);
|
||||
}
|
||||
|
||||
void obs_regsiter_modeless_ui_s(const struct obs_modeless_ui *info, size_t size)
|
||||
void obs_register_modeless_ui_s(const struct obs_modeless_ui *info, size_t size)
|
||||
{
|
||||
#define CHECK_REQUIRED_VAL_(info, val, func) \
|
||||
CHECK_REQUIRED_VAL(struct obs_modeless_ui, info, val, func)
|
||||
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);
|
||||
CHECK_REQUIRED_VAL_(info, task, obs_register_modeless_ui);
|
||||
CHECK_REQUIRED_VAL_(info, target, obs_register_modeless_ui);
|
||||
CHECK_REQUIRED_VAL_(info, create, obs_register_modeless_ui);
|
||||
#undef CHECK_REQUIRED_VAL_
|
||||
|
||||
REGISTER_OBS_DEF(size, obs_modeless_ui, obs->modeless_ui_callbacks,
|
||||
|
|
|
|||
258
libobs/obs-nix.c
258
libobs/obs-nix.c
|
|
@ -16,6 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "obs-internal.h"
|
||||
#if defined(__FreeBSD__)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
|
@ -28,13 +29,13 @@
|
|||
#include <sys/sysinfo.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <xcb/xcb.h>
|
||||
#if USE_XINPUT
|
||||
#include <xcb/xinput.h>
|
||||
#endif
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <inttypes.h>
|
||||
#include "util/dstr.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
const char *get_module_extension(void)
|
||||
{
|
||||
|
|
@ -236,6 +237,34 @@ static void log_kernel_version(void)
|
|||
blog(LOG_INFO, "Kernel Version: %s %s", info.sysname, info.release);
|
||||
}
|
||||
|
||||
static void log_x_info(void)
|
||||
{
|
||||
Display *dpy = XOpenDisplay(NULL);
|
||||
if (!dpy) {
|
||||
blog(LOG_INFO, "Unable to open X display");
|
||||
return;
|
||||
}
|
||||
|
||||
int protocol_version = ProtocolVersion(dpy);
|
||||
int protocol_revision = ProtocolRevision(dpy);
|
||||
int vendor_release = VendorRelease(dpy);
|
||||
const char *vendor_name = ServerVendor(dpy);
|
||||
|
||||
if (strstr(vendor_name, "X.Org")) {
|
||||
blog(LOG_INFO, "Window System: X%d.%d, Vendor: %s, Version: %d"
|
||||
".%d.%d", protocol_version, protocol_revision,
|
||||
vendor_name, vendor_release / 10000000,
|
||||
(vendor_release / 100000) % 100,
|
||||
(vendor_release / 1000) % 100);
|
||||
} else {
|
||||
blog(LOG_INFO, "Window System: X%d.%d - vendor string: %s - "
|
||||
"vendor release: %d", protocol_version,
|
||||
protocol_revision, vendor_name, vendor_release);
|
||||
}
|
||||
|
||||
XCloseDisplay(dpy);
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
static void log_distribution_info(void)
|
||||
{
|
||||
|
|
@ -292,6 +321,7 @@ void log_system_info(void)
|
|||
#if defined(__linux__)
|
||||
log_distribution_info();
|
||||
#endif
|
||||
log_x_info();
|
||||
}
|
||||
|
||||
/* So here's how linux works with key mapping:
|
||||
|
|
@ -327,6 +357,12 @@ struct obs_hotkeys_platform {
|
|||
xcb_keysym_t *keysyms;
|
||||
int num_keysyms;
|
||||
int syms_per_code;
|
||||
|
||||
#if USE_XINPUT
|
||||
bool pressed[XINPUT_MOUSE_LEN];
|
||||
bool update[XINPUT_MOUSE_LEN];
|
||||
bool button_pressed[XINPUT_MOUSE_LEN];
|
||||
#endif
|
||||
};
|
||||
|
||||
#define MOUSE_1 (1<<16)
|
||||
|
|
@ -707,6 +743,54 @@ error1:
|
|||
return error != NULL || reply == NULL;
|
||||
}
|
||||
|
||||
static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context,
|
||||
xcb_connection_t *connection)
|
||||
{
|
||||
int def_screen_idx = XDefaultScreen(context->display);
|
||||
xcb_screen_iterator_t iter;
|
||||
|
||||
iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
|
||||
while (iter.rem) {
|
||||
if (def_screen_idx-- == 0)
|
||||
return iter.data;
|
||||
|
||||
xcb_screen_next(&iter);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline xcb_window_t root_window(obs_hotkeys_platform_t *context,
|
||||
xcb_connection_t *connection)
|
||||
{
|
||||
xcb_screen_t *screen = default_screen(context, connection);
|
||||
if (screen)
|
||||
return screen->root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if USE_XINPUT
|
||||
static inline void registerMouseEvents(struct obs_core_hotkeys *hotkeys)
|
||||
{
|
||||
obs_hotkeys_platform_t *context = hotkeys->platform_context;
|
||||
xcb_connection_t *connection = XGetXCBConnection(
|
||||
context->display);
|
||||
xcb_window_t window = root_window(context, connection);
|
||||
|
||||
struct {
|
||||
xcb_input_event_mask_t head;
|
||||
xcb_input_xi_event_mask_t mask;
|
||||
} mask;
|
||||
mask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
|
||||
mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);
|
||||
mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS |
|
||||
XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE;
|
||||
|
||||
xcb_input_xi_select_events(connection, window, 1, &mask.head);
|
||||
xcb_flush(connection);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
|
||||
{
|
||||
Display *display = XOpenDisplay(NULL);
|
||||
|
|
@ -716,6 +800,9 @@ bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
|
|||
hotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t));
|
||||
hotkeys->platform_context->display = display;
|
||||
|
||||
#if USE_XINPUT
|
||||
registerMouseEvents(hotkeys);
|
||||
#endif
|
||||
fill_base_keysyms(hotkeys);
|
||||
fill_keycodes(hotkeys);
|
||||
return true;
|
||||
|
|
@ -735,41 +822,147 @@ void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
|
|||
hotkeys->platform_context = NULL;
|
||||
}
|
||||
|
||||
static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context,
|
||||
xcb_connection_t *connection)
|
||||
{
|
||||
int def_screen_idx = XDefaultScreen(context->display);
|
||||
xcb_screen_iterator_t iter;
|
||||
|
||||
iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
|
||||
while (iter.rem) {
|
||||
if (def_screen_idx-- == 0) {
|
||||
return iter.data;
|
||||
break;
|
||||
}
|
||||
|
||||
xcb_screen_next(&iter);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline xcb_window_t root_window(obs_hotkeys_platform_t *context,
|
||||
xcb_connection_t *connection)
|
||||
{
|
||||
xcb_screen_t *screen = default_screen(context, connection);
|
||||
if (screen)
|
||||
return screen->root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mouse_button_pressed(xcb_connection_t *connection,
|
||||
obs_hotkeys_platform_t *context, obs_key_t key)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
#if USE_XINPUT
|
||||
memset(context->pressed, 0, XINPUT_MOUSE_LEN);
|
||||
memset(context->update, 0, XINPUT_MOUSE_LEN);
|
||||
|
||||
xcb_generic_event_t *ev;
|
||||
while ((ev = xcb_poll_for_event(connection))) {
|
||||
if ((ev->response_type & ~80) == XCB_GE_GENERIC) {
|
||||
switch (((xcb_ge_event_t *) ev)->event_type) {
|
||||
case XCB_INPUT_RAW_BUTTON_PRESS: {
|
||||
xcb_input_raw_button_press_event_t *mot;
|
||||
mot = (xcb_input_raw_button_press_event_t *) ev;
|
||||
if (mot->detail < XINPUT_MOUSE_LEN) {
|
||||
context->pressed[mot->detail-1] = true;
|
||||
context->update[mot->detail-1] = true;
|
||||
} else {
|
||||
blog(LOG_WARNING, "Unsupported button");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_RAW_BUTTON_RELEASE: {
|
||||
xcb_input_raw_button_release_event_t *mot;
|
||||
mot = (xcb_input_raw_button_release_event_t *) ev;
|
||||
if (mot->detail < XINPUT_MOUSE_LEN)
|
||||
context->update[mot->detail-1] = true;
|
||||
else
|
||||
blog(LOG_WARNING, "Unsupported button");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(ev);
|
||||
}
|
||||
|
||||
// Mouse 2 for OBS is Right Click and Mouse 3 is Wheel Click.
|
||||
// Mouse Wheel axis clicks (xinput mot->detail 4 5 6 7) are ignored.
|
||||
switch (key) {
|
||||
case OBS_KEY_MOUSE1:
|
||||
ret = context->pressed[0] || context->button_pressed[0];
|
||||
break;
|
||||
case OBS_KEY_MOUSE2:
|
||||
ret = context->pressed[2] || context->button_pressed[2];
|
||||
break;
|
||||
case OBS_KEY_MOUSE3:
|
||||
ret = context->pressed[1] || context->button_pressed[1];
|
||||
break;
|
||||
case OBS_KEY_MOUSE4:
|
||||
ret = context->pressed[7] || context->button_pressed[7];
|
||||
break;
|
||||
case OBS_KEY_MOUSE5:
|
||||
ret = context->pressed[8] || context->button_pressed[8];
|
||||
break;
|
||||
case OBS_KEY_MOUSE6:
|
||||
ret = context->pressed[9] || context->button_pressed[9];
|
||||
break;
|
||||
case OBS_KEY_MOUSE7:
|
||||
ret = context->pressed[10] || context->button_pressed[10];
|
||||
break;
|
||||
case OBS_KEY_MOUSE8:
|
||||
ret = context->pressed[11] || context->button_pressed[11];
|
||||
break;
|
||||
case OBS_KEY_MOUSE9:
|
||||
ret = context->pressed[12] || context->button_pressed[12];
|
||||
break;
|
||||
case OBS_KEY_MOUSE10:
|
||||
ret = context->pressed[13] || context->button_pressed[13];
|
||||
break;
|
||||
case OBS_KEY_MOUSE11:
|
||||
ret = context->pressed[14] || context->button_pressed[14];
|
||||
break;
|
||||
case OBS_KEY_MOUSE12:
|
||||
ret = context->pressed[15] || context->button_pressed[15];
|
||||
break;
|
||||
case OBS_KEY_MOUSE13:
|
||||
ret = context->pressed[16] || context->button_pressed[16];
|
||||
break;
|
||||
case OBS_KEY_MOUSE14:
|
||||
ret = context->pressed[17] || context->button_pressed[17];
|
||||
break;
|
||||
case OBS_KEY_MOUSE15:
|
||||
ret = context->pressed[18] || context->button_pressed[18];
|
||||
break;
|
||||
case OBS_KEY_MOUSE16:
|
||||
ret = context->pressed[19] || context->button_pressed[19];
|
||||
break;
|
||||
case OBS_KEY_MOUSE17:
|
||||
ret = context->pressed[20] || context->button_pressed[20];
|
||||
break;
|
||||
case OBS_KEY_MOUSE18:
|
||||
ret = context->pressed[21] || context->button_pressed[21];
|
||||
break;
|
||||
case OBS_KEY_MOUSE19:
|
||||
ret = context->pressed[22] || context->button_pressed[22];
|
||||
break;
|
||||
case OBS_KEY_MOUSE20:
|
||||
ret = context->pressed[23] || context->button_pressed[23];
|
||||
break;
|
||||
case OBS_KEY_MOUSE21:
|
||||
ret = context->pressed[24] || context->button_pressed[24];
|
||||
break;
|
||||
case OBS_KEY_MOUSE22:
|
||||
ret = context->pressed[25] || context->button_pressed[25];
|
||||
break;
|
||||
case OBS_KEY_MOUSE23:
|
||||
ret = context->pressed[26] || context->button_pressed[26];
|
||||
break;
|
||||
case OBS_KEY_MOUSE24:
|
||||
ret = context->pressed[27] || context->button_pressed[27];
|
||||
break;
|
||||
case OBS_KEY_MOUSE25:
|
||||
ret = context->pressed[28] || context->button_pressed[28];
|
||||
break;
|
||||
case OBS_KEY_MOUSE26:
|
||||
ret = context->pressed[29] || context->button_pressed[29];
|
||||
break;
|
||||
case OBS_KEY_MOUSE27:
|
||||
ret = context->pressed[30] || context->button_pressed[30];
|
||||
break;
|
||||
case OBS_KEY_MOUSE28:
|
||||
ret = context->pressed[31] || context->button_pressed[31];
|
||||
break;
|
||||
case OBS_KEY_MOUSE29:
|
||||
ret = context->pressed[32] || context->button_pressed[32];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i != XINPUT_MOUSE_LEN; i++)
|
||||
if (context->update[i])
|
||||
context->button_pressed[i] = context->pressed[i];
|
||||
#else
|
||||
xcb_generic_error_t *error = NULL;
|
||||
xcb_query_pointer_cookie_t qpc;
|
||||
xcb_query_pointer_reply_t *reply;
|
||||
bool ret = false;
|
||||
|
||||
qpc = xcb_query_pointer(connection, root_window(context, connection));
|
||||
reply = xcb_query_pointer_reply(connection, qpc, &error);
|
||||
|
|
@ -789,6 +982,7 @@ static bool mouse_button_pressed(xcb_connection_t *connection,
|
|||
|
||||
free(reply);
|
||||
free(error);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#if BUILD_CAPTIONS
|
||||
#include <caption/caption.h>
|
||||
#include <caption/avc.h>
|
||||
#include <caption/mpeg.h>
|
||||
#endif
|
||||
|
||||
static inline bool active(const struct obs_output *output)
|
||||
|
|
@ -419,6 +419,18 @@ bool obs_output_active(const obs_output_t *output)
|
|||
(active(output) || reconnecting(output)) : false;
|
||||
}
|
||||
|
||||
uint32_t obs_output_get_flags(const obs_output_t *output)
|
||||
{
|
||||
return obs_output_valid(output, "obs_output_get_flags") ?
|
||||
output->info.flags : 0;
|
||||
}
|
||||
|
||||
uint32_t obs_get_output_flags(const char *id)
|
||||
{
|
||||
const struct obs_output_info *info = find_output(id);
|
||||
return info ? info->flags : 0;
|
||||
}
|
||||
|
||||
static inline obs_data_t *get_defaults(const struct obs_output_info *info)
|
||||
{
|
||||
obs_data_t *settings = obs_data_create();
|
||||
|
|
@ -977,7 +989,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
|
|||
if (out->priority > 1)
|
||||
return false;
|
||||
|
||||
sei_init(&sei);
|
||||
sei_init(&sei, 0.0);
|
||||
|
||||
da_init(out_data);
|
||||
da_push_back_array(out_data, &ref, sizeof(ref));
|
||||
|
|
@ -1541,7 +1553,7 @@ static void hook_data_capture(struct obs_output *output, bool encoded,
|
|||
encoded_callback, output);
|
||||
} else {
|
||||
if (has_video)
|
||||
video_output_connect(output->video,
|
||||
start_raw_video(output->video,
|
||||
get_video_conversion(output),
|
||||
default_raw_video_callback, output);
|
||||
if (has_audio)
|
||||
|
|
@ -1797,7 +1809,7 @@ static void *end_data_capture_thread(void *data)
|
|||
stop_audio_encoders(output, encoded_callback);
|
||||
} else {
|
||||
if (has_video)
|
||||
video_output_disconnect(output->video,
|
||||
stop_raw_video(output->video,
|
||||
default_raw_video_callback, output);
|
||||
if (has_audio)
|
||||
audio_output_disconnect(output->audio,
|
||||
|
|
@ -2082,34 +2094,14 @@ void obs_output_output_caption_text1(obs_output_t *output, const char *text)
|
|||
|
||||
// split text into 32 character strings
|
||||
int size = (int)strlen(text);
|
||||
int r;
|
||||
size_t char_count;
|
||||
size_t line_length = 0;
|
||||
size_t trimmed_length = 0;
|
||||
|
||||
blog(LOG_DEBUG, "Caption text: %s", text);
|
||||
|
||||
pthread_mutex_lock(&output->caption_mutex);
|
||||
|
||||
for (r = 0 ; 0 < size && CAPTION_LINE_CHARS > r; ++r) {
|
||||
line_length = utf8_line_length(text);
|
||||
trimmed_length = utf8_trimmed_length(text, line_length);
|
||||
char_count = utf8_char_count(text, trimmed_length);
|
||||
|
||||
if (SCREEN_COLS < char_count) {
|
||||
char_count = utf8_wrap_length(text, CAPTION_LINE_CHARS);
|
||||
line_length = utf8_string_length(text, char_count + 1);
|
||||
}
|
||||
|
||||
output->caption_tail = caption_text_new(
|
||||
text,
|
||||
line_length,
|
||||
output->caption_tail,
|
||||
&output->caption_head);
|
||||
|
||||
text += line_length;
|
||||
size -= (int)line_length;
|
||||
}
|
||||
output->caption_tail = caption_text_new(
|
||||
text, size,
|
||||
output->caption_tail,
|
||||
&output->caption_head);
|
||||
|
||||
pthread_mutex_unlock(&output->caption_mutex);
|
||||
}
|
||||
|
|
|
|||
1165
libobs/obs-scene.c
1165
libobs/obs-scene.c
File diff suppressed because it is too large
Load diff
|
|
@ -32,12 +32,17 @@ struct obs_scene_item {
|
|||
volatile long ref;
|
||||
volatile bool removed;
|
||||
|
||||
bool is_group;
|
||||
bool update_transform;
|
||||
bool update_group_resize;
|
||||
|
||||
int64_t id;
|
||||
|
||||
struct obs_scene *parent;
|
||||
struct obs_source *source;
|
||||
volatile long active_refs;
|
||||
volatile long defer_update;
|
||||
volatile long defer_group_resize;
|
||||
bool user_visible;
|
||||
bool visible;
|
||||
bool selected;
|
||||
|
|
@ -81,6 +86,11 @@ struct obs_scene_item {
|
|||
struct obs_scene {
|
||||
struct obs_source *source;
|
||||
|
||||
bool is_group;
|
||||
bool custom_size;
|
||||
uint32_t cx;
|
||||
uint32_t cy;
|
||||
|
||||
int64_t id_counter;
|
||||
|
||||
pthread_mutex_t video_mutex;
|
||||
|
|
|
|||
|
|
@ -347,10 +347,14 @@ static obs_source_t *obs_source_create_internal(const char *id,
|
|||
|
||||
blog(LOG_DEBUG, "%ssource '%s' (%s) created",
|
||||
private ? "private " : "", name, id);
|
||||
obs_source_dosignal(source, "source_create", NULL);
|
||||
|
||||
source->flags = source->default_flags;
|
||||
source->enabled = true;
|
||||
|
||||
if (!private) {
|
||||
obs_source_dosignal(source, "source_create", NULL);
|
||||
}
|
||||
|
||||
return source;
|
||||
|
||||
fail:
|
||||
|
|
@ -427,9 +431,16 @@ static void duplicate_filters(obs_source_t *dst, obs_source_t *src,
|
|||
|
||||
void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src)
|
||||
{
|
||||
if (!obs_source_valid(dst, "obs_source_copy_filters"))
|
||||
return;
|
||||
if (!obs_source_valid(src, "obs_source_copy_filters"))
|
||||
return;
|
||||
|
||||
duplicate_filters(dst, src, dst->context.private);
|
||||
}
|
||||
|
||||
extern obs_scene_t *obs_group_from_source(const obs_source_t *source);
|
||||
|
||||
obs_source_t *obs_source_duplicate(obs_source_t *source,
|
||||
const char *new_name, bool create_private)
|
||||
{
|
||||
|
|
@ -446,6 +457,11 @@ obs_source_t *obs_source_duplicate(obs_source_t *source,
|
|||
|
||||
if (source->info.type == OBS_SOURCE_TYPE_SCENE) {
|
||||
obs_scene_t *scene = obs_scene_from_source(source);
|
||||
if (!scene)
|
||||
scene = obs_group_from_source(source);
|
||||
if (!scene)
|
||||
return NULL;
|
||||
|
||||
obs_scene_t *new_scene = obs_scene_duplicate(scene, new_name,
|
||||
create_private ? OBS_SCENE_DUP_PRIVATE_COPY :
|
||||
OBS_SCENE_DUP_COPY);
|
||||
|
|
@ -1720,9 +1736,18 @@ static inline void obs_source_render_async_video(obs_source_t *source)
|
|||
|
||||
static inline void obs_source_render_filters(obs_source_t *source)
|
||||
{
|
||||
obs_source_t *first_filter;
|
||||
|
||||
pthread_mutex_lock(&source->filter_mutex);
|
||||
first_filter = source->filters.array[0];
|
||||
obs_source_addref(first_filter);
|
||||
pthread_mutex_unlock(&source->filter_mutex);
|
||||
|
||||
source->rendering_filter = true;
|
||||
obs_source_video_render(source->filters.array[0]);
|
||||
obs_source_video_render(first_filter);
|
||||
source->rendering_filter = false;
|
||||
|
||||
obs_source_release(first_filter);
|
||||
}
|
||||
|
||||
void obs_source_default_render(obs_source_t *source)
|
||||
|
|
@ -2270,6 +2295,12 @@ static void copy_frame_data(struct obs_source_frame *dst,
|
|||
}
|
||||
}
|
||||
|
||||
void obs_source_frame_copy(struct obs_source_frame *dst,
|
||||
const struct obs_source_frame *src)
|
||||
{
|
||||
copy_frame_data(dst, src);
|
||||
}
|
||||
|
||||
static inline bool async_texture_changed(struct obs_source *source,
|
||||
const struct obs_source_frame *frame)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -334,8 +334,8 @@ end:
|
|||
profile_end(stage_output_texture_name);
|
||||
}
|
||||
|
||||
static inline void render_video(struct obs_core_video *video, int cur_texture,
|
||||
int prev_texture)
|
||||
static inline void render_video(struct obs_core_video *video, bool raw_active,
|
||||
int cur_texture, int prev_texture)
|
||||
{
|
||||
gs_begin_scene();
|
||||
|
||||
|
|
@ -343,11 +343,14 @@ static inline void render_video(struct obs_core_video *video, int cur_texture,
|
|||
gs_set_cull_mode(GS_NEITHER);
|
||||
|
||||
render_main_texture(video, cur_texture);
|
||||
render_output_texture(video, cur_texture, prev_texture);
|
||||
if (video->gpu_conversion)
|
||||
render_convert_texture(video, cur_texture, prev_texture);
|
||||
|
||||
stage_output_texture(video, cur_texture, prev_texture);
|
||||
if (raw_active) {
|
||||
render_output_texture(video, cur_texture, prev_texture);
|
||||
if (video->gpu_conversion)
|
||||
render_convert_texture(video, cur_texture, prev_texture);
|
||||
|
||||
stage_output_texture(video, cur_texture, prev_texture);
|
||||
}
|
||||
|
||||
gs_set_render_target(NULL, NULL);
|
||||
gs_enable_blending(true);
|
||||
|
|
@ -522,7 +525,7 @@ static inline void output_video_data(struct obs_core_video *video,
|
|||
}
|
||||
}
|
||||
|
||||
static inline void video_sleep(struct obs_core_video *video,
|
||||
static inline void video_sleep(struct obs_core_video *video, bool active,
|
||||
uint64_t *p_time, uint64_t interval_ns)
|
||||
{
|
||||
struct obs_vframe_info vframe_info;
|
||||
|
|
@ -543,8 +546,9 @@ static inline void video_sleep(struct obs_core_video *video,
|
|||
|
||||
vframe_info.timestamp = cur_time;
|
||||
vframe_info.count = count;
|
||||
circlebuf_push_back(&video->vframe_info_buffer, &vframe_info,
|
||||
sizeof(vframe_info));
|
||||
if (active)
|
||||
circlebuf_push_back(&video->vframe_info_buffer, &vframe_info,
|
||||
sizeof(vframe_info));
|
||||
}
|
||||
|
||||
static const char *output_frame_gs_context_name = "gs_context(video->graphics)";
|
||||
|
|
@ -552,7 +556,7 @@ static const char *output_frame_render_video_name = "render_video";
|
|||
static const char *output_frame_download_frame_name = "download_frame";
|
||||
static const char *output_frame_gs_flush_name = "gs_flush";
|
||||
static const char *output_frame_output_video_data_name = "output_video_data";
|
||||
static inline void output_frame(void)
|
||||
static inline void output_frame(bool raw_active)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
int cur_texture = video->cur_texture;
|
||||
|
|
@ -566,12 +570,14 @@ static inline void output_frame(void)
|
|||
gs_enter_context(video->graphics);
|
||||
|
||||
profile_start(output_frame_render_video_name);
|
||||
render_video(video, cur_texture, prev_texture);
|
||||
render_video(video, raw_active, cur_texture, prev_texture);
|
||||
profile_end(output_frame_render_video_name);
|
||||
|
||||
profile_start(output_frame_download_frame_name);
|
||||
frame_ready = download_frame(video, prev_texture, &frame);
|
||||
profile_end(output_frame_download_frame_name);
|
||||
if (raw_active) {
|
||||
profile_start(output_frame_download_frame_name);
|
||||
frame_ready = download_frame(video, prev_texture, &frame);
|
||||
profile_end(output_frame_download_frame_name);
|
||||
}
|
||||
|
||||
profile_start(output_frame_gs_flush_name);
|
||||
gs_flush();
|
||||
|
|
@ -580,7 +586,7 @@ static inline void output_frame(void)
|
|||
gs_leave_context();
|
||||
profile_end(output_frame_gs_context_name);
|
||||
|
||||
if (frame_ready) {
|
||||
if (raw_active && frame_ready) {
|
||||
struct obs_vframe_info vframe_info;
|
||||
circlebuf_pop_front(&video->vframe_info_buffer, &vframe_info,
|
||||
sizeof(vframe_info));
|
||||
|
|
@ -597,6 +603,17 @@ static inline void output_frame(void)
|
|||
|
||||
#define NBSP "\xC2\xA0"
|
||||
|
||||
static void clear_frame_data(void)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
memset(video->textures_rendered, 0, sizeof(video->textures_rendered));
|
||||
memset(video->textures_output, 0, sizeof(video->textures_output));
|
||||
memset(video->textures_copied, 0, sizeof(video->textures_copied));
|
||||
memset(video->textures_converted, 0, sizeof(video->textures_converted));
|
||||
circlebuf_free(&video->vframe_info_buffer);
|
||||
video->cur_texture = 0;
|
||||
}
|
||||
|
||||
static const char *tick_sources_name = "tick_sources";
|
||||
static const char *render_displays_name = "render_displays";
|
||||
static const char *output_frame_name = "output_frame";
|
||||
|
|
@ -607,6 +624,7 @@ void *obs_graphics_thread(void *param)
|
|||
uint64_t frame_time_total_ns = 0;
|
||||
uint64_t fps_total_ns = 0;
|
||||
uint32_t fps_total_frames = 0;
|
||||
bool raw_was_active = false;
|
||||
|
||||
obs->video.video_time = os_gettime_ns();
|
||||
|
||||
|
|
@ -622,6 +640,11 @@ void *obs_graphics_thread(void *param)
|
|||
while (!video_output_stopped(obs->video.video)) {
|
||||
uint64_t frame_start = os_gettime_ns();
|
||||
uint64_t frame_time_ns;
|
||||
bool raw_active = obs->video.raw_active > 0;
|
||||
|
||||
if (!raw_was_active && raw_active)
|
||||
clear_frame_data();
|
||||
raw_was_active = raw_active;
|
||||
|
||||
profile_start(video_thread_name);
|
||||
|
||||
|
|
@ -630,7 +653,7 @@ void *obs_graphics_thread(void *param)
|
|||
profile_end(tick_sources_name);
|
||||
|
||||
profile_start(output_frame_name);
|
||||
output_frame();
|
||||
output_frame(raw_active);
|
||||
profile_end(output_frame_name);
|
||||
|
||||
profile_start(render_displays_name);
|
||||
|
|
@ -643,7 +666,8 @@ void *obs_graphics_thread(void *param)
|
|||
|
||||
profile_reenable_thread();
|
||||
|
||||
video_sleep(&obs->video, &obs->video.video_time, interval);
|
||||
video_sleep(&obs->video, raw_active, &obs->video.video_time,
|
||||
interval);
|
||||
|
||||
frame_time_total_ns += frame_time_ns;
|
||||
fps_total_ns += (obs->video.video_time - last_time);
|
||||
|
|
|
|||
|
|
@ -255,10 +255,16 @@ static inline void write_header(struct exception_handler_data *data)
|
|||
ts = *localtime(&now);
|
||||
strftime(date_time, sizeof(date_time), "%Y-%m-%d, %X", &ts);
|
||||
|
||||
const char *obs_bitness;
|
||||
if (sizeof(void*) == 8)
|
||||
obs_bitness = "64";
|
||||
else
|
||||
obs_bitness = "32";
|
||||
|
||||
dstr_catf(&data->str, "Unhandled exception: %x\r\n"
|
||||
"Date/Time: %s\r\n"
|
||||
"Fault address: %"PRIX64" (%s)\r\n"
|
||||
"libobs version: "OBS_VERSION"\r\n"
|
||||
"libobs version: "OBS_VERSION" (%s-bit)\r\n"
|
||||
"Windows version: %d.%d build %d (revision: %d; "
|
||||
"%s-bit)\r\n"
|
||||
"CPU: %s\r\n\r\n",
|
||||
|
|
@ -266,6 +272,7 @@ static inline void write_header(struct exception_handler_data *data)
|
|||
date_time,
|
||||
data->main_trace.instruction_ptr,
|
||||
data->module_name.array,
|
||||
obs_bitness,
|
||||
data->win_version.major, data->win_version.minor,
|
||||
data->win_version.build, data->win_version.revis,
|
||||
is_64_bit_windows() ? "64" : "32",
|
||||
|
|
|
|||
|
|
@ -788,6 +788,13 @@ void reset_win32_symbol_paths(void)
|
|||
da_free(paths);
|
||||
}
|
||||
|
||||
extern void initialize_crash_handler(void);
|
||||
|
||||
void obs_init_win32_crash_handler(void)
|
||||
{
|
||||
initialize_crash_handler();
|
||||
}
|
||||
|
||||
void initialize_com(void)
|
||||
{
|
||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
|
|
|
|||
169
libobs/obs.c
169
libobs/obs.c
|
|
@ -218,7 +218,7 @@ static bool obs_init_textures(struct obs_video_info *ovi)
|
|||
gs_effect_t *obs_load_effect(gs_effect_t **effect, const char *file)
|
||||
{
|
||||
if (!*effect) {
|
||||
char *filename = find_libobs_data_file(file);
|
||||
char *filename = obs_find_data_file(file);
|
||||
*effect = gs_effect_create_from_file(filename, NULL);
|
||||
bfree(filename);
|
||||
}
|
||||
|
|
@ -250,49 +250,49 @@ static int obs_init_graphics(struct obs_video_info *ovi)
|
|||
|
||||
gs_enter_context(video->graphics);
|
||||
|
||||
char *filename = find_libobs_data_file("default.effect");
|
||||
char *filename = obs_find_data_file("default.effect");
|
||||
video->default_effect = gs_effect_create_from_file(filename,
|
||||
NULL);
|
||||
bfree(filename);
|
||||
|
||||
if (gs_get_device_type() == GS_DEVICE_OPENGL) {
|
||||
filename = find_libobs_data_file("default_rect.effect");
|
||||
filename = obs_find_data_file("default_rect.effect");
|
||||
video->default_rect_effect = gs_effect_create_from_file(
|
||||
filename, NULL);
|
||||
bfree(filename);
|
||||
}
|
||||
|
||||
filename = find_libobs_data_file("opaque.effect");
|
||||
filename = obs_find_data_file("opaque.effect");
|
||||
video->opaque_effect = gs_effect_create_from_file(filename,
|
||||
NULL);
|
||||
bfree(filename);
|
||||
|
||||
filename = find_libobs_data_file("solid.effect");
|
||||
filename = obs_find_data_file("solid.effect");
|
||||
video->solid_effect = gs_effect_create_from_file(filename,
|
||||
NULL);
|
||||
bfree(filename);
|
||||
|
||||
filename = find_libobs_data_file("format_conversion.effect");
|
||||
filename = obs_find_data_file("format_conversion.effect");
|
||||
video->conversion_effect = gs_effect_create_from_file(filename,
|
||||
NULL);
|
||||
bfree(filename);
|
||||
|
||||
filename = find_libobs_data_file("bicubic_scale.effect");
|
||||
filename = obs_find_data_file("bicubic_scale.effect");
|
||||
video->bicubic_effect = gs_effect_create_from_file(filename,
|
||||
NULL);
|
||||
bfree(filename);
|
||||
|
||||
filename = find_libobs_data_file("lanczos_scale.effect");
|
||||
filename = obs_find_data_file("lanczos_scale.effect");
|
||||
video->lanczos_effect = gs_effect_create_from_file(filename,
|
||||
NULL);
|
||||
bfree(filename);
|
||||
|
||||
filename = find_libobs_data_file("bilinear_lowres_scale.effect");
|
||||
filename = obs_find_data_file("bilinear_lowres_scale.effect");
|
||||
video->bilinear_lowres_effect = gs_effect_create_from_file(filename,
|
||||
NULL);
|
||||
bfree(filename);
|
||||
|
||||
filename = find_libobs_data_file("premultiplied_alpha.effect");
|
||||
filename = obs_find_data_file("premultiplied_alpha.effect");
|
||||
video->premultiplied_alpha_effect = gs_effect_create_from_file(filename,
|
||||
NULL);
|
||||
bfree(filename);
|
||||
|
|
@ -565,6 +565,7 @@ static bool obs_init_data(void)
|
|||
if (!obs_view_init(&data->main_view))
|
||||
goto fail;
|
||||
|
||||
data->private_data = obs_data_create();
|
||||
data->valid = true;
|
||||
|
||||
fail:
|
||||
|
|
@ -620,6 +621,7 @@ static void obs_free_data(void)
|
|||
pthread_mutex_destroy(&data->draw_callbacks_mutex);
|
||||
da_free(data->draw_callbacks);
|
||||
da_free(data->tick_callbacks);
|
||||
obs_data_release(data->private_data);
|
||||
}
|
||||
|
||||
static const char *obs_signals[] = {
|
||||
|
|
@ -741,6 +743,7 @@ static inline void obs_free_hotkeys(void)
|
|||
}
|
||||
|
||||
extern const struct obs_source_info scene_info;
|
||||
extern const struct obs_source_info group_info;
|
||||
|
||||
extern void log_system_info(void);
|
||||
|
||||
|
|
@ -771,16 +774,60 @@ static bool obs_init(const char *locale, const char *module_config_path,
|
|||
obs->module_config_path = bstrdup(module_config_path);
|
||||
obs->locale = bstrdup(locale);
|
||||
obs_register_source(&scene_info);
|
||||
obs_register_source(&group_info);
|
||||
add_default_module_paths();
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
extern void initialize_crash_handler(void);
|
||||
extern void initialize_com(void);
|
||||
extern void uninitialize_com(void);
|
||||
#endif
|
||||
|
||||
/* Separate from actual context initialization
|
||||
* since this can be set before startup and persist
|
||||
* after shutdown. */
|
||||
static DARRAY(struct dstr) core_module_paths = {0};
|
||||
|
||||
char *obs_find_data_file(const char *file)
|
||||
{
|
||||
struct dstr path = {0};
|
||||
|
||||
char *result = find_libobs_data_file(file);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
for (size_t i = 0; i < core_module_paths.num; ++i) {
|
||||
if (check_path(file, core_module_paths.array[i].array, &path))
|
||||
return path.array;
|
||||
}
|
||||
|
||||
dstr_free(&path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void obs_add_data_path(const char *path)
|
||||
{
|
||||
struct dstr *new_path = da_push_back_new(core_module_paths);
|
||||
dstr_init_copy(new_path, path);
|
||||
da_push_back(core_module_paths, new_path);
|
||||
}
|
||||
|
||||
bool obs_remove_data_path(const char *path)
|
||||
{
|
||||
for (size_t i = 0; i < core_module_paths.num; ++i) {
|
||||
int result = dstr_cmp(&core_module_paths.array[i], path);
|
||||
|
||||
if (result == 0) {
|
||||
dstr_free(&core_module_paths.array[i]);
|
||||
da_erase(core_module_paths, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *obs_startup_name = "obs_startup";
|
||||
bool obs_startup(const char *locale, const char *module_config_path,
|
||||
profiler_name_store_t *store)
|
||||
|
|
@ -795,7 +842,6 @@ bool obs_startup(const char *locale, const char *module_config_path,
|
|||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
initialize_crash_handler();
|
||||
initialize_com();
|
||||
#endif
|
||||
|
||||
|
|
@ -1262,10 +1308,14 @@ void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param)
|
|||
obs_source_t *next_source =
|
||||
(obs_source_t*)source->context.next;
|
||||
|
||||
if ((source->info.type == OBS_SOURCE_TYPE_INPUT) != 0 &&
|
||||
!source->context.private &&
|
||||
!enum_proc(param, source))
|
||||
if (source->info.id == group_info.id &&
|
||||
!enum_proc(param, source)) {
|
||||
break;
|
||||
} else if (source->info.type == OBS_SOURCE_TYPE_INPUT &&
|
||||
!source->context.private &&
|
||||
!enum_proc(param, source)) {
|
||||
break;
|
||||
}
|
||||
|
||||
source = next_source;
|
||||
}
|
||||
|
|
@ -1468,6 +1518,23 @@ void obs_render_main_texture(void)
|
|||
gs_draw_sprite(tex, 0, 0, 0);
|
||||
}
|
||||
|
||||
gs_texture_t *obs_get_main_texture(void)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
int last_tex;
|
||||
|
||||
if (!obs) return NULL;
|
||||
|
||||
last_tex = video->cur_texture == 0
|
||||
? NUM_TEXTURES - 1
|
||||
: video->cur_texture - 1;
|
||||
|
||||
if (!video->textures_rendered[last_tex])
|
||||
return NULL;
|
||||
|
||||
return video->render_textures[last_tex];
|
||||
}
|
||||
|
||||
void obs_set_master_volume(float volume)
|
||||
{
|
||||
struct calldata data = {0};
|
||||
|
|
@ -1627,7 +1694,8 @@ void obs_load_sources(obs_data_array_t *array, obs_load_source_cb cb,
|
|||
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
|
||||
obs_transition_load(source, source_data);
|
||||
obs_source_load(source);
|
||||
cb(private_data, source);
|
||||
if (cb)
|
||||
cb(private_data, source);
|
||||
}
|
||||
obs_data_release(source_data);
|
||||
}
|
||||
|
|
@ -1950,7 +2018,7 @@ bool obs_set_audio_monitoring_device(const char *name, const char *id)
|
|||
if (!obs || !name || !id || !*name || !*id)
|
||||
return false;
|
||||
|
||||
#if defined(_WIN32) || HAVE_PULSEAUDIO
|
||||
#if defined(_WIN32) || HAVE_PULSEAUDIO || defined(__APPLE__)
|
||||
pthread_mutex_lock(&obs->audio.monitoring_mutex);
|
||||
|
||||
if (strcmp(id, obs->audio.monitoring_device_id) == 0) {
|
||||
|
|
@ -2054,3 +2122,70 @@ uint32_t obs_get_lagged_frames(void)
|
|||
{
|
||||
return obs ? obs->video.lagged_frames : 0;
|
||||
}
|
||||
|
||||
void start_raw_video(video_t *v, const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
os_atomic_inc_long(&video->raw_active);
|
||||
video_output_connect(v, conversion, callback, param);
|
||||
}
|
||||
|
||||
void stop_raw_video(video_t *v,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
os_atomic_dec_long(&video->raw_active);
|
||||
video_output_disconnect(v, callback, param);
|
||||
}
|
||||
|
||||
void obs_add_raw_video_callback(
|
||||
const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
if (!obs)
|
||||
return;
|
||||
start_raw_video(video->video, conversion, callback, param);
|
||||
}
|
||||
|
||||
void obs_remove_raw_video_callback(
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
if (!obs)
|
||||
return;
|
||||
stop_raw_video(video->video, callback, param);
|
||||
}
|
||||
|
||||
void obs_apply_private_data(obs_data_t *settings)
|
||||
{
|
||||
if (!obs || !settings)
|
||||
return;
|
||||
|
||||
obs_data_apply(obs->data.private_data, settings);
|
||||
}
|
||||
|
||||
void obs_set_private_data(obs_data_t *settings)
|
||||
{
|
||||
if (!obs)
|
||||
return;
|
||||
|
||||
obs_data_clear(obs->data.private_data);
|
||||
if (settings)
|
||||
obs_data_apply(obs->data.private_data, settings);
|
||||
}
|
||||
|
||||
obs_data_t *obs_get_private_data(void)
|
||||
{
|
||||
if (!obs)
|
||||
return NULL;
|
||||
|
||||
obs_data_t *private_data = obs->data.private_data;
|
||||
obs_data_addref(private_data);
|
||||
return private_data;
|
||||
}
|
||||
|
|
|
|||
123
libobs/obs.h
123
libobs/obs.h
|
|
@ -242,6 +242,32 @@ struct obs_source_frame {
|
|||
/* ------------------------------------------------------------------------- */
|
||||
/* OBS context */
|
||||
|
||||
/**
|
||||
* Find a core libobs data file
|
||||
* @param path name of the base file
|
||||
* @return A string containing the full path to the file.
|
||||
* Use bfree after use.
|
||||
*/
|
||||
EXPORT char *obs_find_data_file(const char *file);
|
||||
|
||||
/**
|
||||
* Add a path to search libobs data files in.
|
||||
* @param path Full path to directory to look in.
|
||||
* The string is copied.
|
||||
*/
|
||||
EXPORT void obs_add_data_path(const char *path);
|
||||
|
||||
/**
|
||||
* Remove a path from libobs core data paths.
|
||||
* @param path The path to compare to currently set paths.
|
||||
* It does not need to be the same pointer, but
|
||||
* the path string must match an entry fully.
|
||||
* @return Whether or not the path was successfully removed.
|
||||
* If false, the path could not be found.
|
||||
*/
|
||||
EXPORT bool obs_remove_data_path(const char *path);
|
||||
|
||||
|
||||
/**
|
||||
* Initializes OBS
|
||||
*
|
||||
|
|
@ -276,6 +302,12 @@ EXPORT void obs_set_locale(const char *locale);
|
|||
/** @return the current locale */
|
||||
EXPORT const char *obs_get_locale(void);
|
||||
|
||||
/** Initialize the Windows-specific crash handler */
|
||||
|
||||
#ifdef _WIN32
|
||||
EXPORT void obs_init_win32_crash_handler(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the profiler name store (see util/profiler.h) used by OBS, which is
|
||||
* either a name store passed to obs_startup, an internal name store, or NULL
|
||||
|
|
@ -564,6 +596,10 @@ EXPORT void obs_render_main_view(void);
|
|||
/** Renders the last main output texture */
|
||||
EXPORT void obs_render_main_texture(void);
|
||||
|
||||
/** Returns the last main output texture. This can return NULL if the texture
|
||||
* is unavailable. */
|
||||
EXPORT gs_texture_t *obs_get_main_texture(void);
|
||||
|
||||
/** Sets the master user volume */
|
||||
EXPORT void obs_set_master_volume(float volume);
|
||||
|
||||
|
|
@ -576,6 +612,12 @@ EXPORT obs_data_t *obs_save_source(obs_source_t *source);
|
|||
/** Loads a source from settings data */
|
||||
EXPORT obs_source_t *obs_load_source(obs_data_t *data);
|
||||
|
||||
/** Send a save signal to sources */
|
||||
EXPORT void obs_source_save(obs_source_t *source);
|
||||
|
||||
/** Send a load signal to sources */
|
||||
EXPORT void obs_source_load(obs_source_t *source);
|
||||
|
||||
typedef void (*obs_load_source_cb)(void *private_data, obs_source_t *source);
|
||||
|
||||
/** Loads sources from a data array */
|
||||
|
|
@ -624,6 +666,26 @@ EXPORT void obs_remove_main_render_callback(
|
|||
void (*draw)(void *param, uint32_t cx, uint32_t cy),
|
||||
void *param);
|
||||
|
||||
EXPORT void obs_add_raw_video_callback(
|
||||
const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
EXPORT void obs_remove_raw_video_callback(
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
|
||||
EXPORT uint64_t obs_get_video_frame_time(void);
|
||||
|
||||
EXPORT double obs_get_active_fps(void);
|
||||
EXPORT uint64_t obs_get_average_frame_time_ns(void);
|
||||
|
||||
EXPORT uint32_t obs_get_total_frames(void);
|
||||
EXPORT uint32_t obs_get_lagged_frames(void);
|
||||
|
||||
EXPORT void obs_apply_private_data(obs_data_t *settings);
|
||||
EXPORT void obs_set_private_data(obs_data_t *settings);
|
||||
EXPORT obs_data_t *obs_get_private_data(void);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* View context */
|
||||
|
|
@ -650,14 +712,6 @@ EXPORT obs_source_t *obs_view_get_source(obs_view_t *view,
|
|||
/** Renders the sources of this view context */
|
||||
EXPORT void obs_view_render(obs_view_t *view);
|
||||
|
||||
EXPORT uint64_t obs_get_video_frame_time(void);
|
||||
|
||||
EXPORT double obs_get_active_fps(void);
|
||||
EXPORT uint64_t obs_get_average_frame_time_ns(void);
|
||||
|
||||
EXPORT uint32_t obs_get_total_frames(void);
|
||||
EXPORT uint32_t obs_get_lagged_frames(void);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Display context */
|
||||
|
|
@ -1275,6 +1329,15 @@ EXPORT void obs_scene_enum_items(obs_scene_t *scene,
|
|||
EXPORT bool obs_scene_reorder_items(obs_scene_t *scene,
|
||||
obs_sceneitem_t * const *item_order, size_t item_order_size);
|
||||
|
||||
struct obs_sceneitem_order_info {
|
||||
obs_sceneitem_t *group;
|
||||
obs_sceneitem_t *item;
|
||||
};
|
||||
|
||||
EXPORT bool obs_scene_reorder_items2(obs_scene_t *scene,
|
||||
struct obs_sceneitem_order_info *item_order,
|
||||
size_t item_order_size);
|
||||
|
||||
/** Adds/creates a new scene item for a source */
|
||||
EXPORT obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source);
|
||||
|
||||
|
|
@ -1364,6 +1427,8 @@ EXPORT void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item,
|
|||
EXPORT enum obs_scale_type obs_sceneitem_get_scale_filter(
|
||||
obs_sceneitem_t *item);
|
||||
|
||||
EXPORT void obs_sceneitem_force_update_transform(obs_sceneitem_t *item);
|
||||
|
||||
EXPORT void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item);
|
||||
EXPORT void obs_sceneitem_defer_update_end(obs_sceneitem_t *item);
|
||||
|
||||
|
|
@ -1371,6 +1436,39 @@ EXPORT void obs_sceneitem_defer_update_end(obs_sceneitem_t *item);
|
|||
* automatically. Returns an incremented reference. */
|
||||
EXPORT obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item);
|
||||
|
||||
EXPORT obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene,
|
||||
const char *name);
|
||||
EXPORT obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene,
|
||||
const char *name, obs_sceneitem_t **items, size_t count);
|
||||
|
||||
EXPORT obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene,
|
||||
const char *name);
|
||||
|
||||
EXPORT bool obs_sceneitem_is_group(obs_sceneitem_t *item);
|
||||
|
||||
EXPORT obs_scene_t *obs_sceneitem_group_get_scene(
|
||||
const obs_sceneitem_t *group);
|
||||
|
||||
EXPORT void obs_sceneitem_group_ungroup(obs_sceneitem_t *group);
|
||||
|
||||
EXPORT void obs_sceneitem_group_add_item(obs_sceneitem_t *group,
|
||||
obs_sceneitem_t *item);
|
||||
EXPORT void obs_sceneitem_group_remove_item(obs_sceneitem_t *group,
|
||||
obs_sceneitem_t *item);
|
||||
|
||||
EXPORT obs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene,
|
||||
obs_sceneitem_t *item);
|
||||
|
||||
EXPORT bool obs_source_is_group(const obs_source_t *source);
|
||||
EXPORT bool obs_scene_is_group(const obs_scene_t *scene);
|
||||
|
||||
EXPORT void obs_sceneitem_group_enum_items(obs_sceneitem_t *group,
|
||||
bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*),
|
||||
void *param);
|
||||
|
||||
EXPORT void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item);
|
||||
EXPORT void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Outputs */
|
||||
|
|
@ -1440,6 +1538,12 @@ EXPORT void obs_output_force_stop(obs_output_t *output);
|
|||
/** Returns whether the output is active */
|
||||
EXPORT bool obs_output_active(const obs_output_t *output);
|
||||
|
||||
/** Returns output capability flags */
|
||||
EXPORT uint32_t obs_output_get_flags(const obs_output_t *output);
|
||||
|
||||
/** Returns output capability flags */
|
||||
EXPORT uint32_t obs_get_output_flags(const char *id);
|
||||
|
||||
/** Gets the default settings for an output type */
|
||||
EXPORT obs_data_t *obs_output_defaults(const char *id);
|
||||
|
||||
|
|
@ -1717,6 +1821,7 @@ EXPORT enum video_format obs_encoder_get_preferred_video_format(
|
|||
|
||||
/** Gets the default settings for an encoder type */
|
||||
EXPORT obs_data_t *obs_encoder_defaults(const char *id);
|
||||
EXPORT obs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder);
|
||||
|
||||
/** Returns the property list, if any. Free with obs_properties_destroy */
|
||||
EXPORT obs_properties_t *obs_get_encoder_properties(const char *id);
|
||||
|
|
@ -1895,6 +2000,8 @@ static inline void obs_source_frame_destroy(struct obs_source_frame *frame)
|
|||
}
|
||||
}
|
||||
|
||||
EXPORT void obs_source_frame_copy(struct obs_source_frame *dst,
|
||||
const struct obs_source_frame *src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ public:
|
|||
callback (callback_),
|
||||
param (param_)
|
||||
{
|
||||
signal_handler_connect(handler, signal, callback, param);
|
||||
signal_handler_connect_ref(handler, signal, callback, param);
|
||||
}
|
||||
|
||||
inline void Disconnect()
|
||||
|
|
@ -236,7 +236,7 @@ public:
|
|||
signal = signal_;
|
||||
callback = callback_;
|
||||
param = param_;
|
||||
signal_handler_connect(handler, signal, callback, param);
|
||||
signal_handler_connect_ref(handler, signal, callback, param);
|
||||
}
|
||||
|
||||
OBSSignal(const OBSSignal&) = delete;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,24 @@
|
|||
#define BUILD_CAPTIONS @BUILD_CAPTIONS@
|
||||
#define HAVE_DBUS @HAVE_DBUS@
|
||||
#define HAVE_PULSEAUDIO @HAVE_PULSEAUDIO@
|
||||
#define USE_XINPUT @USE_XINPUT@
|
||||
#define LIBOBS_IMAGEMAGICK_DIR_STYLE_6L 6
|
||||
#define LIBOBS_IMAGEMAGICK_DIR_STYLE_7GE 7
|
||||
#define LIBOBS_IMAGEMAGICK_DIR_STYLE @LIBOBS_IMAGEMAGICK_DIR_STYLE@
|
||||
|
||||
/* NOTE: Release candidate version numbers internally are always the previous
|
||||
* main release number! For example, if the current public release is 21.0 and
|
||||
* the build is 22.0 release candidate 1, internally the build number (defined
|
||||
* by LIBOBS_API_VER/etc) will always be 21.0, despite the OBS_VERSION string
|
||||
* saying "22.0 RC1".
|
||||
*
|
||||
* If the release candidate version number is 0.0.0 and the RC number is 0,
|
||||
* that means it's not a release candidate build. */
|
||||
#define OBS_RELEASE_CANDIDATE_MAJOR @OBS_RELEASE_CANDIDATE_MAJOR@
|
||||
#define OBS_RELEASE_CANDIDATE_MINOR @OBS_RELEASE_CANDIDATE_MINOR@
|
||||
#define OBS_RELEASE_CANDIDATE_PATCH @OBS_RELEASE_CANDIDATE_PATCH@
|
||||
#define OBS_RELEASE_CANDIDATE_VER \
|
||||
MAKE_SEMANTIC_VERSION(OBS_RELEASE_CANDIDATE_MAJOR, \
|
||||
OBS_RELEASE_CANDIDATE_MINOR, \
|
||||
OBS_RELEASE_CANDIDATE_PATCH)
|
||||
#define OBS_RELEASE_CANDIDATE @OBS_RELEASE_CANDIDATE@
|
||||
|
|
|
|||
|
|
@ -76,11 +76,12 @@ char *cf_literal_to_str(const char *literal, size_t count)
|
|||
if (literal[0] != '\"' && literal[0] != '\'')
|
||||
return NULL;
|
||||
|
||||
str = bmalloc(count - 1);
|
||||
temp_src = literal;
|
||||
/* strip leading and trailing quote characters */
|
||||
str = bzalloc(--count);
|
||||
temp_src = literal + 1;
|
||||
temp_dst = str;
|
||||
|
||||
while (*temp_src) {
|
||||
while (*temp_src && --count > 0) {
|
||||
if (*temp_src == '\\') {
|
||||
temp_src++;
|
||||
cf_convert_from_escape_literal(&temp_dst, &temp_src);
|
||||
|
|
|
|||
|
|
@ -207,11 +207,13 @@ static inline void *darray_push_back_new(const size_t element_size,
|
|||
static inline size_t darray_push_back_array(const size_t element_size,
|
||||
struct darray *dst, const void *array, const size_t num)
|
||||
{
|
||||
size_t old_num = dst->num;
|
||||
|
||||
assert(array != NULL);
|
||||
assert(num != 0);
|
||||
size_t old_num;
|
||||
if (!dst)
|
||||
return 0;
|
||||
if (!array || !num)
|
||||
return dst->num;
|
||||
|
||||
old_num = dst->num;
|
||||
darray_resize(element_size, dst, dst->num+num);
|
||||
memcpy(darray_item(element_size, dst, old_num), array,
|
||||
element_size*num);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ static inline bool create_process(const char *cmd_line, HANDLE stdin_handle,
|
|||
bool success = false;
|
||||
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK;
|
||||
si.hStdInput = stdin_handle;
|
||||
si.hStdOutput = stdout_handle;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue