New upstream version 19.0.3+dfsg1
This commit is contained in:
parent
3708b8e092
commit
1f1bbb3518
534 changed files with 13862 additions and 2459 deletions
|
|
@ -12,8 +12,8 @@ static inline bool cf_to_cstr(CFStringRef ref, char *buf, size_t size)
|
|||
return (bool)CFStringGetCString(ref, buf, size, kCFStringEncodingUTF8);
|
||||
}
|
||||
|
||||
static void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb,
|
||||
void *data, AudioDeviceID id)
|
||||
static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb,
|
||||
void *data, AudioDeviceID id, bool allow_inputs)
|
||||
{
|
||||
UInt32 size = 0;
|
||||
CFStringRef cf_name = NULL;
|
||||
|
|
@ -21,6 +21,7 @@ static void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb,
|
|||
char name[1024];
|
||||
char uid[1024];
|
||||
OSStatus stat;
|
||||
bool cont = true;
|
||||
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreams,
|
||||
|
|
@ -29,16 +30,18 @@ static void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb,
|
|||
};
|
||||
|
||||
/* check to see if it's a mac input device */
|
||||
AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
|
||||
if (!size)
|
||||
return;
|
||||
if (!allow_inputs) {
|
||||
AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
|
||||
if (!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;
|
||||
return true;
|
||||
|
||||
addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
|
||||
stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
|
||||
|
|
@ -55,16 +58,18 @@ static void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
cb(data, name, uid);
|
||||
cont = cb(data, name, uid);
|
||||
|
||||
fail:
|
||||
if (cf_name)
|
||||
CFRelease(cf_name);
|
||||
if (cf_uid)
|
||||
CFRelease(cf_uid);
|
||||
return cont;
|
||||
}
|
||||
|
||||
void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)
|
||||
static void enum_audio_devices(obs_enum_audio_device_cb cb, void *data,
|
||||
bool allow_inputs)
|
||||
{
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioHardwarePropertyDevices,
|
||||
|
|
@ -88,9 +93,104 @@ void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)
|
|||
stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
|
||||
0, NULL, &size, ids);
|
||||
if (success(stat, "get data")) {
|
||||
for (UInt32 i = 0; i < count; i++)
|
||||
obs_enum_audio_monitoring_device(cb, data, ids[i]);
|
||||
for (UInt32 i = 0; i < count; i++) {
|
||||
if (!obs_enum_audio_monitoring_device(cb, data, ids[i],
|
||||
allow_inputs))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(ids);
|
||||
}
|
||||
|
||||
void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)
|
||||
{
|
||||
enum_audio_devices(cb, data, false);
|
||||
}
|
||||
|
||||
static bool alloc_default_id(void *data, const char *name, const char *id)
|
||||
{
|
||||
char **p_id = data;
|
||||
UNUSED_PARAMETER(name);
|
||||
|
||||
*p_id = bstrdup(id);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void get_default_id(char **p_id)
|
||||
{
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioHardwarePropertyDefaultSystemOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
if (*p_id)
|
||||
return;
|
||||
|
||||
OSStatus stat;
|
||||
AudioDeviceID id = 0;
|
||||
UInt32 size = sizeof(id);
|
||||
|
||||
stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
|
||||
NULL, &size, &id);
|
||||
if (success(stat, "AudioObjectGetPropertyData"))
|
||||
obs_enum_audio_monitoring_device(alloc_default_id, p_id, id,
|
||||
true);
|
||||
if (!*p_id)
|
||||
*p_id = bzalloc(1);
|
||||
}
|
||||
|
||||
struct device_name_info {
|
||||
const char *id;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static bool enum_device_name(void *data, const char *name, const char *id)
|
||||
{
|
||||
struct device_name_info *info = data;
|
||||
|
||||
if (strcmp(info->id, id) == 0) {
|
||||
info->name = bstrdup(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool devices_match(const char *id1, const char *id2)
|
||||
{
|
||||
struct device_name_info info = {0};
|
||||
char *default_id = NULL;
|
||||
char *name1 = NULL;
|
||||
char *name2 = NULL;
|
||||
bool match;
|
||||
|
||||
if (!id1 || !id2)
|
||||
return false;
|
||||
|
||||
if (strcmp(id1, "default") == 0) {
|
||||
get_default_id(&default_id);
|
||||
id1 = default_id;
|
||||
}
|
||||
if (strcmp(id2, "default") == 0) {
|
||||
get_default_id(&default_id);
|
||||
id2 = default_id;
|
||||
}
|
||||
|
||||
info.id = id1;
|
||||
enum_audio_devices(enum_device_name, &info, true);
|
||||
name1 = info.name;
|
||||
|
||||
info.name = NULL;
|
||||
info.id = id2;
|
||||
enum_audio_devices(enum_device_name, &info, true);
|
||||
name2 = info.name;
|
||||
|
||||
match = name1 && name2 && strcmp(name1, name2) == 0;
|
||||
bfree(default_id);
|
||||
bfree(name1);
|
||||
bfree(name2);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ struct audio_monitor {
|
|||
|
||||
volatile bool active;
|
||||
bool paused;
|
||||
bool ignore;
|
||||
};
|
||||
|
||||
static inline bool fill_buffer(struct audio_monitor *monitor)
|
||||
|
|
@ -137,7 +138,10 @@ static void buffer_audio(void *data, AudioQueueRef aq, AudioQueueBufferRef buf)
|
|||
UNUSED_PARAMETER(aq);
|
||||
}
|
||||
|
||||
static bool audio_monitor_init(struct audio_monitor *monitor)
|
||||
extern bool devices_match(const char *id1, const char *id2);
|
||||
|
||||
static bool audio_monitor_init(struct audio_monitor *monitor,
|
||||
obs_source_t *source)
|
||||
{
|
||||
const struct audio_output_info *info = audio_output_get_info(
|
||||
obs->audio.audio);
|
||||
|
|
@ -156,6 +160,8 @@ static bool audio_monitor_init(struct audio_monitor *monitor)
|
|||
.mBitsPerChannel = sizeof(float) * 8
|
||||
};
|
||||
|
||||
monitor->source = source;
|
||||
|
||||
monitor->channels = channels;
|
||||
monitor->buffer_size =
|
||||
channels * sizeof(float) * info->samples_per_sec / 100 * 3;
|
||||
|
|
@ -163,14 +169,26 @@ static bool audio_monitor_init(struct audio_monitor *monitor)
|
|||
|
||||
pthread_mutex_init_value(&monitor->mutex);
|
||||
|
||||
stat = AudioQueueNewOutput(&desc, buffer_audio, monitor, NULL, NULL, 0,
|
||||
&monitor->queue);
|
||||
if (!success(stat, "AudioStreamBasicDescription")) {
|
||||
const char *uid = obs->audio.monitoring_device_id;
|
||||
if (!uid || !*uid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *uid = obs->audio.monitoring_device_id;
|
||||
if (!uid || !*uid) {
|
||||
if (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {
|
||||
obs_data_t *s = obs_source_get_settings(source);
|
||||
const char *s_dev_id = obs_data_get_string(s, "device_id");
|
||||
bool match = devices_match(s_dev_id, uid);
|
||||
obs_data_release(s);
|
||||
|
||||
if (match) {
|
||||
monitor->ignore = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
stat = AudioQueueNewOutput(&desc, buffer_audio, monitor, NULL, NULL, 0,
|
||||
&monitor->queue);
|
||||
if (!success(stat, "AudioStreamBasicDescription")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -266,19 +284,20 @@ static void audio_monitor_free(struct audio_monitor *monitor)
|
|||
pthread_mutex_destroy(&monitor->mutex);
|
||||
}
|
||||
|
||||
static void audio_monitor_init_final(struct audio_monitor *monitor,
|
||||
obs_source_t *source)
|
||||
static void audio_monitor_init_final(struct audio_monitor *monitor)
|
||||
{
|
||||
monitor->source = source;
|
||||
obs_source_add_audio_capture_callback(source, on_audio_playback,
|
||||
monitor);
|
||||
if (monitor->ignore)
|
||||
return;
|
||||
|
||||
obs_source_add_audio_capture_callback(monitor->source,
|
||||
on_audio_playback, monitor);
|
||||
}
|
||||
|
||||
struct audio_monitor *audio_monitor_create(obs_source_t *source)
|
||||
{
|
||||
struct audio_monitor *monitor = bzalloc(sizeof(*monitor));
|
||||
|
||||
if (!audio_monitor_init(monitor)) {
|
||||
if (!audio_monitor_init(monitor, source)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +305,7 @@ struct audio_monitor *audio_monitor_create(obs_source_t *source)
|
|||
da_push_back(obs->audio.monitors, &monitor);
|
||||
pthread_mutex_unlock(&obs->audio.monitoring_mutex);
|
||||
|
||||
audio_monitor_init_final(monitor, source);
|
||||
audio_monitor_init_final(monitor);
|
||||
return monitor;
|
||||
|
||||
fail:
|
||||
|
|
@ -303,9 +322,9 @@ void audio_monitor_reset(struct audio_monitor *monitor)
|
|||
audio_monitor_free(monitor);
|
||||
memset(monitor, 0, sizeof(*monitor));
|
||||
|
||||
success = audio_monitor_init(monitor);
|
||||
success = audio_monitor_init(monitor, source);
|
||||
if (success)
|
||||
audio_monitor_init_final(monitor, source);
|
||||
audio_monitor_init_final(monitor);
|
||||
}
|
||||
|
||||
void audio_monitor_destroy(struct audio_monitor *monitor)
|
||||
|
|
|
|||
|
|
@ -103,3 +103,66 @@ fail:
|
|||
safe_release(enumerator);
|
||||
safe_release(collection);
|
||||
}
|
||||
|
||||
static void get_default_id(char **p_id)
|
||||
{
|
||||
IMMDeviceEnumerator *immde = NULL;
|
||||
IMMDevice *device = NULL;
|
||||
WCHAR *w_id = NULL;
|
||||
HRESULT hr;
|
||||
|
||||
if (*p_id)
|
||||
return;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
||||
&IID_IMMDeviceEnumerator, &immde);
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = immde->lpVtbl->GetDefaultAudioEndpoint(immde,
|
||||
eRender, eConsole, &device);
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = device->lpVtbl->GetId(device, &w_id);
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
os_wcs_to_utf8_ptr(w_id, 0, p_id);
|
||||
|
||||
fail:
|
||||
if (!*p_id)
|
||||
*p_id = bzalloc(1);
|
||||
if (immde)
|
||||
immde->lpVtbl->Release(immde);
|
||||
if (device)
|
||||
device->lpVtbl->Release(device);
|
||||
if (w_id)
|
||||
CoTaskMemFree(w_id);
|
||||
}
|
||||
|
||||
bool devices_match(const char *id1, const char *id2)
|
||||
{
|
||||
char *default_id = NULL;
|
||||
bool match;
|
||||
|
||||
if (!id1 || !id2)
|
||||
return false;
|
||||
|
||||
if (strcmp(id1, "default") == 0) {
|
||||
get_default_id(&default_id);
|
||||
id1 = default_id;
|
||||
}
|
||||
if (strcmp(id2, "default") == 0) {
|
||||
get_default_id(&default_id);
|
||||
id2 = default_id;
|
||||
}
|
||||
|
||||
match = strcmp(id1, id2) == 0;
|
||||
bfree(default_id);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ struct audio_monitor {
|
|||
audio_resampler_t *resampler;
|
||||
uint32_t sample_rate;
|
||||
uint32_t channels;
|
||||
bool source_has_video : 1;
|
||||
bool source_has_video;
|
||||
bool ignore;
|
||||
|
||||
int64_t lowest_audio_offset;
|
||||
struct circlebuf delay_buffer;
|
||||
|
|
@ -203,6 +204,9 @@ unlock:
|
|||
|
||||
static inline void audio_monitor_free(struct audio_monitor *monitor)
|
||||
{
|
||||
if (monitor->ignore)
|
||||
return;
|
||||
|
||||
if (monitor->source) {
|
||||
obs_source_remove_audio_capture_callback(
|
||||
monitor->source, on_audio_playback, monitor);
|
||||
|
|
@ -235,7 +239,10 @@ static enum speaker_layout convert_speaker_layout(DWORD layout, WORD channels)
|
|||
return (enum speaker_layout)channels;
|
||||
}
|
||||
|
||||
static bool audio_monitor_init(struct audio_monitor *monitor)
|
||||
extern bool devices_match(const char *id1, const char *id2);
|
||||
|
||||
static bool audio_monitor_init(struct audio_monitor *monitor,
|
||||
obs_source_t *source)
|
||||
{
|
||||
IMMDeviceEnumerator *immde = NULL;
|
||||
WAVEFORMATEX *wfex = NULL;
|
||||
|
|
@ -243,12 +250,26 @@ static bool audio_monitor_init(struct audio_monitor *monitor)
|
|||
UINT32 frames;
|
||||
HRESULT hr;
|
||||
|
||||
pthread_mutex_init_value(&monitor->playback_mutex);
|
||||
|
||||
monitor->source = source;
|
||||
|
||||
const char *id = obs->audio.monitoring_device_id;
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_init_value(&monitor->playback_mutex);
|
||||
if (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {
|
||||
obs_data_t *s = obs_source_get_settings(source);
|
||||
const char *s_dev_id = obs_data_get_string(s, "device_id");
|
||||
bool match = devices_match(s_dev_id, id);
|
||||
obs_data_release(s);
|
||||
|
||||
if (match) {
|
||||
monitor->ignore = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------ *
|
||||
* Init device */
|
||||
|
|
@ -352,14 +373,15 @@ fail:
|
|||
return success;
|
||||
}
|
||||
|
||||
static void audio_monitor_init_final(struct audio_monitor *monitor,
|
||||
obs_source_t *source)
|
||||
static void audio_monitor_init_final(struct audio_monitor *monitor)
|
||||
{
|
||||
monitor->source = source;
|
||||
if (monitor->ignore)
|
||||
return;
|
||||
|
||||
monitor->source_has_video =
|
||||
(source->info.output_flags & OBS_SOURCE_VIDEO) != 0;
|
||||
obs_source_add_audio_capture_callback(source, on_audio_playback,
|
||||
monitor);
|
||||
(monitor->source->info.output_flags & OBS_SOURCE_VIDEO) != 0;
|
||||
obs_source_add_audio_capture_callback(monitor->source,
|
||||
on_audio_playback, monitor);
|
||||
}
|
||||
|
||||
struct audio_monitor *audio_monitor_create(obs_source_t *source)
|
||||
|
|
@ -367,7 +389,7 @@ struct audio_monitor *audio_monitor_create(obs_source_t *source)
|
|||
struct audio_monitor monitor = {0};
|
||||
struct audio_monitor *out;
|
||||
|
||||
if (!audio_monitor_init(&monitor)) {
|
||||
if (!audio_monitor_init(&monitor, source)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +399,7 @@ struct audio_monitor *audio_monitor_create(obs_source_t *source)
|
|||
da_push_back(obs->audio.monitors, &out);
|
||||
pthread_mutex_unlock(&obs->audio.monitoring_mutex);
|
||||
|
||||
audio_monitor_init_final(out, source);
|
||||
audio_monitor_init_final(out);
|
||||
return out;
|
||||
|
||||
fail:
|
||||
|
|
@ -391,14 +413,14 @@ void audio_monitor_reset(struct audio_monitor *monitor)
|
|||
bool success;
|
||||
|
||||
pthread_mutex_lock(&monitor->playback_mutex);
|
||||
success = audio_monitor_init(&new_monitor);
|
||||
success = audio_monitor_init(&new_monitor, monitor->source);
|
||||
pthread_mutex_unlock(&monitor->playback_mutex);
|
||||
|
||||
if (success) {
|
||||
obs_source_t *source = monitor->source;
|
||||
audio_monitor_free(monitor);
|
||||
*monitor = new_monitor;
|
||||
audio_monitor_init_final(monitor, source);
|
||||
audio_monitor_init_final(monitor);
|
||||
} else {
|
||||
audio_monitor_free(&new_monitor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ static inline void calldata_clear(struct calldata *data)
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* NOTE: 'get' functions return true only if paramter exists, and is the
|
||||
/* NOTE: 'get' functions return true only if parameter exists, and is the
|
||||
* same type. They return false otherwise. */
|
||||
|
||||
static inline bool calldata_get_int(const calldata_t *data, const char *name,
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ static int parse_param(struct cf_parser *cfp, struct decl_info *decl)
|
|||
int code;
|
||||
struct decl_param param = {0};
|
||||
|
||||
/* get stprage specifiers */
|
||||
/* get storage specifiers */
|
||||
code = cf_next_name_ref(cfp, &ref, TYPE_OR_STORAGE, ",");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
|
@ -120,7 +120,7 @@ static int parse_param(struct cf_parser *cfp, struct decl_info *decl)
|
|||
return code;
|
||||
}
|
||||
|
||||
/* parameters not marked with specifers are input parameters */
|
||||
/* parameters not marked with specifiers are input parameters */
|
||||
if (param.flags == 0)
|
||||
param.flags = CALL_PARAM_IN;
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ bool parse_decl_string(struct decl_info *decl, const char *decl_string)
|
|||
ret_param.flags = CALL_PARAM_OUT;
|
||||
|
||||
cf_parser_init(&cfp);
|
||||
if (!cf_parser_parse(&cfp, decl_string, "declaraion"))
|
||||
if (!cf_parser_parse(&cfp, decl_string, "declaration"))
|
||||
goto fail;
|
||||
|
||||
code = cf_get_name_ref(&cfp, &ret_type, "return type", NULL);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ uniform float input_height_i;
|
|||
uniform float input_width_i_d2;
|
||||
uniform float input_height_i_d2;
|
||||
|
||||
uniform int int_width;
|
||||
uniform int int_input_width;
|
||||
uniform int int_u_plane_offset;
|
||||
uniform int int_v_plane_offset;
|
||||
|
||||
uniform texture2d image;
|
||||
|
||||
sampler_state def_sampler {
|
||||
|
|
@ -235,6 +240,13 @@ float4 PSPlanar444(VertInOut vert_in) : TARGET
|
|||
return out_val[2];
|
||||
}
|
||||
|
||||
float GetIntOffsetColor(int offset)
|
||||
{
|
||||
return image.Load(int3(offset % int_input_width,
|
||||
offset / int_input_width,
|
||||
0)).r;
|
||||
}
|
||||
|
||||
float4 PSPacked422_Reverse(VertInOut vert_in, int u_pos, int v_pos,
|
||||
int y0_pos, int y1_pos) : TARGET
|
||||
{
|
||||
|
|
@ -250,59 +262,37 @@ float4 PSPacked422_Reverse(VertInOut vert_in, int u_pos, int v_pos,
|
|||
texel[u_pos], texel[v_pos], 1.0);
|
||||
}
|
||||
|
||||
float GetOffsetColor(float offset)
|
||||
{
|
||||
float2 uv;
|
||||
|
||||
offset += PRECISION_OFFSET;
|
||||
uv.x = floor(fmod(offset, input_width)) * input_width_i;
|
||||
uv.y = floor(offset * input_width_i) * input_height_i;
|
||||
|
||||
uv.xy += float2(input_width_i_d2, input_height_i_d2);
|
||||
|
||||
return image.Sample(def_sampler, uv).r;
|
||||
}
|
||||
|
||||
float4 PSPlanar420_Reverse(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float x = vert_in.uv.x;
|
||||
float y = vert_in.uv.y;
|
||||
float x_offset = floor(x * width + PRECISION_OFFSET);
|
||||
float y_offset = floor(y * height + PRECISION_OFFSET);
|
||||
int x = int(vert_in.uv.x * width + PRECISION_OFFSET);
|
||||
int y = int(vert_in.uv.y * height + PRECISION_OFFSET);
|
||||
|
||||
float lum_offset = y_offset * width + x_offset + PRECISION_OFFSET;
|
||||
lum_offset = floor(lum_offset);
|
||||
|
||||
float ch_offset = floor(y_offset * 0.5 + PRECISION_OFFSET) * width_d2 +
|
||||
(x_offset * 0.5) + PRECISION_OFFSET;
|
||||
ch_offset = floor(ch_offset);
|
||||
int lum_offset = y * int_width + x;
|
||||
int chroma_offset = (y / 2) * (int_width / 2) + x / 2;
|
||||
int chroma1 = int_u_plane_offset + chroma_offset;
|
||||
int chroma2 = int_v_plane_offset + chroma_offset;
|
||||
|
||||
return float4(
|
||||
GetOffsetColor(lum_offset),
|
||||
GetOffsetColor(u_plane_offset + ch_offset),
|
||||
GetOffsetColor(v_plane_offset + ch_offset),
|
||||
GetIntOffsetColor(lum_offset),
|
||||
GetIntOffsetColor(chroma1),
|
||||
GetIntOffsetColor(chroma2),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
||||
float4 PSNV12_Reverse(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float x = vert_in.uv.x;
|
||||
float y = vert_in.uv.y;
|
||||
float x_offset = floor(x * width + PRECISION_OFFSET);
|
||||
float y_offset = floor(y * height + PRECISION_OFFSET);
|
||||
int x = int(vert_in.uv.x * width + PRECISION_OFFSET);
|
||||
int y = int(vert_in.uv.y * height + PRECISION_OFFSET);
|
||||
|
||||
float lum_offset = y_offset * width + x_offset + PRECISION_OFFSET;
|
||||
lum_offset = floor(lum_offset);
|
||||
|
||||
float ch_offset = floor(y_offset * 0.5 + PRECISION_OFFSET) * width_d2 +
|
||||
(x_offset * 0.5);
|
||||
ch_offset = floor(ch_offset * 2.0 + PRECISION_OFFSET);
|
||||
int lum_offset = y * int_width + x;
|
||||
int chroma_offset = (y / 2) * (int_width / 2) + x / 2;
|
||||
int chroma = int_u_plane_offset + chroma_offset * 2;
|
||||
|
||||
return float4(
|
||||
GetOffsetColor(lum_offset),
|
||||
GetOffsetColor(u_plane_offset + ch_offset),
|
||||
GetOffsetColor(u_plane_offset + ch_offset + 1.0),
|
||||
GetIntOffsetColor(lum_offset),
|
||||
GetIntOffsetColor(chroma),
|
||||
GetIntOffsetColor(chroma + 1),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
uniform float4x4 ViewProj;
|
||||
uniform float4 color = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
uniform float4 randomvals1;
|
||||
uniform float4 randomvals2;
|
||||
uniform float4 randomvals3;
|
||||
|
||||
struct SolidVertInOut {
|
||||
float4 pos : POSITION;
|
||||
};
|
||||
|
|
@ -17,6 +21,19 @@ float4 PSSolid(SolidVertInOut vert_in) : TARGET
|
|||
return color;
|
||||
}
|
||||
|
||||
float rand(float4 pos, float4 rand_vals)
|
||||
{
|
||||
return 0.5 + 0.5 * frac(sin(dot(pos.xy, float2(rand_vals.x, rand_vals.y))) * rand_vals.z);
|
||||
}
|
||||
|
||||
float4 PSRandom(SolidVertInOut vert_in) : TARGET
|
||||
{
|
||||
return float4(rand(vert_in.pos, randomvals1),
|
||||
rand(vert_in.pos, randomvals2),
|
||||
rand(vert_in.pos, randomvals3),
|
||||
1.0);
|
||||
}
|
||||
|
||||
struct SolidColoredVertInOut {
|
||||
float4 pos : POSITION;
|
||||
float4 color : COLOR;
|
||||
|
|
@ -52,3 +69,12 @@ technique SolidColored
|
|||
pixel_shader = PSSolidColored(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique Random
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSSolid(vert_in);
|
||||
pixel_shader = PSRandom(vert_in);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,12 +228,14 @@ static void ep_parse_struct(struct effect_parser *ep)
|
|||
|
||||
case PARSE_UNEXPECTED_CONTINUE:
|
||||
cf_adderror_syntax_error(&ep->cfp);
|
||||
/* Falls through. */
|
||||
case PARSE_CONTINUE:
|
||||
ep_var_free(&var);
|
||||
continue;
|
||||
|
||||
case PARSE_UNEXPECTED_BREAK:
|
||||
cf_adderror_syntax_error(&ep->cfp);
|
||||
/* Falls through. */
|
||||
case PARSE_BREAK:
|
||||
ep_var_free(&var);
|
||||
do_break = true;
|
||||
|
|
|
|||
|
|
@ -519,7 +519,7 @@ EXPORT uint8_t *gs_create_texture_file_data(const char *file,
|
|||
* Draws a 2D sprite
|
||||
*
|
||||
* If width or height is 0, the width or height of the texture will be used.
|
||||
* The flip value specifies whether the texture shoudl be flipped on the U or V
|
||||
* The flip value specifies whether the texture should be flipped on the U or V
|
||||
* axis with GS_FLIP_U and GS_FLIP_V.
|
||||
*/
|
||||
EXPORT void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width,
|
||||
|
|
@ -534,7 +534,7 @@ EXPORT void gs_draw_cube_backdrop(gs_texture_t *cubetex, const struct quat *rot,
|
|||
/** sets the viewport to current swap chain size */
|
||||
EXPORT void gs_reset_viewport(void);
|
||||
|
||||
/** sets default screen-sized orthographich mode */
|
||||
/** sets default screen-sized orthographic mode */
|
||||
EXPORT void gs_set_2d_mode(void);
|
||||
/** sets default screen-sized perspective mode */
|
||||
EXPORT void gs_set_3d_mode(double fovy, double znear, double zvar);
|
||||
|
|
|
|||
|
|
@ -277,12 +277,14 @@ static void sp_parse_struct(struct shader_parser *sp)
|
|||
|
||||
case PARSE_UNEXPECTED_CONTINUE:
|
||||
cf_adderror_syntax_error(&sp->cfp);
|
||||
/* Falls through. */
|
||||
case PARSE_CONTINUE:
|
||||
shader_var_free(&var);
|
||||
continue;
|
||||
|
||||
case PARSE_UNEXPECTED_BREAK:
|
||||
cf_adderror_syntax_error(&sp->cfp);
|
||||
/* Falls through. */
|
||||
case PARSE_BREAK:
|
||||
shader_var_free(&var);
|
||||
do_break = true;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include "../util/bmem.h"
|
||||
#include "../util/platform.h"
|
||||
#include "../util/profiler.h"
|
||||
|
|
@ -34,6 +35,7 @@ extern profiler_name_store_t *obs_get_profiler_name_store(void);
|
|||
|
||||
struct cached_frame_info {
|
||||
struct video_data frame;
|
||||
int skipped;
|
||||
int count;
|
||||
};
|
||||
|
||||
|
|
@ -114,6 +116,7 @@ static inline bool video_output_cur_frame(struct video_output *video)
|
|||
{
|
||||
struct cached_frame_info *frame_info;
|
||||
bool complete;
|
||||
bool skipped;
|
||||
|
||||
/* -------------------------------- */
|
||||
|
||||
|
|
@ -143,6 +146,7 @@ static inline bool video_output_cur_frame(struct video_output *video)
|
|||
|
||||
frame_info->frame.timestamp += video->frame_time;
|
||||
complete = --frame_info->count == 0;
|
||||
skipped = frame_info->skipped > 0;
|
||||
|
||||
if (complete) {
|
||||
if (++video->first_added == video->info.cache_size)
|
||||
|
|
@ -150,6 +154,9 @@ static inline bool video_output_cur_frame(struct video_output *video)
|
|||
|
||||
if (++video->available_frames == video->info.cache_size)
|
||||
video->last_added = video->first_added;
|
||||
} else if (skipped) {
|
||||
--frame_info->skipped;
|
||||
++video->skipped_frames;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&video->data_mutex);
|
||||
|
|
@ -333,6 +340,11 @@ bool video_output_connect(video_t *video,
|
|||
|
||||
pthread_mutex_lock(&video->input_mutex);
|
||||
|
||||
if (video->inputs.num == 0) {
|
||||
video->skipped_frames = 0;
|
||||
video->total_frames = 0;
|
||||
}
|
||||
|
||||
if (video_get_input_idx(video, callback, param) == DARRAY_INVALID) {
|
||||
struct video_input input;
|
||||
memset(&input, 0, sizeof(input));
|
||||
|
|
@ -378,6 +390,20 @@ void video_output_disconnect(video_t *video,
|
|||
da_erase(video->inputs, idx);
|
||||
}
|
||||
|
||||
if (video->inputs.num == 0) {
|
||||
double percentage_skipped = (double)video->skipped_frames /
|
||||
(double)video->total_frames * 100.0;
|
||||
|
||||
if (video->skipped_frames)
|
||||
blog(LOG_INFO, "Video stopped, number of "
|
||||
"skipped frames due "
|
||||
"to encoding lag: "
|
||||
"%"PRIu32"/%"PRIu32" (%0.1f%%)",
|
||||
video->skipped_frames,
|
||||
video->total_frames,
|
||||
percentage_skipped);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&video->input_mutex);
|
||||
}
|
||||
|
||||
|
|
@ -403,8 +429,8 @@ bool video_output_lock_frame(video_t *video, struct video_frame *frame,
|
|||
pthread_mutex_lock(&video->data_mutex);
|
||||
|
||||
if (video->available_frames == 0) {
|
||||
video->skipped_frames += count;
|
||||
video->cache[video->last_added].count += count;
|
||||
video->cache[video->last_added].skipped += count;
|
||||
locked = false;
|
||||
|
||||
} else {
|
||||
|
|
@ -416,6 +442,7 @@ bool video_output_lock_frame(video_t *video, struct video_frame *frame,
|
|||
cfi = &video->cache[video->last_added];
|
||||
cfi->frame.timestamp = timestamp;
|
||||
cfi->count = count;
|
||||
cfi->skipped = 0;
|
||||
|
||||
memcpy(frame, &cfi->frame, sizeof(*frame));
|
||||
|
||||
|
|
|
|||
|
|
@ -200,14 +200,6 @@ EXPORT bool obs_volmeter_attach_source(obs_volmeter_t *volmeter,
|
|||
*/
|
||||
EXPORT void obs_volmeter_detach_source(obs_volmeter_t *volmeter);
|
||||
|
||||
/**
|
||||
* @brief Get signal handler for the volume meter object
|
||||
* @param volmeter pointer to the volume meter object
|
||||
* @return signal handler
|
||||
*/
|
||||
EXPORT signal_handler_t *obs_volmeter_get_signal_handler(
|
||||
obs_volmeter_t *volmeter);
|
||||
|
||||
/**
|
||||
* @brief Set the update interval for the volume meter
|
||||
* @param volmeter pointer to the volume meter object
|
||||
|
|
|
|||
|
|
@ -108,10 +108,20 @@ static bool discard_if_stopped(obs_source_t *source, size_t channels)
|
|||
/* if perpetually pending data, it means the audio has stopped,
|
||||
* so clear the audio data */
|
||||
if (last_size == size) {
|
||||
if (!source->pending_stop) {
|
||||
source->pending_stop = true;
|
||||
#if DEBUG_AUDIO == 1
|
||||
blog(LOG_DEBUG, "doing pending stop trick: '%s'",
|
||||
source->context.name);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t ch = 0; ch < channels; ch++)
|
||||
circlebuf_pop_front(&source->audio_input_buf[ch], NULL,
|
||||
source->audio_input_buf[ch].size);
|
||||
|
||||
source->pending_stop = false;
|
||||
source->audio_ts = 0;
|
||||
source->last_audio_input_buf_size = 0;
|
||||
#if DEBUG_AUDIO == 1
|
||||
|
|
@ -179,7 +189,7 @@ static inline void discard_audio(struct obs_core_audio *audio,
|
|||
if (start_point == AUDIO_OUTPUT_FRAMES) {
|
||||
#if DEBUG_AUDIO == 1
|
||||
if (is_audio_source)
|
||||
blog(LOG_DEBUG, "can't dicard, start point is "
|
||||
blog(LOG_DEBUG, "can't discard, start point is "
|
||||
"at audio frame count");
|
||||
#endif
|
||||
return;
|
||||
|
|
@ -212,6 +222,7 @@ static inline void discard_audio(struct obs_core_audio *audio,
|
|||
ts->end);
|
||||
#endif
|
||||
|
||||
source->pending_stop = false;
|
||||
source->audio_ts = ts->end;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,23 +94,8 @@ static void log_processor_speed(void)
|
|||
|
||||
static void log_processor_cores(void)
|
||||
{
|
||||
size_t size;
|
||||
int physical_cores = 0, logical_cores = 0;
|
||||
int ret;
|
||||
|
||||
size = sizeof(physical_cores);
|
||||
ret = sysctlbyname("machdep.cpu.core_count", &physical_cores,
|
||||
&size, NULL, 0);
|
||||
if (ret != 0)
|
||||
return;
|
||||
|
||||
ret = sysctlbyname("machdep.cpu.thread_count", &logical_cores,
|
||||
&size, NULL, 0);
|
||||
if (ret != 0)
|
||||
return;
|
||||
|
||||
blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
|
||||
physical_cores, logical_cores);
|
||||
os_get_physical_cores(), os_get_logical_cores());
|
||||
}
|
||||
|
||||
static void log_available_memory(void)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
/*
|
||||
* Increment if major breaking API changes
|
||||
*/
|
||||
#define LIBOBS_API_MAJOR_VER 18
|
||||
#define LIBOBS_API_MAJOR_VER 19
|
||||
|
||||
/*
|
||||
* Increment if backward-compatible additions
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
*
|
||||
* Reset to zero each major or minor version
|
||||
*/
|
||||
#define LIBOBS_API_PATCH_VER 1
|
||||
#define LIBOBS_API_PATCH_VER 3
|
||||
|
||||
#define MAKE_SEMANTIC_VERSION(major, minor, patch) \
|
||||
((major << 24) | \
|
||||
|
|
|
|||
|
|
@ -682,7 +682,6 @@ obs_data_t *obs_data_create_from_json_file_safe(const char *json_file,
|
|||
|
||||
/* delete current file if corrupt to prevent it from
|
||||
* being backed up again */
|
||||
os_unlink(json_file);
|
||||
os_rename(backup_file.array, json_file);
|
||||
|
||||
file_data = obs_data_create_from_json_file(json_file);
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ struct obs_encoder_info {
|
|||
|
||||
/**
|
||||
* Updates the settings for this encoder (usually used for things like
|
||||
* changeing birate while active)
|
||||
* changing bitrate while active)
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param settings New settings for this encoder
|
||||
|
|
|
|||
|
|
@ -1197,8 +1197,8 @@ reset:
|
|||
|
||||
struct obs_hotkey_internal_inject {
|
||||
obs_key_combination_t hotkey;
|
||||
bool pressed : 1;
|
||||
bool strict_modifiers : 1;
|
||||
bool pressed;
|
||||
bool strict_modifiers;
|
||||
};
|
||||
|
||||
static inline bool inject_hotkey(void *data,
|
||||
|
|
@ -1251,8 +1251,8 @@ void obs_hotkey_enable_strict_modifiers(bool enable)
|
|||
|
||||
struct obs_query_hotkeys_helper {
|
||||
uint32_t modifiers;
|
||||
bool no_press : 1;
|
||||
bool strict_modifiers : 1;
|
||||
bool no_press;
|
||||
bool strict_modifiers;
|
||||
};
|
||||
|
||||
static inline bool query_hotkey(void *data,
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ struct obs_hotkeys_translations {
|
|||
* that may not have translations. If the operating system can provide
|
||||
* translations for these keys, it will use the operating system's translation
|
||||
* over these translations. If no translations are specified, it will use
|
||||
* the default english translations for that specific operating system. */
|
||||
* the default English translations for that specific operating system. */
|
||||
EXPORT void obs_hotkeys_set_translations_s(
|
||||
struct obs_hotkeys_translations *translations, size_t size);
|
||||
|
||||
|
|
|
|||
|
|
@ -140,8 +140,8 @@ struct obs_hotkey_pair {
|
|||
obs_hotkey_pair_id pair_id;
|
||||
obs_hotkey_id id[2];
|
||||
obs_hotkey_active_func func[2];
|
||||
bool pressed0 : 1;
|
||||
bool pressed1 : 1;
|
||||
bool pressed0;
|
||||
bool pressed1;
|
||||
void *data[2];
|
||||
};
|
||||
|
||||
|
|
@ -166,8 +166,8 @@ void obs_hotkeys_free(void);
|
|||
|
||||
struct obs_hotkey_binding {
|
||||
obs_key_combination_t key;
|
||||
bool pressed : 1;
|
||||
bool modifiers_match : 1;
|
||||
bool pressed;
|
||||
bool modifiers_match;
|
||||
|
||||
obs_hotkey_id hotkey_id;
|
||||
obs_hotkey_t *hotkey;
|
||||
|
|
@ -244,6 +244,7 @@ struct obs_core_video {
|
|||
int cur_texture;
|
||||
|
||||
uint64_t video_time;
|
||||
uint64_t video_avg_frame_time_ns;
|
||||
double video_fps;
|
||||
video_t *video;
|
||||
pthread_t video_thread;
|
||||
|
|
@ -275,6 +276,8 @@ struct obs_core_video {
|
|||
gs_effect_t *deinterlace_blend_2x_effect;
|
||||
gs_effect_t *deinterlace_yadif_effect;
|
||||
gs_effect_t *deinterlace_yadif_2x_effect;
|
||||
|
||||
struct obs_video_info ovi;
|
||||
};
|
||||
|
||||
struct audio_monitor;
|
||||
|
|
@ -313,6 +316,8 @@ struct obs_core_data {
|
|||
pthread_mutex_t encoders_mutex;
|
||||
pthread_mutex_t services_mutex;
|
||||
pthread_mutex_t audio_sources_mutex;
|
||||
pthread_mutex_t draw_callbacks_mutex;
|
||||
DARRAY(struct draw_callback) draw_callbacks;
|
||||
|
||||
struct obs_view main_view;
|
||||
|
||||
|
|
@ -332,9 +337,9 @@ struct obs_core_hotkeys {
|
|||
pthread_t hotkey_thread;
|
||||
bool hotkey_thread_initialized;
|
||||
os_event_t *stop_event;
|
||||
bool thread_disable_press : 1;
|
||||
bool strict_modifiers : 1;
|
||||
bool reroute_hotkeys : 1;
|
||||
bool thread_disable_press;
|
||||
bool strict_modifiers;
|
||||
bool reroute_hotkeys;
|
||||
DARRAY(obs_hotkey_binding_t) bindings;
|
||||
|
||||
obs_hotkey_callback_router_func router_func;
|
||||
|
|
@ -562,6 +567,7 @@ struct obs_source {
|
|||
/* audio */
|
||||
bool audio_failed;
|
||||
bool audio_pending;
|
||||
bool pending_stop;
|
||||
bool user_muted;
|
||||
bool muted;
|
||||
struct obs_source *next_audio_source;
|
||||
|
|
@ -602,6 +608,8 @@ struct obs_source {
|
|||
bool async_flip;
|
||||
bool async_active;
|
||||
bool async_update_texture;
|
||||
bool async_unbuffered;
|
||||
struct obs_source_frame *async_preload_frame;
|
||||
DARRAY(struct async_frame) async_cache;
|
||||
DARRAY(struct obs_source_frame*)async_frames;
|
||||
pthread_mutex_t async_mutex;
|
||||
|
|
@ -637,12 +645,12 @@ struct obs_source {
|
|||
obs_hotkey_pair_id mute_unmute_key;
|
||||
obs_hotkey_id push_to_mute_key;
|
||||
obs_hotkey_id push_to_talk_key;
|
||||
bool push_to_mute_enabled : 1;
|
||||
bool push_to_mute_pressed : 1;
|
||||
bool user_push_to_mute_pressed : 1;
|
||||
bool push_to_talk_enabled : 1;
|
||||
bool push_to_talk_pressed : 1;
|
||||
bool user_push_to_talk_pressed : 1;
|
||||
bool push_to_mute_enabled;
|
||||
bool push_to_mute_pressed;
|
||||
bool user_push_to_mute_pressed;
|
||||
bool push_to_talk_enabled;
|
||||
bool push_to_talk_pressed;
|
||||
bool user_push_to_talk_pressed;
|
||||
uint64_t push_to_mute_delay;
|
||||
uint64_t push_to_mute_stop_time;
|
||||
uint64_t push_to_talk_delay;
|
||||
|
|
@ -664,7 +672,7 @@ struct obs_source {
|
|||
uint32_t transition_cx;
|
||||
uint32_t transition_cy;
|
||||
uint32_t transition_fixed_duration;
|
||||
bool transition_use_fixed_duration : 1;
|
||||
bool transition_use_fixed_duration;
|
||||
enum obs_transition_mode transition_mode;
|
||||
enum obs_transition_scale_type transition_scale_type;
|
||||
struct matrix4 transition_matrices[2];
|
||||
|
|
@ -828,7 +836,6 @@ struct obs_output {
|
|||
uint32_t starting_drawn_count;
|
||||
uint32_t starting_lagged_count;
|
||||
uint32_t starting_frame_count;
|
||||
uint32_t starting_skipped_frame_count;
|
||||
|
||||
int total_frames;
|
||||
|
||||
|
|
@ -865,6 +872,8 @@ struct obs_output {
|
|||
volatile long delay_restart_refs;
|
||||
volatile bool delay_active;
|
||||
volatile bool delay_capturing;
|
||||
|
||||
char *last_error_message;
|
||||
};
|
||||
|
||||
static inline void do_output_signal(struct obs_output *output,
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ bool obs_module_load(void)
|
|||
* may need loading.
|
||||
*
|
||||
* @return Return true to continue loading the module, otherwise
|
||||
* false to indcate failure and unload the module
|
||||
* false to indicate failure and unload the module
|
||||
*/
|
||||
MODULE_EXPORT bool obs_module_load(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@ void obs_output_destroy(obs_output_t *output)
|
|||
circlebuf_free(&output->delay_data);
|
||||
if (output->owns_info_id)
|
||||
bfree((void*)output->info.id);
|
||||
if (output->last_error_message)
|
||||
bfree(output->last_error_message);
|
||||
bfree(output);
|
||||
}
|
||||
}
|
||||
|
|
@ -228,6 +230,10 @@ bool obs_output_actual_start(obs_output_t *output)
|
|||
|
||||
os_event_wait(output->stopping_event);
|
||||
output->stop_code = 0;
|
||||
if (output->last_error_message) {
|
||||
bfree(output->last_error_message);
|
||||
output->last_error_message = NULL;
|
||||
}
|
||||
|
||||
if (output->context.data)
|
||||
success = output->info.start(output->context.data);
|
||||
|
|
@ -235,8 +241,6 @@ bool obs_output_actual_start(obs_output_t *output)
|
|||
if (success && output->video) {
|
||||
output->starting_frame_count =
|
||||
video_output_get_total_frames(output->video);
|
||||
output->starting_skipped_frame_count =
|
||||
video_output_get_skipped_frames(output->video);
|
||||
output->starting_drawn_count = obs->video.total_frames;
|
||||
output->starting_lagged_count = obs->video.lagged_frames;
|
||||
}
|
||||
|
|
@ -280,24 +284,19 @@ static void log_frame_info(struct obs_output *output)
|
|||
struct obs_core_video *video = &obs->video;
|
||||
|
||||
uint32_t video_frames = video_output_get_total_frames(output->video);
|
||||
uint32_t video_skipped = video_output_get_skipped_frames(output->video);
|
||||
|
||||
uint32_t total = video_frames - output->starting_frame_count;
|
||||
uint32_t skipped = video_skipped - output->starting_skipped_frame_count;
|
||||
|
||||
uint32_t drawn = video->total_frames - output->starting_drawn_count;
|
||||
uint32_t lagged = video->lagged_frames - output->starting_lagged_count;
|
||||
|
||||
int dropped = obs_output_get_frames_dropped(output);
|
||||
|
||||
double percentage_skipped = 0.0f;
|
||||
double percentage_lagged = 0.0f;
|
||||
double percentage_dropped = 0.0f;
|
||||
|
||||
if (total) {
|
||||
percentage_skipped = (double)skipped / (double)total * 100.0;
|
||||
if (total)
|
||||
percentage_dropped = (double)dropped / (double)total * 100.0;
|
||||
}
|
||||
if (drawn)
|
||||
percentage_lagged = (double)lagged / (double)drawn * 100.0;
|
||||
|
||||
|
|
@ -307,11 +306,6 @@ static void log_frame_info(struct obs_output *output)
|
|||
blog(LOG_INFO, "Output '%s': Total drawn frames: %"PRIu32,
|
||||
output->context.name, drawn);
|
||||
|
||||
if (total && skipped)
|
||||
blog(LOG_INFO, "Output '%s': Number of skipped frames due "
|
||||
"to encoding lag: %"PRIu32" (%0.1f%%)",
|
||||
output->context.name,
|
||||
skipped, percentage_skipped);
|
||||
if (drawn && lagged)
|
||||
blog(LOG_INFO, "Output '%s': Number of lagged frames due "
|
||||
"to rendering lag/stalls: %"PRIu32" (%0.1f%%)",
|
||||
|
|
@ -352,10 +346,10 @@ void obs_output_actual_stop(obs_output_t *output, bool force, uint64_t ts)
|
|||
obs_output_end_data_capture(output);
|
||||
os_event_signal(output->stopping_event);
|
||||
} else {
|
||||
call_stop = data_active(output);
|
||||
call_stop = true;
|
||||
}
|
||||
} else {
|
||||
call_stop = data_active(output);
|
||||
call_stop = true;
|
||||
}
|
||||
|
||||
if (output->context.data && call_stop) {
|
||||
|
|
@ -1013,7 +1007,7 @@ static inline void send_interleaved(struct obs_output *output)
|
|||
struct encoder_packet out = output->interleaved_packets.array[0];
|
||||
|
||||
/* do not send an interleaved packet if there's no packet of the
|
||||
* opposing type of a higher timstamp in the interleave buffer.
|
||||
* opposing type of a higher timestamp in the interleave buffer.
|
||||
* this ensures that the timestamps are monotonic */
|
||||
if (!has_higher_opposing_ts(output, &out))
|
||||
return;
|
||||
|
|
@ -1570,12 +1564,15 @@ static inline void signal_reconnect_success(struct obs_output *output)
|
|||
static inline void signal_stop(struct obs_output *output)
|
||||
{
|
||||
struct calldata params;
|
||||
uint8_t stack[128];
|
||||
|
||||
calldata_init_fixed(¶ms, stack, sizeof(stack));
|
||||
calldata_init(¶ms);
|
||||
calldata_set_string(¶ms, "last_error", output->last_error_message);
|
||||
calldata_set_int(¶ms, "code", output->stop_code);
|
||||
calldata_set_ptr(¶ms, "output", output);
|
||||
|
||||
signal_handler_signal(output->context.signals, "stop", ¶ms);
|
||||
|
||||
calldata_free(¶ms);
|
||||
}
|
||||
|
||||
static inline void convert_flags(const struct obs_output *output,
|
||||
|
|
@ -1822,6 +1819,7 @@ static void obs_output_end_data_capture_internal(obs_output_t *output,
|
|||
if (signal) {
|
||||
signal_stop(output);
|
||||
output->stop_code = OBS_OUTPUT_SUCCESS;
|
||||
os_event_signal(output->stopping_event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -2069,7 +2067,7 @@ void obs_output_output_caption_text1(obs_output_t *output, const char *text)
|
|||
if (!active(output))
|
||||
return;
|
||||
|
||||
// split text into 32 charcter strings
|
||||
// split text into 32 character strings
|
||||
int size = (int)strlen(text);
|
||||
int r;
|
||||
size_t char_count;
|
||||
|
|
@ -2117,3 +2115,43 @@ float obs_output_get_congestion(obs_output_t *output)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int obs_output_get_connect_time_ms(obs_output_t *output)
|
||||
{
|
||||
if (!obs_output_valid(output, "obs_output_get_connect_time_ms"))
|
||||
return -1;
|
||||
|
||||
if (output->info.get_connect_time_ms)
|
||||
return output->info.get_connect_time_ms(output->context.data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *obs_output_get_last_error(obs_output_t *output)
|
||||
{
|
||||
if (!obs_output_valid(output, "obs_output_get_last_error"))
|
||||
return NULL;
|
||||
|
||||
return output->last_error_message;
|
||||
}
|
||||
|
||||
void obs_output_set_last_error(obs_output_t *output, const char *message)
|
||||
{
|
||||
if (!obs_output_valid(output, "obs_output_set_last_error"))
|
||||
return;
|
||||
|
||||
if (output->last_error_message)
|
||||
bfree(output->last_error_message);
|
||||
|
||||
if (message)
|
||||
output->last_error_message = bstrdup(message);
|
||||
else
|
||||
output->last_error_message = NULL;
|
||||
}
|
||||
|
||||
bool obs_output_reconnecting(const obs_output_t *output)
|
||||
{
|
||||
if (!obs_output_valid(output, "obs_output_reconnecting"))
|
||||
return false;
|
||||
|
||||
return reconnecting(output);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ struct obs_output_info {
|
|||
void (*free_type_data)(void *type_data);
|
||||
|
||||
float (*get_congestion)(void *data);
|
||||
int (*get_connect_time_ms)(void *data);
|
||||
};
|
||||
|
||||
EXPORT void obs_register_output_s(const struct obs_output_info *info,
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ static void *scene_create(obs_data_t *settings, struct obs_source *source)
|
|||
signal_handler_add_array(obs_source_get_signal_handler(source),
|
||||
obs_scene_signals);
|
||||
|
||||
scene->id_counter = 0;
|
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
|
||||
|
|
@ -608,6 +610,9 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
|||
obs_data_set_default_int(item_data, "align",
|
||||
OBS_ALIGN_TOP | OBS_ALIGN_LEFT);
|
||||
|
||||
if (obs_data_has_user_value(item_data, "id"))
|
||||
item->id = obs_data_get_int(item_data, "id");
|
||||
|
||||
item->rot = (float)obs_data_get_double(item_data, "rot");
|
||||
item->align = (uint32_t)obs_data_get_int(item_data, "align");
|
||||
visible = obs_data_get_bool(item_data, "visible");
|
||||
|
|
@ -659,8 +664,9 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
|||
update_item_transform(item);
|
||||
}
|
||||
|
||||
static void scene_load(void *scene, obs_data_t *settings)
|
||||
static void scene_load(void *data, obs_data_t *settings)
|
||||
{
|
||||
struct obs_scene *scene = data;
|
||||
obs_data_array_t *items = obs_data_get_array(settings, "items");
|
||||
size_t count, i;
|
||||
|
||||
|
|
@ -676,6 +682,9 @@ static void scene_load(void *scene, obs_data_t *settings)
|
|||
obs_data_release(item_data);
|
||||
}
|
||||
|
||||
if (obs_data_has_user_value(settings, "id_counter"))
|
||||
scene->id_counter = obs_data_get_int(settings, "id_counter");
|
||||
|
||||
obs_data_array_release(items);
|
||||
}
|
||||
|
||||
|
|
@ -699,6 +708,7 @@ static void scene_save_item(obs_data_array_t *array,
|
|||
obs_data_set_int (item_data, "crop_top", (int)item->crop.top);
|
||||
obs_data_set_int (item_data, "crop_right", (int)item->crop.right);
|
||||
obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom);
|
||||
obs_data_set_int (item_data, "id", item->id);
|
||||
|
||||
if (item->scale_filter == OBS_SCALE_POINT)
|
||||
scale_filter = "point";
|
||||
|
|
@ -731,6 +741,8 @@ static void scene_save(void *data, obs_data_t *settings)
|
|||
item = item->next;
|
||||
}
|
||||
|
||||
obs_data_set_int(settings, "id_counter", scene->id_counter);
|
||||
|
||||
full_unlock(scene);
|
||||
|
||||
obs_data_set_array(settings, "items", array);
|
||||
|
|
@ -1160,6 +1172,28 @@ obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name)
|
|||
return item;
|
||||
}
|
||||
|
||||
obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id)
|
||||
{
|
||||
struct obs_scene_item *item;
|
||||
|
||||
if (!scene)
|
||||
return NULL;
|
||||
|
||||
full_lock(scene);
|
||||
|
||||
item = scene->first_item;
|
||||
while (item) {
|
||||
if (item->id == id)
|
||||
break;
|
||||
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
full_unlock(scene);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void obs_scene_enum_items(obs_scene_t *scene,
|
||||
bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*),
|
||||
void *param)
|
||||
|
|
@ -1307,6 +1341,7 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
|
|||
|
||||
item = bzalloc(sizeof(struct obs_scene_item));
|
||||
item->source = source;
|
||||
item->id = ++scene->id_counter;
|
||||
item->parent = scene;
|
||||
item->ref = 1;
|
||||
item->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
|
||||
|
|
@ -1919,3 +1954,11 @@ void obs_sceneitem_defer_update_end(obs_sceneitem_t *item)
|
|||
if (os_atomic_dec_long(&item->defer_update) == 0)
|
||||
update_item_transform(item);
|
||||
}
|
||||
|
||||
int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item)
|
||||
{
|
||||
if (!obs_ptr_valid(item, "obs_sceneitem_get_id"))
|
||||
return 0;
|
||||
|
||||
return item->id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ struct obs_scene_item {
|
|||
volatile long ref;
|
||||
volatile bool removed;
|
||||
|
||||
int64_t id;
|
||||
|
||||
struct obs_scene *parent;
|
||||
struct obs_source *source;
|
||||
volatile long active_refs;
|
||||
|
|
@ -49,7 +51,7 @@ struct obs_scene_item {
|
|||
uint32_t align;
|
||||
|
||||
/* last width/height of the source, this is used to check whether
|
||||
* ths transform needs updating */
|
||||
* the transform needs updating */
|
||||
uint32_t last_width;
|
||||
uint32_t last_height;
|
||||
|
||||
|
|
@ -76,6 +78,8 @@ struct obs_scene_item {
|
|||
struct obs_scene {
|
||||
struct obs_source *source;
|
||||
|
||||
int64_t id_counter;
|
||||
|
||||
pthread_mutex_t video_mutex;
|
||||
pthread_mutex_t audio_mutex;
|
||||
struct obs_scene_item *first_item;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ static bool ready_deinterlace_frames(obs_source_t *source, uint64_t sys_time)
|
|||
uint64_t frame_offset = 0;
|
||||
size_t idx = 1;
|
||||
|
||||
if ((source->flags & OBS_SOURCE_FLAG_UNBUFFERED) != 0) {
|
||||
if (source->async_unbuffered) {
|
||||
while (source->async_frames.num > 2) {
|
||||
da_erase(source->async_frames, 0);
|
||||
remove_async_frame(source, next_frame);
|
||||
|
|
|
|||
|
|
@ -365,6 +365,27 @@ obs_source_t *obs_source_create_private(const char *id, const char *name,
|
|||
return obs_source_create_internal(id, name, settings, NULL, true);
|
||||
}
|
||||
|
||||
static char *get_new_filter_name(obs_source_t *dst, const char *name)
|
||||
{
|
||||
struct dstr new_name = {0};
|
||||
int inc = 0;
|
||||
|
||||
dstr_copy(&new_name, name);
|
||||
|
||||
for (;;) {
|
||||
obs_source_t *existing_filter = obs_source_get_filter_by_name(
|
||||
dst, new_name.array);
|
||||
if (!existing_filter)
|
||||
break;
|
||||
|
||||
obs_source_release(existing_filter);
|
||||
|
||||
dstr_printf(&new_name, "%s %d", name, ++inc + 1);
|
||||
}
|
||||
|
||||
return new_name.array;
|
||||
}
|
||||
|
||||
static void duplicate_filters(obs_source_t *dst, obs_source_t *src,
|
||||
bool private)
|
||||
{
|
||||
|
|
@ -380,9 +401,13 @@ static void duplicate_filters(obs_source_t *dst, obs_source_t *src,
|
|||
|
||||
for (size_t i = filters.num; i > 0; i--) {
|
||||
obs_source_t *src_filter = filters.array[i - 1];
|
||||
obs_source_t *dst_filter = obs_source_duplicate(src_filter,
|
||||
src_filter->context.name, private);
|
||||
char *new_name = get_new_filter_name(dst,
|
||||
src_filter->context.name);
|
||||
|
||||
obs_source_t *dst_filter = obs_source_duplicate(src_filter,
|
||||
new_name, private);
|
||||
|
||||
bfree(new_name);
|
||||
obs_source_filter_add(dst, dst_filter);
|
||||
obs_source_release(dst_filter);
|
||||
obs_source_release(src_filter);
|
||||
|
|
@ -391,6 +416,15 @@ static void duplicate_filters(obs_source_t *dst, obs_source_t *src,
|
|||
da_free(filters);
|
||||
}
|
||||
|
||||
void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src)
|
||||
{
|
||||
duplicate_filters(dst, src, dst->context.private ?
|
||||
OBS_SCENE_DUP_PRIVATE_COPY :
|
||||
OBS_SCENE_DUP_COPY);
|
||||
|
||||
obs_source_release(src);
|
||||
}
|
||||
|
||||
obs_source_t *obs_source_duplicate(obs_source_t *source,
|
||||
const char *new_name, bool create_private)
|
||||
{
|
||||
|
|
@ -532,6 +566,8 @@ void obs_source_destroy(struct obs_source *source)
|
|||
audio_resampler_destroy(source->resampler);
|
||||
bfree(source->audio_output_buf[0][0]);
|
||||
|
||||
obs_source_frame_destroy(source->async_preload_frame);
|
||||
|
||||
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
|
||||
obs_transition_free(source);
|
||||
|
||||
|
|
@ -1037,6 +1073,7 @@ static void reset_audio_data(obs_source_t *source, uint64_t os_time)
|
|||
|
||||
source->last_audio_input_buf_size = 0;
|
||||
source->audio_ts = os_time;
|
||||
source->next_audio_sys_ts_min = os_time;
|
||||
}
|
||||
|
||||
static void handle_ts_jump(obs_source_t *source, uint64_t expected,
|
||||
|
|
@ -1452,6 +1489,12 @@ static inline void set_eparam(gs_effect_t *effect, const char *name, float val)
|
|||
gs_effect_set_float(param, val);
|
||||
}
|
||||
|
||||
static inline void set_eparami(gs_effect_t *effect, const char *name, int val)
|
||||
{
|
||||
gs_eparam_t *param = gs_effect_get_param_by_name(effect, name);
|
||||
gs_effect_set_int(param, val);
|
||||
}
|
||||
|
||||
static bool update_async_texrender(struct obs_source *source,
|
||||
const struct obs_source_frame *frame,
|
||||
gs_texture_t *tex, gs_texrender_t *texrender)
|
||||
|
|
@ -1479,22 +1522,16 @@ static bool update_async_texrender(struct obs_source *source,
|
|||
gs_effect_set_texture(gs_effect_get_param_by_name(conv, "image"), tex);
|
||||
set_eparam(conv, "width", (float)cx);
|
||||
set_eparam(conv, "height", (float)cy);
|
||||
set_eparam(conv, "width_i", 1.0f / cx);
|
||||
set_eparam(conv, "height_i", 1.0f / cy);
|
||||
set_eparam(conv, "width_d2", cx * 0.5f);
|
||||
set_eparam(conv, "height_d2", cy * 0.5f);
|
||||
set_eparam(conv, "width_d2_i", 1.0f / (cx * 0.5f));
|
||||
set_eparam(conv, "height_d2_i", 1.0f / (cy * 0.5f));
|
||||
set_eparam(conv, "input_width", convert_width);
|
||||
set_eparam(conv, "input_height", convert_height);
|
||||
set_eparam(conv, "input_width_i", 1.0f / convert_width);
|
||||
set_eparam(conv, "input_height_i", 1.0f / convert_height);
|
||||
set_eparam(conv, "input_width_i_d2", (1.0f / convert_width) * 0.5f);
|
||||
set_eparam(conv, "input_height_i_d2", (1.0f / convert_height) * 0.5f);
|
||||
set_eparam(conv, "u_plane_offset",
|
||||
(float)source->async_plane_offset[0]);
|
||||
set_eparam(conv, "v_plane_offset",
|
||||
(float)source->async_plane_offset[1]);
|
||||
|
||||
set_eparami(conv, "int_width", (int)cx);
|
||||
set_eparami(conv, "int_input_width", (int)source->async_convert_width);
|
||||
set_eparami(conv, "int_u_plane_offset",
|
||||
(int)source->async_plane_offset[0]);
|
||||
set_eparami(conv, "int_v_plane_offset",
|
||||
(int)source->async_plane_offset[1]);
|
||||
|
||||
gs_ortho(0.f, (float)cx, 0.f, (float)cy, -100.f, 100.f);
|
||||
|
||||
|
|
@ -1658,7 +1695,7 @@ static inline void obs_source_render_filters(obs_source_t *source)
|
|||
source->rendering_filter = false;
|
||||
}
|
||||
|
||||
static void obs_source_default_render(obs_source_t *source)
|
||||
void obs_source_default_render(obs_source_t *source)
|
||||
{
|
||||
gs_effect_t *effect = obs->video.default_effect;
|
||||
gs_technique_t *tech = gs_effect_get_technique(effect, "Draw");
|
||||
|
|
@ -1849,6 +1886,18 @@ obs_source_t *obs_filter_get_target(const obs_source_t *filter)
|
|||
filter->filter_target : NULL;
|
||||
}
|
||||
|
||||
static bool filter_compatible(obs_source_t *source, obs_source_t *filter)
|
||||
{
|
||||
uint32_t s_caps = source->info.output_flags;
|
||||
uint32_t f_caps = filter->info.output_flags;
|
||||
|
||||
if ((f_caps & OBS_SOURCE_AUDIO) != 0 &&
|
||||
(f_caps & OBS_SOURCE_VIDEO) == 0)
|
||||
f_caps &= ~OBS_SOURCE_ASYNC;
|
||||
|
||||
return (s_caps & f_caps) == f_caps;
|
||||
}
|
||||
|
||||
void obs_source_filter_add(obs_source_t *source, obs_source_t *filter)
|
||||
{
|
||||
struct calldata cd;
|
||||
|
|
@ -1868,6 +1917,11 @@ void obs_source_filter_add(obs_source_t *source, obs_source_t *filter)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!filter_compatible(source, filter)) {
|
||||
pthread_mutex_unlock(&source->filter_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
obs_source_addref(filter);
|
||||
|
||||
filter->filter_parent = source;
|
||||
|
|
@ -2313,6 +2367,62 @@ void obs_source_output_video(obs_source_t *source,
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool preload_frame_changed(obs_source_t *source,
|
||||
const struct obs_source_frame *in)
|
||||
{
|
||||
if (!source->async_preload_frame)
|
||||
return true;
|
||||
|
||||
return in->width != source->async_preload_frame->width ||
|
||||
in->height != source->async_preload_frame->height ||
|
||||
in->format != source->async_preload_frame->format;
|
||||
}
|
||||
|
||||
void obs_source_preload_video(obs_source_t *source,
|
||||
const struct obs_source_frame *frame)
|
||||
{
|
||||
if (!obs_source_valid(source, "obs_source_preload_video"))
|
||||
return;
|
||||
if (!frame)
|
||||
return;
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
if (preload_frame_changed(source, frame)) {
|
||||
obs_source_frame_destroy(source->async_preload_frame);
|
||||
source->async_preload_frame = obs_source_frame_create(
|
||||
frame->format,
|
||||
frame->width,
|
||||
frame->height);
|
||||
}
|
||||
|
||||
copy_frame_data(source->async_preload_frame, frame);
|
||||
set_async_texture_size(source, source->async_preload_frame);
|
||||
update_async_texture(source, source->async_preload_frame,
|
||||
source->async_texture,
|
||||
source->async_texrender);
|
||||
|
||||
source->last_frame_ts = frame->timestamp;
|
||||
|
||||
obs_leave_graphics();
|
||||
}
|
||||
|
||||
void obs_source_show_preloaded_video(obs_source_t *source)
|
||||
{
|
||||
uint64_t sys_ts;
|
||||
|
||||
if (!obs_source_valid(source, "obs_source_show_preloaded_video"))
|
||||
return;
|
||||
|
||||
source->async_active = true;
|
||||
|
||||
pthread_mutex_lock(&source->audio_buf_mutex);
|
||||
sys_ts = os_gettime_ns();
|
||||
reset_audio_timing(source, source->last_frame_ts, sys_ts);
|
||||
reset_audio_data(source, sys_ts);
|
||||
pthread_mutex_unlock(&source->audio_buf_mutex);
|
||||
}
|
||||
|
||||
static inline struct obs_audio_data *filter_async_audio(obs_source_t *source,
|
||||
struct obs_audio_data *in)
|
||||
{
|
||||
|
|
@ -2509,7 +2619,7 @@ static bool ready_async_frame(obs_source_t *source, uint64_t sys_time)
|
|||
uint64_t frame_time = next_frame->timestamp;
|
||||
uint64_t frame_offset = 0;
|
||||
|
||||
if ((source->flags & OBS_SOURCE_FLAG_UNBUFFERED) != 0) {
|
||||
if (source->async_unbuffered) {
|
||||
while (source->async_frames.num > 1) {
|
||||
da_erase(source->async_frames, 0);
|
||||
remove_async_frame(source, next_frame);
|
||||
|
|
@ -3903,8 +4013,6 @@ void obs_source_set_monitoring_type(obs_source_t *source,
|
|||
|
||||
if (!obs_source_valid(source, "obs_source_set_monitoring_type"))
|
||||
return;
|
||||
if (source->info.output_flags & OBS_SOURCE_DO_NOT_MONITOR)
|
||||
return;
|
||||
if (source->monitoring_type == type)
|
||||
return;
|
||||
|
||||
|
|
@ -3929,3 +4037,17 @@ enum obs_monitoring_type obs_source_get_monitoring_type(
|
|||
return obs_source_valid(source, "obs_source_get_monitoring_type") ?
|
||||
source->monitoring_type : OBS_MONITORING_TYPE_NONE;
|
||||
}
|
||||
|
||||
void obs_source_set_async_unbuffered(obs_source_t *source, bool unbuffered)
|
||||
{
|
||||
if (!obs_source_valid(source, "obs_source_set_async_unbuffered"))
|
||||
return;
|
||||
|
||||
source->async_unbuffered = unbuffered;
|
||||
}
|
||||
|
||||
bool obs_source_async_unbuffered(const obs_source_t *source)
|
||||
{
|
||||
return obs_source_valid(source, "obs_source_async_unbuffered") ?
|
||||
source->async_unbuffered : false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,10 +123,12 @@ enum obs_source_type {
|
|||
/**
|
||||
* Source cannot have its audio monitored
|
||||
*
|
||||
* Specifies that this source may cause a feedback loop if audio is monitored.
|
||||
* Specifies that this source may cause a feedback loop if audio is monitored
|
||||
* with a device selected as desktop audio.
|
||||
*
|
||||
* This is used primarily with desktop audio capture sources.
|
||||
*/
|
||||
#define OBS_SOURCE_DO_NOT_MONITOR (1<<9)
|
||||
#define OBS_SOURCE_DO_NOT_SELF_MONITOR (1<<9)
|
||||
|
||||
/** @} */
|
||||
|
||||
|
|
@ -171,7 +173,7 @@ struct obs_source_info {
|
|||
* Creates the source data for the source
|
||||
*
|
||||
* @param settings Settings to initialize the source with
|
||||
* @param source Source that this data is assoicated with
|
||||
* @param source Source that this data is associated with
|
||||
* @return The data associated with this source
|
||||
*/
|
||||
void *(*create)(obs_data_t *settings, obs_source_t *source);
|
||||
|
|
@ -257,7 +259,7 @@ struct obs_source_info {
|
|||
* If the source output flags do not include SOURCE_CUSTOM_DRAW, all
|
||||
* a source needs to do is set the "image" parameter of the effect to
|
||||
* the desired texture, and then draw. If the output flags include
|
||||
* SOURCE_COLOR_MATRIX, you may optionally set the the "color_matrix"
|
||||
* SOURCE_COLOR_MATRIX, you may optionally set the "color_matrix"
|
||||
* parameter of the effect to a custom 4x4 conversion matrix (by
|
||||
* default it will be set to an YUV->RGB conversion matrix)
|
||||
*
|
||||
|
|
@ -429,7 +431,7 @@ EXPORT void obs_register_source_s(const struct obs_source_info *info,
|
|||
size_t size);
|
||||
|
||||
/**
|
||||
* Regsiters a source definition to the current obs context. This should be
|
||||
* Registers a source definition to the current obs context. This should be
|
||||
* used in obs_module_load.
|
||||
*
|
||||
* @param info Pointer to the source definition structure
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ struct obs_modal_ui {
|
|||
};
|
||||
|
||||
/**
|
||||
* Regsiters a modal UI definition to the current obs context. This should be
|
||||
* Registers a modal UI definition to the current obs context. This should be
|
||||
* used in obs_module_load.
|
||||
*
|
||||
* @param info Pointer to the modal definition structure
|
||||
|
|
|
|||
|
|
@ -105,6 +105,19 @@ static inline void render_main_texture(struct obs_core_video *video,
|
|||
gs_clear(GS_CLEAR_COLOR, &clear_color, 1.0f, 0);
|
||||
|
||||
set_render_size(video->base_width, video->base_height);
|
||||
|
||||
pthread_mutex_lock(&obs->data.draw_callbacks_mutex);
|
||||
|
||||
for (size_t i = 0; i < obs->data.draw_callbacks.num; i++) {
|
||||
struct draw_callback *callback;
|
||||
callback = obs->data.draw_callbacks.array+i;
|
||||
|
||||
callback->draw(callback->param,
|
||||
video->base_width, video->base_height);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&obs->data.draw_callbacks_mutex);
|
||||
|
||||
obs_view_render(&obs->data.main_view);
|
||||
|
||||
video->textures_rendered[cur_texture] = true;
|
||||
|
|
@ -573,6 +586,7 @@ void *obs_video_thread(void *param)
|
|||
{
|
||||
uint64_t last_time = 0;
|
||||
uint64_t interval = video_output_get_frame_time(obs->video.video);
|
||||
uint64_t frame_time_total_ns = 0;
|
||||
uint64_t fps_total_ns = 0;
|
||||
uint32_t fps_total_frames = 0;
|
||||
|
||||
|
|
@ -586,6 +600,9 @@ void *obs_video_thread(void *param)
|
|||
profile_register_root(video_thread_name, interval);
|
||||
|
||||
while (!video_output_stopped(obs->video.video)) {
|
||||
uint64_t frame_start = os_gettime_ns();
|
||||
uint64_t frame_time_ns;
|
||||
|
||||
profile_start(video_thread_name);
|
||||
|
||||
profile_start(tick_sources_name);
|
||||
|
|
@ -600,18 +617,25 @@ void *obs_video_thread(void *param)
|
|||
output_frame();
|
||||
profile_end(output_frame_name);
|
||||
|
||||
frame_time_ns = os_gettime_ns() - frame_start;
|
||||
|
||||
profile_end(video_thread_name);
|
||||
|
||||
profile_reenable_thread();
|
||||
|
||||
video_sleep(&obs->video, &obs->video.video_time, interval);
|
||||
|
||||
frame_time_total_ns += frame_time_ns;
|
||||
fps_total_ns += (obs->video.video_time - last_time);
|
||||
fps_total_frames++;
|
||||
|
||||
if (fps_total_ns >= 1000000000ULL) {
|
||||
obs->video.video_fps = (double)fps_total_frames /
|
||||
((double)fps_total_ns / 1000000000.0);
|
||||
obs->video.video_avg_frame_time_ns =
|
||||
frame_time_total_ns / (uint64_t)fps_total_frames;
|
||||
|
||||
frame_time_total_ns = 0;
|
||||
fps_total_ns = 0;
|
||||
fps_total_frames = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,54 +105,10 @@ static void log_processor_info(void)
|
|||
RegCloseKey(key);
|
||||
}
|
||||
|
||||
static DWORD num_logical_cores(ULONG_PTR mask)
|
||||
{
|
||||
DWORD left_shift = sizeof(ULONG_PTR) * 8 - 1;
|
||||
DWORD bit_set_count = 0;
|
||||
ULONG_PTR bit_test = (ULONG_PTR)1 << left_shift;
|
||||
|
||||
for (DWORD i = 0; i <= left_shift; ++i) {
|
||||
bit_set_count += ((mask & bit_test) ? 1 : 0);
|
||||
bit_test /= 2;
|
||||
}
|
||||
|
||||
return bit_set_count;
|
||||
}
|
||||
|
||||
static void log_processor_cores(void)
|
||||
{
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION info = NULL, temp = NULL;
|
||||
DWORD len = 0;
|
||||
|
||||
GetLogicalProcessorInformation(info, &len);
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
|
||||
info = malloc(len);
|
||||
|
||||
if (GetLogicalProcessorInformation(info, &len)) {
|
||||
DWORD num = len / sizeof(*info);
|
||||
int physical_cores = 0;
|
||||
int logical_cores = 0;
|
||||
|
||||
temp = info;
|
||||
|
||||
for (DWORD i = 0; i < num; i++) {
|
||||
if (temp->Relationship == RelationProcessorCore) {
|
||||
ULONG_PTR mask = temp->ProcessorMask;
|
||||
|
||||
physical_cores++;
|
||||
logical_cores += num_logical_cores(mask);
|
||||
}
|
||||
|
||||
temp++;
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
|
||||
physical_cores, logical_cores);
|
||||
}
|
||||
|
||||
free(info);
|
||||
blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
|
||||
os_get_physical_cores(), os_get_logical_cores());
|
||||
}
|
||||
|
||||
static void log_available_memory(void)
|
||||
|
|
|
|||
75
libobs/obs.c
75
libobs/obs.c
|
|
@ -390,6 +390,7 @@ static int obs_init_video(struct obs_video_info *ovi)
|
|||
return OBS_VIDEO_FAIL;
|
||||
|
||||
video->thread_initialized = true;
|
||||
video->ovi = *ovi;
|
||||
return OBS_VIDEO_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
@ -541,6 +542,7 @@ static bool obs_init_data(void)
|
|||
assert(data != NULL);
|
||||
|
||||
pthread_mutex_init_value(&obs->data.displays_mutex);
|
||||
pthread_mutex_init_value(&obs->data.draw_callbacks_mutex);
|
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0)
|
||||
return false;
|
||||
|
|
@ -558,6 +560,8 @@ static bool obs_init_data(void)
|
|||
goto fail;
|
||||
if (pthread_mutex_init(&data->services_mutex, &attr) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&obs->data.draw_callbacks_mutex, NULL) != 0)
|
||||
goto fail;
|
||||
if (!obs_view_init(&data->main_view))
|
||||
goto fail;
|
||||
|
||||
|
|
@ -613,6 +617,8 @@ static void obs_free_data(void)
|
|||
pthread_mutex_destroy(&data->outputs_mutex);
|
||||
pthread_mutex_destroy(&data->encoders_mutex);
|
||||
pthread_mutex_destroy(&data->services_mutex);
|
||||
pthread_mutex_destroy(&data->draw_callbacks_mutex);
|
||||
da_free(data->draw_callbacks);
|
||||
}
|
||||
|
||||
static const char *obs_signals[] = {
|
||||
|
|
@ -818,9 +824,6 @@ void obs_shutdown(void)
|
|||
} while (false)
|
||||
|
||||
FREE_REGISTERED_TYPES(obs_source_info, obs->source_types);
|
||||
FREE_REGISTERED_TYPES(obs_source_info, obs->input_types);
|
||||
FREE_REGISTERED_TYPES(obs_source_info, obs->filter_types);
|
||||
FREE_REGISTERED_TYPES(obs_source_info, obs->transition_types);
|
||||
FREE_REGISTERED_TYPES(obs_output_info, obs->output_types);
|
||||
FREE_REGISTERED_TYPES(obs_encoder_info, obs->encoder_types);
|
||||
FREE_REGISTERED_TYPES(obs_service_info, obs->service_types);
|
||||
|
|
@ -829,6 +832,10 @@ void obs_shutdown(void)
|
|||
|
||||
#undef FREE_REGISTERED_TYPES
|
||||
|
||||
da_free(obs->input_types);
|
||||
da_free(obs->filter_types);
|
||||
da_free(obs->transition_types);
|
||||
|
||||
stop_video();
|
||||
stop_hotkeys();
|
||||
|
||||
|
|
@ -1007,28 +1014,11 @@ bool obs_reset_audio(const struct obs_audio_info *oai)
|
|||
bool obs_get_video_info(struct obs_video_info *ovi)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
const struct video_output_info *info;
|
||||
|
||||
if (!obs || !video->graphics)
|
||||
return false;
|
||||
|
||||
info = video_output_get_info(video->video);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
memset(ovi, 0, sizeof(struct obs_video_info));
|
||||
ovi->base_width = video->base_width;
|
||||
ovi->base_height = video->base_height;
|
||||
ovi->gpu_conversion= video->gpu_conversion;
|
||||
ovi->scale_type = video->scale_type;
|
||||
ovi->colorspace = info->colorspace;
|
||||
ovi->range = info->range;
|
||||
ovi->output_width = info->width;
|
||||
ovi->output_height = info->height;
|
||||
ovi->output_format = info->format;
|
||||
ovi->fps_num = info->fps_num;
|
||||
ovi->fps_den = info->fps_den;
|
||||
|
||||
*ovi = video->ovi;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1866,6 +1856,11 @@ double obs_get_active_fps(void)
|
|||
return obs ? obs->video.video_fps : 0.0;
|
||||
}
|
||||
|
||||
uint64_t obs_get_average_frame_time_ns(void)
|
||||
{
|
||||
return obs ? obs->video.video_avg_frame_time_ns : 0;
|
||||
}
|
||||
|
||||
enum obs_obj_type obs_obj_get_type(void *obj)
|
||||
{
|
||||
struct obs_context_data *context = obj;
|
||||
|
|
@ -1941,3 +1936,41 @@ void obs_get_audio_monitoring_device(const char **name, const char **id)
|
|||
if (id)
|
||||
*id = obs->audio.monitoring_device_id;
|
||||
}
|
||||
|
||||
void obs_add_main_render_callback(
|
||||
void (*draw)(void *param, uint32_t cx, uint32_t cy),
|
||||
void *param)
|
||||
{
|
||||
if (!obs)
|
||||
return;
|
||||
|
||||
struct draw_callback data = {draw, param};
|
||||
|
||||
pthread_mutex_lock(&obs->data.draw_callbacks_mutex);
|
||||
da_push_back(obs->data.draw_callbacks, &data);
|
||||
pthread_mutex_unlock(&obs->data.draw_callbacks_mutex);
|
||||
}
|
||||
|
||||
void obs_remove_main_render_callback(
|
||||
void (*draw)(void *param, uint32_t cx, uint32_t cy),
|
||||
void *param)
|
||||
{
|
||||
if (!obs)
|
||||
return;
|
||||
|
||||
struct draw_callback data = {draw, param};
|
||||
|
||||
pthread_mutex_lock(&obs->data.draw_callbacks_mutex);
|
||||
da_erase_item(obs->data.draw_callbacks, &data);
|
||||
pthread_mutex_unlock(&obs->data.draw_callbacks_mutex);
|
||||
}
|
||||
|
||||
uint32_t obs_get_total_frames(void)
|
||||
{
|
||||
return obs ? obs->video.total_frames : 0;
|
||||
}
|
||||
|
||||
uint32_t obs_get_lagged_frames(void)
|
||||
{
|
||||
return obs ? obs->video.lagged_frames : 0;
|
||||
}
|
||||
|
|
|
|||
61
libobs/obs.h
61
libobs/obs.h
|
|
@ -279,15 +279,15 @@ EXPORT const char *obs_get_locale(void);
|
|||
EXPORT profiler_name_store_t *obs_get_profiler_name_store(void);
|
||||
|
||||
/**
|
||||
* Sets base video ouput base resolution/fps/format.
|
||||
* Sets base video output base resolution/fps/format.
|
||||
*
|
||||
* @note This data cannot be changed if an output is corrently active.
|
||||
* @note This data cannot be changed if an output is currently active.
|
||||
* @note The graphics module cannot be changed without fully destroying the
|
||||
* OBS context.
|
||||
*
|
||||
* @param ovi Pointer to an obs_video_info structure containing the
|
||||
* specification of the graphics subsystem,
|
||||
* @return OBS_VIDEO_SUCCESS if sucessful
|
||||
* @return OBS_VIDEO_SUCCESS if successful
|
||||
* OBS_VIDEO_NOT_SUPPORTED if the adapter lacks capabilities
|
||||
* OBS_VIDEO_INVALID_PARAM if a parameter is invalid
|
||||
* OBS_VIDEO_CURRENTLY_ACTIVE if video is currently active
|
||||
|
|
@ -335,7 +335,7 @@ EXPORT int obs_open_module(obs_module_t **module, const char *path,
|
|||
|
||||
/**
|
||||
* Initializes the module, which calls its obs_module_load export. If the
|
||||
* module is alrady loaded, then this function does nothing and returns
|
||||
* module is already loaded, then this function does nothing and returns
|
||||
* successful.
|
||||
*/
|
||||
EXPORT bool obs_init_module(obs_module_t *module);
|
||||
|
|
@ -591,6 +591,13 @@ EXPORT void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb,
|
|||
EXPORT bool obs_set_audio_monitoring_device(const char *name, const char *id);
|
||||
EXPORT void obs_get_audio_monitoring_device(const char **name, const char **id);
|
||||
|
||||
EXPORT void obs_add_main_render_callback(
|
||||
void (*draw)(void *param, uint32_t cx, uint32_t cy),
|
||||
void *param);
|
||||
EXPORT void obs_remove_main_render_callback(
|
||||
void (*draw)(void *param, uint32_t cx, uint32_t cy),
|
||||
void *param);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* View context */
|
||||
|
|
@ -620,6 +627,10 @@ 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);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
|
@ -760,6 +771,9 @@ EXPORT obs_source_t *obs_filter_get_parent(const obs_source_t *filter);
|
|||
*/
|
||||
EXPORT obs_source_t *obs_filter_get_target(const obs_source_t *filter);
|
||||
|
||||
/** Used to directly render a non-async source without any filter processing */
|
||||
EXPORT void obs_source_default_render(obs_source_t *source);
|
||||
|
||||
/** Adds a filter to the source (which is used whenever the source is used) */
|
||||
EXPORT void obs_source_filter_add(obs_source_t *source, obs_source_t *filter);
|
||||
|
||||
|
|
@ -823,8 +837,8 @@ EXPORT bool obs_source_active(const obs_source_t *source);
|
|||
*/
|
||||
EXPORT bool obs_source_showing(const obs_source_t *source);
|
||||
|
||||
/** Specifies that async video frames should be played as soon as possible */
|
||||
#define OBS_SOURCE_FLAG_UNBUFFERED (1<<0)
|
||||
/** Unused flag */
|
||||
#define OBS_SOURCE_FLAG_UNUSED_1 (1<<0)
|
||||
/** Specifies to force audio to mono */
|
||||
#define OBS_SOURCE_FLAG_FORCE_MONO (1<<1)
|
||||
|
||||
|
|
@ -869,6 +883,8 @@ EXPORT void obs_source_enum_filters(obs_source_t *source,
|
|||
EXPORT obs_source_t *obs_source_get_filter_by_name(obs_source_t *source,
|
||||
const char *name);
|
||||
|
||||
EXPORT void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src);
|
||||
|
||||
EXPORT bool obs_source_enabled(const obs_source_t *source);
|
||||
EXPORT void obs_source_set_enabled(obs_source_t *source, bool enabled);
|
||||
|
||||
|
|
@ -974,6 +990,13 @@ EXPORT void obs_source_draw(gs_texture_t *image, int x, int y,
|
|||
EXPORT void obs_source_output_video(obs_source_t *source,
|
||||
const struct obs_source_frame *frame);
|
||||
|
||||
/** Preloads asynchronous video data to allow instantaneous playback */
|
||||
EXPORT void obs_source_preload_video(obs_source_t *source,
|
||||
const struct obs_source_frame *frame);
|
||||
|
||||
/** Shows any preloaded video data */
|
||||
EXPORT void obs_source_show_preloaded_video(obs_source_t *source);
|
||||
|
||||
/** Outputs audio data (always asynchronous) */
|
||||
EXPORT void obs_source_output_audio(obs_source_t *source,
|
||||
const struct obs_source_audio *audio);
|
||||
|
|
@ -1080,6 +1103,10 @@ EXPORT uint64_t obs_source_get_audio_timestamp(const obs_source_t *source);
|
|||
EXPORT void obs_source_get_audio_mix(const obs_source_t *source,
|
||||
struct obs_source_audio_mix *audio);
|
||||
|
||||
EXPORT void obs_source_set_async_unbuffered(obs_source_t *source,
|
||||
bool unbuffered);
|
||||
EXPORT bool obs_source_async_unbuffered(const obs_source_t *source);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Transition-specific functions */
|
||||
enum obs_transition_target {
|
||||
|
|
@ -1162,7 +1189,7 @@ EXPORT void obs_transition_swap_end(obs_source_t *tr_dest,
|
|||
* Creates a scene.
|
||||
*
|
||||
* A scene is a source which is a container of other sources with specific
|
||||
* display oriantations. Scenes can also be used like any other source.
|
||||
* display orientations. Scenes can also be used like any other source.
|
||||
*/
|
||||
EXPORT obs_scene_t *obs_scene_create(const char *name);
|
||||
|
||||
|
|
@ -1194,6 +1221,9 @@ EXPORT obs_scene_t *obs_scene_from_source(const obs_source_t *source);
|
|||
EXPORT obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene,
|
||||
const char *name);
|
||||
|
||||
EXPORT obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene,
|
||||
int64_t id);
|
||||
|
||||
/** Enumerates sources within a scene */
|
||||
EXPORT void obs_scene_enum_items(obs_scene_t *scene,
|
||||
bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*),
|
||||
|
|
@ -1224,7 +1254,7 @@ EXPORT obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item);
|
|||
EXPORT void obs_sceneitem_select(obs_sceneitem_t *item, bool select);
|
||||
EXPORT bool obs_sceneitem_selected(const obs_sceneitem_t *item);
|
||||
|
||||
/* Functions for gettings/setting specific orientation of a scene item */
|
||||
/* Functions for getting/setting specific orientation of a scene item */
|
||||
EXPORT void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos);
|
||||
EXPORT void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot_deg);
|
||||
EXPORT void obs_sceneitem_set_scale(obs_sceneitem_t *item,
|
||||
|
|
@ -1242,6 +1272,8 @@ EXPORT void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item,
|
|||
EXPORT void obs_sceneitem_set_bounds(obs_sceneitem_t *item,
|
||||
const struct vec2 *bounds);
|
||||
|
||||
EXPORT int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item);
|
||||
|
||||
EXPORT void obs_sceneitem_get_pos(const obs_sceneitem_t *item,
|
||||
struct vec2 *pos);
|
||||
EXPORT float obs_sceneitem_get_rot(const obs_sceneitem_t *item);
|
||||
|
|
@ -1388,12 +1420,6 @@ EXPORT signal_handler_t *obs_output_get_signal_handler(
|
|||
/** Returns the procedure handler for an output */
|
||||
EXPORT proc_handler_t *obs_output_get_proc_handler(const obs_output_t *output);
|
||||
|
||||
/**
|
||||
* Sets the current video media context associated with this output,
|
||||
* required for non-encoded outputs
|
||||
*/
|
||||
EXPORT void obs_output_set_video(obs_output_t *output, video_t *video);
|
||||
|
||||
/**
|
||||
* Sets the current audio/video media contexts associated with this output,
|
||||
* required for non-encoded outputs. Can be null.
|
||||
|
|
@ -1486,6 +1512,13 @@ EXPORT void obs_output_output_caption_text1(obs_output_t *output,
|
|||
#endif
|
||||
|
||||
EXPORT float obs_output_get_congestion(obs_output_t *output);
|
||||
EXPORT int obs_output_get_connect_time_ms(obs_output_t *output);
|
||||
|
||||
EXPORT bool obs_output_reconnecting(const obs_output_t *output);
|
||||
|
||||
/** Pass a string of the last output error, for UI use */
|
||||
EXPORT void obs_output_set_last_error(obs_output_t *output,
|
||||
const char *message);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Functions used by outputs */
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ enum {
|
|||
* Use if a problem occurs that doesn't affect the program and is
|
||||
* recoverable.
|
||||
*
|
||||
* Use in places where where failure isn't entirely unexpected, and can
|
||||
* Use in places where failure isn't entirely unexpected, and can
|
||||
* be handled safely.
|
||||
*/
|
||||
LOG_WARNING = 200,
|
||||
|
||||
/**
|
||||
* Informative essage to be displayed in the log.
|
||||
* Informative message to be displayed in the log.
|
||||
*/
|
||||
LOG_INFO = 300,
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
* incredibly inept moron could possibly be managing the visual C compiler
|
||||
* project. They should be fired, and legally forbidden to have a job in
|
||||
* ANYTHING even REMOTELY related to programming. FOREVER. This should also
|
||||
* apply to the next 10 generations all of their descendents. */
|
||||
* apply to the next 10 generations all of their descendants. */
|
||||
#ifndef __cplusplus
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ static bool cf_is_token_break(struct base_token *start_token,
|
|||
start_token->type = BASETOKEN_DIGIT;
|
||||
break;
|
||||
}
|
||||
/* Falls through. */
|
||||
|
||||
case BASETOKEN_NONE:
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ static inline void cf_def_free(struct cf_def *cfd)
|
|||
* + option to exclude features such as #import, variadic macros, and other
|
||||
* features for certain language implementations
|
||||
* + macro parameter string operator #
|
||||
* + macro parameter token concactenation operator ##
|
||||
* + macro parameter token concatenation operator ##
|
||||
* + predefined macros
|
||||
* + restricted macros
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -445,14 +445,10 @@ int config_save_safe(config_t *config, const char *temp_ext,
|
|||
if (*backup_ext != '.')
|
||||
dstr_cat(&backup_file, ".");
|
||||
dstr_cat(&backup_file, backup_ext);
|
||||
|
||||
os_unlink(backup_file.array);
|
||||
os_rename(file, backup_file.array);
|
||||
} else {
|
||||
os_unlink(file);
|
||||
}
|
||||
|
||||
os_rename(temp_file.array, file);
|
||||
if (os_safe_replace(file, temp_file.array, backup_file.array) != 0)
|
||||
ret = CONFIG_ERROR;
|
||||
|
||||
cleanup:
|
||||
pthread_mutex_unlock(&config->mutex);
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ EXPORT bool config_remove_value(config_t *config, const char *section,
|
|||
* These do *not* actually set any values, they only set what values will be
|
||||
* returned for config_get_* if the specified variable does not exist.
|
||||
*
|
||||
* You can initialize the defaults programmitically using config_set_default_*
|
||||
* You can initialize the defaults programmatically using config_set_default_*
|
||||
* functions (recommended for most cases), or you can initialize it via a file
|
||||
* with config_open_defaults.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ extern "C" {
|
|||
* NOTE: Not type-safe when using directly.
|
||||
* Specifying size per call with inline maximizes compiler optimizations
|
||||
*
|
||||
* See DARRAY macro at the bottom of thhe file for slightly safer usage.
|
||||
* See DARRAY macro at the bottom of the file for slightly safer usage.
|
||||
*/
|
||||
|
||||
#define DARRAY_INVALID ((size_t)-1)
|
||||
|
|
@ -438,7 +438,7 @@ static inline void darray_swap(const size_t element_size,
|
|||
* Makes it a little easier to use as well.
|
||||
*
|
||||
* I did -not- want to use a gigantic macro to generate a crapload of
|
||||
* typsafe inline functions per type. It just feels like a mess to me.
|
||||
* typesafe inline functions per type. It just feels like a mess to me.
|
||||
*/
|
||||
|
||||
#define DARRAY(type) \
|
||||
|
|
|
|||
|
|
@ -197,6 +197,11 @@ wchar_t *wstrstri(const wchar_t *str, const wchar_t *find)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool is_padding(char ch)
|
||||
{
|
||||
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
|
||||
}
|
||||
|
||||
char *strdepad(char *str)
|
||||
{
|
||||
char *temp;
|
||||
|
|
@ -210,7 +215,7 @@ char *strdepad(char *str)
|
|||
temp = str;
|
||||
|
||||
/* remove preceding spaces/tabs */
|
||||
while (*temp == ' ' || *temp == '\t')
|
||||
while (is_padding(*temp))
|
||||
++temp;
|
||||
|
||||
len = strlen(str);
|
||||
|
|
@ -219,7 +224,7 @@ char *strdepad(char *str)
|
|||
|
||||
if (len) {
|
||||
temp = str + (len-1);
|
||||
while (*temp == ' ' || *temp == '\t')
|
||||
while (is_padding(*temp))
|
||||
*(temp--) = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
#include <dlfcn.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <mach/mach.h>
|
||||
|
|
@ -312,3 +314,41 @@ void os_inhibit_sleep_destroy(os_inhibit_t *info)
|
|||
bfree(info);
|
||||
}
|
||||
}
|
||||
|
||||
static int physical_cores = 0;
|
||||
static int logical_cores = 0;
|
||||
static bool core_count_initialized = false;
|
||||
|
||||
static void os_get_cores_internal(void)
|
||||
{
|
||||
if (core_count_initialized)
|
||||
return;
|
||||
|
||||
core_count_initialized = true;
|
||||
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
size = sizeof(physical_cores);
|
||||
ret = sysctlbyname("machdep.cpu.core_count", &physical_cores,
|
||||
&size, NULL, 0);
|
||||
if (ret != 0)
|
||||
return;
|
||||
|
||||
ret = sysctlbyname("machdep.cpu.thread_count", &logical_cores,
|
||||
&size, NULL, 0);
|
||||
}
|
||||
|
||||
int os_get_physical_cores(void)
|
||||
{
|
||||
if (!core_count_initialized)
|
||||
os_get_cores_internal();
|
||||
return physical_cores;
|
||||
}
|
||||
|
||||
int os_get_logical_cores(void)
|
||||
{
|
||||
if (!core_count_initialized)
|
||||
os_get_cores_internal();
|
||||
return logical_cores;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -432,6 +432,13 @@ int os_rename(const char *old_path, const char *new_path)
|
|||
return rename(old_path, new_path);
|
||||
}
|
||||
|
||||
int os_safe_replace(const char *target, const char *from, const char *backup)
|
||||
{
|
||||
if (backup && os_file_exists(target) && rename(target, backup) != 0)
|
||||
return -1;
|
||||
return rename(from, target);
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
os_performance_token_t *os_request_high_performance(const char *reason)
|
||||
{
|
||||
|
|
@ -615,3 +622,69 @@ void os_breakpoint()
|
|||
{
|
||||
raise(SIGTRAP);
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
static int physical_cores = 0;
|
||||
static int logical_cores = 0;
|
||||
static bool core_count_initialized = false;
|
||||
|
||||
/* return sysconf(_SC_NPROCESSORS_ONLN); */
|
||||
|
||||
static void os_get_cores_internal(void)
|
||||
{
|
||||
if (core_count_initialized)
|
||||
return;
|
||||
|
||||
core_count_initialized = true;
|
||||
|
||||
logical_cores = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
#ifndef __linux__
|
||||
physical_cores = logical_cores;
|
||||
#else
|
||||
char *text = os_quick_read_utf8_file("/proc/cpuinfo");
|
||||
char *core_id = text;
|
||||
|
||||
if (!text || !*text) {
|
||||
physical_cores = logical_cores;
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
core_id = strstr(core_id, "\ncore id");
|
||||
if (!core_id)
|
||||
break;
|
||||
physical_cores++;
|
||||
core_id++;
|
||||
}
|
||||
|
||||
if (physical_cores == 0)
|
||||
physical_cores = logical_cores;
|
||||
|
||||
bfree(text);
|
||||
#endif
|
||||
}
|
||||
|
||||
int os_get_physical_cores(void)
|
||||
{
|
||||
if (!core_count_initialized)
|
||||
os_get_cores_internal();
|
||||
return physical_cores;
|
||||
}
|
||||
|
||||
int os_get_logical_cores(void)
|
||||
{
|
||||
if (!core_count_initialized)
|
||||
os_get_cores_internal();
|
||||
return logical_cores;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t os_get_free_disk_space(const char *dir)
|
||||
{
|
||||
struct statvfs info;
|
||||
if (statvfs(dir, &info) != 0)
|
||||
return 0;
|
||||
|
||||
return (uint64_t)info.f_frsize * (uint64_t)info.f_bavail;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -549,7 +549,8 @@ int os_rename(const char *old_path, const char *new_path)
|
|||
goto error;
|
||||
}
|
||||
|
||||
code = MoveFileW(old_path_utf16, new_path_utf16) ? 0 : -1;
|
||||
code = MoveFileExW(old_path_utf16, new_path_utf16,
|
||||
MOVEFILE_REPLACE_EXISTING) ? 0 : -1;
|
||||
|
||||
error:
|
||||
bfree(old_path_utf16);
|
||||
|
|
@ -557,6 +558,36 @@ error:
|
|||
return code;
|
||||
}
|
||||
|
||||
int os_safe_replace(const char *target, const char *from, const char *backup)
|
||||
{
|
||||
wchar_t *wtarget = NULL;
|
||||
wchar_t *wfrom = NULL;
|
||||
wchar_t *wbackup = NULL;
|
||||
int code = -1;
|
||||
|
||||
if (!target || !from)
|
||||
return -1;
|
||||
if (!os_utf8_to_wcs_ptr(target, 0, &wtarget))
|
||||
return -1;
|
||||
if (!os_utf8_to_wcs_ptr(from, 0, &wfrom))
|
||||
goto fail;
|
||||
if (backup && !os_utf8_to_wcs_ptr(backup, 0, &wbackup))
|
||||
goto fail;
|
||||
|
||||
if (ReplaceFileW(wtarget, wfrom, wbackup, 0, NULL, NULL)) {
|
||||
code = 0;
|
||||
} else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
||||
code = MoveFileExW(wfrom, wtarget, MOVEFILE_REPLACE_EXISTING)
|
||||
? 0 : -1;
|
||||
}
|
||||
|
||||
fail:
|
||||
bfree(wtarget);
|
||||
bfree(wfrom);
|
||||
bfree(wbackup);
|
||||
return code;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinst_dll, DWORD reason, LPVOID reserved)
|
||||
{
|
||||
switch (reason) {
|
||||
|
|
@ -848,3 +879,83 @@ void os_breakpoint(void)
|
|||
{
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
DWORD num_logical_cores(ULONG_PTR mask)
|
||||
{
|
||||
DWORD left_shift = sizeof(ULONG_PTR) * 8 - 1;
|
||||
DWORD bit_set_count = 0;
|
||||
ULONG_PTR bit_test = (ULONG_PTR)1 << left_shift;
|
||||
|
||||
for (DWORD i = 0; i <= left_shift; ++i) {
|
||||
bit_set_count += ((mask & bit_test) ? 1 : 0);
|
||||
bit_test /= 2;
|
||||
}
|
||||
|
||||
return bit_set_count;
|
||||
}
|
||||
|
||||
static int physical_cores = 0;
|
||||
static int logical_cores = 0;
|
||||
static bool core_count_initialized = false;
|
||||
|
||||
static void os_get_cores_internal(void)
|
||||
{
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION info = NULL, temp = NULL;
|
||||
DWORD len = 0;
|
||||
|
||||
if (core_count_initialized)
|
||||
return;
|
||||
|
||||
core_count_initialized = true;
|
||||
|
||||
GetLogicalProcessorInformation(info, &len);
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
|
||||
info = malloc(len);
|
||||
|
||||
if (GetLogicalProcessorInformation(info, &len)) {
|
||||
DWORD num = len / sizeof(*info);
|
||||
temp = info;
|
||||
|
||||
for (DWORD i = 0; i < num; i++) {
|
||||
if (temp->Relationship == RelationProcessorCore) {
|
||||
ULONG_PTR mask = temp->ProcessorMask;
|
||||
|
||||
physical_cores++;
|
||||
logical_cores += num_logical_cores(mask);
|
||||
}
|
||||
|
||||
temp++;
|
||||
}
|
||||
}
|
||||
|
||||
free(info);
|
||||
}
|
||||
|
||||
int os_get_physical_cores(void)
|
||||
{
|
||||
if (!core_count_initialized)
|
||||
os_get_cores_internal();
|
||||
return physical_cores;
|
||||
}
|
||||
|
||||
int os_get_logical_cores(void)
|
||||
{
|
||||
if (!core_count_initialized)
|
||||
os_get_cores_internal();
|
||||
return logical_cores;
|
||||
}
|
||||
|
||||
uint64_t os_get_free_disk_space(const char *dir)
|
||||
{
|
||||
wchar_t *wdir = NULL;
|
||||
if (!os_utf8_to_wcs_ptr(dir, 0, &wdir))
|
||||
return 0;
|
||||
|
||||
ULARGE_INTEGER free;
|
||||
bool success = !!GetDiskFreeSpaceExW(wdir, &free, NULL, NULL);
|
||||
bfree(wdir);
|
||||
|
||||
return success ? free.QuadPart : 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ bool os_quick_write_mbs_file(const char *path, const char *str, size_t len)
|
|||
if (mbs_len)
|
||||
fwrite(mbs, 1, mbs_len, f);
|
||||
bfree(mbs);
|
||||
fflush(f);
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
|
|
@ -264,6 +265,7 @@ bool os_quick_write_utf8_file(const char *path, const char *str, size_t len,
|
|||
fwrite("\xEF\xBB\xBF", 1, 3, f);
|
||||
if (len)
|
||||
fwrite(str, 1, len, f);
|
||||
fflush(f);
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
|
|
@ -297,17 +299,10 @@ bool os_quick_write_utf8_file_safe(const char *path, const char *str,
|
|||
if (*backup_ext != '.')
|
||||
dstr_cat(&backup_path, ".");
|
||||
dstr_cat(&backup_path, backup_ext);
|
||||
|
||||
os_unlink(backup_path.array);
|
||||
os_rename(path, backup_path.array);
|
||||
|
||||
dstr_free(&backup_path);
|
||||
} else {
|
||||
os_unlink(path);
|
||||
}
|
||||
|
||||
os_rename(temp_path.array, path);
|
||||
success = true;
|
||||
if (os_safe_replace(path, temp_path.array, backup_path.array) == 0)
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
dstr_free(&backup_path);
|
||||
|
|
|
|||
|
|
@ -153,6 +153,8 @@ EXPORT int os_rmdir(const char *path);
|
|||
EXPORT char *os_getcwd(char *path, size_t size);
|
||||
EXPORT int os_chdir(const char *path);
|
||||
|
||||
EXPORT uint64_t os_get_free_disk_space(const char *dir);
|
||||
|
||||
#define MKDIR_EXISTS 1
|
||||
#define MKDIR_SUCCESS 0
|
||||
#define MKDIR_ERROR -1
|
||||
|
|
@ -161,6 +163,8 @@ EXPORT int os_mkdir(const char *path);
|
|||
EXPORT int os_mkdirs(const char *path);
|
||||
EXPORT int os_rename(const char *old_path, const char *new_path);
|
||||
EXPORT int os_copyfile(const char *file_in, const char *file_out);
|
||||
EXPORT int os_safe_replace(const char *target_path, const char *from_path,
|
||||
const char *backup_path);
|
||||
|
||||
EXPORT char *os_generate_formatted_filename(const char *extension, bool space,
|
||||
const char *format);
|
||||
|
|
@ -174,6 +178,9 @@ EXPORT void os_inhibit_sleep_destroy(os_inhibit_t *info);
|
|||
|
||||
EXPORT void os_breakpoint(void);
|
||||
|
||||
EXPORT int os_get_physical_cores(void);
|
||||
EXPORT int os_get_logical_cores(void);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strtoll _strtoi64
|
||||
#if _MSC_VER < 1900
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
/*
|
||||
* Text Lookup interface
|
||||
*
|
||||
* Used for storing and looking up localized strings. Stores locazation
|
||||
* Used for storing and looking up localized strings. Stores localization
|
||||
* strings in a radix/trie tree to efficiently look up associated strings via a
|
||||
* unique string identifier name.
|
||||
*/
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* opaque typdef */
|
||||
/* opaque typedef */
|
||||
struct text_lookup;
|
||||
typedef struct text_lookup lookup_t;
|
||||
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ size_t wchar_to_utf8(const wchar_t *in, size_t insize, char *out,
|
|||
|
||||
/*
|
||||
* NOTE: do not check here for forbidden UTF-8 characters.
|
||||
* They cannot appear here because we do proper convertion.
|
||||
* They cannot appear here because we do proper conversion.
|
||||
*/
|
||||
|
||||
p += n;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ public:
|
|||
inline bool operator!() {return ptr == NULL;}
|
||||
inline bool operator==(T p) {return ptr == p;}
|
||||
inline bool operator!=(T p) {return ptr != p;}
|
||||
|
||||
inline T *Get() const {return ptr;}
|
||||
};
|
||||
|
||||
class ConfigFile {
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ typedef unsigned __int64 uintmax_t;
|
|||
|
||||
/* 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
Accoding to Douglas Gwyn <gwyn@arl.mil>:
|
||||
According to Douglas Gwyn <gwyn@arl.mil>:
|
||||
"This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC
|
||||
9899:1999 as initially published, the expansion was required
|
||||
to be an integer constant of precisely matching type, which
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue