New upstream version 22.0.3+dfsg1

This commit is contained in:
Sebastian Ramacher 2018-12-16 17:14:58 +01:00
parent 665f64a933
commit cdc9a9fc87
334 changed files with 14525 additions and 2639 deletions

View file

@ -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}

View file

@ -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);

View file

@ -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")) {

View file

@ -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,

View file

@ -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);

View file

@ -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, " ");

View file

@ -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)

View file

@ -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)

View file

@ -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
};

View file

@ -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;

View file

@ -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 WhittakerShannon
* 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)
{

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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);
/* ------------------------------------------------------------------------- */

View file

@ -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);

View file

@ -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)
{

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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;
}

View file

@ -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);
}

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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)
{

View file

@ -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);

View file

@ -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",

View file

@ -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);

View file

@ -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;
}

View file

@ -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
}

View file

@ -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;

View file

@ -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@

View file

@ -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);

View file

@ -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);

View file

@ -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;