New upstream version 19.0.3+dfsg1

This commit is contained in:
Sebastian Ramacher 2017-06-29 21:01:10 +02:00
parent 3708b8e092
commit 1f1bbb3518
534 changed files with 13862 additions and 2459 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(&params, stack, sizeof(stack));
calldata_init(&params);
calldata_set_string(&params, "last_error", output->last_error_message);
calldata_set_int(&params, "code", output->stop_code);
calldata_set_ptr(&params, "output", output);
signal_handler_signal(output->context.signals, "stop", &params);
calldata_free(&params);
}
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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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.
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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