New upstream version 0.15.4+dfsg1
This commit is contained in:
parent
55d5047af0
commit
67704ac59c
359 changed files with 8423 additions and 1050 deletions
|
|
@ -125,6 +125,8 @@ void gs_technique_end(gs_technique_t *tech)
|
|||
|
||||
da_free(param->cur_val);
|
||||
param->changed = false;
|
||||
if (param->next_sampler)
|
||||
param->next_sampler = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +149,9 @@ static void upload_shader_params(struct darray *pass_params, bool changed_only)
|
|||
struct gs_effect_param *eparam = param->eparam;
|
||||
gs_sparam_t *sparam = param->sparam;
|
||||
|
||||
if (eparam->next_sampler)
|
||||
gs_shader_set_next_sampler(sparam, eparam->next_sampler);
|
||||
|
||||
if (changed_only && !eparam->changed)
|
||||
continue;
|
||||
|
||||
|
|
@ -378,3 +383,14 @@ void gs_effect_set_default(gs_eparam_t *param)
|
|||
effect_setval_inline(param, param->default_val.array,
|
||||
param->default_val.num);
|
||||
}
|
||||
|
||||
void gs_effect_set_next_sampler(gs_eparam_t *param, gs_samplerstate_t *sampler)
|
||||
{
|
||||
if (!param) {
|
||||
blog(LOG_ERROR, "gs_effect_set_next_sampler: invalid param");
|
||||
return;
|
||||
}
|
||||
|
||||
if (param->type == GS_SHADER_PARAM_TEXTURE)
|
||||
param->next_sampler = sampler;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ struct gs_effect_param {
|
|||
DARRAY(uint8_t) default_val;
|
||||
|
||||
gs_effect_t *effect;
|
||||
gs_samplerstate_t *next_sampler;
|
||||
|
||||
/*char *full_name;
|
||||
float scroller_min, scroller_max, scroller_inc, scroller_mul;*/
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ bool load_graphics_imports(struct gs_exports *exports, void *module,
|
|||
GRAPHICS_IMPORT(gs_shader_set_texture);
|
||||
GRAPHICS_IMPORT(gs_shader_set_val);
|
||||
GRAPHICS_IMPORT(gs_shader_set_default);
|
||||
GRAPHICS_IMPORT(gs_shader_set_next_sampler);
|
||||
|
||||
/* OSX/Cocoa specific functions */
|
||||
#ifdef __APPLE__
|
||||
|
|
|
|||
|
|
@ -225,6 +225,8 @@ struct gs_exports {
|
|||
void (*gs_shader_set_val)(gs_sparam_t *param, const void *val,
|
||||
size_t size);
|
||||
void (*gs_shader_set_default)(gs_sparam_t *param);
|
||||
void (*gs_shader_set_next_sampler)(gs_sparam_t *param,
|
||||
gs_samplerstate_t *sampler);
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* OSX/Cocoa specific functions */
|
||||
|
|
|
|||
|
|
@ -2089,6 +2089,16 @@ void gs_shader_set_default(gs_sparam_t *param)
|
|||
graphics->exports.gs_shader_set_default(param);
|
||||
}
|
||||
|
||||
void gs_shader_set_next_sampler(gs_sparam_t *param, gs_samplerstate_t *sampler)
|
||||
{
|
||||
graphics_t *graphics = thread_graphics;
|
||||
|
||||
if (!gs_valid_p("gs_shader_set_next_sampler", param))
|
||||
return;
|
||||
|
||||
graphics->exports.gs_shader_set_next_sampler(param, sampler);
|
||||
}
|
||||
|
||||
void gs_texture_destroy(gs_texture_t *tex)
|
||||
{
|
||||
graphics_t *graphics = thread_graphics;
|
||||
|
|
|
|||
|
|
@ -325,6 +325,8 @@ EXPORT void gs_shader_set_vec4(gs_sparam_t *param, const struct vec4 *val);
|
|||
EXPORT void gs_shader_set_texture(gs_sparam_t *param, gs_texture_t *val);
|
||||
EXPORT void gs_shader_set_val(gs_sparam_t *param, const void *val, size_t size);
|
||||
EXPORT void gs_shader_set_default(gs_sparam_t *param);
|
||||
EXPORT void gs_shader_set_next_sampler(gs_sparam_t *param,
|
||||
gs_samplerstate_t *sampler);
|
||||
|
||||
/* ---------------------------------------------------
|
||||
* effect functions
|
||||
|
|
@ -393,6 +395,8 @@ EXPORT void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val);
|
|||
EXPORT void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val);
|
||||
EXPORT void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size);
|
||||
EXPORT void gs_effect_set_default(gs_eparam_t *param);
|
||||
EXPORT void gs_effect_set_next_sampler(gs_eparam_t *param,
|
||||
gs_samplerstate_t *sampler);
|
||||
|
||||
/* ---------------------------------------------------
|
||||
* texture render helper functions
|
||||
|
|
|
|||
|
|
@ -318,9 +318,10 @@ void gs_image_file_update_texture(gs_image_file_t *image)
|
|||
if (!image->is_animated_gif || !image->loaded)
|
||||
return;
|
||||
|
||||
if (image->animation_frame_cache[image->cur_frame]) {
|
||||
gs_texture_set_image(image->texture,
|
||||
image->animation_frame_cache[image->cur_frame],
|
||||
image->gif.width * 4, false);
|
||||
}
|
||||
if (!image->animation_frame_cache[image->cur_frame])
|
||||
decode_new_frame(image, image->cur_frame);
|
||||
|
||||
gs_texture_set_image(image->texture,
|
||||
image->animation_frame_cache[image->cur_frame],
|
||||
image->gif.width * 4, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ void matrix4_scale_i(struct matrix4 *dst, const struct vec3 *v,
|
|||
|
||||
bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m)
|
||||
{
|
||||
struct vec4 *dstv = (struct vec4 *)dst;
|
||||
struct vec4 *dstv;
|
||||
float det;
|
||||
float m3x3[9];
|
||||
int i, j, sign;
|
||||
|
|
@ -261,25 +261,38 @@ bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m)
|
|||
|
||||
void matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
if (dst == m) {
|
||||
struct matrix4 temp = *m;
|
||||
matrix4_transpose(dst, &temp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Add SSE */
|
||||
temp.x.x = m->x.x;
|
||||
temp.x.y = m->y.x;
|
||||
temp.x.z = m->z.x;
|
||||
temp.x.w = m->t.x;
|
||||
temp.y.x = m->x.y;
|
||||
temp.y.y = m->y.y;
|
||||
temp.y.z = m->z.y;
|
||||
temp.y.w = m->t.y;
|
||||
temp.z.x = m->x.z;
|
||||
temp.z.y = m->y.z;
|
||||
temp.z.z = m->z.z;
|
||||
temp.z.w = m->t.z;
|
||||
temp.t.x = m->x.w;
|
||||
temp.t.y = m->y.w;
|
||||
temp.t.z = m->z.w;
|
||||
temp.t.w = m->t.w;
|
||||
#ifdef NO_INTRINSICS
|
||||
dst->x.x = m->x.x;
|
||||
dst->x.y = m->y.x;
|
||||
dst->x.z = m->z.x;
|
||||
dst->x.w = m->t.x;
|
||||
dst->y.x = m->x.y;
|
||||
dst->y.y = m->y.y;
|
||||
dst->y.z = m->z.y;
|
||||
dst->y.w = m->t.y;
|
||||
dst->z.x = m->x.z;
|
||||
dst->z.y = m->y.z;
|
||||
dst->z.z = m->z.z;
|
||||
dst->z.w = m->t.z;
|
||||
dst->t.x = m->x.w;
|
||||
dst->t.y = m->y.w;
|
||||
dst->t.z = m->z.w;
|
||||
dst->t.w = m->t.w;
|
||||
#else
|
||||
__m128 a0 = _mm_unpacklo_ps(m->x.m, m->z.m);
|
||||
__m128 a1 = _mm_unpacklo_ps(m->y.m, m->t.m);
|
||||
__m128 a2 = _mm_unpackhi_ps(m->x.m, m->z.m);
|
||||
__m128 a3 = _mm_unpackhi_ps(m->y.m, m->t.m);
|
||||
|
||||
matrix4_copy(dst, &temp);
|
||||
dst->x.m = _mm_unpacklo_ps(a0, a1);
|
||||
dst->y.m = _mm_unpackhi_ps(a0, a1);
|
||||
dst->z.m = _mm_unpacklo_ps(a2, a3);
|
||||
dst->t.m = _mm_unpackhi_ps(a2, a3);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,8 +119,7 @@ void shader_sampler_convert(struct shader_sampler *ss,
|
|||
else if (astrcmpi(state, "MaxAnisotropy") == 0)
|
||||
info->max_anisotropy = (int)strtol(value, NULL, 10);
|
||||
else if (astrcmpi(state, "BorderColor") == 0)
|
||||
info->border_color = (*value == '#') ?
|
||||
strtol(value + 1, NULL, 16) : 0;
|
||||
info->border_color = strtol(value + 1, NULL, 16);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +137,7 @@ static int sp_parse_sampler_state_item(struct shader_parser *sp,
|
|||
ret = cf_next_token_should_be(&sp->cfp, "=", ";", NULL);
|
||||
if (ret != PARSE_SUCCESS) goto fail;
|
||||
|
||||
ret = cf_next_name(&sp->cfp, &value, "value name", ";");
|
||||
ret = cf_next_token_copy(&sp->cfp, &value);
|
||||
if (ret != PARSE_SUCCESS) goto fail;
|
||||
|
||||
ret = cf_next_token_should_be(&sp->cfp, ";", ";", NULL);
|
||||
|
|
|
|||
|
|
@ -395,10 +395,13 @@ static void volmeter_update_audio_settings(obs_volmeter_t *volmeter)
|
|||
{
|
||||
audio_t *audio = obs_get_audio();
|
||||
const unsigned int sr = audio_output_get_sample_rate(audio);
|
||||
uint32_t channels = (uint32_t)audio_output_get_channels(audio);
|
||||
|
||||
volmeter->channels = (uint32_t)audio_output_get_channels(audio);
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
volmeter->channels = channels;
|
||||
volmeter->update_frames = volmeter->update_ms * sr / 1000;
|
||||
volmeter->peakhold_frames = volmeter->peakhold_ms * sr / 1000;
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
}
|
||||
|
||||
obs_fader_t *obs_fader_create(enum obs_fader_type type)
|
||||
|
|
@ -544,22 +547,24 @@ float obs_fader_get_mul(obs_fader_t *fader)
|
|||
bool obs_fader_attach_source(obs_fader_t *fader, obs_source_t *source)
|
||||
{
|
||||
signal_handler_t *sh;
|
||||
float vol;
|
||||
|
||||
if (!fader || !source)
|
||||
return false;
|
||||
|
||||
obs_fader_detach_source(fader);
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_connect(sh, "volume",
|
||||
fader_source_volume_changed, fader);
|
||||
signal_handler_connect(sh, "destroy",
|
||||
fader_source_destroyed, fader);
|
||||
vol = obs_source_get_volume(source);
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
fader->source = source;
|
||||
fader->cur_db = mul_to_db(obs_source_get_volume(source));
|
||||
fader->cur_db = mul_to_db(vol);
|
||||
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
|
|
@ -569,25 +574,25 @@ bool obs_fader_attach_source(obs_fader_t *fader, obs_source_t *source)
|
|||
void obs_fader_detach_source(obs_fader_t *fader)
|
||||
{
|
||||
signal_handler_t *sh;
|
||||
obs_source_t *source;
|
||||
|
||||
if (!fader)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
source = fader->source;
|
||||
fader->source = NULL;
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
if (!fader->source)
|
||||
goto exit;
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
sh = obs_source_get_signal_handler(fader->source);
|
||||
sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_disconnect(sh, "volume",
|
||||
fader_source_volume_changed, fader);
|
||||
signal_handler_disconnect(sh, "destroy",
|
||||
fader_source_destroyed, fader);
|
||||
|
||||
fader->source = NULL;
|
||||
|
||||
exit:
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
}
|
||||
|
||||
void obs_fader_add_callback(obs_fader_t *fader, obs_fader_changed_t callback,
|
||||
|
|
@ -674,14 +679,13 @@ void obs_volmeter_destroy(obs_volmeter_t *volmeter)
|
|||
bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, obs_source_t *source)
|
||||
{
|
||||
signal_handler_t *sh;
|
||||
float vol;
|
||||
|
||||
if (!volmeter || !source)
|
||||
return false;
|
||||
|
||||
obs_volmeter_detach_source(volmeter);
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
|
||||
sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_connect(sh, "volume",
|
||||
volmeter_source_volume_changed, volmeter);
|
||||
|
|
@ -689,9 +693,12 @@ bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, obs_source_t *source)
|
|||
volmeter_source_destroyed, volmeter);
|
||||
obs_source_add_audio_capture_callback(source,
|
||||
volmeter_source_data_received, volmeter);
|
||||
vol = obs_source_get_volume(source);
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
|
||||
volmeter->source = source;
|
||||
volmeter->cur_db = mul_to_db(obs_source_get_volume(source));
|
||||
volmeter->cur_db = mul_to_db(vol);
|
||||
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
|
|
@ -701,27 +708,26 @@ bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, obs_source_t *source)
|
|||
void obs_volmeter_detach_source(obs_volmeter_t *volmeter)
|
||||
{
|
||||
signal_handler_t *sh;
|
||||
obs_source_t *source;
|
||||
|
||||
if (!volmeter)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
source = volmeter->source;
|
||||
volmeter->source = NULL;
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
if (!volmeter->source)
|
||||
goto exit;
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
sh = obs_source_get_signal_handler(volmeter->source);
|
||||
sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_disconnect(sh, "volume",
|
||||
volmeter_source_volume_changed, volmeter);
|
||||
signal_handler_disconnect(sh, "destroy",
|
||||
volmeter_source_destroyed, volmeter);
|
||||
obs_source_remove_audio_capture_callback(volmeter->source,
|
||||
obs_source_remove_audio_capture_callback(source,
|
||||
volmeter_source_data_received, volmeter);
|
||||
|
||||
volmeter->source = NULL;
|
||||
|
||||
exit:
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
}
|
||||
|
||||
void obs_volmeter_set_update_interval(obs_volmeter_t *volmeter,
|
||||
|
|
@ -732,8 +738,9 @@ void obs_volmeter_set_update_interval(obs_volmeter_t *volmeter,
|
|||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
volmeter->update_ms = ms;
|
||||
volmeter_update_audio_settings(volmeter);
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
volmeter_update_audio_settings(volmeter);
|
||||
}
|
||||
|
||||
unsigned int obs_volmeter_get_update_interval(obs_volmeter_t *volmeter)
|
||||
|
|
@ -755,8 +762,9 @@ void obs_volmeter_set_peak_hold(obs_volmeter_t *volmeter, const unsigned int ms)
|
|||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
volmeter->peakhold_ms = ms;
|
||||
volmeter_update_audio_settings(volmeter);
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
volmeter_update_audio_settings(volmeter);
|
||||
}
|
||||
|
||||
unsigned int obs_volmeter_get_peak_hold(obs_volmeter_t *volmeter)
|
||||
|
|
|
|||
|
|
@ -34,14 +34,14 @@
|
|||
*
|
||||
* Reset to zero each major version
|
||||
*/
|
||||
#define LIBOBS_API_MINOR_VER 14
|
||||
#define LIBOBS_API_MINOR_VER 15
|
||||
|
||||
/*
|
||||
* Increment if backward-compatible bug fix
|
||||
*
|
||||
* Reset to zero each major or minor version
|
||||
*/
|
||||
#define LIBOBS_API_PATCH_VER 2
|
||||
#define LIBOBS_API_PATCH_VER 4
|
||||
|
||||
#define MAKE_SEMANTIC_VERSION(major, minor, patch) \
|
||||
((major << 24) | \
|
||||
|
|
|
|||
|
|
@ -156,10 +156,13 @@ static inline obs_data_t *get_item_autoselect_obj(struct obs_data_item *item)
|
|||
|
||||
static inline obs_data_array_t *get_item_array(struct obs_data_item *item)
|
||||
{
|
||||
obs_data_array_t **array;
|
||||
|
||||
if (!item)
|
||||
return NULL;
|
||||
|
||||
return *(obs_data_array_t**)get_item_data(item);
|
||||
array = (obs_data_array_t**)get_item_data(item);
|
||||
return array ? *array : NULL;
|
||||
}
|
||||
|
||||
static inline obs_data_array_t *get_item_default_array(
|
||||
|
|
@ -1495,6 +1498,14 @@ enum obs_data_number_type obs_data_item_numtype(obs_data_item_t *item)
|
|||
return num->type;
|
||||
}
|
||||
|
||||
const char *obs_data_item_get_name(obs_data_item_t *item)
|
||||
{
|
||||
if (!item)
|
||||
return NULL;
|
||||
|
||||
return get_item_name(item);
|
||||
}
|
||||
|
||||
void obs_data_item_set_string(obs_data_item_t **item, const char *val)
|
||||
{
|
||||
obs_set_string(NULL, item, NULL, val, set_item);
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ EXPORT void obs_data_item_remove(obs_data_item_t **item);
|
|||
/* Gets Item type */
|
||||
EXPORT enum obs_data_type obs_data_item_gettype(obs_data_item_t *item);
|
||||
EXPORT enum obs_data_number_type obs_data_item_numtype(obs_data_item_t *item);
|
||||
EXPORT const char *obs_data_item_get_name(obs_data_item_t *item);
|
||||
|
||||
/* Item set functions */
|
||||
EXPORT void obs_data_item_set_string(obs_data_item_t **item, const char *val);
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ static struct obs_encoder *create_encoder(const char *id,
|
|||
&obs->data.encoders_mutex,
|
||||
&obs->data.first_encoder);
|
||||
|
||||
blog(LOG_INFO, "encoder '%s' (%s) created", name, id);
|
||||
blog(LOG_DEBUG, "encoder '%s' (%s) created", name, id);
|
||||
return encoder;
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +229,7 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder)
|
|||
da_free(encoder->outputs);
|
||||
pthread_mutex_unlock(&encoder->outputs_mutex);
|
||||
|
||||
blog(LOG_INFO, "encoder '%s' destroyed", encoder->context.name);
|
||||
blog(LOG_DEBUG, "encoder '%s' destroyed", encoder->context.name);
|
||||
|
||||
free_audio_buffers(encoder);
|
||||
|
||||
|
|
@ -809,6 +809,7 @@ static inline void do_encode(struct obs_encoder *encoder,
|
|||
* you do not want to use relative timestamps here */
|
||||
pkt.dts_usec = encoder->start_ts / 1000 +
|
||||
packet_dts_usec(&pkt) - encoder->offset_usec;
|
||||
pkt.sys_dts_usec = pkt.dts_usec;
|
||||
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ struct encoder_packet {
|
|||
/* DTS in microseconds */
|
||||
int64_t dts_usec;
|
||||
|
||||
/* System DTS in microseconds */
|
||||
int64_t sys_dts_usec;
|
||||
|
||||
/**
|
||||
* Packet priority
|
||||
*
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ struct obs_core_video {
|
|||
gs_effect_t *lanczos_effect;
|
||||
gs_effect_t *bilinear_lowres_effect;
|
||||
gs_effect_t *premultiplied_alpha_effect;
|
||||
gs_samplerstate_t *point_sampler;
|
||||
gs_stagesurf_t *mapped_surface;
|
||||
int cur_texture;
|
||||
|
||||
|
|
@ -781,20 +782,25 @@ struct obs_output {
|
|||
|
||||
bool received_video;
|
||||
bool received_audio;
|
||||
volatile bool data_active;
|
||||
volatile bool end_data_capture_thread_active;
|
||||
int64_t video_offset;
|
||||
int64_t audio_offsets[MAX_AUDIO_MIXES];
|
||||
int64_t highest_audio_ts;
|
||||
int64_t highest_video_ts;
|
||||
pthread_t end_data_capture_thread;
|
||||
os_event_t *stopping_event;
|
||||
pthread_mutex_t interleaved_mutex;
|
||||
DARRAY(struct encoder_packet) interleaved_packets;
|
||||
int stop_code;
|
||||
|
||||
int reconnect_retry_sec;
|
||||
int reconnect_retry_max;
|
||||
int reconnect_retries;
|
||||
int reconnect_retry_cur_sec;
|
||||
bool reconnecting;
|
||||
pthread_t reconnect_thread;
|
||||
os_event_t *reconnect_stop_event;
|
||||
volatile bool reconnecting;
|
||||
volatile bool reconnect_thread_active;
|
||||
|
||||
uint32_t starting_drawn_count;
|
||||
|
|
@ -804,8 +810,7 @@ struct obs_output {
|
|||
|
||||
int total_frames;
|
||||
|
||||
bool active;
|
||||
volatile bool stopped;
|
||||
volatile bool active;
|
||||
video_t *video;
|
||||
audio_t *audio;
|
||||
obs_encoder_t *video_encoder;
|
||||
|
|
@ -831,8 +836,8 @@ struct obs_output {
|
|||
uint32_t delay_flags;
|
||||
uint32_t delay_cur_flags;
|
||||
volatile long delay_restart_refs;
|
||||
bool delay_active;
|
||||
bool delay_capturing;
|
||||
volatile bool delay_active;
|
||||
volatile bool delay_capturing;
|
||||
};
|
||||
|
||||
static inline void do_output_signal(struct obs_output *output,
|
||||
|
|
@ -849,7 +854,8 @@ extern void obs_output_cleanup_delay(obs_output_t *output);
|
|||
extern bool obs_output_delay_start(obs_output_t *output);
|
||||
extern void obs_output_delay_stop(obs_output_t *output);
|
||||
extern bool obs_output_actual_start(obs_output_t *output);
|
||||
extern void obs_output_actual_stop(obs_output_t *output, bool force);
|
||||
extern void obs_output_actual_stop(obs_output_t *output, bool force,
|
||||
uint64_t ts);
|
||||
|
||||
extern const struct obs_output_info *find_output(const char *id);
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ int obs_open_module(obs_module_t **module, const char *path,
|
|||
if (!module || !path || !obs)
|
||||
return MODULE_ERROR;
|
||||
|
||||
blog(LOG_INFO, "---------------------------------");
|
||||
blog(LOG_DEBUG, "---------------------------------");
|
||||
|
||||
mod.module = os_dlopen(path);
|
||||
if (!mod.module) {
|
||||
|
|
@ -104,7 +104,7 @@ int obs_open_module(obs_module_t **module, const char *path,
|
|||
mod.next = obs->first_module;
|
||||
|
||||
if (mod.file) {
|
||||
blog(LOG_INFO, "Loading module: %s", mod.file);
|
||||
blog(LOG_DEBUG, "Loading module: %s", mod.file);
|
||||
}
|
||||
|
||||
*module = bmemdup(&mod, sizeof(mod));
|
||||
|
|
@ -138,6 +138,14 @@ bool obs_init_module(obs_module_t *module)
|
|||
return module->loaded;
|
||||
}
|
||||
|
||||
void obs_log_loaded_modules(void)
|
||||
{
|
||||
blog(LOG_INFO, " Loaded Modules:");
|
||||
|
||||
for (obs_module_t *mod = obs->first_module; !!mod; mod = mod->next)
|
||||
blog(LOG_INFO, " %s", mod->file);
|
||||
}
|
||||
|
||||
const char *obs_get_module_file_name(obs_module_t *module)
|
||||
{
|
||||
return module ? module->file : NULL;
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ MODULE_EXTERN obs_module_t *obs_current_module(void);
|
|||
/**
|
||||
* Returns the location to a module data file associated with the current
|
||||
* module. Free with bfree when complete. Equivalent to:
|
||||
* obs_find_module_file(obs_current_modile(), file);
|
||||
* obs_find_module_file(obs_current_module(), file);
|
||||
*/
|
||||
#define obs_module_file(file) obs_find_module_file(obs_current_module(), file)
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ MODULE_EXTERN obs_module_t *obs_current_module(void);
|
|||
* Returns the location to a module config file associated with the current
|
||||
* module. Free with bfree when complete. Will return NULL if configuration
|
||||
* directory is not set. Equivalent to:
|
||||
* obs_module_get_config_path(obs_current_modile(), file);
|
||||
* obs_module_get_config_path(obs_current_module(), file);
|
||||
*/
|
||||
#define obs_module_config_path(file) \
|
||||
obs_module_get_config_path(obs_current_module(), file)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,16 @@
|
|||
#include <inttypes.h>
|
||||
#include "obs-internal.h"
|
||||
|
||||
static inline bool delay_active(const struct obs_output *output)
|
||||
{
|
||||
return os_atomic_load_bool(&output->delay_active);
|
||||
}
|
||||
|
||||
static inline bool delay_capturing(const struct obs_output *output)
|
||||
{
|
||||
return os_atomic_load_bool(&output->delay_capturing);
|
||||
}
|
||||
|
||||
static inline void push_packet(struct obs_output *output,
|
||||
struct encoder_packet *packet, uint64_t t)
|
||||
{
|
||||
|
|
@ -37,7 +47,7 @@ static inline void process_delay_data(struct obs_output *output,
|
|||
{
|
||||
switch (dd->msg) {
|
||||
case DELAY_MSG_PACKET:
|
||||
if (!output->delay_active || !output->delay_capturing)
|
||||
if (!delay_active(output) || !delay_capturing(output))
|
||||
obs_free_encoder_packet(&dd->packet);
|
||||
else
|
||||
output->delay_callback(output, &dd->packet);
|
||||
|
|
@ -46,7 +56,7 @@ static inline void process_delay_data(struct obs_output *output,
|
|||
obs_output_actual_start(output);
|
||||
break;
|
||||
case DELAY_MSG_STOP:
|
||||
obs_output_actual_stop(output, false);
|
||||
obs_output_actual_stop(output, false, dd->ts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +73,7 @@ void obs_output_cleanup_delay(obs_output_t *output)
|
|||
}
|
||||
|
||||
output->active_delay_ns = 0;
|
||||
output->delay_restart_refs = 0;
|
||||
os_atomic_set_long(&output->delay_restart_refs, 0);
|
||||
}
|
||||
|
||||
static inline bool pop_packet(struct obs_output *output, uint64_t t)
|
||||
|
|
@ -129,7 +139,7 @@ bool obs_output_delay_start(obs_output_t *output)
|
|||
.ts = os_gettime_ns(),
|
||||
};
|
||||
|
||||
if (!output->delay_active) {
|
||||
if (!delay_active(output)) {
|
||||
bool can_begin = obs_output_can_begin_data_capture(output, 0);
|
||||
if (!can_begin)
|
||||
return false;
|
||||
|
|
@ -141,8 +151,9 @@ bool obs_output_delay_start(obs_output_t *output)
|
|||
circlebuf_push_back(&output->delay_data, &dd, sizeof(dd));
|
||||
pthread_mutex_unlock(&output->delay_mutex);
|
||||
|
||||
if (output->delay_active) {
|
||||
os_atomic_inc_long(&output->delay_restart_refs);
|
||||
os_atomic_inc_long(&output->delay_restart_refs);
|
||||
|
||||
if (delay_active(output)) {
|
||||
do_output_signal(output, "starting");
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,35 @@
|
|||
#include "obs.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
static inline void signal_stop(struct obs_output *output, int code);
|
||||
static inline bool active(const struct obs_output *output)
|
||||
{
|
||||
return os_atomic_load_bool(&output->active);
|
||||
}
|
||||
|
||||
static inline bool reconnecting(const struct obs_output *output)
|
||||
{
|
||||
return os_atomic_load_bool(&output->reconnecting);
|
||||
}
|
||||
|
||||
static inline bool stopping(const struct obs_output *output)
|
||||
{
|
||||
return os_event_try(output->stopping_event) == EAGAIN;
|
||||
}
|
||||
|
||||
static inline bool delay_active(const struct obs_output *output)
|
||||
{
|
||||
return os_atomic_load_bool(&output->delay_active);
|
||||
}
|
||||
|
||||
static inline bool delay_capturing(const struct obs_output *output)
|
||||
{
|
||||
return os_atomic_load_bool(&output->delay_capturing);
|
||||
}
|
||||
|
||||
static inline bool data_capture_ending(const struct obs_output *output)
|
||||
{
|
||||
return os_atomic_load_bool(&output->end_data_capture_thread_active);
|
||||
}
|
||||
|
||||
const struct obs_output_info *find_output(const char *id)
|
||||
{
|
||||
|
|
@ -76,9 +104,13 @@ obs_output_t *obs_output_create(const char *id, const char *name,
|
|||
goto fail;
|
||||
if (pthread_mutex_init(&output->delay_mutex, NULL) != 0)
|
||||
goto fail;
|
||||
if (os_event_init(&output->stopping_event, OS_EVENT_TYPE_MANUAL) != 0)
|
||||
goto fail;
|
||||
if (!init_output_handlers(output, name, settings, hotkey_data))
|
||||
goto fail;
|
||||
|
||||
os_event_signal(output->stopping_event);
|
||||
|
||||
if (!info) {
|
||||
blog(LOG_ERROR, "Output ID '%s' not found", id);
|
||||
|
||||
|
|
@ -114,7 +146,7 @@ obs_output_t *obs_output_create(const char *id, const char *name,
|
|||
&obs->data.outputs_mutex,
|
||||
&obs->data.first_output);
|
||||
|
||||
blog(LOG_INFO, "output '%s' (%s) created", name, id);
|
||||
blog(LOG_DEBUG, "output '%s' (%s) created", name, id);
|
||||
return output;
|
||||
|
||||
fail:
|
||||
|
|
@ -134,18 +166,22 @@ void obs_output_destroy(obs_output_t *output)
|
|||
if (output) {
|
||||
obs_context_data_remove(&output->context);
|
||||
|
||||
blog(LOG_INFO, "output '%s' destroyed", output->context.name);
|
||||
blog(LOG_DEBUG, "output '%s' destroyed", output->context.name);
|
||||
|
||||
if (output->valid && active(output))
|
||||
obs_output_actual_stop(output, true, 0);
|
||||
|
||||
os_event_wait(output->stopping_event);
|
||||
if (data_capture_ending(output))
|
||||
pthread_join(output->end_data_capture_thread, NULL);
|
||||
|
||||
if (output->valid && output->active)
|
||||
obs_output_actual_stop(output, true);
|
||||
if (output->service)
|
||||
output->service->output = NULL;
|
||||
|
||||
free_packets(output);
|
||||
|
||||
if (output->context.data)
|
||||
output->info.destroy(output->context.data);
|
||||
|
||||
free_packets(output);
|
||||
|
||||
if (output->video_encoder) {
|
||||
obs_encoder_remove_output(output->video_encoder,
|
||||
output);
|
||||
|
|
@ -159,6 +195,7 @@ void obs_output_destroy(obs_output_t *output)
|
|||
}
|
||||
}
|
||||
|
||||
os_event_destroy(output->stopping_event);
|
||||
pthread_mutex_destroy(&output->interleaved_mutex);
|
||||
pthread_mutex_destroy(&output->delay_mutex);
|
||||
os_event_destroy(output->reconnect_stop_event);
|
||||
|
|
@ -180,7 +217,8 @@ bool obs_output_actual_start(obs_output_t *output)
|
|||
{
|
||||
bool success = false;
|
||||
|
||||
output->stopped = false;
|
||||
os_event_wait(output->stopping_event);
|
||||
output->stop_code = 0;
|
||||
|
||||
if (output->context.data)
|
||||
success = output->info.start(output->context.data);
|
||||
|
|
@ -194,7 +232,7 @@ bool obs_output_actual_start(obs_output_t *output)
|
|||
output->starting_lagged_count = obs->video.lagged_frames;
|
||||
}
|
||||
|
||||
if (output->delay_restart_refs)
|
||||
if (os_atomic_load_long(&output->delay_restart_refs))
|
||||
os_atomic_dec_long(&output->delay_restart_refs);
|
||||
|
||||
return success;
|
||||
|
|
@ -222,6 +260,11 @@ bool obs_output_start(obs_output_t *output)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool data_active(struct obs_output *output)
|
||||
{
|
||||
return os_atomic_load_bool(&output->data_active);
|
||||
}
|
||||
|
||||
static void log_frame_info(struct obs_output *output)
|
||||
{
|
||||
struct obs_core_video *video = &obs->video;
|
||||
|
|
@ -272,27 +315,47 @@ static void log_frame_info(struct obs_output *output)
|
|||
dropped, percentage_dropped);
|
||||
}
|
||||
|
||||
void obs_output_actual_stop(obs_output_t *output, bool force)
|
||||
static inline void signal_stop(struct obs_output *output);
|
||||
|
||||
void obs_output_actual_stop(obs_output_t *output, bool force, uint64_t ts)
|
||||
{
|
||||
output->stopped = true;
|
||||
bool call_stop = true;
|
||||
bool was_reconnecting = false;
|
||||
|
||||
os_event_signal(output->reconnect_stop_event);
|
||||
if (output->reconnect_thread_active)
|
||||
pthread_join(output->reconnect_thread, NULL);
|
||||
if (stopping(output))
|
||||
return;
|
||||
os_event_reset(output->stopping_event);
|
||||
|
||||
if (output->context.data)
|
||||
output->info.stop(output->context.data);
|
||||
|
||||
if (output->video)
|
||||
log_frame_info(output);
|
||||
|
||||
if (output->delay_active && (force || !output->delay_restart_refs)) {
|
||||
output->delay_active = false;
|
||||
obs_output_end_data_capture(output);
|
||||
was_reconnecting = reconnecting(output) && !delay_active(output);
|
||||
if (reconnecting(output)) {
|
||||
os_event_signal(output->reconnect_stop_event);
|
||||
if (output->reconnect_thread_active)
|
||||
pthread_join(output->reconnect_thread, NULL);
|
||||
}
|
||||
|
||||
if (force || !output->delay_active)
|
||||
signal_stop(output, OBS_OUTPUT_SUCCESS);
|
||||
if (force) {
|
||||
if (delay_active(output)) {
|
||||
call_stop = delay_capturing(output);
|
||||
os_atomic_set_bool(&output->delay_active, false);
|
||||
os_atomic_set_bool(&output->delay_capturing, false);
|
||||
output->stop_code = OBS_OUTPUT_SUCCESS;
|
||||
obs_output_end_data_capture(output);
|
||||
os_event_signal(output->stopping_event);
|
||||
} else {
|
||||
call_stop = data_active(output);
|
||||
}
|
||||
} else {
|
||||
call_stop = data_active(output);
|
||||
}
|
||||
|
||||
if (output->context.data && call_stop) {
|
||||
output->info.stop(output->context.data, ts);
|
||||
|
||||
} else if (was_reconnecting) {
|
||||
output->stop_code = OBS_OUTPUT_SUCCESS;
|
||||
signal_stop(output);
|
||||
os_event_signal(output->stopping_event);
|
||||
}
|
||||
}
|
||||
|
||||
void obs_output_stop(obs_output_t *output)
|
||||
|
|
@ -302,26 +365,40 @@ void obs_output_stop(obs_output_t *output)
|
|||
return;
|
||||
if (!output->context.data)
|
||||
return;
|
||||
if (!active(output) && !reconnecting(output))
|
||||
return;
|
||||
if (reconnecting(output)) {
|
||||
obs_output_force_stop(output);
|
||||
return;
|
||||
}
|
||||
|
||||
encoded = (output->info.flags & OBS_OUTPUT_ENCODED) != 0;
|
||||
|
||||
if (encoded && output->active_delay_ns) {
|
||||
obs_output_delay_stop(output);
|
||||
} else {
|
||||
obs_output_actual_stop(output, false);
|
||||
|
||||
} else if (!stopping(output)) {
|
||||
do_output_signal(output, "stopping");
|
||||
obs_output_actual_stop(output, false, os_gettime_ns());
|
||||
}
|
||||
}
|
||||
|
||||
void obs_output_force_stop(obs_output_t *output)
|
||||
{
|
||||
obs_output_actual_stop(output, true);
|
||||
if (!obs_output_valid(output, "obs_output_force_stop"))
|
||||
return;
|
||||
|
||||
if (!stopping(output)) {
|
||||
output->stop_code = 0;
|
||||
do_output_signal(output, "stopping");
|
||||
obs_output_actual_stop(output, true, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool obs_output_active(const obs_output_t *output)
|
||||
{
|
||||
return (output != NULL) ?
|
||||
(output->active || output->reconnecting) : false;
|
||||
(active(output) || reconnecting(output)) : false;
|
||||
}
|
||||
|
||||
static inline obs_data_t *get_defaults(const struct obs_output_info *info)
|
||||
|
|
@ -442,7 +519,7 @@ void obs_output_set_mixer(obs_output_t *output, size_t mixer_idx)
|
|||
if (!obs_output_valid(output, "obs_output_set_mixer"))
|
||||
return;
|
||||
|
||||
if (!output->active)
|
||||
if (!active(output))
|
||||
output->mixer_idx = mixer_idx;
|
||||
}
|
||||
|
||||
|
|
@ -547,7 +624,7 @@ void obs_output_set_service(obs_output_t *output, obs_service_t *service)
|
|||
{
|
||||
if (!obs_output_valid(output, "obs_output_set_service"))
|
||||
return;
|
||||
if (output->active || !service || service->active)
|
||||
if (active(output) || !service || service->active)
|
||||
return;
|
||||
|
||||
if (service->output)
|
||||
|
|
@ -580,7 +657,7 @@ uint64_t obs_output_get_total_bytes(const obs_output_t *output)
|
|||
if (!output->info.get_total_bytes)
|
||||
return 0;
|
||||
|
||||
if (output->delay_active && !output->delay_capturing)
|
||||
if (delay_active(output) && !delay_capturing(output))
|
||||
return 0;
|
||||
|
||||
return output->info.get_total_bytes(output->context.data);
|
||||
|
|
@ -610,7 +687,7 @@ void obs_output_set_preferred_size(obs_output_t *output, uint32_t width,
|
|||
if ((output->info.flags & OBS_OUTPUT_VIDEO) == 0)
|
||||
return;
|
||||
|
||||
if (output->active) {
|
||||
if (active(output)) {
|
||||
blog(LOG_WARNING, "output '%s': Cannot set the preferred "
|
||||
"resolution while the output is active",
|
||||
obs_output_get_name(output));
|
||||
|
|
@ -879,8 +956,7 @@ static inline void send_interleaved(struct obs_output *output)
|
|||
output->total_frames++;
|
||||
|
||||
da_erase(output->interleaved_packets, 0);
|
||||
if (!output->stopped)
|
||||
output->info.encoded_packet(output->context.data, &out);
|
||||
output->info.encoded_packet(output->context.data, &out);
|
||||
obs_free_encoder_packet(&out);
|
||||
}
|
||||
|
||||
|
|
@ -940,7 +1016,7 @@ static int prune_premature_packets(struct obs_output *output)
|
|||
int max_idx;
|
||||
int64_t duration_usec;
|
||||
int64_t max_diff = 0;
|
||||
int64_t diff;
|
||||
int64_t diff = 0;
|
||||
|
||||
video_idx = find_first_packet_type_idx(output, OBS_ENCODER_VIDEO, 0);
|
||||
if (video_idx == -1) {
|
||||
|
|
@ -1212,6 +1288,9 @@ static void interleave_packets(void *data, struct encoder_packet *packet)
|
|||
struct encoder_packet out;
|
||||
bool was_started;
|
||||
|
||||
if (!active(output))
|
||||
return;
|
||||
|
||||
if (packet->type == OBS_ENCODER_AUDIO)
|
||||
packet->track_idx = get_track_index(output, packet);
|
||||
|
||||
|
|
@ -1223,6 +1302,9 @@ static void interleave_packets(void *data, struct encoder_packet *packet)
|
|||
!packet->keyframe) {
|
||||
discard_unused_audio_packets(output, packet->dts_usec);
|
||||
pthread_mutex_unlock(&output->interleaved_mutex);
|
||||
|
||||
if (output->active_delay_ns)
|
||||
obs_free_encoder_packet(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1263,22 +1345,24 @@ static void default_encoded_callback(void *param, struct encoder_packet *packet)
|
|||
{
|
||||
struct obs_output *output = param;
|
||||
|
||||
if (packet->type == OBS_ENCODER_AUDIO)
|
||||
packet->track_idx = get_track_index(output, packet);
|
||||
if (data_active(output)) {
|
||||
if (packet->type == OBS_ENCODER_AUDIO)
|
||||
packet->track_idx = get_track_index(output, packet);
|
||||
|
||||
if (!output->stopped)
|
||||
output->info.encoded_packet(output->context.data, packet);
|
||||
|
||||
if (packet->type == OBS_ENCODER_VIDEO)
|
||||
output->total_frames++;
|
||||
}
|
||||
|
||||
if (output->active_delay_ns)
|
||||
obs_free_encoder_packet(packet);
|
||||
|
||||
if (packet->type == OBS_ENCODER_VIDEO)
|
||||
output->total_frames++;
|
||||
}
|
||||
|
||||
static void default_raw_video_callback(void *param, struct video_data *frame)
|
||||
{
|
||||
struct obs_output *output = param;
|
||||
if (!output->stopped)
|
||||
if (data_active(output))
|
||||
output->info.raw_video(output->context.data, frame);
|
||||
output->total_frames++;
|
||||
}
|
||||
|
|
@ -1287,8 +1371,10 @@ static void default_raw_audio_callback(void *param, size_t mix_idx,
|
|||
struct audio_data *frames)
|
||||
{
|
||||
struct obs_output *output = param;
|
||||
if (!output->stopped)
|
||||
output->info.raw_audio(output->context.data, frames);
|
||||
if (!data_active(output))
|
||||
return;
|
||||
|
||||
output->info.raw_audio(output->context.data, frames);
|
||||
|
||||
UNUSED_PARAMETER(mix_idx);
|
||||
}
|
||||
|
|
@ -1342,7 +1428,7 @@ static void hook_data_capture(struct obs_output *output, bool encoded,
|
|||
output->delay_cur_flags = output->delay_flags;
|
||||
output->delay_callback = encoded_callback;
|
||||
encoded_callback = process_delay;
|
||||
output->delay_active = true;
|
||||
os_atomic_set_bool(&output->delay_active, true);
|
||||
|
||||
blog(LOG_INFO, "Output '%s': %"PRIu32" second delay "
|
||||
"active, preserve on disconnect is %s",
|
||||
|
|
@ -1390,13 +1476,13 @@ static inline void signal_reconnect_success(struct obs_output *output)
|
|||
do_output_signal(output, "reconnect_success");
|
||||
}
|
||||
|
||||
static inline void signal_stop(struct obs_output *output, int code)
|
||||
static inline void signal_stop(struct obs_output *output)
|
||||
{
|
||||
struct calldata params;
|
||||
uint8_t stack[128];
|
||||
|
||||
calldata_init_fixed(¶ms, stack, sizeof(stack));
|
||||
calldata_set_int(¶ms, "code", code);
|
||||
calldata_set_int(¶ms, "code", output->stop_code);
|
||||
calldata_set_ptr(¶ms, "output", output);
|
||||
signal_handler_signal(output->context.signals, "stop", ¶ms);
|
||||
}
|
||||
|
|
@ -1424,8 +1510,11 @@ bool obs_output_can_begin_data_capture(const obs_output_t *output,
|
|||
if (!obs_output_valid(output, "obs_output_can_begin_data_capture"))
|
||||
return false;
|
||||
|
||||
if (output->delay_active) return true;
|
||||
if (output->active) return false;
|
||||
if (delay_active(output)) return true;
|
||||
if (active(output)) return false;
|
||||
|
||||
if (data_capture_ending(output))
|
||||
pthread_join(output->end_data_capture_thread, NULL);
|
||||
|
||||
convert_flags(output, flags, &encoded, &has_video, &has_audio,
|
||||
&has_service);
|
||||
|
|
@ -1492,7 +1581,7 @@ bool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags)
|
|||
if (!obs_output_valid(output, "obs_output_initialize_encoders"))
|
||||
return false;
|
||||
|
||||
if (output->active) return output->delay_active;
|
||||
if (active(output)) return delay_active(output);
|
||||
|
||||
convert_flags(output, flags, &encoded, &has_video, &has_audio,
|
||||
&has_service);
|
||||
|
|
@ -1514,17 +1603,17 @@ bool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags)
|
|||
|
||||
static bool begin_delayed_capture(obs_output_t *output)
|
||||
{
|
||||
if (output->delay_capturing)
|
||||
if (delay_capturing(output))
|
||||
return false;
|
||||
|
||||
pthread_mutex_lock(&output->interleaved_mutex);
|
||||
reset_packet_data(output);
|
||||
output->delay_capturing = true;
|
||||
os_atomic_set_bool(&output->delay_capturing, true);
|
||||
pthread_mutex_unlock(&output->interleaved_mutex);
|
||||
|
||||
if (output->reconnecting) {
|
||||
if (reconnecting(output)) {
|
||||
signal_reconnect_success(output);
|
||||
output->reconnecting = false;
|
||||
os_atomic_set_bool(&output->reconnecting, false);
|
||||
} else {
|
||||
signal_start(output);
|
||||
}
|
||||
|
|
@ -1539,8 +1628,8 @@ bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags)
|
|||
if (!obs_output_valid(output, "obs_output_begin_data_capture"))
|
||||
return false;
|
||||
|
||||
if (output->delay_active) return begin_delayed_capture(output);
|
||||
if (output->active) return false;
|
||||
if (delay_active(output)) return begin_delayed_capture(output);
|
||||
if (active(output)) return false;
|
||||
|
||||
output->total_frames = 0;
|
||||
|
||||
|
|
@ -1551,19 +1640,20 @@ bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags)
|
|||
has_service))
|
||||
return false;
|
||||
|
||||
os_atomic_set_bool(&output->data_active, true);
|
||||
hook_data_capture(output, encoded, has_video, has_audio);
|
||||
|
||||
if (has_service)
|
||||
obs_service_activate(output->service);
|
||||
|
||||
do_output_signal(output, "activate");
|
||||
output->active = true;
|
||||
os_atomic_set_bool(&output->active, true);
|
||||
|
||||
if (output->reconnecting) {
|
||||
if (reconnecting(output)) {
|
||||
signal_reconnect_success(output);
|
||||
output->reconnecting = false;
|
||||
os_atomic_set_bool(&output->reconnecting, false);
|
||||
|
||||
} else if (output->delay_active) {
|
||||
} else if (delay_active(output)) {
|
||||
do_output_signal(output, "starting");
|
||||
|
||||
} else {
|
||||
|
|
@ -1584,20 +1674,11 @@ static inline void stop_audio_encoders(obs_output_t *output,
|
|||
}
|
||||
}
|
||||
|
||||
void obs_output_end_data_capture(obs_output_t *output)
|
||||
static void *end_data_capture_thread(void *data)
|
||||
{
|
||||
bool encoded, has_video, has_audio, has_service;
|
||||
encoded_callback_t encoded_callback;
|
||||
|
||||
if (!obs_output_valid(output, "obs_output_end_data_capture"))
|
||||
return;
|
||||
|
||||
if (output->delay_active) {
|
||||
output->delay_capturing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!output->active) return;
|
||||
obs_output_t *output = data;
|
||||
|
||||
convert_flags(output, 0, &encoded, &has_video, &has_audio,
|
||||
&has_service);
|
||||
|
|
@ -1631,7 +1712,66 @@ void obs_output_end_data_capture(obs_output_t *output)
|
|||
obs_output_cleanup_delay(output);
|
||||
|
||||
do_output_signal(output, "deactivate");
|
||||
output->active = false;
|
||||
os_atomic_set_bool(&output->active, false);
|
||||
os_event_signal(output->stopping_event);
|
||||
os_atomic_set_bool(&output->end_data_capture_thread_active, false);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void obs_output_end_data_capture_internal(obs_output_t *output,
|
||||
bool signal)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!obs_output_valid(output, "obs_output_end_data_capture"))
|
||||
return;
|
||||
|
||||
if (!active(output) || !data_active(output)) {
|
||||
if (signal) {
|
||||
signal_stop(output);
|
||||
output->stop_code = OBS_OUTPUT_SUCCESS;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (delay_active(output)) {
|
||||
os_atomic_set_bool(&output->delay_capturing, false);
|
||||
|
||||
if (!os_atomic_load_long(&output->delay_restart_refs)) {
|
||||
os_atomic_set_bool(&output->delay_active, false);
|
||||
} else {
|
||||
os_event_signal(output->stopping_event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
os_atomic_set_bool(&output->data_active, false);
|
||||
|
||||
if (output->video)
|
||||
log_frame_info(output);
|
||||
|
||||
if (data_capture_ending(output))
|
||||
pthread_join(output->end_data_capture_thread, NULL);
|
||||
|
||||
os_atomic_set_bool(&output->end_data_capture_thread_active, true);
|
||||
ret = pthread_create(&output->end_data_capture_thread, NULL,
|
||||
end_data_capture_thread, output);
|
||||
if (ret != 0) {
|
||||
blog(LOG_WARNING, "Failed to create end_data_capture_thread "
|
||||
"for output '%s'!", output->context.name);
|
||||
end_data_capture_thread(output);
|
||||
}
|
||||
|
||||
if (signal) {
|
||||
signal_stop(output);
|
||||
output->stop_code = OBS_OUTPUT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
void obs_output_end_data_capture(obs_output_t *output)
|
||||
{
|
||||
obs_output_end_data_capture_internal(output, true);
|
||||
}
|
||||
|
||||
static void *reconnect_thread(void *param)
|
||||
|
|
@ -1647,48 +1787,51 @@ static void *reconnect_thread(void *param)
|
|||
if (os_event_try(output->reconnect_stop_event) == EAGAIN)
|
||||
pthread_detach(output->reconnect_thread);
|
||||
else
|
||||
output->reconnecting = false;
|
||||
os_atomic_set_bool(&output->reconnecting, false);
|
||||
|
||||
output->reconnect_thread_active = false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MAX_RETRY_SEC (15 * 60)
|
||||
|
||||
static void output_reconnect(struct obs_output *output)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!output->reconnecting) {
|
||||
if (!reconnecting(output)) {
|
||||
output->reconnect_retry_cur_sec = output->reconnect_retry_sec;
|
||||
output->reconnect_retries = 0;
|
||||
}
|
||||
|
||||
if (output->reconnect_retries >= output->reconnect_retry_max) {
|
||||
output->reconnecting = false;
|
||||
if (output->delay_active) {
|
||||
output->delay_active = false;
|
||||
obs_output_end_data_capture(output);
|
||||
}
|
||||
signal_stop(output, OBS_OUTPUT_DISCONNECTED);
|
||||
output->stop_code = OBS_OUTPUT_DISCONNECTED;
|
||||
os_atomic_set_bool(&output->reconnecting, false);
|
||||
if (delay_active(output))
|
||||
os_atomic_set_bool(&output->delay_active, false);
|
||||
obs_output_end_data_capture(output);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!output->reconnecting) {
|
||||
output->reconnecting = true;
|
||||
if (!reconnecting(output)) {
|
||||
os_atomic_set_bool(&output->reconnecting, true);
|
||||
os_event_reset(output->reconnect_stop_event);
|
||||
}
|
||||
|
||||
if (output->reconnect_retries) {
|
||||
output->reconnect_retry_cur_sec *= 2;
|
||||
if (output->reconnect_retry_cur_sec > MAX_RETRY_SEC)
|
||||
output->reconnect_retry_cur_sec = MAX_RETRY_SEC;
|
||||
}
|
||||
|
||||
output->reconnect_retries++;
|
||||
|
||||
output->stop_code = OBS_OUTPUT_DISCONNECTED;
|
||||
ret = pthread_create(&output->reconnect_thread, NULL,
|
||||
&reconnect_thread, output);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "Failed to create reconnect thread");
|
||||
output->reconnecting = false;
|
||||
signal_stop(output, OBS_OUTPUT_DISCONNECTED);
|
||||
os_atomic_set_bool(&output->reconnecting, false);
|
||||
} else {
|
||||
blog(LOG_INFO, "Output '%s': Reconnecting in %d seconds..",
|
||||
output->context.name,
|
||||
|
|
@ -1698,22 +1841,30 @@ static void output_reconnect(struct obs_output *output)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool can_reconnect(const obs_output_t *output, int code)
|
||||
{
|
||||
bool reconnect_active = output->reconnect_retry_max != 0;
|
||||
|
||||
return (reconnecting(output) && code != OBS_OUTPUT_SUCCESS) ||
|
||||
(reconnect_active && code == OBS_OUTPUT_DISCONNECTED);
|
||||
}
|
||||
|
||||
void obs_output_signal_stop(obs_output_t *output, int code)
|
||||
{
|
||||
if (!obs_output_valid(output, "obs_output_signal_stop"))
|
||||
return;
|
||||
|
||||
obs_output_end_data_capture(output);
|
||||
output->stop_code = code;
|
||||
|
||||
if ((output->reconnecting && code != OBS_OUTPUT_SUCCESS) ||
|
||||
code == OBS_OUTPUT_DISCONNECTED) {
|
||||
if (can_reconnect(output, code)) {
|
||||
if (delay_active(output))
|
||||
os_atomic_inc_long(&output->delay_restart_refs);
|
||||
obs_output_end_data_capture_internal(output, false);
|
||||
output_reconnect(output);
|
||||
} else {
|
||||
if (output->delay_active) {
|
||||
output->delay_active = false;
|
||||
obs_output_end_data_capture(output);
|
||||
}
|
||||
signal_stop(output, code);
|
||||
if (delay_active(output))
|
||||
os_atomic_set_bool(&output->delay_active, false);
|
||||
obs_output_end_data_capture(output);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ struct obs_output_info {
|
|||
void (*destroy)(void *data);
|
||||
|
||||
bool (*start)(void *data);
|
||||
void (*stop)(void *data);
|
||||
void (*stop)(void *data, uint64_t ts);
|
||||
|
||||
void (*raw_video)(void *data, struct video_data *frame);
|
||||
void (*raw_audio)(void *data, struct audio_data *frames);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ struct list_data {
|
|||
};
|
||||
|
||||
struct editable_list_data {
|
||||
bool allow_files;
|
||||
enum obs_editable_list_type type;
|
||||
char *filter;
|
||||
char *default_path;
|
||||
};
|
||||
|
|
@ -145,6 +145,7 @@ struct obs_properties;
|
|||
struct obs_property {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
const char *long_desc;
|
||||
enum obs_property_type type;
|
||||
bool visible;
|
||||
bool enabled;
|
||||
|
|
@ -503,7 +504,7 @@ obs_property_t *obs_properties_add_font(obs_properties_t *props,
|
|||
|
||||
obs_property_t *obs_properties_add_editable_list(obs_properties_t *props,
|
||||
const char *name, const char *desc,
|
||||
bool allow_files, const char *filter,
|
||||
enum obs_editable_list_type type, const char *filter,
|
||||
const char *default_path)
|
||||
{
|
||||
if (!props || has_prop(props, name)) return NULL;
|
||||
|
|
@ -511,7 +512,7 @@ obs_property_t *obs_properties_add_editable_list(obs_properties_t *props,
|
|||
OBS_PROPERTY_EDITABLE_LIST);
|
||||
|
||||
struct editable_list_data *data = get_property_data(p);
|
||||
data->allow_files = allow_files;
|
||||
data->type = type;
|
||||
data->filter = bstrdup(filter);
|
||||
data->default_path = bstrdup(default_path);
|
||||
return p;
|
||||
|
|
@ -584,7 +585,8 @@ bool obs_property_button_clicked(obs_property_t *p, void *obj)
|
|||
struct button_data *data = get_type_data(p,
|
||||
OBS_PROPERTY_BUTTON);
|
||||
if (data && data->callback)
|
||||
return data->callback(p->parent, p, context->data);
|
||||
return data->callback(p->parent, p,
|
||||
(context ? context->data : NULL));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -605,6 +607,11 @@ void obs_property_set_description(obs_property_t *p, const char *description)
|
|||
if (p) p->desc = description;
|
||||
}
|
||||
|
||||
void obs_property_set_long_description(obs_property_t *p, const char *long_desc)
|
||||
{
|
||||
if (p) p->long_desc = long_desc;
|
||||
}
|
||||
|
||||
const char *obs_property_name(obs_property_t *p)
|
||||
{
|
||||
return p ? p->name : NULL;
|
||||
|
|
@ -615,6 +622,11 @@ const char *obs_property_description(obs_property_t *p)
|
|||
return p ? p->desc : NULL;
|
||||
}
|
||||
|
||||
const char *obs_property_long_description(obs_property_t *p)
|
||||
{
|
||||
return p ? p->long_desc : NULL;
|
||||
}
|
||||
|
||||
enum obs_property_type obs_property_get_type(obs_property_t *p)
|
||||
{
|
||||
return p ? p->type : OBS_PROPERTY_INVALID;
|
||||
|
|
@ -863,11 +875,11 @@ double obs_property_list_item_float(obs_property_t *p, size_t idx)
|
|||
data->items.array[idx].d : 0.0;
|
||||
}
|
||||
|
||||
bool obs_property_editable_list_allow_files(obs_property_t *p)
|
||||
enum obs_editable_list_type obs_property_editable_list_type(obs_property_t *p)
|
||||
{
|
||||
struct editable_list_data *data = get_type_data(p,
|
||||
OBS_PROPERTY_EDITABLE_LIST);
|
||||
return data ? data->allow_files : false;
|
||||
return data ? data->type : OBS_EDITABLE_LIST_TYPE_STRINGS;
|
||||
}
|
||||
|
||||
const char *obs_property_editable_list_filter(obs_property_t *p)
|
||||
|
|
|
|||
|
|
@ -70,6 +70,12 @@ enum obs_combo_type {
|
|||
OBS_COMBO_TYPE_LIST,
|
||||
};
|
||||
|
||||
enum obs_editable_list_type {
|
||||
OBS_EDITABLE_LIST_TYPE_STRINGS,
|
||||
OBS_EDITABLE_LIST_TYPE_FILES,
|
||||
OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS
|
||||
};
|
||||
|
||||
enum obs_path_type {
|
||||
OBS_PATH_FILE,
|
||||
OBS_PATH_FILE_SAVE,
|
||||
|
|
@ -202,7 +208,7 @@ EXPORT obs_property_t *obs_properties_add_font(obs_properties_t *props,
|
|||
|
||||
EXPORT obs_property_t *obs_properties_add_editable_list(obs_properties_t *props,
|
||||
const char *name, const char *description,
|
||||
bool allow_files, const char *filter,
|
||||
enum obs_editable_list_type type, const char *filter,
|
||||
const char *default_path);
|
||||
|
||||
EXPORT obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props,
|
||||
|
|
@ -229,9 +235,12 @@ EXPORT void obs_property_set_enabled(obs_property_t *p, bool enabled);
|
|||
|
||||
EXPORT void obs_property_set_description(obs_property_t *p,
|
||||
const char *description);
|
||||
EXPORT void obs_property_set_long_description(obs_property_t *p,
|
||||
const char *long_description);
|
||||
|
||||
EXPORT const char * obs_property_name(obs_property_t *p);
|
||||
EXPORT const char * obs_property_description(obs_property_t *p);
|
||||
EXPORT const char * obs_property_long_description(obs_property_t *p);
|
||||
EXPORT enum obs_property_type obs_property_get_type(obs_property_t *p);
|
||||
EXPORT bool obs_property_enabled(obs_property_t *p);
|
||||
EXPORT bool obs_property_visible(obs_property_t *p);
|
||||
|
|
@ -281,7 +290,7 @@ EXPORT const char *obs_property_list_item_string(obs_property_t *p, size_t idx);
|
|||
EXPORT long long obs_property_list_item_int(obs_property_t *p, size_t idx);
|
||||
EXPORT double obs_property_list_item_float(obs_property_t *p, size_t idx);
|
||||
|
||||
EXPORT bool obs_property_editable_list_allow_files(obs_property_t *p);
|
||||
EXPORT enum obs_editable_list_type obs_property_editable_list_type(obs_property_t *p);
|
||||
EXPORT const char *obs_property_editable_list_filter(obs_property_t *p);
|
||||
EXPORT const char *obs_property_editable_list_default_path(obs_property_t *p);
|
||||
|
||||
|
|
|
|||
|
|
@ -337,6 +337,8 @@ static void update_item_transform(struct obs_scene_item *item)
|
|||
matrix4_translate3f(&item->draw_transform, &item->draw_transform,
|
||||
item->pos.x, item->pos.y, 0.0f);
|
||||
|
||||
item->output_scale = scale;
|
||||
|
||||
/* ----------------------- */
|
||||
|
||||
if (item->bounds_type != OBS_BOUNDS_NONE) {
|
||||
|
|
@ -383,15 +385,76 @@ static inline bool crop_enabled(const struct obs_sceneitem_crop *crop)
|
|||
return crop->left || crop->right || crop->top || crop->bottom;
|
||||
}
|
||||
|
||||
static inline bool scale_filter_enabled(const struct obs_scene_item *item)
|
||||
{
|
||||
return item->scale_filter != OBS_SCALE_DISABLE;
|
||||
}
|
||||
|
||||
static inline bool item_is_scene(const struct obs_scene_item *item)
|
||||
{
|
||||
return item->source && item->source->info.type == OBS_SOURCE_TYPE_SCENE;
|
||||
}
|
||||
|
||||
static inline bool item_texture_enabled(const struct obs_scene_item *item)
|
||||
{
|
||||
return crop_enabled(&item->crop) || scale_filter_enabled(item) ||
|
||||
item_is_scene(item);
|
||||
}
|
||||
|
||||
static void render_item_texture(struct obs_scene_item *item)
|
||||
{
|
||||
gs_texture_t *tex = gs_texrender_get_texture(item->item_render);
|
||||
gs_effect_t *effect = obs->video.default_effect;
|
||||
enum obs_scale_type type = item->scale_filter;
|
||||
uint32_t cx = gs_texture_get_width(tex);
|
||||
uint32_t cy = gs_texture_get_height(tex);
|
||||
|
||||
if (type != OBS_SCALE_DISABLE) {
|
||||
if (type == OBS_SCALE_POINT) {
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(
|
||||
effect, "image");
|
||||
gs_effect_set_next_sampler(image,
|
||||
obs->video.point_sampler);
|
||||
|
||||
} else if (!close_float(item->output_scale.x, 1.0f, EPSILON) ||
|
||||
!close_float(item->output_scale.y, 1.0f, EPSILON)) {
|
||||
gs_eparam_t *scale_param;
|
||||
|
||||
if (item->output_scale.x < 0.5f ||
|
||||
item->output_scale.y < 0.5f) {
|
||||
effect = obs->video.bilinear_lowres_effect;
|
||||
} else if (type == OBS_SCALE_BICUBIC) {
|
||||
effect = obs->video.bicubic_effect;
|
||||
} else if (type == OBS_SCALE_LANCZOS) {
|
||||
effect = obs->video.lanczos_effect;
|
||||
}
|
||||
|
||||
scale_param = gs_effect_get_param_by_name(effect,
|
||||
"base_dimension_i");
|
||||
if (scale_param) {
|
||||
struct vec2 base_res_i = {
|
||||
1.0f / (float)cx,
|
||||
1.0f / (float)cy
|
||||
};
|
||||
|
||||
gs_effect_set_vec2(scale_param, &base_res_i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (gs_effect_loop(effect, "Draw"))
|
||||
obs_source_draw(tex, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
static inline void render_item(struct obs_scene_item *item)
|
||||
{
|
||||
if (item->crop_render) {
|
||||
if (item->item_render) {
|
||||
uint32_t width = obs_source_get_width(item->source);
|
||||
uint32_t height = obs_source_get_height(item->source);
|
||||
uint32_t cx = calc_cx(item, width);
|
||||
uint32_t cy = calc_cy(item, height);
|
||||
|
||||
if (cx && cy && gs_texrender_begin(item->crop_render, cx, cy)) {
|
||||
if (cx && cy && gs_texrender_begin(item->item_render, cx, cy)) {
|
||||
float cx_scale = (float)width / (float)cx;
|
||||
float cy_scale = (float)height / (float)cy;
|
||||
struct vec4 clear_color;
|
||||
|
|
@ -408,17 +471,14 @@ static inline void render_item(struct obs_scene_item *item)
|
|||
0.0f);
|
||||
|
||||
obs_source_video_render(item->source);
|
||||
gs_texrender_end(item->crop_render);
|
||||
gs_texrender_end(item->item_render);
|
||||
}
|
||||
}
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_mul(&item->draw_transform);
|
||||
if (item->crop_render) {
|
||||
gs_texture_t *tex = gs_texrender_get_texture(item->crop_render);
|
||||
|
||||
while (gs_effect_loop(obs->video.default_effect, "Draw"))
|
||||
obs_source_draw(tex, 0, 0, 0, 0, 0);
|
||||
if (item->item_render) {
|
||||
render_item_texture(item);
|
||||
} else {
|
||||
obs_source_video_render(item->source);
|
||||
}
|
||||
|
|
@ -433,8 +493,8 @@ static void scene_video_tick(void *data, float seconds)
|
|||
video_lock(scene);
|
||||
item = scene->first_item;
|
||||
while (item) {
|
||||
if (item->crop_render)
|
||||
gs_texrender_reset(item->crop_render);
|
||||
if (item->item_render)
|
||||
gs_texrender_reset(item->item_render);
|
||||
item = item->next;
|
||||
}
|
||||
video_unlock(scene);
|
||||
|
|
@ -511,6 +571,7 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
|||
{
|
||||
const char *name = obs_data_get_string(item_data, "name");
|
||||
obs_source_t *source = obs_get_source_by_name(name);
|
||||
const char *scale_filter_str;
|
||||
struct obs_scene_item *item;
|
||||
bool visible;
|
||||
|
||||
|
|
@ -553,15 +614,29 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
|||
item->crop.right = (uint32_t)obs_data_get_int(item_data, "crop_right");
|
||||
item->crop.bottom = (uint32_t)obs_data_get_int(item_data, "crop_bottom");
|
||||
|
||||
if (item->crop_render && !crop_enabled(&item->crop)) {
|
||||
scale_filter_str = obs_data_get_string(item_data, "scale_filter");
|
||||
item->scale_filter = OBS_SCALE_DISABLE;
|
||||
|
||||
if (scale_filter_str) {
|
||||
if (astrcmpi(scale_filter_str, "point") == 0)
|
||||
item->scale_filter = OBS_SCALE_POINT;
|
||||
else if (astrcmpi(scale_filter_str, "bilinear") == 0)
|
||||
item->scale_filter = OBS_SCALE_BILINEAR;
|
||||
else if (astrcmpi(scale_filter_str, "bicubic") == 0)
|
||||
item->scale_filter = OBS_SCALE_BICUBIC;
|
||||
else if (astrcmpi(scale_filter_str, "lanczos") == 0)
|
||||
item->scale_filter = OBS_SCALE_LANCZOS;
|
||||
}
|
||||
|
||||
if (item->item_render && !item_texture_enabled(item)) {
|
||||
obs_enter_graphics();
|
||||
gs_texrender_destroy(item->crop_render);
|
||||
item->crop_render = NULL;
|
||||
gs_texrender_destroy(item->item_render);
|
||||
item->item_render = NULL;
|
||||
obs_leave_graphics();
|
||||
|
||||
} else if (!item->crop_render && crop_enabled(&item->crop)) {
|
||||
} else if (!item->item_render && item_texture_enabled(item)) {
|
||||
obs_enter_graphics();
|
||||
item->crop_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||
item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||
obs_leave_graphics();
|
||||
}
|
||||
|
||||
|
|
@ -595,6 +670,7 @@ static void scene_save_item(obs_data_array_t *array,
|
|||
{
|
||||
obs_data_t *item_data = obs_data_create();
|
||||
const char *name = obs_source_get_name(item->source);
|
||||
const char *scale_filter;
|
||||
|
||||
obs_data_set_string(item_data, "name", name);
|
||||
obs_data_set_bool (item_data, "visible", item->user_visible);
|
||||
|
|
@ -610,6 +686,19 @@ static void scene_save_item(obs_data_array_t *array,
|
|||
obs_data_set_int (item_data, "crop_right", (int)item->crop.right);
|
||||
obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom);
|
||||
|
||||
if (item->scale_filter == OBS_SCALE_POINT)
|
||||
scale_filter = "point";
|
||||
else if (item->scale_filter == OBS_SCALE_BILINEAR)
|
||||
scale_filter = "bilinear";
|
||||
else if (item->scale_filter == OBS_SCALE_BICUBIC)
|
||||
scale_filter = "bicubic";
|
||||
else if (item->scale_filter == OBS_SCALE_LANCZOS)
|
||||
scale_filter = "lanczos";
|
||||
else
|
||||
scale_filter = "disable";
|
||||
|
||||
obs_data_set_string(item_data, "scale_filter", scale_filter);
|
||||
|
||||
obs_data_array_push_back(array, item_data);
|
||||
obs_data_release(item_data);
|
||||
}
|
||||
|
|
@ -1197,6 +1286,12 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
|
|||
item->visible = true;
|
||||
}
|
||||
|
||||
if (item_texture_enabled(item)) {
|
||||
obs_enter_graphics();
|
||||
item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||
obs_leave_graphics();
|
||||
}
|
||||
|
||||
full_lock(scene);
|
||||
|
||||
last = scene->first_item;
|
||||
|
|
@ -1227,9 +1322,9 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
|
|||
static void obs_sceneitem_destroy(obs_sceneitem_t *item)
|
||||
{
|
||||
if (item) {
|
||||
if (item->crop_render) {
|
||||
if (item->item_render) {
|
||||
obs_enter_graphics();
|
||||
gs_texrender_destroy(item->crop_render);
|
||||
gs_texrender_destroy(item->item_render);
|
||||
obs_leave_graphics();
|
||||
}
|
||||
obs_hotkey_pair_unregister(item->toggle_visibility);
|
||||
|
|
@ -1305,7 +1400,7 @@ void obs_sceneitem_select(obs_sceneitem_t *item, bool select)
|
|||
uint8_t stack[128];
|
||||
const char *command = select ? "item_select" : "item_deselect";
|
||||
|
||||
if (!item || item->selected == select)
|
||||
if (!item || item->selected == select || !item->parent)
|
||||
return;
|
||||
|
||||
item->selected = select;
|
||||
|
|
@ -1695,7 +1790,7 @@ static inline bool crop_equal(const struct obs_sceneitem_crop *crop1,
|
|||
void obs_sceneitem_set_crop(obs_sceneitem_t *item,
|
||||
const struct obs_sceneitem_crop *crop)
|
||||
{
|
||||
bool now_enabled;
|
||||
bool item_tex_now_enabled;
|
||||
|
||||
if (!obs_ptr_valid(item, "obs_sceneitem_set_crop"))
|
||||
return;
|
||||
|
|
@ -1704,16 +1799,17 @@ void obs_sceneitem_set_crop(obs_sceneitem_t *item,
|
|||
if (crop_equal(crop, &item->crop))
|
||||
return;
|
||||
|
||||
now_enabled = crop_enabled(crop);
|
||||
item_tex_now_enabled = crop_enabled(crop) ||
|
||||
scale_filter_enabled(item) || item_is_scene(item);
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
if (!now_enabled) {
|
||||
gs_texrender_destroy(item->crop_render);
|
||||
item->crop_render = NULL;
|
||||
if (!item_tex_now_enabled) {
|
||||
gs_texrender_destroy(item->item_render);
|
||||
item->item_render = NULL;
|
||||
|
||||
} else if (!item->crop_render) {
|
||||
item->crop_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||
} else if (!item->item_render) {
|
||||
item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||
}
|
||||
|
||||
memcpy(&item->crop, crop, sizeof(*crop));
|
||||
|
|
@ -1738,6 +1834,36 @@ void obs_sceneitem_get_crop(const obs_sceneitem_t *item,
|
|||
memcpy(crop, &item->crop, sizeof(*crop));
|
||||
}
|
||||
|
||||
void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item,
|
||||
enum obs_scale_type filter)
|
||||
{
|
||||
if (!obs_ptr_valid(item, "obs_sceneitem_set_scale_filter"))
|
||||
return;
|
||||
|
||||
item->scale_filter = filter;
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
if (!item_texture_enabled(item)) {
|
||||
gs_texrender_destroy(item->item_render);
|
||||
item->item_render = NULL;
|
||||
|
||||
} else if (!item->item_render) {
|
||||
item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||
}
|
||||
|
||||
obs_leave_graphics();
|
||||
|
||||
update_item_transform(item);
|
||||
}
|
||||
|
||||
enum obs_scale_type obs_sceneitem_get_scale_filter(
|
||||
obs_sceneitem_t *item)
|
||||
{
|
||||
return obs_ptr_valid(item, "obs_sceneitem_get_scale_filter") ?
|
||||
item->scale_filter : OBS_SCALE_DISABLE;
|
||||
}
|
||||
|
||||
void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)
|
||||
{
|
||||
if (!obs_ptr_valid(item, "obs_sceneitem_defer_update_begin"))
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ struct obs_scene_item {
|
|||
bool visible;
|
||||
bool selected;
|
||||
|
||||
gs_texrender_t *crop_render;
|
||||
gs_texrender_t *item_render;
|
||||
struct obs_sceneitem_crop crop;
|
||||
|
||||
struct vec2 pos;
|
||||
|
|
@ -53,6 +53,9 @@ struct obs_scene_item {
|
|||
uint32_t last_width;
|
||||
uint32_t last_height;
|
||||
|
||||
struct vec2 output_scale;
|
||||
enum obs_scale_type scale_filter;
|
||||
|
||||
struct matrix4 box_transform;
|
||||
struct matrix4 draw_transform;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,8 +66,7 @@ static obs_service_t *obs_service_create_internal(const char *id,
|
|||
&obs->data.services_mutex,
|
||||
&obs->data.first_service);
|
||||
|
||||
blog(private ? LOG_DEBUG : LOG_INFO, "service '%s' (%s) created",
|
||||
name, id);
|
||||
blog(LOG_DEBUG, "service '%s' (%s) created", name, id);
|
||||
return service;
|
||||
}
|
||||
|
||||
|
|
@ -92,8 +91,7 @@ static void actually_destroy_service(struct obs_service *service)
|
|||
if (service->output)
|
||||
service->output->service = NULL;
|
||||
|
||||
blog(service->context.private ? LOG_DEBUG : LOG_INFO,
|
||||
"service '%s' destroyed", service->context.name);
|
||||
blog(LOG_DEBUG, "service '%s' destroyed", service->context.name);
|
||||
|
||||
obs_context_data_free(&service->context);
|
||||
if (service->owns_info_id)
|
||||
|
|
|
|||
|
|
@ -99,14 +99,26 @@ void obs_transition_clear(obs_source_t *transition)
|
|||
|
||||
void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy);
|
||||
|
||||
static inline uint32_t get_cx(obs_source_t *tr)
|
||||
{
|
||||
return tr->transition_cx ?
|
||||
tr->transition_cx : tr->transition_actual_cx;
|
||||
}
|
||||
|
||||
static inline uint32_t get_cy(obs_source_t *tr)
|
||||
{
|
||||
return tr->transition_cy ?
|
||||
tr->transition_cy : tr->transition_actual_cy;
|
||||
}
|
||||
|
||||
static void recalculate_transition_matrix(obs_source_t *tr, size_t idx)
|
||||
{
|
||||
obs_source_t *child;
|
||||
struct matrix4 mat;
|
||||
struct vec2 pos;
|
||||
struct vec2 scale;
|
||||
float tr_cx = (float)tr->transition_actual_cx;
|
||||
float tr_cy = (float)tr->transition_actual_cy;
|
||||
float tr_cx = (float)get_cx(tr);
|
||||
float tr_cy = (float)get_cy(tr);
|
||||
float source_cx;
|
||||
float source_cy;
|
||||
float tr_aspect = tr_cx / tr_cy;
|
||||
|
|
@ -368,6 +380,9 @@ bool obs_transition_start(obs_source_t *transition,
|
|||
obs_source_dosignal(transition, "source_transition_start",
|
||||
"transition_start");
|
||||
|
||||
recalculate_transition_size(transition);
|
||||
recalculate_transition_matrices(transition);
|
||||
|
||||
/* TODO: Add mode */
|
||||
UNUSED_PARAMETER(mode);
|
||||
return true;
|
||||
|
|
@ -596,8 +611,8 @@ void obs_transition_enum_sources(obs_source_t *transition,
|
|||
static inline void render_child(obs_source_t *transition,
|
||||
obs_source_t *child, size_t idx)
|
||||
{
|
||||
uint32_t cx = transition->transition_actual_cx;
|
||||
uint32_t cy = transition->transition_actual_cy;
|
||||
uint32_t cx = get_cx(transition);
|
||||
uint32_t cy = get_cy(transition);
|
||||
struct vec4 blank;
|
||||
if (!child)
|
||||
return;
|
||||
|
|
@ -605,6 +620,7 @@ static inline void render_child(obs_source_t *transition,
|
|||
if (gs_texrender_begin(transition->transition_texrender[idx], cx, cy)) {
|
||||
vec4_zero(&blank);
|
||||
gs_clear(GS_CLEAR_COLOR, &blank, 0.0f, 0);
|
||||
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_mul(&transition->transition_matrices[idx]);
|
||||
|
|
@ -633,6 +649,7 @@ void obs_transition_video_render(obs_source_t *transition,
|
|||
obs_transition_video_render_callback_t callback)
|
||||
{
|
||||
struct transition_state state;
|
||||
struct matrix4 matrices[2];
|
||||
bool locked = false;
|
||||
bool stopped = false;
|
||||
bool video_stopped = false;
|
||||
|
|
@ -655,6 +672,8 @@ void obs_transition_video_render(obs_source_t *transition,
|
|||
}
|
||||
}
|
||||
copy_transition_state(transition, &state);
|
||||
matrices[0] = transition->transition_matrices[0];
|
||||
matrices[1] = transition->transition_matrices[1];
|
||||
|
||||
unlock_transition(transition);
|
||||
|
||||
|
|
@ -663,6 +682,8 @@ void obs_transition_video_render(obs_source_t *transition,
|
|||
|
||||
if (state.transitioning_video && locked && callback) {
|
||||
gs_texture_t *tex[2];
|
||||
uint32_t cx;
|
||||
uint32_t cy;
|
||||
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (state.s[i]) {
|
||||
|
|
@ -675,17 +696,26 @@ void obs_transition_video_render(obs_source_t *transition,
|
|||
}
|
||||
}
|
||||
|
||||
callback(transition->context.data, tex[0], tex[1], t,
|
||||
transition->transition_actual_cx,
|
||||
transition->transition_actual_cy);
|
||||
cx = get_cx(transition);
|
||||
cy = get_cy(transition);
|
||||
if (cx && cy)
|
||||
callback(transition->context.data, tex[0], tex[1], t,
|
||||
cx, cy);
|
||||
|
||||
} else if (state.transitioning_audio) {
|
||||
if (state.s[1])
|
||||
if (state.s[1]) {
|
||||
gs_matrix_push();
|
||||
gs_matrix_mul(&matrices[1]);
|
||||
obs_source_video_render(state.s[1]);
|
||||
|
||||
gs_matrix_pop();
|
||||
}
|
||||
} else {
|
||||
if (state.s[0])
|
||||
if (state.s[0]) {
|
||||
gs_matrix_push();
|
||||
gs_matrix_mul(&matrices[0]);
|
||||
obs_source_video_render(state.s[0]);
|
||||
gs_matrix_pop();
|
||||
}
|
||||
}
|
||||
|
||||
if (locked)
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ static obs_source_t *obs_source_create_internal(const char *id,
|
|||
if (!source->context.data)
|
||||
blog(LOG_ERROR, "Failed to create source '%s'!", name);
|
||||
|
||||
blog(private ? LOG_DEBUG : LOG_INFO, "%ssource '%s' (%s) created",
|
||||
blog(LOG_DEBUG, "%ssource '%s' (%s) created",
|
||||
private ? "private " : "", name, id);
|
||||
obs_source_dosignal(source, "source_create", NULL);
|
||||
|
||||
|
|
@ -405,6 +405,14 @@ obs_source_t *obs_source_duplicate(obs_source_t *source,
|
|||
return source;
|
||||
}
|
||||
|
||||
if (source->info.type == OBS_SOURCE_TYPE_SCENE) {
|
||||
obs_scene_t *scene = obs_scene_from_source(source);
|
||||
obs_scene_t *new_scene = obs_scene_duplicate(scene, new_name,
|
||||
create_private ? OBS_SCENE_DUP_PRIVATE_COPY :
|
||||
OBS_SCENE_DUP_COPY);
|
||||
return obs_scene_get_source(new_scene);
|
||||
}
|
||||
|
||||
settings = obs_data_create();
|
||||
obs_data_apply(settings, source->context.settings);
|
||||
|
||||
|
|
@ -482,8 +490,7 @@ void obs_source_destroy(struct obs_source *source)
|
|||
|
||||
obs_context_data_remove(&source->context);
|
||||
|
||||
blog(source->context.private ? LOG_DEBUG : LOG_INFO,
|
||||
"%ssource '%s' destroyed",
|
||||
blog(LOG_DEBUG, "%ssource '%s' destroyed",
|
||||
source->context.private ? "private " : "",
|
||||
source->context.name);
|
||||
|
||||
|
|
@ -882,15 +889,12 @@ void obs_source_activate(obs_source_t *source, enum view_type type)
|
|||
if (!obs_source_valid(source, "obs_source_activate"))
|
||||
return;
|
||||
|
||||
if (os_atomic_inc_long(&source->show_refs) == 1) {
|
||||
obs_source_enum_active_tree(source, show_tree, NULL);
|
||||
}
|
||||
os_atomic_inc_long(&source->show_refs);
|
||||
obs_source_enum_active_tree(source, show_tree, NULL);
|
||||
|
||||
if (type == MAIN_VIEW) {
|
||||
if (os_atomic_inc_long(&source->activate_refs) == 1) {
|
||||
obs_source_enum_active_tree(source, activate_tree,
|
||||
NULL);
|
||||
}
|
||||
os_atomic_inc_long(&source->activate_refs);
|
||||
obs_source_enum_active_tree(source, activate_tree, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -899,12 +903,14 @@ void obs_source_deactivate(obs_source_t *source, enum view_type type)
|
|||
if (!obs_source_valid(source, "obs_source_deactivate"))
|
||||
return;
|
||||
|
||||
if (os_atomic_dec_long(&source->show_refs) == 0) {
|
||||
if (os_atomic_load_long(&source->show_refs) > 0) {
|
||||
os_atomic_dec_long(&source->show_refs);
|
||||
obs_source_enum_active_tree(source, hide_tree, NULL);
|
||||
}
|
||||
|
||||
if (type == MAIN_VIEW) {
|
||||
if (os_atomic_dec_long(&source->activate_refs) == 0) {
|
||||
if (os_atomic_load_long(&source->activate_refs) > 0) {
|
||||
os_atomic_dec_long(&source->activate_refs);
|
||||
obs_source_enum_active_tree(source, deactivate_tree,
|
||||
NULL);
|
||||
}
|
||||
|
|
@ -1268,7 +1274,7 @@ static inline bool set_planar420_sizes(struct obs_source *source,
|
|||
size += size/2;
|
||||
|
||||
source->async_convert_width = frame->width;
|
||||
source->async_convert_height = (size / frame->width + 1) & 0xFFFFFFFE;
|
||||
source->async_convert_height = size / frame->width;
|
||||
source->async_texture_format = GS_R8;
|
||||
source->async_plane_offset[0] = (int)(frame->data[1] - frame->data[0]);
|
||||
source->async_plane_offset[1] = (int)(frame->data[2] - frame->data[0]);
|
||||
|
|
@ -1282,7 +1288,7 @@ static inline bool set_nv12_sizes(struct obs_source *source,
|
|||
size += size/2;
|
||||
|
||||
source->async_convert_width = frame->width;
|
||||
source->async_convert_height = (size / frame->width + 1) & 0xFFFFFFFE;
|
||||
source->async_convert_height = size / frame->width;
|
||||
source->async_texture_format = GS_R8;
|
||||
source->async_plane_offset[0] = (int)(frame->data[1] - frame->data[0]);
|
||||
return true;
|
||||
|
|
@ -1857,8 +1863,7 @@ void obs_source_filter_add(obs_source_t *source, obs_source_t *filter)
|
|||
signal_handler_signal(source->context.signals, "filter_add", &cd);
|
||||
|
||||
if (source && filter)
|
||||
blog(source->context.private ? LOG_DEBUG : LOG_INFO,
|
||||
"- filter '%s' (%s) added to source '%s'",
|
||||
blog(LOG_DEBUG, "- filter '%s' (%s) added to source '%s'",
|
||||
filter->context.name, filter->info.id,
|
||||
source->context.name);
|
||||
}
|
||||
|
|
@ -1894,8 +1899,7 @@ static bool obs_source_filter_remove_refless(obs_source_t *source,
|
|||
signal_handler_signal(source->context.signals, "filter_remove", &cd);
|
||||
|
||||
if (source && filter)
|
||||
blog(source->context.private ? LOG_DEBUG : LOG_INFO,
|
||||
"- filter '%s' (%s) removed from source '%s'",
|
||||
blog(LOG_DEBUG, "- filter '%s' (%s) removed from source '%s'",
|
||||
filter->context.name, filter->info.id,
|
||||
source->context.name);
|
||||
|
||||
|
|
@ -2599,7 +2603,8 @@ void obs_source_set_name(obs_source_t *source, const char *name)
|
|||
if (!obs_source_valid(source, "obs_source_set_name"))
|
||||
return;
|
||||
|
||||
if (!name || !*name || strcmp(name, source->context.name) != 0) {
|
||||
if (!name || !*name || !source->context.name ||
|
||||
strcmp(name, source->context.name) != 0) {
|
||||
struct calldata data;
|
||||
char *prev_name = bstrdup(source->context.name);
|
||||
obs_context_data_setname(&source->context, name);
|
||||
|
|
@ -2677,7 +2682,7 @@ bool obs_source_process_filter_begin(obs_source_t *filter,
|
|||
enum obs_allow_direct_render allow_direct)
|
||||
{
|
||||
obs_source_t *target, *parent;
|
||||
uint32_t target_flags, parent_flags;
|
||||
uint32_t parent_flags;
|
||||
int cx, cy;
|
||||
|
||||
if (!obs_ptr_valid(filter, "obs_source_process_filter_begin"))
|
||||
|
|
@ -2697,7 +2702,6 @@ bool obs_source_process_filter_begin(obs_source_t *filter,
|
|||
return false;
|
||||
}
|
||||
|
||||
target_flags = target->info.output_flags;
|
||||
parent_flags = parent->info.output_flags;
|
||||
cx = get_base_width(target);
|
||||
cy = get_base_height(target);
|
||||
|
|
@ -2750,7 +2754,7 @@ void obs_source_process_filter_tech_end(obs_source_t *filter, gs_effect_t *effec
|
|||
{
|
||||
obs_source_t *target, *parent;
|
||||
gs_texture_t *texture;
|
||||
uint32_t target_flags, parent_flags;
|
||||
uint32_t parent_flags;
|
||||
|
||||
if (!filter) return;
|
||||
|
||||
|
|
@ -2760,7 +2764,6 @@ void obs_source_process_filter_tech_end(obs_source_t *filter, gs_effect_t *effec
|
|||
if (!target || !parent)
|
||||
return;
|
||||
|
||||
target_flags = target->info.output_flags;
|
||||
parent_flags = parent->info.output_flags;
|
||||
|
||||
const char *tech = tech_name ? tech_name : "Draw";
|
||||
|
|
@ -2779,14 +2782,13 @@ void obs_source_process_filter_end(obs_source_t *filter, gs_effect_t *effect,
|
|||
{
|
||||
obs_source_t *target, *parent;
|
||||
gs_texture_t *texture;
|
||||
uint32_t target_flags, parent_flags;
|
||||
uint32_t parent_flags;
|
||||
|
||||
if (!obs_ptr_valid(filter, "obs_source_process_filter_end"))
|
||||
return;
|
||||
|
||||
target = obs_filter_get_target(filter);
|
||||
parent = obs_filter_get_parent(filter);
|
||||
target_flags = target->info.output_flags;
|
||||
parent_flags = parent->info.output_flags;
|
||||
|
||||
if (can_bypass(target, parent, parent_flags, filter->allow_direct)) {
|
||||
|
|
@ -2910,7 +2912,11 @@ static void enum_source_tree_callback(obs_source_t *parent, obs_source_t *child,
|
|||
void *param)
|
||||
{
|
||||
struct source_enum_data *data = param;
|
||||
bool is_transition = child->info.type == OBS_SOURCE_TYPE_TRANSITION;
|
||||
|
||||
if (is_transition)
|
||||
obs_transition_enum_sources(child,
|
||||
enum_source_tree_callback, param);
|
||||
if (child->info.enum_active_sources) {
|
||||
if (child->context.data) {
|
||||
child->info.enum_active_sources(child->context.data,
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ static inline gs_effect_t *get_scale_effect_internal(
|
|||
switch (video->scale_type) {
|
||||
case OBS_SCALE_BILINEAR: return video->default_effect;
|
||||
case OBS_SCALE_LANCZOS: return video->lanczos_effect;
|
||||
case OBS_SCALE_BICUBIC:;
|
||||
case OBS_SCALE_BICUBIC:
|
||||
default:;
|
||||
}
|
||||
|
||||
return video->bicubic_effect;
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ static void log_available_memory(void)
|
|||
#ifdef _WIN64
|
||||
const char *note = "";
|
||||
#else
|
||||
const char *note = " (NOTE: 4 gigs max is normal for 32bit programs)";
|
||||
const char *note = " (NOTE: 2 or 4 gigs max is normal for 32bit programs)";
|
||||
#endif
|
||||
|
||||
blog(LOG_INFO, "Physical Memory: %luMB Total, %luMB Free%s",
|
||||
|
|
@ -181,6 +181,25 @@ static void log_windows_version(void)
|
|||
ver.major, ver.minor, ver.build, ver.revis);
|
||||
}
|
||||
|
||||
static void log_admin_status(void)
|
||||
{
|
||||
SID_IDENTIFIER_AUTHORITY auth = SECURITY_NT_AUTHORITY;
|
||||
PSID admin_group;
|
||||
BOOL success;
|
||||
|
||||
success = AllocateAndInitializeSid(&auth, 2,
|
||||
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
|
||||
0, 0, 0, 0, 0, 0, &admin_group);
|
||||
if (success) {
|
||||
if (!CheckTokenMembership(NULL, admin_group, &success))
|
||||
success = false;
|
||||
FreeSid(admin_group);
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "Running as administrator: %s",
|
||||
success ? "true" : "false");
|
||||
}
|
||||
|
||||
typedef HRESULT (WINAPI *dwm_is_composition_enabled_t)(BOOL*);
|
||||
|
||||
static void log_aero(void)
|
||||
|
|
@ -219,6 +238,7 @@ void log_system_info(void)
|
|||
log_processor_cores();
|
||||
log_available_memory();
|
||||
log_windows_version();
|
||||
log_admin_status();
|
||||
log_aero();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ static int obs_init_graphics(struct obs_video_info *ovi)
|
|||
struct obs_core_video *video = &obs->video;
|
||||
uint8_t transparent_tex_data[2*2*4] = {0};
|
||||
const uint8_t *transparent_tex = transparent_tex_data;
|
||||
struct gs_sampler_info point_sampler = {0};
|
||||
bool success = true;
|
||||
int errorcode;
|
||||
|
||||
|
|
@ -296,6 +297,8 @@ static int obs_init_graphics(struct obs_video_info *ovi)
|
|||
NULL);
|
||||
bfree(filename);
|
||||
|
||||
video->point_sampler = gs_samplerstate_create(&point_sampler);
|
||||
|
||||
obs->video.transparent_texture = gs_texture_create(2, 2, GS_RGBA, 1,
|
||||
&transparent_tex, 0);
|
||||
|
||||
|
|
@ -315,6 +318,8 @@ static int obs_init_graphics(struct obs_video_info *ovi)
|
|||
success = false;
|
||||
if (!video->transparent_texture)
|
||||
success = false;
|
||||
if (!video->point_sampler)
|
||||
success = false;
|
||||
|
||||
gs_leave_context();
|
||||
return success ? OBS_VIDEO_SUCCESS : OBS_VIDEO_FAIL;
|
||||
|
|
@ -459,6 +464,8 @@ static void obs_free_graphics(void)
|
|||
|
||||
gs_texture_destroy(video->transparent_texture);
|
||||
|
||||
gs_samplerstate_destroy(video->point_sampler);
|
||||
|
||||
gs_effect_destroy(video->default_effect);
|
||||
gs_effect_destroy(video->default_rect_effect);
|
||||
gs_effect_destroy(video->opaque_effect);
|
||||
|
|
|
|||
10
libobs/obs.h
10
libobs/obs.h
|
|
@ -112,6 +112,8 @@ enum obs_allow_direct_render {
|
|||
};
|
||||
|
||||
enum obs_scale_type {
|
||||
OBS_SCALE_DISABLE,
|
||||
OBS_SCALE_POINT,
|
||||
OBS_SCALE_BICUBIC,
|
||||
OBS_SCALE_BILINEAR,
|
||||
OBS_SCALE_LANCZOS
|
||||
|
|
@ -338,6 +340,9 @@ EXPORT int obs_open_module(obs_module_t **module, const char *path,
|
|||
*/
|
||||
EXPORT bool obs_init_module(obs_module_t *module);
|
||||
|
||||
/** Logs loaded modules */
|
||||
EXPORT void obs_log_loaded_modules(void);
|
||||
|
||||
/** Returns the module file name */
|
||||
EXPORT const char *obs_get_module_file_name(obs_module_t *module);
|
||||
|
||||
|
|
@ -1253,6 +1258,11 @@ EXPORT void obs_sceneitem_set_crop(obs_sceneitem_t *item,
|
|||
EXPORT void obs_sceneitem_get_crop(const obs_sceneitem_t *item,
|
||||
struct obs_sceneitem_crop *crop);
|
||||
|
||||
EXPORT void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item,
|
||||
enum obs_scale_type filter);
|
||||
EXPORT enum obs_scale_type obs_sceneitem_get_scale_filter(
|
||||
obs_sceneitem_t *item);
|
||||
|
||||
EXPORT void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item);
|
||||
EXPORT void obs_sceneitem_defer_update_end(obs_sceneitem_t *item);
|
||||
|
||||
|
|
|
|||
|
|
@ -264,6 +264,15 @@ static inline int cf_next_name(struct cf_parser *p, char **dst,
|
|||
return cf_get_name(p, dst, name, goto_token);
|
||||
}
|
||||
|
||||
static inline int cf_next_token_copy(struct cf_parser *p, char **dst)
|
||||
{
|
||||
if (!cf_next_valid_token(p))
|
||||
return PARSE_EOF;
|
||||
|
||||
cf_copy_token(p, dst);
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
static inline int cf_get_name_ref(struct cf_parser *p, struct strref *dst,
|
||||
const char *name, const char *goto_token)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -237,9 +237,11 @@ static inline void dstr_copy_dstr(struct dstr *dst, const struct dstr *src)
|
|||
if (dst->array)
|
||||
dstr_free(dst);
|
||||
|
||||
dstr_ensure_capacity(dst, src->len + 1);
|
||||
memcpy(dst->array, src->array, src->len + 1);
|
||||
dst->len = src->len;
|
||||
if (src->len) {
|
||||
dstr_ensure_capacity(dst, src->len + 1);
|
||||
memcpy(dst->array, src->array, src->len + 1);
|
||||
dst->len = src->len;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dstr_reserve(struct dstr *dst, const size_t capacity)
|
||||
|
|
|
|||
|
|
@ -69,11 +69,12 @@ uint64_t os_gettime_ns(void)
|
|||
return f();
|
||||
}
|
||||
|
||||
/* gets the location ~/Library/Application Support/[name] */
|
||||
int os_get_config_path(char *dst, size_t size, const char *name)
|
||||
/* gets the location [domain mask]/Library/Application Support/[name] */
|
||||
static int os_get_path_internal(char *dst, size_t size, const char *name,
|
||||
NSSearchPathDomainMask domainMask)
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(
|
||||
NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSApplicationSupportDirectory, domainMask, YES);
|
||||
|
||||
if([paths count] == 0)
|
||||
bcrash("Could not get home directory (platform-cocoa)");
|
||||
|
|
@ -87,10 +88,11 @@ int os_get_config_path(char *dst, size_t size, const char *name)
|
|||
return snprintf(dst, size, "%s/%s", base_path, name);
|
||||
}
|
||||
|
||||
char *os_get_config_path_ptr(const char *name)
|
||||
static char *os_get_path_ptr_internal(const char *name,
|
||||
NSSearchPathDomainMask domainMask)
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(
|
||||
NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSApplicationSupportDirectory, domainMask, YES);
|
||||
|
||||
if([paths count] == 0)
|
||||
bcrash("Could not get home directory (platform-cocoa)");
|
||||
|
|
@ -113,6 +115,26 @@ char *os_get_config_path_ptr(const char *name)
|
|||
return path.array;
|
||||
}
|
||||
|
||||
int os_get_config_path(char *dst, size_t size, const char *name)
|
||||
{
|
||||
return os_get_path_internal(dst, size, name, NSUserDomainMask);
|
||||
}
|
||||
|
||||
char *os_get_config_path_ptr(const char *name)
|
||||
{
|
||||
return os_get_path_ptr_internal(name, NSUserDomainMask);
|
||||
}
|
||||
|
||||
int os_get_program_data_path(char *dst, size_t size, const char *name)
|
||||
{
|
||||
return os_get_path_internal(dst, size, name, NSLocalDomainMask);
|
||||
}
|
||||
|
||||
char *os_get_program_data_path_ptr(const char *name)
|
||||
{
|
||||
return os_get_path_ptr_internal(name, NSLocalDomainMask);
|
||||
}
|
||||
|
||||
struct os_cpu_usage_info {
|
||||
int64_t last_cpu_time;
|
||||
int64_t last_sys_time;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,11 @@ void *os_dlopen(const char *path)
|
|||
return NULL;
|
||||
|
||||
dstr_init_copy(&dylib_name, path);
|
||||
#ifdef __APPLE__
|
||||
if (!dstr_find(&dylib_name, ".so") && !dstr_find(&dylib_name, ".dylib"))
|
||||
#else
|
||||
if (!dstr_find(&dylib_name, ".so"))
|
||||
#endif
|
||||
dstr_cat(&dylib_name, ".so");
|
||||
|
||||
void *res = dlopen(dylib_name.array, RTLD_LAZY);
|
||||
|
|
@ -68,7 +72,8 @@ void *os_dlsym(void *module, const char *func)
|
|||
|
||||
void os_dlclose(void *module)
|
||||
{
|
||||
dlclose(module);
|
||||
if (module)
|
||||
dlclose(module);
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
|
|
@ -236,6 +241,20 @@ char *os_get_config_path_ptr(const char *name)
|
|||
#endif
|
||||
}
|
||||
|
||||
int os_get_program_data_path(char *dst, size_t size, const char *name)
|
||||
{
|
||||
return snprintf(dst, size, "/usr/local/share/%s", !!name ? name : "");
|
||||
}
|
||||
|
||||
char *os_get_program_data_path_ptr(const char *name)
|
||||
{
|
||||
size_t len = snprintf(NULL, 0, "/usr/local/share/%s", !!name ? name : "");
|
||||
char *str = bmalloc(len + 1);
|
||||
snprintf(str, len + 1, "/usr/local/share/%s", !!name ? name : "");
|
||||
str[len] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool os_file_exists(const char *path)
|
||||
|
|
|
|||
|
|
@ -208,12 +208,13 @@ uint64_t os_gettime_ns(void)
|
|||
return (uint64_t)time_val;
|
||||
}
|
||||
|
||||
/* returns %appdata%\[name] on windows */
|
||||
int os_get_config_path(char *dst, size_t size, const char *name)
|
||||
/* returns [folder]\[name] on windows */
|
||||
static int os_get_path_internal(char *dst, size_t size, const char *name,
|
||||
int folder)
|
||||
{
|
||||
wchar_t path_utf16[MAX_PATH];
|
||||
|
||||
SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
|
||||
SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT,
|
||||
path_utf16);
|
||||
|
||||
if (os_wcs_to_utf8(path_utf16, 0, dst, size) != 0) {
|
||||
|
|
@ -231,13 +232,13 @@ int os_get_config_path(char *dst, size_t size, const char *name)
|
|||
return -1;
|
||||
}
|
||||
|
||||
char *os_get_config_path_ptr(const char *name)
|
||||
static char *os_get_path_ptr_internal(const char *name, int folder)
|
||||
{
|
||||
char *ptr;
|
||||
wchar_t path_utf16[MAX_PATH];
|
||||
struct dstr path;
|
||||
|
||||
SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
|
||||
SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT,
|
||||
path_utf16);
|
||||
|
||||
os_wcs_to_utf8_ptr(path_utf16, 0, &ptr);
|
||||
|
|
@ -247,6 +248,26 @@ char *os_get_config_path_ptr(const char *name)
|
|||
return path.array;
|
||||
}
|
||||
|
||||
int os_get_config_path(char *dst, size_t size, const char *name)
|
||||
{
|
||||
return os_get_path_internal(dst, size, name, CSIDL_APPDATA);
|
||||
}
|
||||
|
||||
char *os_get_config_path_ptr(const char *name)
|
||||
{
|
||||
return os_get_path_ptr_internal(name, CSIDL_APPDATA);
|
||||
}
|
||||
|
||||
int os_get_program_data_path(char *dst, size_t size, const char *name)
|
||||
{
|
||||
return os_get_path_internal(dst, size, name, CSIDL_COMMON_APPDATA);
|
||||
}
|
||||
|
||||
char *os_get_program_data_path_ptr(const char *name)
|
||||
{
|
||||
return os_get_path_ptr_internal(name, CSIDL_COMMON_APPDATA);
|
||||
}
|
||||
|
||||
bool os_file_exists(const char *path)
|
||||
{
|
||||
WIN32_FIND_DATAW wfd;
|
||||
|
|
|
|||
|
|
@ -633,3 +633,26 @@ int os_mkdirs(const char *dir)
|
|||
dstr_free(&dir_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *os_get_path_extension(const char *path)
|
||||
{
|
||||
struct dstr temp;
|
||||
size_t pos = 0;
|
||||
char *period;
|
||||
char *slash;
|
||||
|
||||
dstr_init_copy(&temp, path);
|
||||
dstr_replace(&temp, "\\", "/");
|
||||
|
||||
slash = strrchr(temp.array, '/');
|
||||
period = strrchr(temp.array, '.');
|
||||
if (period)
|
||||
pos = (size_t)(period - temp.array);
|
||||
|
||||
dstr_free(&temp);
|
||||
|
||||
if (!period || slash > period)
|
||||
return NULL;
|
||||
|
||||
return path + pos;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,11 +108,16 @@ EXPORT uint64_t os_gettime_ns(void);
|
|||
EXPORT int os_get_config_path(char *dst, size_t size, const char *name);
|
||||
EXPORT char *os_get_config_path_ptr(const char *name);
|
||||
|
||||
EXPORT int os_get_program_data_path(char *dst, size_t size, const char *name);
|
||||
EXPORT char *os_get_program_data_path_ptr(const char *name);
|
||||
|
||||
EXPORT bool os_file_exists(const char *path);
|
||||
|
||||
EXPORT size_t os_get_abs_path(const char *path, char *abspath, size_t size);
|
||||
EXPORT char *os_get_abs_path_ptr(const char *path);
|
||||
|
||||
EXPORT const char *os_get_path_extension(const char *path);
|
||||
|
||||
struct os_dir;
|
||||
typedef struct os_dir os_dir_t;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue