diff --git a/CMakeLists.txt b/CMakeLists.txt index 8df8f9d..1d4f8db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") include(ObsHelpers) include(ObsCpack) +include(GNUInstallDirs) if(MSVC AND NOT EXISTS "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user") file(GENERATE diff --git a/README b/README index 50545b6..c11d512 100644 --- a/README +++ b/README @@ -98,7 +98,7 @@ What was wrong with the original OBS? - The devices and audio code was poor, I had no idea what I was getting into when I started writing them in. I did not realize beforehand all the device-specific quirks that each device/system could have. Some devices - had bad timing and quirks that I never aniticipated while writing them. + had bad timing and quirks that I never anticipated while writing them. I struggled with devices, and my original design for the audio subsystem for example morphed over and over into an abomination that, though works, is basically this giant duct-taped zombie monster. diff --git a/cmake/Modules/FindLibVLC.cmake b/cmake/Modules/FindLibVLC.cmake new file mode 100644 index 0000000..3f68885 --- /dev/null +++ b/cmake/Modules/FindLibVLC.cmake @@ -0,0 +1,72 @@ +# Once done these will be defined: +# +# LIBVLC_FOUND +# LIBVLC_INCLUDE_DIRS +# LIBVLC_LIBRARIES +# +# For use in OBS: +# +# VLC_INCLUDE_DIR + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_check_modules(_VLC QUIET VLC) +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_lib_suffix 64) +else() + set(_lib_suffix 32) +endif() + +find_path(VLC_INCLUDE_DIR + NAMES libvlc.h + HINTS + ENV VLCPath${_lib_suffix} + ENV VLCPath + ENV DepsPath${_lib_suffix} + ENV DepsPath + ${VLCPath${_lib_suffix}} + ${VLCPath} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_VLC_INCLUDE_DIRS} + PATHS + /usr/include /usr/local/include /opt/local/include /sw/include + PATH_SUFFIXES + vlc include/vlc include) + +find_library(VLC_LIB + NAMES ${_VLC_LIBRARIES} VLC libVLC + HINTS + ENV VLCPath${_lib_suffix} + ENV VLCPath + ENV DepsPath${_lib_suffix} + ENV DepsPath + ${VLCPath${_lib_suffix}} + ${VLCPath} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_VLC_LIBRARY_DIRS} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibVLC_INCLUDES DEFAULT_MSG VLC_INCLUDE_DIR) +find_package_handle_standard_args(LibVLC DEFAULT_MSG VLC_LIB VLC_INCLUDE_DIR) +mark_as_advanced(VLC_INCLUDE_DIR VLC_LIB) + +if(LIBVLC_INCLUDES_FOUND) + set(LIBVLC_INCLUDE_DIRS ${VLC_INCLUDE_DIR}) +endif() + +if(LIBVLC_FOUND) + set(LIBVLC_LIBRARIES ${VLC_LIB}) +endif() diff --git a/cmake/Modules/FindLibspeexdsp.cmake b/cmake/Modules/FindLibspeexdsp.cmake new file mode 100644 index 0000000..be1d972 --- /dev/null +++ b/cmake/Modules/FindLibspeexdsp.cmake @@ -0,0 +1,68 @@ +# Once done these will be defined: +# +# LIBSPEEXDSP_FOUND +# LIBSPEEXDSP_INCLUDE_DIRS +# LIBSPEEXDSP_LIBRARIES +# +# For use in OBS: +# +# SPEEXDSP_INCLUDE_DIR + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_check_modules(_SPEEXDSP QUIET speexdsp libspeexdsp) +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_lib_suffix 64) +else() + set(_lib_suffix 32) +endif() + +find_path(SPEEXDSP_INCLUDE_DIR + NAMES speex/speex_preprocess.h + HINTS + ENV speexPath${_lib_suffix} + ENV speexPath + ENV DepsPath${_lib_suffix} + ENV DepsPath + ${speexPath${_lib_suffix}} + ${speexPath} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_SPEEXDSP_INCLUDE_DIRS} + PATHS + /usr/include /usr/local/include /opt/local/include /sw/include + PATH_SUFFIXES + include) + +find_library(SPEEXDSP_LIB + NAMES ${_SPEEXDSP_LIBRARIES} speexdsp libspeexdsp + HINTS + ENV speexPath${_lib_suffix} + ENV speexPath + ENV DepsPath${_lib_suffix} + ENV DepsPath + ${speexPath${_lib_suffix}} + ${speexPath} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_SPEEXDSP_LIBRARY_DIRS} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libspeexdsp DEFAULT_MSG SPEEXDSP_LIB SPEEXDSP_INCLUDE_DIR) +mark_as_advanced(SPEEXDSP_INCLUDE_DIR SPEEXDSP_LIB) + +if(LIBSPEEXDSP_FOUND) + set(LIBSPEEXDSP_INCLUDE_DIRS ${SPEEXDSP_INCLUDE_DIR}) + set(LIBSPEEXDSP_LIBRARIES ${SPEEXDSP_LIB}) +endif() diff --git a/cmake/external/ObsPluginHelpers.cmake b/cmake/external/ObsPluginHelpers.cmake index abc3185..b0651dc 100644 --- a/cmake/external/ObsPluginHelpers.cmake +++ b/cmake/external/ObsPluginHelpers.cmake @@ -106,13 +106,13 @@ endfunction() # 'target' is the destination target project being installed to # 'additional_target' specifies the additional binary function(install_external_plugin_bin_to_data target additional_target) - install(TARGETS ${target} + install(TARGETS ${additional_target} LIBRARY DESTINATION "data" RUNTIME DESTINATION "data") - add_custom_command(TARGET ${target} POST_BUILD + add_custom_command(TARGET ${additional_target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy - "$" - "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${plugin_target}/data/$" + "$" + "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${target}/data/$" VERBATIM) endfunction() @@ -128,12 +128,36 @@ function(install_external_plugin_bin_to_arch_data target additional_target) set(_bit_suffix "/32bit") endif() - install(TARGETS ${target} + install(TARGETS ${additional_target} LIBRARY DESTINATION "data${_bit_suffix}" RUNTIME DESTINATION "data${_bit_suffix}") - add_custom_command(TARGET ${target} POST_BUILD + add_custom_command(TARGET ${additional_target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy - "$" - "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${plugin_target}/data${_bit_suffix}/$" + "$" + "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${target}/data${_bit_suffix}/$" + VERBATIM) +endfunction() + +# Installs an additional file in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac. +# 'target' is the destination target project being installed to +# 'additional_target' specifies the additional binary +function(install_external_plugin_data_file_to_arch_data target additional_target file_target) + if(APPLE) + set(_bit_suffix "") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_bit_suffix "/64bit") + else() + set(_bit_suffix "/32bit") + endif() + + get_filename_component(file_target_name ${file_target} NAME) + + install(TARGETS ${additional_target} + LIBRARY DESTINATION "data${_bit_suffix}" + RUNTIME DESTINATION "data${_bit_suffix}") + add_custom_command(TARGET ${additional_target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy + "${file_target}" + "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${target}/data${_bit_suffix}/${file_target_name}" VERBATIM) endfunction() diff --git a/deps/w32-pthreads/CMakeLists.txt b/deps/w32-pthreads/CMakeLists.txt index 497d5ab..992ec69 100644 --- a/deps/w32-pthreads/CMakeLists.txt +++ b/deps/w32-pthreads/CMakeLists.txt @@ -19,7 +19,8 @@ add_library(w32-pthreads SHARED target_compile_definitions(w32-pthreads PRIVATE __CLEANUP_C PTW32_BUILD) target_include_directories(w32-pthreads - PUBLIC .) + PUBLIC + "$") target_link_libraries(w32-pthreads) -install_obs_core(w32-pthreads) +install_obs_core(w32-pthreads EXPORT w32-pthreads) diff --git a/deps/w32-pthreads/w32-pthreadsConfig.cmake.in b/deps/w32-pthreads/w32-pthreadsConfig.cmake.in new file mode 100644 index 0000000..2fe4aec --- /dev/null +++ b/deps/w32-pthreads/w32-pthreadsConfig.cmake.in @@ -0,0 +1,15 @@ +# - Config file for the w32-pthreads package +# It defines the following variables +# THREADS_INCLUDE_DIRS +# THREADS_LIBRARIES + +set(W32_PTHREADS_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@") + +# Cleanup possible relative paths +get_filename_component(W32_PTHREADS_INCLUDE_DIRS "${THREADS_INCLUDE_DIRS}" ABSOLUTE) + +if(NOT TARGET w32-pthreads) + include("${CMAKE_CURRENT_LIST_DIR}/w32-pthreadsTarget.cmake") +endif() + +set(W32_PTHREADS_LIBRARIES w32-pthreads) diff --git a/deps/w32-pthreads/w32-pthreadsConfigVersion.cmake.in b/deps/w32-pthreads/w32-pthreadsConfigVersion.cmake.in new file mode 100644 index 0000000..e69de29 diff --git a/libobs-d3d11/d3d11-shader.cpp b/libobs-d3d11/d3d11-shader.cpp index f4e27a7..95cfbb0 100644 --- a/libobs-d3d11/d3d11-shader.cpp +++ b/libobs-d3d11/d3d11-shader.cpp @@ -222,10 +222,18 @@ inline void gs_shader::UpdateParam(vector &constData, upload = true; param.changed = false; } + } else if (param.curValue.size() == sizeof(gs_texture_t*)) { gs_texture_t *tex; memcpy(&tex, param.curValue.data(), sizeof(gs_texture_t*)); device_load_texture(device, tex, param.textureID); + + if (param.nextSampler) { + ID3D11SamplerState *state = param.nextSampler->state; + device->context->PSSetSamplers(param.textureID, 1, + &state); + param.nextSampler = nullptr; + } } } @@ -384,3 +392,8 @@ void gs_shader_set_default(gs_sparam_t *param) shader_setval_inline(param, param->defaultValue.data(), param->defaultValue.size()); } + +void gs_shader_set_next_sampler(gs_sparam_t *param, gs_samplerstate_t *sampler) +{ + param->nextSampler = sampler; +} diff --git a/libobs-d3d11/d3d11-subsystem.hpp b/libobs-d3d11/d3d11-subsystem.hpp index 8d70391..d413508 100644 --- a/libobs-d3d11/d3d11-subsystem.hpp +++ b/libobs-d3d11/d3d11-subsystem.hpp @@ -356,6 +356,7 @@ struct gs_shader_param { gs_shader_param_type type; uint32_t textureID; + struct gs_sampler_state *nextSampler = nullptr; int arrayCount; diff --git a/libobs-opengl/gl-shader.c b/libobs-opengl/gl-shader.c index 650b29e..1ffcce4 100644 --- a/libobs-opengl/gl-shader.c +++ b/libobs-opengl/gl-shader.c @@ -487,6 +487,12 @@ static void program_set_param_data(struct gs_program *program, } } else if (pp->param->type == GS_SHADER_PARAM_TEXTURE) { + if (pp->param->next_sampler) { + program->device->cur_samplers[pp->param->sampler_id] = + pp->param->next_sampler; + pp->param->next_sampler = NULL; + } + glUniform1i(pp->obj, pp->param->texture_id); device_load_texture(program->device, pp->param->texture, pp->param->texture_id); @@ -720,3 +726,8 @@ void gs_shader_set_default(gs_sparam_t *param) { gs_shader_set_val(param, param->def_value.array, param->def_value.num); } + +void gs_shader_set_next_sampler(gs_sparam_t *param, gs_samplerstate_t *sampler) +{ + param->next_sampler = sampler; +} diff --git a/libobs-opengl/gl-subsystem.h b/libobs-opengl/gl-subsystem.h index 8f360b9..4467876 100644 --- a/libobs-opengl/gl-subsystem.h +++ b/libobs-opengl/gl-subsystem.h @@ -300,6 +300,7 @@ struct gs_shader_param { char *name; gs_shader_t *shader; + gs_samplerstate_t *next_sampler; GLint texture_id; size_t sampler_id; int array_count; diff --git a/libobs/graphics/effect.c b/libobs/graphics/effect.c index 26623f4..737391d 100644 --- a/libobs/graphics/effect.c +++ b/libobs/graphics/effect.c @@ -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; +} diff --git a/libobs/graphics/effect.h b/libobs/graphics/effect.h index 8eb5464..d76fb85 100644 --- a/libobs/graphics/effect.h +++ b/libobs/graphics/effect.h @@ -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;*/ diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index dbe1eb6..6d00b9a 100644 --- a/libobs/graphics/graphics-imports.c +++ b/libobs/graphics/graphics-imports.c @@ -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__ diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 6c2333d..f009cc7 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -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 */ diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index f273bdb..a1f466d 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -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; diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 584d29e..6c7fc66 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -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 diff --git a/libobs/graphics/image-file.c b/libobs/graphics/image-file.c index 4b05154..b68539d 100644 --- a/libobs/graphics/image-file.c +++ b/libobs/graphics/image-file.c @@ -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); } diff --git a/libobs/graphics/matrix4.c b/libobs/graphics/matrix4.c index 12d1ebd..a0a7a25 100644 --- a/libobs/graphics/matrix4.c +++ b/libobs/graphics/matrix4.c @@ -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 } diff --git a/libobs/graphics/shader-parser.c b/libobs/graphics/shader-parser.c index 2d852bd..6771d4c 100644 --- a/libobs/graphics/shader-parser.c +++ b/libobs/graphics/shader-parser.c @@ -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); diff --git a/libobs/obs-audio-controls.c b/libobs/obs-audio-controls.c index 6e3ee33..cae7c9e 100644 --- a/libobs/obs-audio-controls.c +++ b/libobs/obs-audio-controls.c @@ -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) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 4172595..6450cfa 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -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) | \ diff --git a/libobs/obs-data.c b/libobs/obs-data.c index 41b33f4..5f94b32 100644 --- a/libobs/obs-data.c +++ b/libobs/obs-data.c @@ -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); diff --git a/libobs/obs-data.h b/libobs/obs-data.h index c07b110..824d23a 100644 --- a/libobs/obs-data.h +++ b/libobs/obs-data.h @@ -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); diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index a0e54f5..e1ce46f 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -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); diff --git a/libobs/obs-encoder.h b/libobs/obs-encoder.h index 89a782a..bb393f5 100644 --- a/libobs/obs-encoder.h +++ b/libobs/obs-encoder.h @@ -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 * diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index e9df2f2..429aa7b 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -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); diff --git a/libobs/obs-module.c b/libobs/obs-module.c index 638ae69..41c46a4 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -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; diff --git a/libobs/obs-module.h b/libobs/obs-module.h index 7b98904..5d446db 100644 --- a/libobs/obs-module.h +++ b/libobs/obs-module.h @@ -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) diff --git a/libobs/obs-output-delay.c b/libobs/obs-output-delay.c index 8073968..2bcc6bb 100644 --- a/libobs/obs-output-delay.c +++ b/libobs/obs-output-delay.c @@ -18,6 +18,16 @@ #include #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; } diff --git a/libobs/obs-output.c b/libobs/obs-output.c index d450a6f..21f6745 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -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); } } diff --git a/libobs/obs-output.h b/libobs/obs-output.h index 03629e3..1b42530 100644 --- a/libobs/obs-output.h +++ b/libobs/obs-output.h @@ -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); diff --git a/libobs/obs-properties.c b/libobs/obs-properties.c index a0e7cd3..c2d5a99 100644 --- a/libobs/obs-properties.c +++ b/libobs/obs-properties.c @@ -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) diff --git a/libobs/obs-properties.h b/libobs/obs-properties.h index 8d400a6..c3b7446 100644 --- a/libobs/obs-properties.h +++ b/libobs/obs-properties.h @@ -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); diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 3fa74e3..d12eabc 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -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")) diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 3c953a3..8dbe8b8 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -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; diff --git a/libobs/obs-service.c b/libobs/obs-service.c index c9ef6b3..9a4a4e9 100644 --- a/libobs/obs-service.c +++ b/libobs/obs-service.c @@ -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) diff --git a/libobs/obs-source-transition.c b/libobs/obs-source-transition.c index fb079dd..d92be31 100644 --- a/libobs/obs-source-transition.c +++ b/libobs/obs-source-transition.c @@ -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) diff --git a/libobs/obs-source.c b/libobs/obs-source.c index e465dc0..044e688 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -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, diff --git a/libobs/obs-video.c b/libobs/obs-video.c index bc06a2c..6b046a9 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -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; diff --git a/libobs/obs-windows.c b/libobs/obs-windows.c index e282a27..8bc4683 100644 --- a/libobs/obs-windows.c +++ b/libobs/obs-windows.c @@ -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(); } diff --git a/libobs/obs.c b/libobs/obs.c index 3c93b86..0601343 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -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); diff --git a/libobs/obs.h b/libobs/obs.h index bc2d995..d9ba78b 100644 --- a/libobs/obs.h +++ b/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); diff --git a/libobs/util/cf-parser.h b/libobs/util/cf-parser.h index 05b2482..b8538d2 100644 --- a/libobs/util/cf-parser.h +++ b/libobs/util/cf-parser.h @@ -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) { diff --git a/libobs/util/dstr.h b/libobs/util/dstr.h index c367c09..a07ae1c 100644 --- a/libobs/util/dstr.h +++ b/libobs/util/dstr.h @@ -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) diff --git a/libobs/util/platform-cocoa.m b/libobs/util/platform-cocoa.m index 7d8b105..29076d6 100644 --- a/libobs/util/platform-cocoa.m +++ b/libobs/util/platform-cocoa.m @@ -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; diff --git a/libobs/util/platform-nix.c b/libobs/util/platform-nix.c index e1bc921..6c7b4ba 100644 --- a/libobs/util/platform-nix.c +++ b/libobs/util/platform-nix.c @@ -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) diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 119426c..2e33cfa 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -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; diff --git a/libobs/util/platform.c b/libobs/util/platform.c index af031e6..ffccf8a 100644 --- a/libobs/util/platform.c +++ b/libobs/util/platform.c @@ -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; +} diff --git a/libobs/util/platform.h b/libobs/util/platform.h index 8081717..b08c40a 100644 --- a/libobs/util/platform.h +++ b/libobs/util/platform.h @@ -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; diff --git a/obs/CMakeLists.txt b/obs/CMakeLists.txt index c98b9bc..4185188 100644 --- a/obs/CMakeLists.txt +++ b/obs/CMakeLists.txt @@ -228,7 +228,7 @@ install_obs_core(obs) install_obs_data(obs data obs-studio) if (UNIX AND UNIX_STRUCTURE AND NOT APPLE) - install(FILES dist/obs.desktop DESTINATION share/applications) + install(FILES dist/obs.desktop DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/applications) install(FILES forms/images/obs.png - DESTINATION share/icons/hicolor/256x256/apps) + DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons/hicolor/256x256/apps) endif() diff --git a/obs/audio-encoders.cpp b/obs/audio-encoders.cpp index b1b1d60..09bec9e 100644 --- a/obs/audio-encoders.cpp +++ b/obs/audio-encoders.cpp @@ -189,7 +189,7 @@ static void PopulateBitrateMap() << " kbit/s: '" << EncoderName(entry.second) << "' (" << entry.second << ')'; - blog(LOG_INFO, "AAC encoder bitrate mapping:%s", + blog(LOG_DEBUG, "AAC encoder bitrate mapping:%s", ss.str().c_str()); }); } diff --git a/obs/data/locale.ini b/obs/data/locale.ini index 5c40ddd..0e4e93f 100644 --- a/obs/data/locale.ini +++ b/obs/data/locale.ini @@ -116,4 +116,7 @@ Name=Tiếng Việt Name=தமிழ் [lt-LT] -Name=Lietuvių kalba \ No newline at end of file +Name=Lietuvių kalba + +[et-EE] +Name=eesti keel \ No newline at end of file diff --git a/obs/data/locale/ar-SA.ini b/obs/data/locale/ar-SA.ini index e1d53c5..f1b1678 100644 --- a/obs/data/locale/ar-SA.ini +++ b/obs/data/locale/ar-SA.ini @@ -1,6 +1,6 @@ Language="العربية" -Region="جامعة الدول العربية" +Region="الوطن العربي" OK="موافق" Apply="تطبيق" @@ -91,7 +91,9 @@ Output.ConnectFail.Error="حدث خطأ غير متوقع عند محاولة ا Output.ConnectFail.Disconnected="تم قطع الاتصال من السيرفر." Output.RecordFail.Title="فشل في بدء التسجيل" +Output.RecordNoSpace.Msg="لا توجد مساحة كافية على القرص لمتابعة التسجيل." Output.RecordError.Title="خطأ في التسجيل" +Output.RecordError.Msg="حدث خطأ غير محدد أثناء التسجيل." Output.BadPath.Title="مسار الملف غير صحيح" Output.BadPath.Text="مسار الإخراج ملف تكوين غير صالح. الرجاء التحقق من الإعدادات الخاصة بك للتأكد من أنه تم تعيين مسار ملف صحيح." @@ -109,6 +111,10 @@ LicenseAgreement.Exit="خروج" Remux.SourceFile="تسجيل OBS" Remux.TargetFile="الملف الهدف" Remux.OBSRecording="تسجيل OBS" +Remux.SelectRecording="إختر تسجيل OBS …" +Remux.SelectTarget="إختر الملف الهدف …" +Remux.FileExistsTitle="الملف الهدف موجود" +Remux.FileExists="الملف الهدف موجود، هل تريد استبداله؟" UpdateAvailable="تحديث جديد متوفر" UpdateAvailable.Text="الإصدار %1.%2.%3 متوفر الآن. انقر هنا للتتحميل" @@ -125,6 +131,7 @@ Basic.DisplayCapture="التقاط الشاشة" Basic.Main.PreviewConextMenu.Enable="تمكين المعاينة" + Deinterlacing.Discard="تجاهل" Basic.Main.AddSceneDlg.Title="أضف المشهد" @@ -230,6 +237,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="التحرك &للقمة" Basic.MainMenu.Edit.Order.MoveToBottom="التحرك &للقاع" Basic.MainMenu.Edit.AdvAudio="&خصائص الصوت المتقدمة" + Basic.MainMenu.SceneCollection="&مجموعة المشاهد" Basic.MainMenu.Profile="&الملف الشخصي" @@ -247,6 +255,10 @@ Basic.Settings.Confirm="لديك تغييرات غير محفوظة. هل تري Basic.Settings.General="عام" Basic.Settings.General.Theme="السمة" Basic.Settings.General.Language="اللغة" +Basic.Settings.General.ScreenSnapping="جذب المصادر لحافة الشاشة" +Basic.Settings.General.CenterSnapping="جذب المصادر للمركز العمودي والأفقي" +Basic.Settings.General.SourceSnapping="جذب المصادر للمصادر الأخرى" +Basic.Settings.General.SnapDistance="حساسية الجذب" Basic.Settings.Stream="بث" Basic.Settings.Stream.StreamType="نوع البث" @@ -254,9 +266,22 @@ Basic.Settings.Stream.StreamType="نوع البث" Basic.Settings.Output="المخرج" Basic.Settings.Output.Format="صيغة التسجيل" Basic.Settings.Output.Encoder="مرمّز" +Basic.Settings.Output.SelectDirectory="حدد دليل التسجيل" +Basic.Settings.Output.SelectFile="حدد ملف التسجيل" Basic.Settings.Output.Mode="نوع المخرج" Basic.Settings.Output.Mode.Simple="بسيط" Basic.Settings.Output.Mode.Adv="متقدم" +Basic.Settings.Output.Mode.FFmpeg="مخرج FFmpeg" +Basic.Settings.Output.Simple.SavePath="مسار التسجيل" +Basic.Settings.Output.Simple.RecordingQuality="جودة التسجيل" +Basic.Settings.Output.Simple.RecordingQuality.Small="جودة عالية، حجم ملف متوسط" +Basic.Settings.Output.Simple.RecordingQuality.HQ="جودة لا تُميز، حجم ملف كبير" +Basic.Settings.Output.Simple.RecordingQuality.Lossless="جودة غير فقودة، حجم ملف كبير بشكل هائل" +Basic.Settings.Output.Simple.Warn.Lossless.Msg="هل أنت متأكد أنك تريد استخدام الجودة الغير فقودة؟" +Basic.Settings.Output.Simple.Warn.Lossless.Title="تحذير الجودة الغير فقودة!" +Basic.Settings.Output.Simple.Encoder.Software="برمجية (x264)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="عتاد (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="عتاد (NVENC)" Basic.Settings.Output.VideoBitrate="معدل البت للفيديو" Basic.Settings.Output.AudioBitrate="معدل البت للصوت" Basic.Settings.Output.Reconnect="إعادة الاتصال تلقائياً" @@ -264,6 +289,7 @@ Basic.Settings.Output.RetryDelay="إعادة محاولة تأخير (ثوان)" Basic.Settings.Output.MaxRetries="أقصى عدد للمحاولات" +Basic.Settings.Output.Adv.Recording.Type.Standard="قياسي" Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="‮كل الملفات" Basic.Settings.Output.Adv.FFmpeg.Format="صيغة الحاوية" diff --git a/obs/data/locale/bg-BG.ini b/obs/data/locale/bg-BG.ini index 1499e6a..fa2a20b 100644 --- a/obs/data/locale/bg-BG.ini +++ b/obs/data/locale/bg-BG.ini @@ -111,6 +111,7 @@ Basic.DisplayCapture="Заснемане на екрана" Basic.Main.PreviewConextMenu.Enable="Разреши преглед" + Basic.Main.AddSceneDlg.Title="Добави сцена" Basic.Main.AddSceneDlg.Text="Моля, въведете името на сцената" @@ -222,6 +223,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Премести &най-отгоре" Basic.MainMenu.Edit.Order.MoveToBottom="Премести най-о&тдолу" + Basic.MainMenu.Help="&Помощ" Basic.MainMenu.Help.Logs="\"Log\" &файлове" Basic.MainMenu.Help.Logs.UploadCurrentLog="Качи &текущия \"Log\" файл" diff --git a/obs/data/locale/ca-ES.ini b/obs/data/locale/ca-ES.ini index 540d82a..beff9c2 100644 --- a/obs/data/locale/ca-ES.ini +++ b/obs/data/locale/ca-ES.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS és actualment actiu. Tots els directes/gravacions s'atur ConfirmRemove.Title="Confirmeu la supressió" ConfirmRemove.Text="Esteu segur que voleu suprimir «$1»?" +ConfirmRemove.TextMultiple="¿Segur que vols esborrar %1 elements?" Output.ConnectFail.Title="Error en connectar" Output.ConnectFail.BadPath="Ruta o adreça URL no vàlida. Si us plau, comproveu la configuració per confirmar que són vàlids." @@ -144,6 +145,12 @@ Basic.DisplayCapture="Captura de pantalla" Basic.Main.PreviewConextMenu.Enable="Habilita la visualització prèvia" +ScaleFiltering="Escala de filtratge" +ScaleFiltering.Point="Punt" +ScaleFiltering.Bilinear="Bilineal" +ScaleFiltering.Bicubic="Bicúbic" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Desentrellaçar" Deinterlacing.Discard="Descarta" Deinterlacing.Retro="Retro" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Configuració canviada" Basic.PropertiesWindow.Confirm="Hi ha canvis no desats. Voleu mantenir-los?" Basic.PropertiesWindow.NoProperties="No hi ha propietats disponibles" Basic.PropertiesWindow.AddFiles="Afegeix fitxers" +Basic.PropertiesWindow.AddDir="Afegir directori" Basic.PropertiesWindow.AddURL="Afegeix el camí o l'URL" +Basic.PropertiesWindow.AddEditableListDir="Afegir directori a '%1'" Basic.PropertiesWindow.AddEditableListFiles="Afegeix fitxers a '%1'" Basic.PropertiesWindow.AddEditableListEntry="Afegeix una entrada a '%1'" Basic.PropertiesWindow.EditEditableListEntry="Edita l'entrada de '%1'" @@ -250,7 +259,9 @@ Basic.Main.Connecting="S'està connectant..." Basic.Main.StartRecording="Inicia l'enregistrament" Basic.Main.StartStreaming="Inicia l'enregistrament" Basic.Main.StopRecording="Atura l'enregistrament" +Basic.Main.StoppingRecording="Aturant l'enregistrament..." Basic.Main.StopStreaming="Atura l'enregistrament" +Basic.Main.StoppingStreaming="Aturant la transmissió..." Basic.Main.ForceStopStreaming="Atura l'enregistrament (descarta el retard)" Basic.MainMenu.File="&Fitxer" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Mou a la part &superior" Basic.MainMenu.Edit.Order.MoveToBottom="Mou a la part &inferior" Basic.MainMenu.Edit.AdvAudio="&Propietats avançades d'àudio" +Basic.MainMenu.View="Veure" +Basic.MainMenu.View.Toolbars="Barres d'eines" +Basic.MainMenu.View.Toolbars.Listboxes="Quadre de Llista" +Basic.MainMenu.View.SceneTransitions="Transicions d'escena" +Basic.MainMenu.View.StatusBar="Barra d'estat" + Basic.MainMenu.SceneCollection="&Col·lecció d'escenes" Basic.MainMenu.Profile="&Perfil" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Llengua" Basic.Settings.General.WarnBeforeStartingStream="Mostra diàleg de confirmació quan s'iniciï una transmissió" Basic.Settings.General.WarnBeforeStoppingStream="Mostra diàleg de confirmació quan s'aturi una transmissió" +Basic.Settings.General.HideProjectorCursor="Amaga el cursor sobre projectors" Basic.Settings.General.Snapping="Ajustament d'alineació de la font" Basic.Settings.General.ScreenSnapping="Ajustar les fonts a la vora de la pantalla" Basic.Settings.General.CenterSnapping="Ajustar les fonts al centre horitzontal i vertical" Basic.Settings.General.SourceSnapping="Ajustar les fonts a altres fonts" Basic.Settings.General.SnapDistance="Ajusta la sensibilitat" +Basic.Settings.General.RecordWhenStreaming="Enregistra automàticament quan es transmet" +Basic.Settings.General.KeepRecordingWhenStreamStops="Mantenir l'enregistrament quan s'atura la transmissió" Basic.Settings.Stream="Directe" Basic.Settings.Stream.StreamType="Tipus de directe" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Retard de \"prémer-per-parlar\"" Basic.Settings.Audio.UnknownAudioDevice="[Dispositiu no connectat o no disponible]" Basic.Settings.Advanced="Avançat" +Basic.Settings.Advanced.General.ProcessPriority="Prioritat del procés" +Basic.Settings.Advanced.General.ProcessPriority.High="Alta" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Per sobre del normal" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normal" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Inactiva" Basic.Settings.Advanced.FormatWarning="Advertiment: Els formats de color diferents de NV12 estan destinats principalment per a la gravació i no són recomanables quan es fa un directe. Fer un directe pot comportar un major ús de CPU a causa de la conversió de format de color." Basic.Settings.Advanced.Audio.BufferingTime="Temps de buffer d'àudio" Basic.Settings.Advanced.Video.ColorFormat="Format del color" diff --git a/obs/data/locale/cs-CZ.ini b/obs/data/locale/cs-CZ.ini index 9f20351..3ab6a3a 100644 --- a/obs/data/locale/cs-CZ.ini +++ b/obs/data/locale/cs-CZ.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="Ukončením budou všechna vysílání/záznamy zastavena. Jst ConfirmRemove.Title="Potvrzení odebrání" ConfirmRemove.Text="Opravdu si přejete odebrat '$1'?" +ConfirmRemove.TextMultiple="Opravdu si přejete odebrat %1 položky ?" Output.ConnectFail.Title="Spojení se nezdařilo" Output.ConnectFail.BadPath="Chybná cesta nebo adresa připojení. Zkontrolujte, prosím, správnost svých nastavení." @@ -144,6 +145,12 @@ Basic.DisplayCapture="Záznam obrazovky" Basic.Main.PreviewConextMenu.Enable="Povolit náhled" +ScaleFiltering="Filtrování rozsahu" +ScaleFiltering.Point="Bod" +ScaleFiltering.Bilinear="Bilineární" +ScaleFiltering.Bicubic="Bikubický" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Odstranění prokladu" Deinterlacing.Discard="Zahození" Deinterlacing.Retro="Retro" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Nastavení upraveno" Basic.PropertiesWindow.Confirm="Chcete ponechat neuložená nastavení ?" Basic.PropertiesWindow.NoProperties="Nejsou k dispozici žádné vlatnosti" Basic.PropertiesWindow.AddFiles="Přidat soubory" +Basic.PropertiesWindow.AddDir="Přidat složku" Basic.PropertiesWindow.AddURL="Přidat cestu/URL" +Basic.PropertiesWindow.AddEditableListDir="Přidat složku do '%1'" Basic.PropertiesWindow.AddEditableListFiles="Přidat soubory do '%1'" Basic.PropertiesWindow.AddEditableListEntry="Přidat položku do '%1'" Basic.PropertiesWindow.EditEditableListEntry="Upravit položku z '%1'" @@ -250,7 +259,9 @@ Basic.Main.Connecting="Připojování..." Basic.Main.StartRecording="Začít nahrávat" Basic.Main.StartStreaming="Začít vysílat" Basic.Main.StopRecording="Zastavit nahrávání" +Basic.Main.StoppingRecording="Zastavuji nahrávání..." Basic.Main.StopStreaming="Zastavit vysílání" +Basic.Main.StoppingStreaming="Zastavuji vysílání..." Basic.Main.ForceStopStreaming="Zastavit vysání (bez zpoždění)" Basic.MainMenu.File="Soubor (&F)" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Posunout na vršek (&T)" Basic.MainMenu.Edit.Order.MoveToBottom="Posunout na spodek (&B)" Basic.MainMenu.Edit.AdvAudio="Rozšířené vl&astnosti zvuku" +Basic.MainMenu.View="Zobrazit (&V)" +Basic.MainMenu.View.Toolbars="Liš&ty nástrojů" +Basic.MainMenu.View.Toolbars.Listboxes="Seznamy (&L)" +Basic.MainMenu.View.SceneTransitions="Pře&chody scény" +Basic.MainMenu.View.StatusBar="&Stavový řádek" + Basic.MainMenu.SceneCollection="Kolekce &scén" Basic.MainMenu.Profile="&Profil" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Vzhled" Basic.Settings.General.Language="Jazyk" Basic.Settings.General.WarnBeforeStartingStream="Vyžadovat potvrzení pro spuštění vysílání" Basic.Settings.General.WarnBeforeStoppingStream="Vyžadovat potvrzení pro ukončení vysílání" +Basic.Settings.General.HideProjectorCursor="Skrýt kurzor přes projektor" Basic.Settings.General.Snapping="Přichycování zdrojů" Basic.Settings.General.ScreenSnapping="Přichytávat zdroje k okraji obrazovky" Basic.Settings.General.CenterSnapping="Přichytávat zdroje k vertikálnímu a horizontálnímu středu" Basic.Settings.General.SourceSnapping="Přichytávat zdroje k ostatním zdrojům" Basic.Settings.General.SnapDistance="Citlivost přichycení" +Basic.Settings.General.RecordWhenStreaming="Automaticky nahrávat při vysílání" +Basic.Settings.General.KeepRecordingWhenStreamStops="Pokračovat v nahrávání i po zastavení vysílání" Basic.Settings.Stream="Vysílání" Basic.Settings.Stream.StreamType="Typ vysílání" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Zpoždění" Basic.Settings.Audio.UnknownAudioDevice="[Zařízení není připojeno nebo není k dispozici]" Basic.Settings.Advanced="Rozšířené" +Basic.Settings.Advanced.General.ProcessPriority="Priorita procesu" +Basic.Settings.Advanced.General.ProcessPriority.High="Vysoká" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Vyšší než normální" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normální" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Nečinný" Basic.Settings.Advanced.FormatWarning="Varování: Jiné formáty barev než je NV12 jsou primárně určeny pro nahrávání a neměly by být použity pro vysílání. Použití při vysílání zvýší využití CPU." Basic.Settings.Advanced.Audio.BufferingTime="Čas vyrovnávací paměti zvuku" Basic.Settings.Advanced.Video.ColorFormat="Formát barev" diff --git a/obs/data/locale/da-DK.ini b/obs/data/locale/da-DK.ini index 7aa3e1e..86f009d 100644 --- a/obs/data/locale/da-DK.ini +++ b/obs/data/locale/da-DK.ini @@ -140,6 +140,7 @@ Basic.DisplayCapture="Indfang display" Basic.Main.PreviewConextMenu.Enable="Aktiver visning" + Deinterlacing="Deinterlacing" Deinterlacing.Discard="Kassér" Deinterlacing.Retro="Retro" @@ -277,6 +278,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Flyt til &Toppen" Basic.MainMenu.Edit.Order.MoveToBottom="Flyt til &Bunden" Basic.MainMenu.Edit.AdvAudio="&Avancerede lydegenskaber" + Basic.MainMenu.SceneCollection="&Scenesamling" Basic.MainMenu.Profile="&Profil" diff --git a/obs/data/locale/de-DE.ini b/obs/data/locale/de-DE.ini index d251c05..5404756 100644 --- a/obs/data/locale/de-DE.ini +++ b/obs/data/locale/de-DE.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS ist derzeit aktiv. Alle Streams/Aufnahmen werden beendet. ConfirmRemove.Title="Entfernen bestätigen" ConfirmRemove.Text="Sind Sie sicher, dass Sie '$1' entfernen möchten?" +ConfirmRemove.TextMultiple="Sind Sie sicher, dass Sie %1 Elemente löschen möchten?" Output.ConnectFail.Title="Verbindung fehlgeschlagen" Output.ConnectFail.BadPath="Ungültiger Pfad oder Verbindungs-URL. Bitte überprüfen Sie Ihre Einstellungen und stellen Sie sicher, dass diese korrekt sind." @@ -144,6 +145,12 @@ Basic.DisplayCapture="Monitoraufnahme" Basic.Main.PreviewConextMenu.Enable="Vorschau aktivieren" +ScaleFiltering="Skalierungsfilterung" +ScaleFiltering.Point="Point" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Zeilenentflechtung (Deinterlacing)" Deinterlacing.Discard="Verwerfen" Deinterlacing.Retro="Retro" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Einstellungen geändert" Basic.PropertiesWindow.Confirm="Es gibt ungesicherte Änderungen. Möchten Sie diese behalten?" Basic.PropertiesWindow.NoProperties="Keine Eigenschaften verfügbar" Basic.PropertiesWindow.AddFiles="Dateien hinzufügen" +Basic.PropertiesWindow.AddDir="Verzeichnis hinzufügen" Basic.PropertiesWindow.AddURL="Pfad/URL hinzufügen" +Basic.PropertiesWindow.AddEditableListDir="Verzeichnis zu '%1' hinzufügen" Basic.PropertiesWindow.AddEditableListFiles="Dateien zu '%1' hinzufügen" Basic.PropertiesWindow.AddEditableListEntry="Eintrag zu '%1' hinzufügen" Basic.PropertiesWindow.EditEditableListEntry="Eintrag aus '%1' bearbeiten" @@ -250,7 +259,9 @@ Basic.Main.Connecting="Verbinden..." Basic.Main.StartRecording="Aufnahme starten" Basic.Main.StartStreaming="Streaming starten" Basic.Main.StopRecording="Aufnahme stoppen" +Basic.Main.StoppingRecording="Stoppe Aufnahme..." Basic.Main.StopStreaming="Streaming stoppen" +Basic.Main.StoppingStreaming="Stoppe Stream..." Basic.Main.ForceStopStreaming="Streaming stoppen (Verzögerung verwerfen)" Basic.MainMenu.File="Datei (&F)" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Ganz nach oben bewegen (&T)" Basic.MainMenu.Edit.Order.MoveToBottom="Ganz nach unten &bewegen" Basic.MainMenu.Edit.AdvAudio="Erweiterte &Audioeigenschaften" +Basic.MainMenu.View="Ansicht (&V)" +Basic.MainMenu.View.Toolbars="Werkzeugleisten (&T)" +Basic.MainMenu.View.Toolbars.Listboxes="&Listenfelder" +Basic.MainMenu.View.SceneTransitions="Szenenübergänge (&c)" +Basic.MainMenu.View.StatusBar="&Statusleiste" + Basic.MainMenu.SceneCollection="&Szenen-Sammlung" Basic.MainMenu.Profile="&Profil" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Motiv" Basic.Settings.General.Language="Sprache" Basic.Settings.General.WarnBeforeStartingStream="Bestätigungsdialog beim Streamstart anzeigen" Basic.Settings.General.WarnBeforeStoppingStream="Bestätigungsdialog beim Streamstop anzeigen" +Basic.Settings.General.HideProjectorCursor="Mauszeiger über Projektoren verstecken" Basic.Settings.General.Snapping="Quellenausrichtung" Basic.Settings.General.ScreenSnapping="Quellen am Bildschirmrand ausrichten" Basic.Settings.General.CenterSnapping="Quellen zur horizontalen und vertikalen Mitte ausrichten" Basic.Settings.General.SourceSnapping="Quellen an anderen Quellen ausrichten" Basic.Settings.General.SnapDistance="Ausrichtungsempfindlichkeit" +Basic.Settings.General.RecordWhenStreaming="Stream automatisch aufnehmen" +Basic.Settings.General.KeepRecordingWhenStreamStops="Weiter aufnehmen, wenn der Livestream stoppt" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Stream Typ" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Push-To-Talk Verzögerung" Basic.Settings.Audio.UnknownAudioDevice="[Gerät nicht angeschlossen oder nicht verfügbar]" Basic.Settings.Advanced="Erweitert" +Basic.Settings.Advanced.General.ProcessPriority="Prozesspriorität" +Basic.Settings.Advanced.General.ProcessPriority.High="Hoch" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Höher als normal" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normal" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Niedrig" Basic.Settings.Advanced.FormatWarning="Warnung: Andere Farbformate als NV12 sind in erster Linie für die Aufnahme bestimmt und sind für Streaming nicht zu empfehlen. Die erforderliche Farbformatkonvertierung kann eine erhöhte CPU-Auslastung hervorrufen." Basic.Settings.Advanced.Audio.BufferingTime="Audio Pufferungszeit" Basic.Settings.Advanced.Video.ColorFormat="Farbformat" diff --git a/obs/data/locale/el-GR.ini b/obs/data/locale/el-GR.ini index 26b041c..9412b86 100644 --- a/obs/data/locale/el-GR.ini +++ b/obs/data/locale/el-GR.ini @@ -112,6 +112,7 @@ Basic.DisplayCapture="Σύλληψη Οθόνης" Basic.Main.PreviewConextMenu.Enable="Ενεργοποίηση Προεπισκόπησης" + Basic.Main.AddSceneDlg.Title="Προσθήκη Σκηνής" Basic.Main.AddSceneDlg.Text="Παρακαλώ εισάγετε το όνομα της σκηνής" @@ -231,6 +232,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Μετακίνηση στην Κορυφή( Basic.MainMenu.Edit.Order.MoveToBottom="Μετακίνηση τελέιως Κάτω(&B)" Basic.MainMenu.Edit.AdvAudio="Ιδιότητες(&A) Ήχου για Προχωρημένους" + Basic.MainMenu.SceneCollection="&Συλλογή Σκηνών" Basic.MainMenu.Profile="&Προφίλ" diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini index 90cb492..5be0dc1 100644 --- a/obs/data/locale/en-US.ini +++ b/obs/data/locale/en-US.ini @@ -101,6 +101,7 @@ ConfirmExit.Text="OBS is currently active. All streams/recordings will be shut # confirm delete dialog box ConfirmRemove.Title="Confirm Remove" ConfirmRemove.Text="Are you sure you wish to remove '$1'?" +ConfirmRemove.TextMultiple="Are you sure you wish to remove %1 items?" # output connect messages Output.ConnectFail.Title="Failed to connect" @@ -168,6 +169,13 @@ Basic.DisplayCapture="Display Capture" # display context menu Basic.Main.PreviewConextMenu.Enable="Enable Preview" +# scale filtering +ScaleFiltering="Scale Filtering" +ScaleFiltering.Point="Point" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" + # deinterlacing Deinterlacing="Deinterlacing" Deinterlacing.Discard="Discard" @@ -220,7 +228,9 @@ Basic.PropertiesWindow.ConfirmTitle="Settings Changed" Basic.PropertiesWindow.Confirm="There are unsaved changes. Do you want to keep them?" Basic.PropertiesWindow.NoProperties="No properties available" Basic.PropertiesWindow.AddFiles="Add Files" +Basic.PropertiesWindow.AddDir="Add Directory" Basic.PropertiesWindow.AddURL="Add Path/URL" +Basic.PropertiesWindow.AddEditableListDir="Add directory to '%1'" Basic.PropertiesWindow.AddEditableListFiles="Add files to '%1'" Basic.PropertiesWindow.AddEditableListEntry="Add entry to '%1'" Basic.PropertiesWindow.EditEditableListEntry="Edit entry from '%1'" @@ -291,7 +301,9 @@ Basic.Main.Connecting="Connecting..." Basic.Main.StartRecording="Start Recording" Basic.Main.StartStreaming="Start Streaming" Basic.Main.StopRecording="Stop Recording" +Basic.Main.StoppingRecording="Stopping Recording..." Basic.Main.StopStreaming="Stop Streaming" +Basic.Main.StoppingStreaming="Stopping Stream..." Basic.Main.ForceStopStreaming="Stop Streaming (discard delay)" # basic mode file menu @@ -312,6 +324,7 @@ Basic.MainMenu.Edit.Undo="&Undo" Basic.MainMenu.Edit.Redo="&Redo" Basic.MainMenu.Edit.UndoAction="&Undo $1" Basic.MainMenu.Edit.RedoAction="&Redo $1" +Basic.MainMenu.Edit.LockPreview="&Lock Preview" Basic.MainMenu.Edit.Transform="&Transform" Basic.MainMenu.Edit.Transform.EditTransform="&Edit Transform..." Basic.MainMenu.Edit.Transform.ResetTransform="&Reset Transform" @@ -330,6 +343,13 @@ Basic.MainMenu.Edit.Order.MoveToTop="Move to &Top" Basic.MainMenu.Edit.Order.MoveToBottom="Move to &Bottom" Basic.MainMenu.Edit.AdvAudio="&Advanced Audio Properties" +# basic mode view menu +Basic.MainMenu.View="&View" +Basic.MainMenu.View.Toolbars="&Toolbars" +Basic.MainMenu.View.Toolbars.Listboxes="&Listboxes" +Basic.MainMenu.View.SceneTransitions="S&cene Transitions" +Basic.MainMenu.View.StatusBar="&Status Bar" + # basic mode profile/scene collection menus Basic.MainMenu.SceneCollection="&Scene Collection" Basic.MainMenu.Profile="&Profile" @@ -355,11 +375,14 @@ Basic.Settings.General.Theme="Theme" Basic.Settings.General.Language="Language" Basic.Settings.General.WarnBeforeStartingStream="Show confirmation dialog when starting streams" Basic.Settings.General.WarnBeforeStoppingStream="Show confirmation dialog when stopping streams" +Basic.Settings.General.HideProjectorCursor="Hide cursor over projectors" Basic.Settings.General.Snapping="Source Alignment Snapping" Basic.Settings.General.ScreenSnapping="Snap Sources to edge of screen" Basic.Settings.General.CenterSnapping="Snap Sources to horizontal and vertical center" Basic.Settings.General.SourceSnapping="Snap Sources to other sources" Basic.Settings.General.SnapDistance="Snap Sensitivity" +Basic.Settings.General.RecordWhenStreaming="Automatically record when streaming" +Basic.Settings.General.KeepRecordingWhenStreamStops="Keep recording when stream stops" # basic mode 'stream' settings Basic.Settings.Stream="Stream" @@ -488,6 +511,11 @@ Basic.Settings.Audio.UnknownAudioDevice="[Device not connected or not available] # basic mode 'advanced' settings Basic.Settings.Advanced="Advanced" +Basic.Settings.Advanced.General.ProcessPriority="Process Priority" +Basic.Settings.Advanced.General.ProcessPriority.High="High" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Above Normal" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normal" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Idle" Basic.Settings.Advanced.FormatWarning="Warning: Color formats other than NV12 are primarily intended for recording, and are not recommended when streaming. Streaming may incur increased CPU usage due to color format conversion." Basic.Settings.Advanced.Audio.BufferingTime="Audio Buffering Time" Basic.Settings.Advanced.Video.ColorFormat="Color Format" @@ -499,6 +527,8 @@ Basic.Settings.Advanced.StreamDelay="Stream Delay" Basic.Settings.Advanced.StreamDelay.Duration="Duration (seconds)" Basic.Settings.Advanced.StreamDelay.Preserve="Preserve cutoff point (increase delay) when reconnecting" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Estimated Memory Usage: %1 MB" +Basic.Settings.Advanced.Network="Network" +Basic.Settings.Advanced.Network.BindToIP="Bind to IP" # advanced audio properties Basic.AdvAudio="Advanced Audio Properties" diff --git a/obs/data/locale/es-ES.ini b/obs/data/locale/es-ES.ini index eb8d5c7..20ce8c3 100644 --- a/obs/data/locale/es-ES.ini +++ b/obs/data/locale/es-ES.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS esta siendo usado. Todas las transmisiones/grabaciones ser ConfirmRemove.Title="Confirmar borrado" ConfirmRemove.Text="¿Seguro que desea eliminar '$1'?" +ConfirmRemove.TextMultiple="¿Seguro que quieres eliminar %1 elementos?" Output.ConnectFail.Title="Error al conectarse" Output.ConnectFail.BadPath="URL ruta de acceso o conexión no válida. Por favor, compruebe su configuración para confirmar que está correcta." @@ -144,6 +145,12 @@ Basic.DisplayCapture="Captura de pantalla" Basic.Main.PreviewConextMenu.Enable="Habilitar previsualización" +ScaleFiltering="Escala de filtrado" +ScaleFiltering.Point="Punto" +ScaleFiltering.Bilinear="Bilineal" +ScaleFiltering.Bicubic="Bicúbico" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Desentrelazado" Deinterlacing.Discard="Descartar" Deinterlacing.Retro="Retro" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Configuración cambiada" Basic.PropertiesWindow.Confirm="Hay cambios sin guardar. ¿Deseas conservarlos?" Basic.PropertiesWindow.NoProperties="No hay propiedades disponibles" Basic.PropertiesWindow.AddFiles="Agregar archivos" +Basic.PropertiesWindow.AddDir="Añadir Carpeta" Basic.PropertiesWindow.AddURL="Añadir ruta/URL" +Basic.PropertiesWindow.AddEditableListDir="Añadir Carpeta a '%1'" Basic.PropertiesWindow.AddEditableListFiles="Añadir archivos a '%1'" Basic.PropertiesWindow.AddEditableListEntry="Agregar entrada a '%1'" Basic.PropertiesWindow.EditEditableListEntry="Editar entrada de '%1'" @@ -250,7 +259,9 @@ Basic.Main.Connecting="Conectando..." Basic.Main.StartRecording="Iniciar grabación" Basic.Main.StartStreaming="Iniciar Transmisión" Basic.Main.StopRecording="Detener grabación" +Basic.Main.StoppingRecording="Deteniendo la grabación..." Basic.Main.StopStreaming="Detener Transmisión" +Basic.Main.StoppingStreaming="Deteniendo la trasmisión..." Basic.Main.ForceStopStreaming="Parar Transmisión (descartar retraso)" Basic.MainMenu.File="&Archivo" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Mover a la &cima" Basic.MainMenu.Edit.Order.MoveToBottom="Mover al &fondo" Basic.MainMenu.Edit.AdvAudio="Propiedades de &Audio avanzadas" +Basic.MainMenu.View="Vista" +Basic.MainMenu.View.Toolbars="&Barra de Herramientas" +Basic.MainMenu.View.Toolbars.Listboxes="&Cuadro de Lista" +Basic.MainMenu.View.SceneTransitions="&Transición de Escenas" +Basic.MainMenu.View.StatusBar="&Barra de Estado" + Basic.MainMenu.SceneCollection="&Colección de Escenas" Basic.MainMenu.Profile="&Perfil" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" Basic.Settings.General.WarnBeforeStartingStream="Mostrar diálogo de confirmación cuando se inicia una transmisión" Basic.Settings.General.WarnBeforeStoppingStream="Mostrar diálogo de confirmación cuando se para una transmisión" +Basic.Settings.General.HideProjectorCursor="Ocultar el cursor sobre proyectores" Basic.Settings.General.Snapping="Ajuste de alineación de la fuente" Basic.Settings.General.ScreenSnapping="Ajustar las fuentes al borde de la pantalla" Basic.Settings.General.CenterSnapping="Ajustar las fuentes al centro horizontal y vertical" Basic.Settings.General.SourceSnapping="Ajustar las fuentes a otras fuentes" Basic.Settings.General.SnapDistance="Ajustar la sensibilidad" +Basic.Settings.General.RecordWhenStreaming="Grabar automáticamente cuando se transmite" +Basic.Settings.General.KeepRecordingWhenStreamStops="Mantener la grabación cuando se detiene la trasmision" Basic.Settings.Stream="Emision" Basic.Settings.Stream.StreamType="Tipo de Emision" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Retraso de Pulsar para Hablar" Basic.Settings.Audio.UnknownAudioDevice="[Dispositivo no conectado o no está disponible]" Basic.Settings.Advanced="Avanzado" +Basic.Settings.Advanced.General.ProcessPriority="Proceso prioritario" +Basic.Settings.Advanced.General.ProcessPriority.High="Alta" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Mayor a Normal" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normal" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Inactiva" Basic.Settings.Advanced.FormatWarning="ADVERTENCIA: Formatos de Color que no sean NV12 están diseñados principalmente para la grabación y no se recomiendan al retransmitir. La retransmisión puede incurrir en mayor uso de la CPU debido a la conversión de formato de color." Basic.Settings.Advanced.Audio.BufferingTime="Tiempo de búfer de audio" Basic.Settings.Advanced.Video.ColorFormat="Formato de color" diff --git a/obs/data/locale/et-EE.ini b/obs/data/locale/et-EE.ini new file mode 100644 index 0000000..87c0edd --- /dev/null +++ b/obs/data/locale/et-EE.ini @@ -0,0 +1,255 @@ + +Language="Eesti" +Region="Eesti" + +OK="Olgu" +Apply="Rakenda" +Cancel="Loobu" +Close="Sulge" +Save="Salvesta" +Discard="Loobu muudatustest" +Disable="Välja lülitatud" +Yes="Jah" +No="Ei" +Add="Lisa" +Remove="Eemalda" +Rename="Nimeta ümber" +Interact="Kasuta" +Filters="Filtrid" +Properties="Omadused" +MoveUp="Liiguta üles" +MoveDown="Liiguta alla" +Settings="Sätted" +Display="Kuva" +Name="Nimi" +Exit="Välju" +Mixer="Mikser" +Browse="Sirvi" +Mono="Mono" +Stereo="Stereo" +DroppedFrames="Vahele jäetud kaadreid %1 (%2%)" +PreviewProjector="Projektor täisekraanil (eelvaade)" +SceneProjector="Projektor täisekraanil (stseen)" +SourceProjector="Projektor täisekraanil (allikas)" +Clear="Eemalda" +Revert="Tühista" +Show="Näita" +Hide="Peida" +Untitled="Nimetu" +New="Uus" +Duplicate="Tee koopia" +Enable="Sisse lülitatud" +DisableOSXVSync="Lülita OSX V-Sync välja" +ResetOSXVSyncOnExit="Lähtesta OSX V-Sync väljumisel" +HighResourceUsage="Kodeering on ülekoormatud! Proovi kasutada madalamaid videosätteid või kiiremaid kodeerimissätteid." +Transition="Üleminek" +QuickTransitions="Valitud üleminekud" +Left="Vasakult" +Right="Paremalt" +Top="Ülalt" +Bottom="Alt" + +QuickTransitions.SwapScenes="Vaheta üleminekul eelvaade ja väljund" +QuickTransitions.SwapScenesTT="Vahetab pärast üleminekut eelvaate ja väljundi stseenid (kui väljundi esialgne stseen on veel olemas).\nEsialgsele stseenile tehtud muudatusi ei pöörata tagasi." +QuickTransitions.DuplicateScene="Tee stseenist koopia" +QuickTransitions.HotkeyName="Valitud üleminek: %1" + +Basic.AddTransition="Lisa seadistatav üleminek" +Basic.RemoveTransition="Eemalda seadistatav üleminek" +Basic.TransitionProperties="Ülemineku omadused" +Basic.SceneTransitions="Stseeni üleminekud" +Basic.TransitionDuration="Kestus" +Basic.TogglePreviewProgramMode="Stuudiorežiim" + +TransitionNameDlg.Text="Sisesta ülemineku nimi" +TransitionNameDlg.Title="Ülemineku nimi" + +TitleBar.Profile="Profiil" +TitleBar.Scenes="Stseenid" + +NameExists.Title="Nimi on juba kasutusel" +NameExists.Text="See nimi on juba kasutuses." + +NoNameEntered.Title="Palun sisesta sobiv nimi" +NoNameEntered.Text="Tühja nime ei saa kasutada." + +ConfirmStart.Title="Alustada voogedastusega?" +ConfirmStart.Text="Kas soovid kindlasti voogedastust alustada?" + +ConfirmStop.Title="Lõpetada voogedastus?" +ConfirmStop.Text="Kas soovid kindlasti voogedastust lõpetada?" + +ConfirmExit.Title="Kas väljuda OBS-ist?" +ConfirmExit.Text="OBS on hetkel aktiivne. Kõik voogedastused ja salvestused peatatakse. Kas soovid kindlasti väljuda?" + +ConfirmRemove.Title="Ümbernimetamise kinnitamine" +ConfirmRemove.Text="Kas soovid kindlasti eemaldada '$1'?" + +Output.ConnectFail.Title="Ühendamine ei õnnestunud" +Output.ConnectFail.BadPath="Vigane rada või ühenduse URL. Palun veendu, et valitud sätted on õiged." +Output.ConnectFail.ConnectFailed="Serveriga ühendamine ebaõnnestus" + + + + +LicenseAgreement.Exit="Välju" + +Remux.SourceFile="OBS-i salvestus" +Remux.TargetFile="Sihtfail" +Remux.Remux="Konverteeri" +Remux.OBSRecording="OBS-i salvestus" +Remux.FinishedTitle="Konverteerimine valmis" +Remux.Finished="Salvestus on ümber kodeeritud" +Remux.FinishedError="Salvestus on ümber kodeeritud, aga fail võib olla poolik" +Remux.SelectRecording="Vali OBS-i salvestus…" +Remux.SelectTarget="Vali sihtfail…" +Remux.FileExistsTitle="Sihtfail on olemas" +Remux.FileExists="Sihtfail on juba olemas, kas soovid selle asendada?" +Remux.ExitUnfinishedTitle="Kodeerimine on pooleli" +Remux.ExitUnfinished="Kodeerimine ei ole veel lõpetatud. Kui kodeerimine peatada, siis võib sihtfail olla kasutuskõlbmatu.\nKas soovid kindlasti kodeerimise peatada?" + +UpdateAvailable="Värskendus on saadaval" +UpdateAvailable.Text="Saadaval on version %1.%2.%3. Allalaadimiseks vajuta siia" + +Basic.DesktopDevice1="Töölaua heli" +Basic.DesktopDevice2="Töölaua heli 2" + +Basic.Scene="Stseen" + +Basic.Main.PreviewConextMenu.Enable="Lülita eelvaade sisse" + + + +Basic.Main.AddSceneDlg.Title="Stseeni lisamine" +Basic.Main.AddSceneDlg.Text="Sisesta stseeni nimi" + +Basic.Main.DefaultSceneName.Text="Stseen %1" + + + +AddProfile.Title="Profiili lisamine" +AddProfile.Text="Sisesta uue profiili nimi" + +RenameProfile.Title="Profiili ümbernimetamine" + +Basic.Main.PreviewDisabled="Eelvaade on hetkel välja lülitatud" + +Basic.SourceSelect="Loo/vali allikas" +Basic.SourceSelect.CreateNew="Loo uus" +Basic.SourceSelect.AddExisting="Lisa olemasolev" +Basic.SourceSelect.AddVisible="Tee allikas nähtavaks" + +Basic.PropertiesWindow="'%1' omadused" + + + + +Basic.Filters="Filtrid" +Basic.Filters.AudioFilters="Helifiltrid" +Basic.Filters.AddFilter.Title="Filtri nimi" + +Basic.TransformWindow.Rotation="Pööramine" +Basic.TransformWindow.Size="Suurus" +Basic.TransformWindow.Alignment="Joondamine" +Basic.TransformWindow.Crop="Kärbi" + +Basic.TransformWindow.Alignment.TopLeft="Ülale vasakule" +Basic.TransformWindow.Alignment.TopCenter="Ülale keskele" +Basic.TransformWindow.Alignment.TopRight="Ülale paremale" +Basic.TransformWindow.Alignment.CenterLeft="Keskele vasakule" +Basic.TransformWindow.Alignment.Center="Keskele" +Basic.TransformWindow.Alignment.CenterRight="Keskele paremale" +Basic.TransformWindow.Alignment.BottomLeft="Alla vasakule" +Basic.TransformWindow.Alignment.BottomCenter="Alla keskele" +Basic.TransformWindow.Alignment.BottomRight="Alla paremale" + + +Basic.Main.AddSourceHelp.Title="Allikat ei saa lisada" +Basic.Main.AddSourceHelp.Text="Allika lisamiseks peab olema vähemalt üks stseen." + +Basic.Main.Scenes="Stseenid" +Basic.Main.Sources="Allikad" +Basic.Main.Connecting="Ühendamine..." +Basic.Main.StartRecording="Alusta salvestamist" +Basic.Main.StartStreaming="Alusta voogedastust" +Basic.Main.StopRecording="Lõpeta salvestamine" +Basic.Main.StopStreaming="Lõpeta voogedastus" + +Basic.MainMenu.File="&Fail" +Basic.MainMenu.File.Export="&Ekspordi" +Basic.MainMenu.File.Import="&Impordi" +Basic.MainMenu.File.ShowRecordings="Ava salvestuste kaust (&R)" +Basic.MainMenu.File.Settings="&Sätted" +Basic.MainMenu.File.ShowSettingsFolder="Ava sätete kaust" +Basic.MainMenu.File.ShowProfileFolder="Ava profiilide kaust" +Basic.MainMenu.AlwaysOnTop="&Alati pealmine" +Basic.MainMenu.File.Exit="Välju (&X)" + +Basic.MainMenu.Edit="Muuda (&E)" +Basic.MainMenu.Edit.Undo="Võta tagasi (&U)" + + + +Basic.MainMenu.Help.Logs="&Logifailid" +Basic.MainMenu.Help.CheckForUpdates="Otsi värskendusi" + + +Basic.Settings.General.Language="Keel" + +Basic.Settings.Stream="Voogedastus" +Basic.Settings.Stream.StreamType="Voogedastuse tüüp" + +Basic.Settings.Output="Väljund" +Basic.Settings.Output.Format="Salvestusvorming" +Basic.Settings.Output.SelectDirectory="Vali salvestuskaust" +Basic.Settings.Output.Mode="Väljundrežiim" +Basic.Settings.Output.Mode.Simple="Lihtne" +Basic.Settings.Output.Mode.Adv="Täpsemad seaded" +Basic.Settings.Output.Simple.Encoder.Software="Tarkvara (x264)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Riistvara (QSV)" + +Basic.Settings.Output.Adv.AudioTrack="Helirada" +Basic.Settings.Output.Adv.Streaming="Voogedastus" +Basic.Settings.Output.Adv.Audio.Track1="Rada 1" +Basic.Settings.Output.Adv.Audio.Track2="Rada 2" +Basic.Settings.Output.Adv.Audio.Track3="Rada 3" +Basic.Settings.Output.Adv.Audio.Track4="Rada 4" + +Basic.Settings.Output.Adv.Recording="Salvestus" +Basic.Settings.Output.Adv.Recording.Type="Tüüp" +Basic.Settings.Output.Adv.Recording.Filename="Failinime vorming" +Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Kirjuta üle, kui fail on olemas" +Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg väljundi tüüp" +Basic.Settings.Output.Adv.FFmpeg.Type.URL="Kirjuta URL-ile" +Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Kirjuta faili" +Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Kõik failid" +Basic.Settings.Output.Adv.FFmpeg.SavePathURL="Faili rada või URL" +Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Heli" +Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Video" +Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Vaikevorming" + + + +Basic.Settings.Video="Video" +Basic.Settings.Video.Adapter="Kuvaadapter:" +Basic.Settings.Video.Numerator="Lugeja:" +Basic.Settings.Video.Denominator="Nimetaja:" +Basic.Settings.Video.InvalidResolution="Eraldusvõime ei sobi. See peab olema kujul [width]x[height] (nt 1920x1080)" + + + +Basic.Settings.Advanced.Video.ColorRange.Partial="Osaline" +Basic.Settings.Advanced.Video.ColorRange.Full="Täielik" + +Basic.AdvAudio.Name="Nimi" +Basic.AdvAudio.Volume="Helitugevus (%)" +Basic.AdvAudio.AudioTracks="Rajad" + + + +Hotkeys.Insert="Sisesta" +Hotkeys.Delete="Kustuta" + + + + diff --git a/obs/data/locale/eu-ES.ini b/obs/data/locale/eu-ES.ini index 73e37f5..97586a9 100644 --- a/obs/data/locale/eu-ES.ini +++ b/obs/data/locale/eu-ES.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS jardunean dago. Transmisio/grabazio guztiak itzaliko dira. ConfirmRemove.Title="Baieztatu kentzea" ConfirmRemove.Text="Ziur zaude '$1' kendu nahi duzula?" +ConfirmRemove.TextMultiple="Seguru zaude %1 elementuak ezabatu nahi dituzula?" Output.ConnectFail.Title="Huts egin du konektatzean" Output.ConnectFail.BadPath="Helburu edo konexio-URL okerra. Egiaztatu zure ezarpenak baliozkoak direla baieztatzeko." @@ -117,8 +118,8 @@ LicenseAgreement.Exit="Irten" Remux.SourceFile="OBS Grabazioa" Remux.TargetFile="Helburu-fitxategia" -Remux.Remux="Bermultiplexatu" -Remux.OBSRecording="OBS Grabazioa" +Remux.Remux="Multiplexatu" +Remux.OBSRecording="OBS grabazioa" Remux.FinishedTitle="Bihurketa amaituta" Remux.Finished="Grabazioa bihurtuta" Remux.FinishedError="Grabazioa bihurtua, baina fitxategia osatu gabe egon daiteke" @@ -126,7 +127,7 @@ Remux.SelectRecording="Hautatu OBS grabazioa…" Remux.SelectTarget="Hautatu helburu-fitxategia…" Remux.FileExistsTitle="Helburu-fitxategia badago" Remux.FileExists="Helburu-fitxategia badago, ordeztea nahi duzu?" -Remux.ExitUnfinishedTitle="Bermultiplexaketa garatzen" +Remux.ExitUnfinishedTitle="Multiplexazioa martxan" Remux.ExitUnfinished="Bihurketa ez da amaitu, orain gelditzeak fitxategi-helburua erabiltezin bihur dezake.\nZiur zaude bihurtzea gelditu nahi duzula?" UpdateAvailable="Eguneraketa eskuragarria" @@ -134,16 +135,22 @@ UpdateAvailable.Text="%1.%2.%3 bertsioa eskuragarri dago. Klikatu Basic.DesktopDevice1="Mahaigaineko audioa" Basic.DesktopDevice2="Mahaigaineko audioa 2" -Basic.AuxDevice1="Mik/Osag" -Basic.AuxDevice2="Mik/Osag 2" +Basic.AuxDevice1="Mikro/Osag" +Basic.AuxDevice2="Mikro/Osag 2" Basic.AuxDevice3="Mik/Osag 3" -Basic.AuxDevice4="Mik/Osag 4" +Basic.AuxDevice4="Mikro/Osag 4" Basic.Scene="Eszena" Basic.DisplayCapture="Pantaila-kaptura" Basic.Main.PreviewConextMenu.Enable="Gaitu aurrebista" +ScaleFiltering="Eskalaren iragazketa" +ScaleFiltering.Point="Puntua" +ScaleFiltering.Bilinear="Bilineala" +ScaleFiltering.Bicubic="Bikubikoa" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Desgurutzelarkatzea" Deinterlacing.Discard="Baztertu" Deinterlacing.Retro="Retro" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Ezarpenak aldatuta" Basic.PropertiesWindow.Confirm="Gorde gabeko aldaketak daude. Nahi duzu dagoena mantentzea?" Basic.PropertiesWindow.NoProperties="Ezaugarriak ez daude eskuragarri" Basic.PropertiesWindow.AddFiles="Gehitu fitxategiak" +Basic.PropertiesWindow.AddDir="Gehitu direktorioa" Basic.PropertiesWindow.AddURL="Gehitu bidea/URL-a" +Basic.PropertiesWindow.AddEditableListDir="Gehitu direktorioa '%1'-ra" Basic.PropertiesWindow.AddEditableListFiles="Gehitu fitxategiak '%1'-ra" Basic.PropertiesWindow.AddEditableListEntry="Gehitu sarrera '%1'-ra" Basic.PropertiesWindow.EditEditableListEntry="Editatu sarrera '%1'-tik" @@ -250,14 +259,16 @@ Basic.Main.Connecting="Konektatzen..." Basic.Main.StartRecording="Hasi grabazioa" Basic.Main.StartStreaming="Hasi transmisioa" Basic.Main.StopRecording="Gelditu grabazioa" +Basic.Main.StoppingRecording="Grabazioa gelditzen..." Basic.Main.StopStreaming="Gelditu transmisioa" +Basic.Main.StoppingStreaming="Transmisioa gelditzen..." Basic.Main.ForceStopStreaming="Gelditu transmisioa (baztertu atzerapena)" Basic.MainMenu.File="&Fitxategia" Basic.MainMenu.File.Export="&Esportatu" Basic.MainMenu.File.Import="&Inportatu" -Basic.MainMenu.File.ShowRecordings="Erakutsi &Grabazioak" -Basic.MainMenu.File.Remux="&Bermultiplexaketa Grabaketak" +Basic.MainMenu.File.ShowRecordings="Erakutsi &grabazioak" +Basic.MainMenu.File.Remux="Multiplexatu grabazioak" Basic.MainMenu.File.Settings="&Ezarpenak" Basic.MainMenu.File.ShowSettingsFolder="Erakutsi ezarpenen karpeta" Basic.MainMenu.File.ShowProfileFolder="Erakutsi profilaren karpeta" @@ -270,10 +281,10 @@ Basic.MainMenu.Edit.Redo="&Berregin" Basic.MainMenu.Edit.UndoAction="&Desegin $1" Basic.MainMenu.Edit.RedoAction="&Berregin $1" Basic.MainMenu.Edit.Transform="&Eraldatu" -Basic.MainMenu.Edit.Transform.EditTransform="E&ditatu Eraldaketa..." -Basic.MainMenu.Edit.Transform.ResetTransform="&Berrezarri Eraldaketa" +Basic.MainMenu.Edit.Transform.EditTransform="E&ditatu eraldaketa..." +Basic.MainMenu.Edit.Transform.ResetTransform="&Berrezarri eraldaketa" Basic.MainMenu.Edit.Transform.Rotate90CW="Biratu 90 gradu erlojuaren norabidean" -Basic.MainMenu.Edit.Transform.Rotate90CCW="Itzulikatu 90 gradu erlojuaren kontrako norabidean" +Basic.MainMenu.Edit.Transform.Rotate90CCW="Biratu 90 gradu erlojuaren kontrako norabidean" Basic.MainMenu.Edit.Transform.Rotate180="Biratu 180 gradu" Basic.MainMenu.Edit.Transform.FlipHorizontal="Irauli &horizontala" Basic.MainMenu.Edit.Transform.FlipVertical="Irauli &bertikala" @@ -287,11 +298,17 @@ Basic.MainMenu.Edit.Order.MoveToTop="Mugitu &goraino" Basic.MainMenu.Edit.Order.MoveToBottom="Mugitu &beheraino" Basic.MainMenu.Edit.AdvAudio="&Audio ezarpen aurreratuak" +Basic.MainMenu.View="Ikusi" +Basic.MainMenu.View.Toolbars="&Tresna barrak" +Basic.MainMenu.View.Toolbars.Listboxes="&Zerrenda-kutxak" +Basic.MainMenu.View.SceneTransitions="&Eszenen trantsizioak" +Basic.MainMenu.View.StatusBar="Egoera-barra" + Basic.MainMenu.SceneCollection="&Eszena-bilduma" Basic.MainMenu.Profile="&Profila" Basic.MainMenu.Help="&Laguntza" -Basic.MainMenu.Help.Website="Ikusi &Webgunea" +Basic.MainMenu.Help.Website="Ikusi &webgunea" Basic.MainMenu.Help.Logs="&Egunkari-fitxategiak" Basic.MainMenu.Help.Logs.ShowLogs="&Erakutsi egunkari-fitxategiak" Basic.MainMenu.Help.Logs.UploadCurrentLog="Kargatu &uneko egunkari-fitxategiak" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Gaia" Basic.Settings.General.Language="Hizkuntza" Basic.Settings.General.WarnBeforeStartingStream="Erakutsi baieztapen elkarrizketa transmisioak hasterakoan" Basic.Settings.General.WarnBeforeStoppingStream="Erakutsi baieztapen elkarrizketa transmisioak gelditzerakoan" +Basic.Settings.General.HideProjectorCursor="Ezkutatu kurtsorea proiekzioetan" Basic.Settings.General.Snapping="Iturburuaren lerrokatzearen doitzea" Basic.Settings.General.ScreenSnapping="Doitu iturburuak pantailaren ertzera" Basic.Settings.General.CenterSnapping="Doitu iturburuak bertikalki eta horizontalki erdira" Basic.Settings.General.SourceSnapping="Doitu iturburuak beste iturburuetara" Basic.Settings.General.SnapDistance="Doitu sentikortasuna" +Basic.Settings.General.RecordWhenStreaming="Grabatu automatikoki transmisioa egitean" +Basic.Settings.General.KeepRecordingWhenStreamStops="Mantendu grabazioa transmisioa gelditzean" Basic.Settings.Stream="Transmisioa" Basic.Settings.Stream.StreamType="Transmisio-mota" @@ -370,7 +390,7 @@ Basic.Settings.Output.Adv.Recording.Type.Standard="Estandarra" Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Irteera pertsonalizatua (FFmpeg)" Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Erabili transmisio kodetzailea)" Basic.Settings.Output.Adv.Recording.Filename="Fitxategi-izenaren formatua" -Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Gainidatzi fitxategia badago" +Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Gainidatzi fitxategia" Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg Irteera-mota" Basic.Settings.Output.Adv.FFmpeg.Type.URL="Irteera URL-ra" Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Irteera fitxategira" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Sakatu-hitz-egiteko atzerapena" Basic.Settings.Audio.UnknownAudioDevice="[Gailua konektatu gabe edo ez dago eskuragarri]" Basic.Settings.Advanced="Aurreratua" +Basic.Settings.Advanced.General.ProcessPriority="Prozesuaren lehentasuna" +Basic.Settings.Advanced.General.ProcessPriority.High="Altua" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Normala baino handiagoa" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normala" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Inaktiboa" Basic.Settings.Advanced.FormatWarning="Oharra: NV12 ez diren kolore formatuak grabaziorako dira gehienbat, eta ez daude gomendatuta transmisiorako. Transmisioak PUZ erabilpena handitu dezake kolore formatu bihurketa dela medio." Basic.Settings.Advanced.Audio.BufferingTime="Audio bufferratze denbora" Basic.Settings.Advanced.Video.ColorFormat="Kolore formatua" diff --git a/obs/data/locale/fi-FI.ini b/obs/data/locale/fi-FI.ini index dc3ba0d..5d623f8 100644 --- a/obs/data/locale/fi-FI.ini +++ b/obs/data/locale/fi-FI.ini @@ -87,12 +87,13 @@ ConfirmExit.Text="OBS on aktiivinen. Kaikki lähetykset/tallennukset suljetaan. ConfirmRemove.Title="Vahvista poisto" ConfirmRemove.Text="Haluatko varmasti poistaa '$1'?" +ConfirmRemove.TextMultiple="Haluatko varmasti poistaa %1 kohdetta?" Output.ConnectFail.Title="Yhdistäminen epäonnistui" Output.ConnectFail.BadPath="Viallinen polku tai yhteysosoite. Tarkista, että asetuksesi ovat kunnossa." Output.ConnectFail.ConnectFailed="Palvelimelle yhdistäminen epäonnistui" Output.ConnectFail.InvalidStream="Kanavaa tai lähetysavainta ei voida käyttää. Tarkista lähetysavain varmuuden vuoksi. Jos se kuitenkin on oikein, vika saattaa olla yhdistettävässä palvelimessa." -Output.ConnectFail.Error="Odottamaton virhe ilmeni, kun palvelimelle yritettiin yhdistää. Lisää tietoa saat lokitiedostosta." +Output.ConnectFail.Error="Odottamaton virhe ilmeni palvelimeen yhdistäessä. Lisää tietoa saat lokitiedostosta." Output.ConnectFail.Disconnected="Yhteys palvelimeen katkaistiin." Output.RecordFail.Title="Tallennuksen aloittaminen epäonnistui" @@ -127,7 +128,7 @@ Remux.SelectTarget="Valitse kohdetiedosto ..." Remux.FileExistsTitle="Kohdetiedosto on jo olemassa" Remux.FileExists="Kohdetiedosto on jo olemassa, haluatko korvata sen?" Remux.ExitUnfinishedTitle="Muunto on käynnissä" -Remux.ExitUnfinished="Muunto ei ole valmis. Jos keskeytät nyt, kohdetiedostosta saattaa tulla käyttökelvoton.\nHaluatko varmasti pysäyttää remuxauksen?" +Remux.ExitUnfinished="Muunto ei ole valmis. Jos keskeytät nyt, kohdetiedostosta saattaa tulla käyttökelvoton.\nHaluatko varmasti pysäyttää muunnon?" UpdateAvailable="Uusi päivitys saatavilla" UpdateAvailable.Text="Versio %1.%2.%3 on nyt saatavilla. Lataa tästä" @@ -140,10 +141,12 @@ Basic.AuxDevice3="Mic/Aux 3" Basic.AuxDevice4="Mic/Aux 4" Basic.Scene="Skene" -Basic.DisplayCapture="Kaappaa monitori" +Basic.DisplayCapture="Monitori" Basic.Main.PreviewConextMenu.Enable="Näytä esikatselu" +ScaleFiltering="Asteikko suodatus" + Deinterlacing="Lomituksen poisto (Deinterlace)" Deinterlacing.Discard="Ohita" Deinterlacing.Retro="Retro" @@ -186,7 +189,9 @@ Basic.PropertiesWindow.ConfirmTitle="Asetukset ovat muuttuneet" Basic.PropertiesWindow.Confirm="Muutoksia ei ole vielä tallennettu. Haluatko pitää muutokset?" Basic.PropertiesWindow.NoProperties="Ominaisuuksia ei ole saatavilla" Basic.PropertiesWindow.AddFiles="Lisää tiedostoja" +Basic.PropertiesWindow.AddDir="Lisää kansio" Basic.PropertiesWindow.AddURL="Lisää polku/URL-osoite" +Basic.PropertiesWindow.AddEditableListDir="Lisää kansio \"%1\":n" Basic.PropertiesWindow.AddEditableListFiles="Lisää tiedostoja '%1'" Basic.PropertiesWindow.AddEditableListEntry="Lisää merkintä '%1'" Basic.PropertiesWindow.EditEditableListEntry="Muokkaa merkintää '%1'" @@ -228,7 +233,7 @@ Basic.TransformWindow.Alignment.TopCenter="Yläkeski" Basic.TransformWindow.Alignment.TopRight="Yläoikea" Basic.TransformWindow.Alignment.CenterLeft="Keskivasen" Basic.TransformWindow.Alignment.Center="Keskellä" -Basic.TransformWindow.Alignment.CenterRight="Keskioieka" +Basic.TransformWindow.Alignment.CenterRight="Keskioikea" Basic.TransformWindow.Alignment.BottomLeft="Alavasen" Basic.TransformWindow.Alignment.BottomCenter="Alakeski" Basic.TransformWindow.Alignment.BottomRight="Alaoikea" @@ -242,7 +247,7 @@ Basic.TransformWindow.BoundsType.ScaleToHeight="Skaalaa korkeuden rajoihin" Basic.TransformWindow.BoundsType.Stretch="Venytä rajoihin" Basic.Main.AddSourceHelp.Title="Lähdettä ei voi lisätä" -Basic.Main.AddSourceHelp.Text="Tarvitset vähintään yhden skenen, jotta voit lisätä lähteen." +Basic.Main.AddSourceHelp.Text="Tarvitset vähintään yhden skenen lisätäksesi lähteen." Basic.Main.Scenes="Skenet" Basic.Main.Sources="Lähteet" @@ -250,7 +255,9 @@ Basic.Main.Connecting="Yhdistetään..." Basic.Main.StartRecording="Aloita tallennus" Basic.Main.StartStreaming="Aloita lähetys" Basic.Main.StopRecording="Pysäytä tallennus" +Basic.Main.StoppingRecording="Pysäytetään tallennusta..." Basic.Main.StopStreaming="Pysäytä lähetys" +Basic.Main.StoppingStreaming="Pysäytetään lähetystä..." Basic.Main.ForceStopStreaming="Lopeta lähetys (ohita viive)" Basic.MainMenu.File="&Tiedosto" @@ -272,9 +279,9 @@ Basic.MainMenu.Edit.RedoAction="T&ee uudelleen $1" Basic.MainMenu.Edit.Transform="Muu&nna" Basic.MainMenu.Edit.Transform.EditTransform="M&uokkaa muunnosta..." Basic.MainMenu.Edit.Transform.ResetTransform="&Nollaa muunnos" -Basic.MainMenu.Edit.Transform.Rotate90CW="Pyöritä 90 astetta myötäpäivään" -Basic.MainMenu.Edit.Transform.Rotate90CCW="Pyöritä 90 astetta vastapäivään" -Basic.MainMenu.Edit.Transform.Rotate180="Pyöritä 180 astetta" +Basic.MainMenu.Edit.Transform.Rotate90CW="Kierrä 90 astetta myötäpäivään" +Basic.MainMenu.Edit.Transform.Rotate90CCW="Kierrä 90 astetta vastapäivään" +Basic.MainMenu.Edit.Transform.Rotate180="Kierrä 180 astetta" Basic.MainMenu.Edit.Transform.FlipHorizontal="Käännä &vaakatasossa" Basic.MainMenu.Edit.Transform.FlipVertical="Käännä &pystytasossa" Basic.MainMenu.Edit.Transform.FitToScreen="&Sovita ikkunaan" @@ -287,6 +294,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Siirrä &ylimmäksi" Basic.MainMenu.Edit.Order.MoveToBottom="Siirrä &alimmaksi" Basic.MainMenu.Edit.AdvAudio="&Äänen lisäominaisuudet" +Basic.MainMenu.View="&Näkymät" +Basic.MainMenu.View.Toolbars="&Työkalurivit" +Basic.MainMenu.View.Toolbars.Listboxes="&Luetteluruudut" +Basic.MainMenu.View.SceneTransitions="&Skene-siirtymät" +Basic.MainMenu.View.StatusBar="&Tilapalkki" + Basic.MainMenu.SceneCollection="&Skene-kokoelma" Basic.MainMenu.Profile="&Profiili" @@ -296,10 +309,10 @@ Basic.MainMenu.Help.Logs="&Lokitiedostot" Basic.MainMenu.Help.Logs.ShowLogs="&Näytä lokitiedostot" Basic.MainMenu.Help.Logs.UploadCurrentLog="Lähetä n&ykyinen lokitiedosto" Basic.MainMenu.Help.Logs.UploadLastLog="Lähetä edellinen lokitiedosto" -Basic.MainMenu.Help.Logs.ViewCurrentLog="&Katso nykyinen loki" +Basic.MainMenu.Help.Logs.ViewCurrentLog="Näytä ny&kyinen loki" Basic.MainMenu.Help.CheckForUpdates="Tarkista päivitykset" -Basic.Settings.ProgramRestart="Ohjelma pitää käynnistää uudelleen, jotta asetukset tulevat voimaan." +Basic.Settings.ProgramRestart="Ohjelma on käynnistettävä uudelleen, jotta asetukset tulevat voimaan." Basic.Settings.ConfirmTitle="Vahvista muutokset" Basic.Settings.Confirm="Sinulla on tallentamattomia muutoksia. Tallennetaanko?" @@ -307,12 +320,14 @@ Basic.Settings.General="Yleiset" Basic.Settings.General.Theme="Teema" Basic.Settings.General.Language="Kieli" Basic.Settings.General.WarnBeforeStartingStream="Näytä varmistus-ikkuna kun lähetys aloitetaan" -Basic.Settings.General.WarnBeforeStoppingStream="Näytä varmistusikkuna kun lähetys pysäytetään" +Basic.Settings.General.WarnBeforeStoppingStream="Näytä varmistus-ikkuna kun lähetys pysäytetään" Basic.Settings.General.Snapping="Lähteiden kiinnitys" Basic.Settings.General.ScreenSnapping="Kiinnitä lähteitä ruudun reunaan" Basic.Settings.General.CenterSnapping="Kiinnitä lähteitä vaaka- sekä pystysuunnan keskilinjaan" Basic.Settings.General.SourceSnapping="Kiinnitä lähteitä muihin lähteisiin" Basic.Settings.General.SnapDistance="Kiinnityksen herkkyys" +Basic.Settings.General.RecordWhenStreaming="Tallenna automaattisesti kun lähetetään" +Basic.Settings.General.KeepRecordingWhenStreamStops="Jatka tallennusta lähetyksen loputtua" Basic.Settings.Stream="Lähetys" Basic.Settings.Stream.StreamType="Lähetystyyppi" @@ -329,10 +344,10 @@ Basic.Settings.Output.Mode.Adv="Kehittynyt" Basic.Settings.Output.Mode.FFmpeg="FFmpeg ulostulo" Basic.Settings.Output.Simple.SavePath="Tallennuksen polku" Basic.Settings.Output.Simple.RecordingQuality="Tallennuksen laatu" -Basic.Settings.Output.Simple.RecordingQuality.Stream="Lähetyksen laatu" +Basic.Settings.Output.Simple.RecordingQuality.Stream="Sama kuin lähetyksessä" Basic.Settings.Output.Simple.RecordingQuality.Small="Korkea laatu, keskikokoinen tiedostokoko" Basic.Settings.Output.Simple.RecordingQuality.HQ="Erottamaton laatu, suuri tiedostokoko" -Basic.Settings.Output.Simple.RecordingQuality.Lossless="Häviötön laatu, jättimäinen tiedostokoko" +Basic.Settings.Output.Simple.RecordingQuality.Lossless="Häviötön laatu, valtava tiedostokoko" Basic.Settings.Output.Simple.Warn.VideoBitrate="Varoitus: Kuvan bitrate asetetaan arvoon %1, joka on yläraja valitsemassasi palvelussa. Jos haluat varmasti mennä %1:n yli, poista valinta lisäasetuksista kohdasta \"Rajoita bitrate lähetyspalvelun suosituksiin\"." Basic.Settings.Output.Simple.Warn.AudioBitrate="Varoitus: Äänen bitrate asetetaan arvoon %1, joka on yläraja valitsemassasi palvelussa. Jos haluat varmasti mennä %1:n yli, poista valinta lisäasetuksista kohdasta \"Rajoita bitrate lähetyspalvelun suosituksiin\"." Basic.Settings.Output.Simple.Warn.Encoder="Varoitus: Tallentaminen lähetyksestä eroavalla laadulla vaatii prosessorilta lisätyötä jos lähetät ja tallennat samanaikaisesti." @@ -340,20 +355,20 @@ Basic.Settings.Output.Simple.Warn.Lossless="Varoitus: Häviötön laatu luo jär Basic.Settings.Output.Simple.Warn.Lossless.Msg="Haluatko varmasti käyttää häviötöntä laatua?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Häviötön laatu!" Basic.Settings.Output.Simple.Warn.MultipleQSV="Varoitus: Et voi käyttää useampaa QSV-enkooderia lähettäessä ja tallentaessa samaan aikaan. Jos haluat tehdä molempia yhtä aikaa, vaihda lähetys tai tallennus-enkooderi." -Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" -Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Laitteisto (QSV)" -Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Laitteisto (NVENC)" -Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 matala CPU-käyttö, lisää tiedostokokoa)" +Basic.Settings.Output.Simple.Encoder.Software="Ohjelmistopohjainen (x264)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Laitteistopohjainen (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Laitteistopohjainen (NVENC)" +Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Ohjelmistopohjainen (x264 matala CPU-käyttö, lisää tiedostokokoa)" Basic.Settings.Output.VideoBitrate="Kuvan bitrate" Basic.Settings.Output.AudioBitrate="Äänen bitrate" Basic.Settings.Output.Reconnect="Automaattinen uudelleenyhdistys" Basic.Settings.Output.RetryDelay="Uudelleenyrityksen viive (sekunteja)" Basic.Settings.Output.MaxRetries="Uudelleenyhdistyksien määrä" Basic.Settings.Output.Advanced="Käytä enkooderin lisäasetuksia" -Basic.Settings.Output.EncoderPreset="Enkooderin asetus (korkeampi lisää tehon kulutusta)" -Basic.Settings.Output.CustomEncoderSettings="Valinnaiset enkooderiasetukset" -Basic.Settings.Output.CustomMuxerSettings="Valinnaiset muuntaja-asetukset" -Basic.Settings.Output.NoSpaceFileName="Generoi tiedostonimi ilman välilyöntejä" +Basic.Settings.Output.EncoderPreset="Enkooderin asetus (hitaampi asetus lisää suorittimen käyttöä)" +Basic.Settings.Output.CustomEncoderSettings="Valinnaiset enkooderin asetukset" +Basic.Settings.Output.CustomMuxerSettings="Valinnaiset muuntajan asetukset" +Basic.Settings.Output.NoSpaceFileName="Luo tiedostonimi ilman välilyöntejä" Basic.Settings.Output.Adv.Rescale="Uudelleenskaalaa ulostulo" Basic.Settings.Output.Adv.AudioTrack="Ääniraita" @@ -382,23 +397,23 @@ Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Ääni" Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Kuva" Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Oletusmuoto" Basic.Settings.Output.Adv.FFmpeg.FormatDesc="Säiliömuodon kuvaus" -Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Ääni/Kuva koodekki arvataan tiedostopäätteen tai osoitteen perusteella" +Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Ääni/Kuva koodekki päätellään tiedostopäätteen tai osoitteen perusteella" Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Oletus" Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Poista enkooderi käytöstä" Basic.Settings.Output.Adv.FFmpeg.VEncoder="Videoenkooderi" -Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Video-enkooderin asetukset" +Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Videoenkooderin asetukset" Basic.Settings.Output.Adv.FFmpeg.AEncoder="Äänienkooderi" -Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Ääni-enkooderin asetukset" -Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Mukserin asetukset" +Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Äänienkooderin asetukset" +Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muunnon asetukset" FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z" -FilenameFormatting.TT="%CCYY vuosi, neljä digits\n%YY vuoden kaksi viimeistä numeroa (00-99)\n%MM kuukauden desimaalin numero (01-12) \n%DD nolla-pehmustettu kuukauden päivänä (01-31)\n%hh Fi 24h muodossa (00-23)\n%mm minuutin (00-59) \n%ss toinen (00-61) \n%% A % sign\n%a Abbreviated arkipäivä name\n%A koko päivän name\n%b Abbreviated kuukauden name\n%B Kuukauden koko kuukauden, päivän name\n%d nolla-pehmustettu (01-31)\n%H tunnin 24h muodossa (00-23) \n%I Fi 12h muodossa (01-12)\n%m kuukauden desimaalilukuna (01-12)\n%M minuutin (00-59) \n%p AM tai PM designation\n%S toinen (00-61)\n%y vuoden kaksi viimeistä numeroa (00-99)\n%Y Year\n%z ISO 8601 offset UTC tai timezone\n tai abbreviation\n%Z Aikavyöhykkeen nimi tai abbreviation\n" +FilenameFormatting.TT="%CCYY vuosi, neljä numeroa\n%YY Vuosi, kaksi viimeistä numeroa (00-99)\n%MM Kuukausi, kymmenlukuna (01-12) \n%DD Päivä, nolla-etuliitteellä (01-31)\n%hh Tunti, 24-tunnin muodossa (00-23)\n%mm Minuutti (00-59) \n%ss Sekunti (00-61) \n%% %-merkki\n%a Lyhennetty viikonpäivän nimi\n%A Täysi viikonpäivän nimi\n%b Lyhennetty kuukauden nimi\n%B Täysi kuukauden nimi\n%d Kuukauden päivä, nolla-etuliitteellä (01-31)\n%H Tunti, 24-tunnin muodossa (00-23) \n%I Tunti, 12-tunnin muodossa (01-12)\n%m Kuukausi, kymmenlukuna (01-12)\n%M Minuutti (00-59) \n%p AM tai PM merkintä\n%S Sekunti (00-61)\n%y Vuosi, kaksi viimeistä numeroa (00-99)\n%Y Vuosi\n%z ISO 8601 standardin mukainen ajan esittämistapa, UTC tai aikavyöhykkeestä\n Nimi tai lyhennne\n%Z Aikavyöhykkeen nimi tai lyhenne\n" Basic.Settings.Video="Kuva" Basic.Settings.Video.Adapter="Näytönohjain:" Basic.Settings.Video.BaseResolution="Piirtoalueen resoluutio:" -Basic.Settings.Video.ScaledResolution="Skaalattu resoluutio:" +Basic.Settings.Video.ScaledResolution="Ulostulon (skaalattu) resoluutio:" Basic.Settings.Video.DownscaleFilter="Skaalaussuodatin:" Basic.Settings.Video.DisableAeroWindows="Poista Aero käytöstä (Vain Windows)" Basic.Settings.Video.FPS="FPS:" @@ -412,7 +427,7 @@ Basic.Settings.Video.InvalidResolution="Vialliset resoluution asetus. Muodon pi Basic.Settings.Video.CurrentlyActive="Kuvan ulostulo on tällä hetkellä käytössä. Kytke pois kaikki ulostulot vaihtaaksesi kuva-asetuksia." Basic.Settings.Video.DisableAero="Poista Aero käytöstä" -Basic.Settings.Video.DownscaleFilter.Bilinear="Bilinear (nopein, sumeutuu skaalattaessa)" +Basic.Settings.Video.DownscaleFilter.Bilinear="Bilinear (nopein, mutta epätarkka skaalattaessa)" Basic.Settings.Video.DownscaleFilter.Bicubic="Bicubic (Terävöity skaalaus, 16 näytettä)" Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Terävöity skaalaus, 32 näytettä)" @@ -424,16 +439,21 @@ Basic.Settings.Audio.DesktopDevice2="Äänentoistolaite 2" Basic.Settings.Audio.AuxDevice="Mic/Aux -äänilaite" Basic.Settings.Audio.AuxDevice2="Mic/Aux -äänilaite 2" Basic.Settings.Audio.AuxDevice3="Mic/Aux -äänilaite 3" -Basic.Settings.Audio.EnablePushToMute="Käytä Push-to-mute" -Basic.Settings.Audio.PushToMuteDelay="Push-to-mute viive" -Basic.Settings.Audio.EnablePushToTalk="Käytä Push-to-talk" -Basic.Settings.Audio.PushToTalkDelay="Push-to-talk viive" +Basic.Settings.Audio.EnablePushToMute="Ota Push-to-mute käyttöön" +Basic.Settings.Audio.PushToMuteDelay="Push-to-muten viive" +Basic.Settings.Audio.EnablePushToTalk="Ota Push-to-talk käyttöön" +Basic.Settings.Audio.PushToTalkDelay="Push-to-talkin viive" Basic.Settings.Audio.UnknownAudioDevice="[Laitetta ei ole yhdistetty tai se ei ole saatavissa]" Basic.Settings.Advanced="Lisäasetukset" -Basic.Settings.Advanced.FormatWarning="Varoitus: Muut väriformaatit kuin NV12, ovat tarkoitettuja tallentamiseen eikä niitä suositella lähetyksiin. Lähetykset saattavat johtaa suurempaan CPU:n kulutukseen väriformaatin muutoksen vuoksi." +Basic.Settings.Advanced.General.ProcessPriority="Prosessin prioriteetti" +Basic.Settings.Advanced.General.ProcessPriority.High="Suuri" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Suurempi kuin normaali" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normaali" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Pieni" +Basic.Settings.Advanced.FormatWarning="Varoitus: Muut kuin NV12 väriformaatit ovat tarkoitettuja tallentamiseen, eikä niitä suositella lähettämiseen. Väriformaation konversio saattaa johtaa korkeampaan suorittimen käyttöön lähettämisessä." Basic.Settings.Advanced.Audio.BufferingTime="Äänen puskurointiaika" -Basic.Settings.Advanced.Video.ColorFormat="Värimuoto" +Basic.Settings.Advanced.Video.ColorFormat="Väriformaatti" Basic.Settings.Advanced.Video.ColorSpace="YUV väriavaruus" Basic.Settings.Advanced.Video.ColorRange="YUV värialue" Basic.Settings.Advanced.Video.ColorRange.Partial="Osittainen" @@ -448,7 +468,7 @@ Basic.AdvAudio.Name="Nimi" Basic.AdvAudio.Volume="Äänenvoimakkuus (%)" Basic.AdvAudio.Mono="Miksaa yksikanavaiseksi" Basic.AdvAudio.Panning="Balanssi" -Basic.AdvAudio.SyncOffset="Sync Offset (ms)" +Basic.AdvAudio.SyncOffset="Synkronoinnin viivästys (ms)" Basic.AdvAudio.AudioTracks="Raidat" Basic.Settings.Hotkeys="Pikanäppäimet" diff --git a/obs/data/locale/fr-FR.ini b/obs/data/locale/fr-FR.ini index 82b1495..ad76135 100644 --- a/obs/data/locale/fr-FR.ini +++ b/obs/data/locale/fr-FR.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS est actuellement actif. Tous les streams/enregistrements ConfirmRemove.Title="Confirmer la suppression" ConfirmRemove.Text="Êtes-vous sûr de vouloir supprimer « $1 » ?" +ConfirmRemove.TextMultiple="Voulez-vous vraiment supprimer %1 éléments ?" Output.ConnectFail.Title="Échec de la connexion" Output.ConnectFail.BadPath="Adresse de connexion ou chemin invalide. Veuillez vérifier vos paramètres afin de confirmer leur validité." @@ -144,6 +145,12 @@ Basic.DisplayCapture="Afficher la capture" Basic.Main.PreviewConextMenu.Enable="Activer l'aperçu" +ScaleFiltering="Échelle de filtrage" +ScaleFiltering.Point="Point" +ScaleFiltering.Bilinear="Bilinéaire" +ScaleFiltering.Bicubic="Bicubique" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Désentrelacement" Deinterlacing.Discard="Abandonner" Deinterlacing.Retro="Rétro" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Paramètres modifiés" Basic.PropertiesWindow.Confirm="Des modifications n'ont pas été enregistrées. Voulez-vous les conserver ?" Basic.PropertiesWindow.NoProperties="Aucune propriété disponible" Basic.PropertiesWindow.AddFiles="Ajouter des fichiers" +Basic.PropertiesWindow.AddDir="Ajouter un répertoire" Basic.PropertiesWindow.AddURL="Ajouter un chemin/URL" +Basic.PropertiesWindow.AddEditableListDir="Ajouter un répertoire à '%1'" Basic.PropertiesWindow.AddEditableListFiles="Ajouter des fichiers à '%1'" Basic.PropertiesWindow.AddEditableListEntry="Ajouter une entrée à '%1'" Basic.PropertiesWindow.EditEditableListEntry="Éditer l'entrée de '%1'" @@ -200,10 +209,10 @@ Basic.InteractionWindow="Intéragir avec '%1'" Basic.StatusBar.Reconnecting="Déconnecté, reconnexion dans %2 seconde(s) (tentative n° %1)" Basic.StatusBar.AttemptingReconnect="Tentative de reconnexion... (tentative n° %1)" Basic.StatusBar.ReconnectSuccessful="Reconnexion réussie" -Basic.StatusBar.Delay="Délai (%1 s)" -Basic.StatusBar.DelayStartingIn="Délai (commence dans %1 s)" -Basic.StatusBar.DelayStoppingIn="Délai (s'arrête dans %1 s)" -Basic.StatusBar.DelayStartingStoppingIn="Délai (s'arrête dans %1 s, commence dans %2 s)" +Basic.StatusBar.Delay="Retard (%1 s)" +Basic.StatusBar.DelayStartingIn="Retard (commence dans %1 s)" +Basic.StatusBar.DelayStoppingIn="Retard (s'arrête dans %1 s)" +Basic.StatusBar.DelayStartingStoppingIn="Retard (s'arrête dans %1 s, commence dans %2 s)" Basic.Filters="Filtres" Basic.Filters.AsyncFilters="Filtres audio/vidéo" @@ -250,8 +259,10 @@ Basic.Main.Connecting="Connexion en cours..." Basic.Main.StartRecording="Démarrer l'enregistrement" Basic.Main.StartStreaming="Commencer le streaming" Basic.Main.StopRecording="Arrêter l'enregistrement" +Basic.Main.StoppingRecording="Arrêt de l'enregistrement..." Basic.Main.StopStreaming="Arrêter le streaming" -Basic.Main.ForceStopStreaming="Arrêter le streaming (annule le délai)" +Basic.Main.StoppingStreaming="Arrêt du stream..." +Basic.Main.ForceStopStreaming="Arrêter le streaming (annule le retard)" Basic.MainMenu.File="&Fichier" Basic.MainMenu.File.Export="&Exporter" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Déplacer tout en &haut" Basic.MainMenu.Edit.Order.MoveToBottom="Déplacer tout en &bas" Basic.MainMenu.Edit.AdvAudio="Propriétés audio &avancées" +Basic.MainMenu.View="&Afficher" +Basic.MainMenu.View.Toolbars="&Barres d'outils" +Basic.MainMenu.View.Toolbars.Listboxes="&Listes" +Basic.MainMenu.View.SceneTransitions="&Transition de scènes" +Basic.MainMenu.View.StatusBar="&Barre d'état" + Basic.MainMenu.SceneCollection="Collection de &scènes" Basic.MainMenu.Profile="&Profil" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Thème" Basic.Settings.General.Language="Langue" Basic.Settings.General.WarnBeforeStartingStream="Afficher une boîte de dialogue de confirmation au démarrage d'un stream" Basic.Settings.General.WarnBeforeStoppingStream="Afficher une boîte de dialogue de confirmation à l'arrêt d'un stream" +Basic.Settings.General.HideProjectorCursor="Cacher le curseur sur les projecteurs" Basic.Settings.General.Snapping="Déclenchement d'alignement des sources" Basic.Settings.General.ScreenSnapping="Déclencher avec les bords de l'écran" Basic.Settings.General.CenterSnapping="Déclencher avec le centre de l'écran" Basic.Settings.General.SourceSnapping="Déclencher avec d'autres sources" Basic.Settings.General.SnapDistance="Sensibilité du déclenchement" +Basic.Settings.General.RecordWhenStreaming="Enregistrer automatiquement lors d'un stream" +Basic.Settings.General.KeepRecordingWhenStreamStops="Continuer à enregistrer lorsque le stream s’arrête" Basic.Settings.Stream="Flux" Basic.Settings.Stream.StreamType="Type de diffusion" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Délai du Push-to-talk" Basic.Settings.Audio.UnknownAudioDevice="[Périphérique non connecté ou non disponible]" Basic.Settings.Advanced="Avancé" +Basic.Settings.Advanced.General.ProcessPriority="Priorité du processus" +Basic.Settings.Advanced.General.ProcessPriority.High="Haute" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Supérieure à la normale" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normale" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Basse" Basic.Settings.Advanced.FormatWarning="Attention : les formats de couleur autres que NV12 sont principalement destinés à l'enregistrement, et ne sont pas recommandés pour le streaming. Le streaming peut pâtir d'une utilisation élevée du CPU due à la conversion de format de couleur." Basic.Settings.Advanced.Audio.BufferingTime="Temps de mise en mémoire tampon audio" Basic.Settings.Advanced.Video.ColorFormat="Format de couleur" @@ -438,9 +463,9 @@ Basic.Settings.Advanced.Video.ColorSpace="Espace de couleur YUV" Basic.Settings.Advanced.Video.ColorRange="Gamme de couleurs YUV" Basic.Settings.Advanced.Video.ColorRange.Partial="Partielle" Basic.Settings.Advanced.Video.ColorRange.Full="Complète" -Basic.Settings.Advanced.StreamDelay="Délai du stream" +Basic.Settings.Advanced.StreamDelay="Retard du stream" Basic.Settings.Advanced.StreamDelay.Duration="Durée (en secondes)" -Basic.Settings.Advanced.StreamDelay.Preserve="Préserver le point de coupure (augmente le délai) lors d'une reconnexion" +Basic.Settings.Advanced.StreamDelay.Preserve="Préserver le point de coupure (augmente le retard) lors d'une reconnexion" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Utilisation estimée de la mémoire : %1 Mo" Basic.AdvAudio="Propriétés audio avancées" diff --git a/obs/data/locale/gl-ES.ini b/obs/data/locale/gl-ES.ini index ea5f0f7..604d47e 100644 --- a/obs/data/locale/gl-ES.ini +++ b/obs/data/locale/gl-ES.ini @@ -125,6 +125,7 @@ Basic.DisplayCapture="Captura de pantalla" Basic.Main.PreviewConextMenu.Enable="Habilitar vista previa" + Basic.Main.AddSceneDlg.Title="Engadir escena" Basic.Main.AddSceneDlg.Text="Por favor, insire un nome para a escena" @@ -242,6 +243,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Levar para a cima" Basic.MainMenu.Edit.Order.MoveToBottom="Levar para abaixo" Basic.MainMenu.Edit.AdvAudio="Propiedades de audio &avanzadas" + Basic.MainMenu.SceneCollection="&Colección de escenas" Basic.MainMenu.Profile="&Perfil" diff --git a/obs/data/locale/he-IL.ini b/obs/data/locale/he-IL.ini index 3a59890..0f4bc61 100644 --- a/obs/data/locale/he-IL.ini +++ b/obs/data/locale/he-IL.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="תוכנת OBS פעילה כעת. כל הזרמת נתוני ConfirmRemove.Title="אשר הסרה" ConfirmRemove.Text="האם אתה בטוח שברצונך להסיר את '$1'?" +ConfirmRemove.TextMultiple="האם אתה בטוח שברצונך להסיר %1 פריטים?" Output.ConnectFail.Title="ההתחברות נכשלה" Output.ConnectFail.BadPath="URL לא חוקי של נתיב או חיבור. נא בדוק את ההגדרות שלך כדי לוודא כי הם נכונים." @@ -118,6 +119,7 @@ LicenseAgreement.Exit="יציאה" Remux.SourceFile="הקלטת OBS" Remux.TargetFile="קובץ היעד" Remux.Remux="המרה" +Remux.OBSRecording="הקלטת OBS" Remux.FinishedTitle="המרה הסתיימה" Remux.Finished="הקלטה הומרה" Remux.FinishedError="הקלטה הומרה, אבל הקובץ עשוי להיות לא שלם" @@ -143,6 +145,12 @@ Basic.DisplayCapture="הצג לכידת מסך" Basic.Main.PreviewConextMenu.Enable="אפשר תצוגה מקדימה" +ScaleFiltering="מסנן קנה מידה" +ScaleFiltering.Point="נקודה" +ScaleFiltering.Bilinear="ביליניארי" +ScaleFiltering.Bicubic="ביקיוביק" +ScaleFiltering.Lanczos="לנזוס" + Deinterlacing="ביטול שזירה" Deinterlacing.Discard="אל תשמור" Deinterlacing.Retro="רטרו" @@ -185,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="הגדרות שונו" Basic.PropertiesWindow.Confirm="יש שינויים שלא נשמרו. האם ברצונך לשמור אותם?" Basic.PropertiesWindow.NoProperties="אין מאפיינים זמינים" Basic.PropertiesWindow.AddFiles="הוסף קבצים" +Basic.PropertiesWindow.AddDir="הוסף ספריה" Basic.PropertiesWindow.AddURL="הוסף נתיב/כתובת" +Basic.PropertiesWindow.AddEditableListDir="הוסף ספריה ל '%1'" Basic.PropertiesWindow.AddEditableListFiles="הוסף קבצים '%1'" Basic.PropertiesWindow.AddEditableListEntry="הוסף ערך ל- '%1'" Basic.PropertiesWindow.EditEditableListEntry="ערוך ערך של '%1'" @@ -249,7 +259,9 @@ Basic.Main.Connecting="מתחבר..." Basic.Main.StartRecording="התחל הקלטה" Basic.Main.StartStreaming="התחל הזרמת נתונים" Basic.Main.StopRecording="עצור הקלטה" +Basic.Main.StoppingRecording="עוצר הקלטה..." Basic.Main.StopStreaming="עצור זרם נתונים" +Basic.Main.StoppingStreaming="עוצר זרם נתונים..." Basic.Main.ForceStopStreaming="עצור זרם נתונים (בטל השהייה)" Basic.MainMenu.File="קובץ(&F)" @@ -286,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="הזז לקצה עליון(&T)" Basic.MainMenu.Edit.Order.MoveToBottom="הזז לקצה תחתון(&B)" Basic.MainMenu.Edit.AdvAudio="מאפייני קול מתקדמים(&A)" +Basic.MainMenu.View="&מבט" +Basic.MainMenu.View.Toolbars="&סרגלי כלים" +Basic.MainMenu.View.Toolbars.Listboxes="&תיבות רשימה" +Basic.MainMenu.View.SceneTransitions="&מעברי סצינות" +Basic.MainMenu.View.StatusBar="&שורת מצב" + Basic.MainMenu.SceneCollection="אוסף סצינות(&S)" Basic.MainMenu.Profile="פרופיל(&P)" @@ -307,6 +325,14 @@ Basic.Settings.General.Theme="ערכת עיצוב" Basic.Settings.General.Language="שפה" Basic.Settings.General.WarnBeforeStartingStream="הצג תיבת דו-שיח לאישור בעת הפעלת זרם נתונים" Basic.Settings.General.WarnBeforeStoppingStream="הצג תיבת דו-שיח לאישור בעת עצירת זרם נתונים" +Basic.Settings.General.HideProjectorCursor="הסתר את הסמן מעל מקרנים" +Basic.Settings.General.Snapping="יישור הצמדת מקור" +Basic.Settings.General.ScreenSnapping="הצמד מקורות לקצה המסך" +Basic.Settings.General.CenterSnapping="הצמד מקורות למרכז אופקי ואנכי" +Basic.Settings.General.SourceSnapping="הצמד מקור למקור נוסף" +Basic.Settings.General.SnapDistance="רגישות צמד" +Basic.Settings.General.RecordWhenStreaming="הקלטה אוטומטית בעת הזרמת נתונים" +Basic.Settings.General.KeepRecordingWhenStreamStops="המשך הקלטה כאשר הזרמת נתונים מפסיקה" Basic.Settings.Stream="זרם נתונים" Basic.Settings.Stream.StreamType="סוג זרם נתונים" @@ -316,6 +342,7 @@ Basic.Settings.Output.Format="פורמט הקלטה" Basic.Settings.Output.Encoder="מקודד" Basic.Settings.Output.SelectDirectory="בחר ספריית הקלטות" Basic.Settings.Output.SelectFile="בחר קובץ הקלטה" +Basic.Settings.Output.EnforceBitrate="אכוף מגבלות קצב נתונים של שירות הזרמת הנתונים" Basic.Settings.Output.Mode="מצב פלט" Basic.Settings.Output.Mode.Simple="פשוט" Basic.Settings.Output.Mode.Adv="מתקדם" @@ -326,11 +353,16 @@ Basic.Settings.Output.Simple.RecordingQuality.Stream="כמו זרם הנתוני Basic.Settings.Output.Simple.RecordingQuality.Small="איכות גבוהה, גודל קובץ בינוני" Basic.Settings.Output.Simple.RecordingQuality.HQ="איכות בלתי מובחנת, גודל קובץ גדול" Basic.Settings.Output.Simple.RecordingQuality.Lossless="ללא אובדן איכות, גודל קובץ עצום" +Basic.Settings.Output.Simple.Warn.VideoBitrate="אזהרה: קצב הזרמת הוידאו יוגדר ל %1, כאשר זה הגבול העליון עבור שירות הזרמת הנתונים הנוכחי. אם אתה בטוח שאתה רוצה לעבור מעל %1, אפשר את אפשרויות קידוד מתקדמות ובטל סימון \"אכוף הגבלות קצב הזרמת נתונים\"." +Basic.Settings.Output.Simple.Warn.AudioBitrate="אזהרה: קצב הזרמת האודיו יוגדר ל %1, כאשר זה הגבול העליון עבור שירות הזרמת הנתונים הנוכחי. אם אתה בטוח שאתה רוצה לעבור מעל %1, אפשר את אפשרויות קידוד מתקדמות ובטל סימון \"אכוף הגבלות קצב הזרמת נתונים\"." Basic.Settings.Output.Simple.Warn.Encoder="אזהרה: הקלטה עם מקודד תוכנה באיכות שונה מאשר זרם הנתונים ידרוש שימוש במשאבי מעבד נוספים אם מתבצעת הקלטה וזרם נתונים במקביל." Basic.Settings.Output.Simple.Warn.Lossless="אזהרה: איכות ללא אובדן יוצר גדלי קבצים גדולים מאוד! איכות ללא אובדן נתונים יכול להשתמש ביותר מ-7 ג'יגה-בתים של שטח דיסק לדקה ברזולוציות גבוהות ופריימים. שימוש באיכות ללא אובדן אינו מומלץ להקלטות ארוכות אלא אם קיים שטח דיסק פנוי גדול מאד." Basic.Settings.Output.Simple.Warn.Lossless.Msg="האם אתה בטוח שברצונך להשתמש באיכות ללא אובדן איכות?" Basic.Settings.Output.Simple.Warn.Lossless.Title="אזהרה איכות ללא אובדן איכות!" +Basic.Settings.Output.Simple.Warn.MultipleQSV="אזהרה: לא ניתן להשתמש במקודדי QSV נפרדים מרובים בעת הזרמת נתונים והקלטה במקביל. אם ברצונך להזרים ולהקליט בו זמנית, נא בצע שינוי במקודד ההקלטה או מקודד זרם הנתונים." Basic.Settings.Output.Simple.Encoder.Software="תוכנה (x264)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="חומרה (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="חומרה (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="תוכנה (x 264 השימוש בהגדרת מעבד נמוך, גודל הקובץ גדל)" Basic.Settings.Output.VideoBitrate="קצב סיביות וידאו" Basic.Settings.Output.AudioBitrate="קצב סיביות שמע" @@ -419,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="השהיית לחץ-כדי-לדבר" Basic.Settings.Audio.UnknownAudioDevice="[התקן לא מחובר או לא זמין]" Basic.Settings.Advanced="הגדרות מתקדמות" +Basic.Settings.Advanced.General.ProcessPriority="עדיפות תהליך" +Basic.Settings.Advanced.General.ProcessPriority.High="גבוה" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="מעל לרגיל" +Basic.Settings.Advanced.General.ProcessPriority.Normal="רגיל" +Basic.Settings.Advanced.General.ProcessPriority.Idle="לא פעיל" Basic.Settings.Advanced.FormatWarning="אזהרה: תבניות צבע שונות מ-NV12 נועדו בעיקר עבור הקלטה, והם אינם מומלצות בעת הזרמת נתונים. הזרמת נתונים עלולה לגרום למשאבי עיבוד מוגברים כתוצאה מהמרת תבנית צבע." Basic.Settings.Advanced.Audio.BufferingTime="זמן אוגר שמע" Basic.Settings.Advanced.Video.ColorFormat="תבנית צבע" diff --git a/obs/data/locale/hr-HR.ini b/obs/data/locale/hr-HR.ini index 5bed0d7..5b7052b 100644 --- a/obs/data/locale/hr-HR.ini +++ b/obs/data/locale/hr-HR.ini @@ -118,6 +118,7 @@ LicenseAgreement.Exit="Izlaz" Remux.SourceFile="OBS snimak" Remux.TargetFile="Datoteka" Remux.Remux="Remux" +Remux.OBSRecording="OBS snimak" Remux.FinishedTitle="Remux završen" Remux.Finished="Završen remux snimka" Remux.FinishedError="Remux završen, ali datoteka možda nije kompletirana" @@ -143,6 +144,7 @@ Basic.DisplayCapture="Prikaži ulaz" Basic.Main.PreviewConextMenu.Enable="Omogući pregled" + Deinterlacing="Deinterlejsing" Deinterlacing.Discard="Odbaci" Deinterlacing.Retro="Retro" @@ -185,7 +187,9 @@ Basic.PropertiesWindow.ConfirmTitle="Podešavanja promenjena" Basic.PropertiesWindow.Confirm="Postoje podešavanja koja nisu sačuvana. Da li želite da ih sačuvate?" Basic.PropertiesWindow.NoProperties="Nema dostupnih svojstava" Basic.PropertiesWindow.AddFiles="Dodaj datoteke" +Basic.PropertiesWindow.AddDir="Dodaj direktorijum" Basic.PropertiesWindow.AddURL="Dodaj putanju/URL" +Basic.PropertiesWindow.AddEditableListDir="Dodaj direktorijum u '%1'" Basic.PropertiesWindow.AddEditableListFiles="Dodaj datoteke u '%1'" Basic.PropertiesWindow.AddEditableListEntry="Dodaj zapise u '%1'" Basic.PropertiesWindow.EditEditableListEntry="Izmeni zapise za '%1'" @@ -249,7 +253,9 @@ Basic.Main.Connecting="Povezivanje..." Basic.Main.StartRecording="Počni snimanje" Basic.Main.StartStreaming="Počni strimovanje" Basic.Main.StopRecording="Zaustavi snimanje" +Basic.Main.StoppingRecording="Zaustavljanje snimanja..." Basic.Main.StopStreaming="Zaustavi strimovanje" +Basic.Main.StoppingStreaming="Zaustavljanje emitovanja..." Basic.Main.ForceStopStreaming="Zaustavi strimovanje (poništi odlaganje)" Basic.MainMenu.File="&Fajl" @@ -286,6 +292,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Pomeri na vrh (&T)" Basic.MainMenu.Edit.Order.MoveToBottom="Pomeri na dno (&B)" Basic.MainMenu.Edit.AdvAudio="N&apredna podešavanja zvuka" + Basic.MainMenu.SceneCollection="Kolekcija &scena" Basic.MainMenu.Profile="&Profil" @@ -307,6 +314,7 @@ Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Jezik" Basic.Settings.General.WarnBeforeStartingStream="Prikaži prozor za potvrdu kada se započinju strimovi" Basic.Settings.General.WarnBeforeStoppingStream="Prikaži prozor za potvrdu kada se zaustavljaju strimovi" +Basic.Settings.General.HideProjectorCursor="Sakrij pokazivač na projektorima" Basic.Settings.General.Snapping="Poravnavanje privlačenjem izvora" Basic.Settings.General.ScreenSnapping="Privuci izvore ivici ekrana" Basic.Settings.General.CenterSnapping="Privuci izvore horizontalnoj i vertikalnoj sredini" diff --git a/obs/data/locale/hu-HU.ini b/obs/data/locale/hu-HU.ini index 2759f5b..0d31e26 100644 --- a/obs/data/locale/hu-HU.ini +++ b/obs/data/locale/hu-HU.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="Az OBS jelenleg aktív. Minden stream és/vagy felvétel le fo ConfirmRemove.Title="Eltávolítás megerősítése" ConfirmRemove.Text="\"$1\" eltávolítására készül, biztos benne?" +ConfirmRemove.TextMultiple="\"%1\" elem eltávolítására készül, biztos benne?" Output.ConnectFail.Title="Csatlakozás sikertelen" Output.ConnectFail.BadPath="Érvénytelen elérési út vagy kapcsolati URL cím. Kérem, ellenőrizze a beállításokat és győződjön meg az érvényességükről." @@ -144,6 +145,12 @@ Basic.DisplayCapture="Képernyő felvétel" Basic.Main.PreviewConextMenu.Enable="Előnézet bekapcsolása" +ScaleFiltering="Skála-szűrés" +ScaleFiltering.Point="Pont" +ScaleFiltering.Bilinear="Bilineáris" +ScaleFiltering.Bicubic="Kettős köbös" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Váltottsorosság" Deinterlacing.Discard="Elvetés" Deinterlacing.Retro="Retró mód" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Beállítások módosultak" Basic.PropertiesWindow.Confirm="Változtatásai nincsenek elmentve. Szeretné megtartani őket?" Basic.PropertiesWindow.NoProperties="Nincsenek elérhető tulajdonságok" Basic.PropertiesWindow.AddFiles="Fájlok hozzáadása" +Basic.PropertiesWindow.AddDir="Mappa hozzáadása" Basic.PropertiesWindow.AddURL="Elérési út/URL hozzáadása" +Basic.PropertiesWindow.AddEditableListDir="Könyvtár hozzáadása: '%1'" Basic.PropertiesWindow.AddEditableListFiles="Fájlok hozzáadása: '%1'" Basic.PropertiesWindow.AddEditableListEntry="Bejegyzés hozzáadása: '%1'" Basic.PropertiesWindow.EditEditableListEntry="Bejegyzés szerkesztése: '%1'" @@ -250,7 +259,9 @@ Basic.Main.Connecting="Kapcsolódás..." Basic.Main.StartRecording="Felvétel indítása" Basic.Main.StartStreaming="Stream indítása" Basic.Main.StopRecording="Felvétel leállítása" +Basic.Main.StoppingRecording="Felvétel leállítása..." Basic.Main.StopStreaming="Stream leállítása" +Basic.Main.StoppingStreaming="Stream leállítása..." Basic.Main.ForceStopStreaming="Stream leállítása (Késleltetés elvetése)" Basic.MainMenu.File="&Fájl" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Mozgatás a &Tetejére" Basic.MainMenu.Edit.Order.MoveToBottom="Mozgatás az &Aljára" Basic.MainMenu.Edit.AdvAudio="&Speciális hangtulajdonságok" +Basic.MainMenu.View="&Nézet" +Basic.MainMenu.View.Toolbars="&Eszköztárak" +Basic.MainMenu.View.Toolbars.Listboxes="&Gombsor" +Basic.MainMenu.View.SceneTransitions="&Jelenet átmenetek" +Basic.MainMenu.View.StatusBar="&Állapotsor" + Basic.MainMenu.SceneCollection="&Jelenet gyűjtemény" Basic.MainMenu.Profile="&Profil" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Téma" Basic.Settings.General.Language="Nyelv" Basic.Settings.General.WarnBeforeStartingStream="Megerősítő párbeszédpanel megjelenítése stream indításakor" Basic.Settings.General.WarnBeforeStoppingStream="Megerősítő párbeszédpanel megjelenítése stream leállításakor" +Basic.Settings.General.HideProjectorCursor="Projektor nézetben a kurzor elrejtése" Basic.Settings.General.Snapping="Forrás pozicionálásának igazítása" Basic.Settings.General.ScreenSnapping="Források igazítása a képernyő széléhez" Basic.Settings.General.CenterSnapping="Források vízszintes és függőleges középponthoz igazítása" Basic.Settings.General.SourceSnapping="Források igazítása más forrásokhoz" Basic.Settings.General.SnapDistance="Igazítás érzékenysége" +Basic.Settings.General.RecordWhenStreaming="Automatikus felvétel stream esetén" +Basic.Settings.General.KeepRecordingWhenStreamStops="Felvétel folytatása a stream leállása esetén" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Stream típusa" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Push-to-talk késleltetés" Basic.Settings.Audio.UnknownAudioDevice="[Eszköz nincs csatlakoztatva vagy nem elérhető]" Basic.Settings.Advanced="Haladó" +Basic.Settings.Advanced.General.ProcessPriority="Folyamat Prioritás szintje" +Basic.Settings.Advanced.General.ProcessPriority.High="Magas" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Normál feletti" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normál" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Tétlen" Basic.Settings.Advanced.FormatWarning="Figyelem: Az NV12-től eltérő színformátumok elsősorban felvételhez vannak és nem ajánlott a használatuk streamekhez. Adás közben megnövekedett processzor igényt okozhat a színkonverzió." Basic.Settings.Advanced.Audio.BufferingTime="Audio pufferelési idő" Basic.Settings.Advanced.Video.ColorFormat="Színformátum" diff --git a/obs/data/locale/it-IT.ini b/obs/data/locale/it-IT.ini index 5fd9b25..5532c4b 100644 --- a/obs/data/locale/it-IT.ini +++ b/obs/data/locale/it-IT.ini @@ -44,9 +44,28 @@ ResetOSXVSyncOnExit="Reimpostare V-Sync OSX in uscita" HighResourceUsage="Codifica in sovraccarico! È consigliabile abbassare le impostazioni video o utilizzare un settaggio predefinito di codifica più veloce." Transition="Transizione" QuickTransitions="Transizioni rapide" +Left="Sinistra" +Right="Destra" +Top="Alto" +Bottom="Basso" +QuickTransitions.SwapScenes="Scambia Scena Preview/Uscita dopo la Transizione" +QuickTransitions.SwapScenesTT="Scambia la scena in uniscita con quella in preview dopo la transizione (ammesso che la scena in uscita originale ci sia ancora).\nQuesto non modificherà eventuali cambiamenti apportati alla scena di uscita originale." +QuickTransitions.DuplicateScene="Duplica Scena" +QuickTransitions.DuplicateSceneTT="Quando si modifica la stessa scena, permette di modificare la trasformazione/visibilità di source senza modificare l'output. \nPer modificare le proprietà delle source senza modificare l'output, abilità 'Source duplicate'.\nCambiare questo valore resetterà la scena output attuale (se esite ancora)." +QuickTransitions.EditProperties="Duplica Risorsa" +QuickTransitions.EditPropertiesTT="Quando si modifica la stessa scena, consente la modifica di risorse senza modificarne l'output. \nQuesto può essere usato solo se 'Scene doppia' è attivo. \nCerte risorse (come media o catture) non lo supportano e devono essere modificate separatamente. \nCambiare questo valore resetterà l'attuale scena di output (se esiste ancora). \n\nAttenzione: Dato che la risorsa verrà duplicata, questo potrebbe richiedere risorse di sistema o video aggiuntive." +QuickTransitions.HotkeyName="Transizioni rapide: %1" +Basic.AddTransition="Aggiungi transizione configurabile" +Basic.RemoveTransition="Rimuovi transizione configurabile" +Basic.TransitionProperties="Proprietà di transizione" +Basic.SceneTransitions="Transizioni di scena" +Basic.TransitionDuration="Durata" +Basic.TogglePreviewProgramMode="Modalità studio" +TransitionNameDlg.Text="Per favore inserisci il nome della transizione" +TransitionNameDlg.Title="Nome transizione" TitleBar.Profile="Profilo" TitleBar.Scenes="Scene" @@ -57,17 +76,24 @@ NameExists.Text="Il nome è già in uso." NoNameEntered.Title="Inserisci un nome valido" NoNameEntered.Text="Non è possibile utilizzare nomi vuoti." +ConfirmStart.Title="Inizia diretta?" +ConfirmStart.Text="Sei sicuro di voler iniziare una diretta?" +ConfirmStop.Title="Interrompere diretta?" +ConfirmStop.Text="Sei sicuro di voler interrompere questa diretta?" ConfirmExit.Title="Uscire da OBS?" ConfirmExit.Text="OBS è attualmente attivo. Tutte le dirette/registrazioni saranno fermate. Sei sicuro di voler uscire?" ConfirmRemove.Title="Conferma la rimozione" ConfirmRemove.Text="Sei sicuro di voler rimuovere '$1'?" +ConfirmRemove.TextMultiple="Sei sicuro di volere rimuovere %1 elementi?" Output.ConnectFail.Title="Impossibile connettersi" Output.ConnectFail.BadPath="Percorso o URL di connessione non valido. Controlla le tue impostazioni per confermare che siano valide." Output.ConnectFail.ConnectFailed="Connessione al server fallita" +Output.ConnectFail.InvalidStream="Impossibile accedere al canale od alla stream key specificata, per favore, controlla di nuovo la tua stream key. +Se è corretta, potrebbe esserci un problema con la connessione con il server." Output.ConnectFail.Error="Si è verificato un errore non previsto durante la connessione al server. Controlla il file di log per più informazioni." Output.ConnectFail.Disconnected="Disconnesso dal server." @@ -94,6 +120,7 @@ LicenseAgreement.Exit="Esci" Remux.SourceFile="Registrazione OBS" Remux.TargetFile="File di destinazione" Remux.Remux="Converti" +Remux.OBSRecording="Registrazione OBS" Remux.FinishedTitle="Conversione finita" Remux.Finished="Registrazione convertita" Remux.FinishedError="Registrazione convertita, ma il file potrebbe essere incompleta" @@ -119,6 +146,23 @@ Basic.DisplayCapture="Mostra cattura" Basic.Main.PreviewConextMenu.Enable="Abilita Anteprima" +ScaleFiltering="Scala di filtraggio" +ScaleFiltering.Point="Punto" +ScaleFiltering.Bilinear="Bilineare" +ScaleFiltering.Bicubic="Bicubico" +ScaleFiltering.Lanczos="Lanczos" + +Deinterlacing="Deinterlacciamento" +Deinterlacing.Discard="Scarta" +Deinterlacing.Retro="Retrò" +Deinterlacing.Blend="Blend" +Deinterlacing.Blend2x="Blend 2x" +Deinterlacing.Linear="Linear" +Deinterlacing.Linear2x="Linear 2x" +Deinterlacing.Yadif="Yadif" +Deinterlacing.Yadif2x="Yadif 2x" +Deinterlacing.TopFieldFirst="Priorità livello superiore" +Deinterlacing.BottomFieldFirst="Priorità livello inferiore" Basic.Main.AddSceneDlg.Title="Aggiungi scena" Basic.Main.AddSceneDlg.Text="Inserisci il nome della scena" @@ -143,17 +187,23 @@ Basic.SourceSelect.AddExisting="Aggiungi esistente" Basic.SourceSelect.AddVisible="Rendi visibile la provenienza" Basic.PropertiesWindow="Proprietà di '%1'" +Basic.PropertiesWindow.AutoSelectFormat="%1 (autoseleziona: %2)" Basic.PropertiesWindow.SelectColor="Seleziona il colore" Basic.PropertiesWindow.SelectFont="Seleziona il tipo di carattere" Basic.PropertiesWindow.ConfirmTitle="Impostazioni modificate" Basic.PropertiesWindow.Confirm="Ci sono modifiche non salvate. Le vuoi mantenere?" Basic.PropertiesWindow.NoProperties="Nessuna proprietà disponibile" Basic.PropertiesWindow.AddFiles="Aggiungi File" +Basic.PropertiesWindow.AddDir="Aggiungi Cartella" Basic.PropertiesWindow.AddURL="Aggiungi Percorso/URL" +Basic.PropertiesWindow.AddEditableListDir="Aggiungi cartella a '%1'" Basic.PropertiesWindow.AddEditableListFiles="Aggiungi file a '%1'" Basic.PropertiesWindow.AddEditableListEntry="Aggiungi voci a '%1'" Basic.PropertiesWindow.EditEditableListEntry="Modifica voce da '%1'" +Basic.PropertiesView.FPS.Simple="Valori FPS semplici" +Basic.PropertiesView.FPS.Rational="Valori FPS razionali" +Basic.PropertiesView.FPS.ValidFPSRanges="Intervalli di FPS validi:" Basic.InteractionWindow="Interagendo con '%1'" @@ -181,6 +231,7 @@ Basic.TransformWindow.Alignment="Allinea posizioni" Basic.TransformWindow.BoundsType="Tipo di casella di delimitazione" Basic.TransformWindow.BoundsAlignment="Allineamento nel riquadro di delimitazione" Basic.TransformWindow.Bounds="Dimensione casella di delimitazione" +Basic.TransformWindow.Crop="Crop" Basic.TransformWindow.Alignment.TopLeft="In alto a sinistra" Basic.TransformWindow.Alignment.TopCenter="In alto al centro" @@ -209,7 +260,9 @@ Basic.Main.Connecting="Connessione..." Basic.Main.StartRecording="Avvia registrazione" Basic.Main.StartStreaming="Avvia trasmissione" Basic.Main.StopRecording="Ferma registrazione" +Basic.Main.StoppingRecording="Fermando la registrazione..." Basic.Main.StopStreaming="Ferma trasmissione" +Basic.Main.StoppingStreaming="Arresto diretta..." Basic.Main.ForceStopStreaming="Ferma Diretta (annulla ritardo)" Basic.MainMenu.File="&File" @@ -220,6 +273,7 @@ Basic.MainMenu.File.Remux="Converti registrazioni" Basic.MainMenu.File.Settings="&Impostazioni" Basic.MainMenu.File.ShowSettingsFolder="Visualizza cartella impostazioni" Basic.MainMenu.File.ShowProfileFolder="Mostra la cartella del profilo" +Basic.MainMenu.AlwaysOnTop="&Sempre in primo piano" Basic.MainMenu.File.Exit="Esci (&X)" Basic.MainMenu.Edit="&Modifica" @@ -245,6 +299,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Sposta in &primo piano" Basic.MainMenu.Edit.Order.MoveToBottom="Sposta in &fondo" Basic.MainMenu.Edit.AdvAudio="Proprietà Audio &Avanzate" +Basic.MainMenu.View="&Visualizza" +Basic.MainMenu.View.Toolbars="&Barre degli strumenti" +Basic.MainMenu.View.Toolbars.Listboxes="&Listboxes" +Basic.MainMenu.View.SceneTransitions="&Transizioni di scena" +Basic.MainMenu.View.StatusBar="&Barra di stato" + Basic.MainMenu.SceneCollection="&Collezione scene" Basic.MainMenu.Profile="&Profilo" @@ -264,6 +324,16 @@ Basic.Settings.Confirm="Hai dei cambiamenti non salvati. Vuoi salvarli?" Basic.Settings.General="Generali" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Lingua" +Basic.Settings.General.WarnBeforeStartingStream="Chiedi conferma quando si avvia una diretta" +Basic.Settings.General.WarnBeforeStoppingStream="Chiedi conferma quando si termina una diretta" +Basic.Settings.General.HideProjectorCursor="Nascondi cursore sopra proiettori" +Basic.Settings.General.Snapping="Allineamento Snapping Source" +Basic.Settings.General.ScreenSnapping="Snap source nei bordi dello schermo" +Basic.Settings.General.CenterSnapping="Snap source al centro orizzontale e verticale" +Basic.Settings.General.SourceSnapping="Snap sources ad altre sources" +Basic.Settings.General.SnapDistance="Sensibilità Snap" +Basic.Settings.General.RecordWhenStreaming="Registra automaticamente quando si è in diretta" +Basic.Settings.General.KeepRecordingWhenStreamStops="Continua a registrare quando la diretta s'interrompe" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Tipo di stream" @@ -273,6 +343,7 @@ Basic.Settings.Output.Format="Formato di registrazione" Basic.Settings.Output.Encoder="Codifica" Basic.Settings.Output.SelectDirectory="Seleziona cartella di registrazione" Basic.Settings.Output.SelectFile="Seleziona file di registrazione" +Basic.Settings.Output.EnforceBitrate="Forza limiti bitrate del servizio Streaming" Basic.Settings.Output.Mode="Modalità di output" Basic.Settings.Output.Mode.Simple="Semplice" Basic.Settings.Output.Mode.Adv="Avanzate" @@ -283,11 +354,16 @@ Basic.Settings.Output.Simple.RecordingQuality.Stream="Stesso della diretta" Basic.Settings.Output.Simple.RecordingQuality.Small="Alta qualità, medie dimensioni del file" Basic.Settings.Output.Simple.RecordingQuality.HQ="Qualità Indistinguibuile, larghe dimensioni del file" Basic.Settings.Output.Simple.RecordingQuality.Lossless="Senza perdità di qualità, dimensioni del file tremendamente larghe" +Basic.Settings.Output.Simple.Warn.VideoBitrate="Avviso: Il bitrate video in streaming verrà impostato su %1, che è il limite superiore per il servizio di streaming corrente. Se sei sicuro di che voler andare di sopra %1, abilita le opzioni avanzate del encorder e deseleziona \"Imponi streaming limiti di servizio bitrate\"." +Basic.Settings.Output.Simple.Warn.AudioBitrate="Avviso: Il bitrate audio in streaming verrà impostato su %1, che è il limite superiore per il servizio di streaming corrente. Se sei sicuro di che voler andare di sopra %1, abilita le opzioni avanzate del encorder e deseleziona \"Imponi streaming limiti di servizio bitrate\"." Basic.Settings.Output.Simple.Warn.Encoder="Attenzione: Registrare con un encoder software ad una qualità diversa dalla diretta richiederà un utilizzo extra della CPU se registri e vai in diretta allo stesso tempo." Basic.Settings.Output.Simple.Warn.Lossless="Attenzione: La qualità Lossless genera file estremamente grandi! La Lossless puo occupare 7 Gigabytes di spazio per minuto ad alte risoluzioni e framerate. La lossless non è consigliata per lunghe registrazione a meno che tu non abbia molto spazio disponibile sul disco rigido." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Sei sicuro di volere utilizzare la qualità lossless?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Avviso sulla qualità lossless!" +Basic.Settings.Output.Simple.Warn.MultipleQSV="Attenzione: Non è possibile usare più encoder QSV quando si è in diretta e si registra allo stesso tempo. Se vuoi andare in diretta e registrare allo stesso tempo, cambia l'encoder per la registrazione o l'encoder per la diretta." Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 Preset con basso utilizzo della CPU, aumenta le dimensioni del file)" Basic.Settings.Output.VideoBitrate="Bitrate video" Basic.Settings.Output.AudioBitrate="Bitrate Audio" @@ -297,6 +373,8 @@ Basic.Settings.Output.MaxRetries="Tentativi massimi" Basic.Settings.Output.Advanced="Abilita le impostazioni avanzate di codifica" Basic.Settings.Output.EncoderPreset="Codificatore Preset (superiore = meno CPU)" Basic.Settings.Output.CustomEncoderSettings="Impostazioni codificatore personalizzato" +Basic.Settings.Output.CustomMuxerSettings="Impostazioni Muxer personalizzate" +Basic.Settings.Output.NoSpaceFileName="Genera il nome del file senza spazi" Basic.Settings.Output.Adv.Rescale="Riscala uscita" Basic.Settings.Output.Adv.AudioTrack="Traccia Audio" @@ -312,6 +390,8 @@ Basic.Settings.Output.Adv.Recording.Type="Tipo" Basic.Settings.Output.Adv.Recording.Type.Standard="Standard" Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Output personalizzato (FFmpeg)" Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Utilizzare il codificatore del flusso)" +Basic.Settings.Output.Adv.Recording.Filename="Formattazione del nome del file" +Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Sovrascrivi il file se già esistente" Basic.Settings.Output.Adv.FFmpeg.Type="Tipo di Output FFmpeg" Basic.Settings.Output.Adv.FFmpeg.Type.URL="Output in URL" Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Output su File" @@ -332,7 +412,9 @@ Basic.Settings.Output.Adv.FFmpeg.AEncoder="Encoder Audio" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Impostazioni codifica audio (se presente)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Impostazioni Muxer (se possibile)" +FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z" +FilenameFormatting.TT="%CCYY Anno, quattro cifre\n%YY Anno, ultime due cifre (00-99)\n%MM Mese come numero decimale (01-12)\n%DD Giorno del mese, zero-padded (01-31)\n%hh Ore in formato 24 ore (00-23)\n%mm Minuto (00-59)\n%ss Secondi (00-61)\n%% Un % segno\n%a Giorno della settimana abbreviato\n%A Un nome del giorno della settimana intero\n%b Nome del mese abbreviato\n%B Nome del mese intero\n%d Giorno del mese, zero-padded (01-31)\n%H Ore in formato 24 ore (00-23)\n%I Ore in formato 12 ore (01-12)\n%m Mese come numero decimale (01-12)\n%M Minuto (00-59)\n%p Designazione AM o PM\n%S Secondi (00-61)\n%y Anno, ultime due lettere (00-99)\n%Y Anno\n%z ISO 8601 offset da UTC o fuso orario\n Nome o abbreviazione\n%Z Nome o abbreviazione del fuso orario\n" Basic.Settings.Video="Video" Basic.Settings.Video.Adapter="Adattatore video:" @@ -370,6 +452,11 @@ Basic.Settings.Audio.PushToTalkDelay="Ritardo Push-to-talk" Basic.Settings.Audio.UnknownAudioDevice="[Dispositivo non collegato o non disponibile]" Basic.Settings.Advanced="Avanzate" +Basic.Settings.Advanced.General.ProcessPriority="Priorità del processo" +Basic.Settings.Advanced.General.ProcessPriority.High="Alta" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Superiore al normale" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normale" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Bassa" Basic.Settings.Advanced.FormatWarning="Attenzione: I formati colore diversi da NV12 sono principalmente pensati per la registrazione, e non sono consigliati durante le dirette. Lo streaming può avere un utilizzo maggiore delle CPU a causa della conversione del formato del colore." Basic.Settings.Advanced.Audio.BufferingTime="Tempo di buffer audio" Basic.Settings.Advanced.Video.ColorFormat="Formato colore" diff --git a/obs/data/locale/ja-JP.ini b/obs/data/locale/ja-JP.ini index 432cf92..f63efd3 100644 --- a/obs/data/locale/ja-JP.ini +++ b/obs/data/locale/ja-JP.ini @@ -38,7 +38,7 @@ Hide="非表示" Untitled="無題" New="新規" Duplicate="複製" -Enable=" 有効にする" +Enable="有効にする" DisableOSXVSync="OSX の V-Sync を無効にする" ResetOSXVSyncOnExit="終了時に OSX の V-Sync をリセットする" HighResourceUsage="エンコードが高負荷です! ビデオ設定を下げるかより高速のエンコードプリセットの使用を検討してください。" @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS は現在アクティブです。 すべての配信/録 ConfirmRemove.Title="削除の確認" ConfirmRemove.Text="'$1' を削除してもよろしいですか?" +ConfirmRemove.TextMultiple="選択した %1 項目を削除してもよろしいですか?" Output.ConnectFail.Title="接続失敗" Output.ConnectFail.BadPath="パスかURLが無効です。再確認して下さい。" @@ -144,6 +145,12 @@ Basic.DisplayCapture="画面キャプチャ" Basic.Main.PreviewConextMenu.Enable="プレビュー有効化" +ScaleFiltering="スケールフィルタ" +ScaleFiltering.Point="ポイント" +ScaleFiltering.Bilinear="バイリニア" +ScaleFiltering.Bicubic="バイキュービック" +ScaleFiltering.Lanczos="ランチョス" + Deinterlacing="インターレース解除" Deinterlacing.Discard="破棄" Deinterlacing.Retro="レトロ" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="設定の変更" Basic.PropertiesWindow.Confirm="保存されていない変更があります。それらを維持しますか?" Basic.PropertiesWindow.NoProperties="有効なプロパティがありません" Basic.PropertiesWindow.AddFiles="ファイルを追加" +Basic.PropertiesWindow.AddDir="ディレクトリを追加" Basic.PropertiesWindow.AddURL="Path または URL を追加" +Basic.PropertiesWindow.AddEditableListDir="'%1' にディレクトリを追加" Basic.PropertiesWindow.AddEditableListFiles="ファイルを '%1' に追加" Basic.PropertiesWindow.AddEditableListEntry="'%1' にエントリを追加" Basic.PropertiesWindow.EditEditableListEntry="'%1' からのエントリを編集" @@ -250,7 +259,9 @@ Basic.Main.Connecting="接続中..." Basic.Main.StartRecording="録画開始" Basic.Main.StartStreaming="配信開始" Basic.Main.StopRecording="録画終了" +Basic.Main.StoppingRecording="録画を停止しています..." Basic.Main.StopStreaming="配信終了" +Basic.Main.StoppingStreaming="配信を停止しています..." Basic.Main.ForceStopStreaming="配信停止 (遅延破棄)" Basic.MainMenu.File="ファイル(&F)" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="最上部に移動(&T)" Basic.MainMenu.Edit.Order.MoveToBottom="最下部に移動(&B)" Basic.MainMenu.Edit.AdvAudio="オーディオの詳細プロパティ" +Basic.MainMenu.View="表示" +Basic.MainMenu.View.Toolbars="ツールバー" +Basic.MainMenu.View.Toolbars.Listboxes="リストボックス" +Basic.MainMenu.View.SceneTransitions="シーントランジション" +Basic.MainMenu.View.StatusBar="ステータスバー" + Basic.MainMenu.SceneCollection="シーンコレクション" Basic.MainMenu.Profile="プロファイル" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="テーマ" Basic.Settings.General.Language="言語" Basic.Settings.General.WarnBeforeStartingStream="配信を開始するときに確認ダイアログを表示する" Basic.Settings.General.WarnBeforeStoppingStream="配信を停止するときに確認ダイアログを表示する" +Basic.Settings.General.HideProjectorCursor="プロジェクター上のカーソルを非表示にする" Basic.Settings.General.Snapping="ソース配置のスナップ" Basic.Settings.General.ScreenSnapping="画面の端にソースをスナップする" Basic.Settings.General.CenterSnapping="水平方向および垂直方向の中心にソースをスナップする" Basic.Settings.General.SourceSnapping="他のソースにソースをスナップする" Basic.Settings.General.SnapDistance="スナップ感度" +Basic.Settings.General.RecordWhenStreaming="配信時に自動的に録画" +Basic.Settings.General.KeepRecordingWhenStreamStops="配信が停止しても録画を継続" Basic.Settings.Stream="配信" Basic.Settings.Stream.StreamType="配信種別" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="プッシュ-トーク遅延" Basic.Settings.Audio.UnknownAudioDevice="[デバイスが接続されていないまたは利用不可]" Basic.Settings.Advanced="詳細設定" +Basic.Settings.Advanced.General.ProcessPriority="プロセスの優先度" +Basic.Settings.Advanced.General.ProcessPriority.High="高" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="通常以上" +Basic.Settings.Advanced.General.ProcessPriority.Normal="通常" +Basic.Settings.Advanced.General.ProcessPriority.Idle="低" Basic.Settings.Advanced.FormatWarning="警告: NV12以外のカラーフォーマットは主に録画を想定しており、配信には非推奨です。配信ではカラーフォーマットの変換による CPU使用率の増加が発生する可能性があります。" Basic.Settings.Advanced.Audio.BufferingTime="音声バッファー処理時間" Basic.Settings.Advanced.Video.ColorFormat="カラーフォーマット" diff --git a/obs/data/locale/ko-KR.ini b/obs/data/locale/ko-KR.ini index 2de8b51..57048ec 100644 --- a/obs/data/locale/ko-KR.ini +++ b/obs/data/locale/ko-KR.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS가 현재 작동 중입니다. 모든 방송/녹화가 중 ConfirmRemove.Title="제거 확인" ConfirmRemove.Text="'$1'을 정말로 제거하시겠습니까?" +ConfirmRemove.TextMultiple="정말로 %1 개의 항목을 제거하겠습니까?" Output.ConnectFail.Title="연결에 실패했음" Output.ConnectFail.BadPath="잘못된 경로 혹은 연결 주소입니다. 유효한 값인지 설정을 확인하시기 바랍니다. " @@ -144,6 +145,12 @@ Basic.DisplayCapture="캡처 표시" Basic.Main.PreviewConextMenu.Enable="미리보기 활성화" +ScaleFiltering="비율 필터링" +ScaleFiltering.Point="점" +ScaleFiltering.Bilinear="이중선형" +ScaleFiltering.Bicubic="쌍삼차" +ScaleFiltering.Lanczos="란초스" + Deinterlacing="디인터레이싱" Deinterlacing.Discard="저장 안함" Deinterlacing.Retro="레트로" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="설정이 변경됨" Basic.PropertiesWindow.Confirm="아직 저장하지 않은 변경 사항이 있습니다. 변경을 유지하겠습니까?" Basic.PropertiesWindow.NoProperties="가능한 설정이 없음" Basic.PropertiesWindow.AddFiles="파일 추가" +Basic.PropertiesWindow.AddDir="디렉토리 추가" Basic.PropertiesWindow.AddURL="경로 또는 URL 추가" +Basic.PropertiesWindow.AddEditableListDir="'%1 에 디렉터리 항목 추가" Basic.PropertiesWindow.AddEditableListFiles="'%1' 에 파일 추가" Basic.PropertiesWindow.AddEditableListEntry="'%1 에 항목 추가" Basic.PropertiesWindow.EditEditableListEntry="'%1' 내 항목 수정" @@ -250,7 +259,9 @@ Basic.Main.Connecting="연결 중..." Basic.Main.StartRecording="녹화 시작" Basic.Main.StartStreaming="방송 시작" Basic.Main.StopRecording="녹화 중단" +Basic.Main.StoppingRecording="녹화를 중단합니다...." Basic.Main.StopStreaming="방송 중단" +Basic.Main.StoppingStreaming="방송을 중지합니다..." Basic.Main.ForceStopStreaming="방송 중지(지연된 분량도 마무리없이 즉시 송출 중단)" Basic.MainMenu.File="파일(&F)" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="가장 위로 올리기(&T)" Basic.MainMenu.Edit.Order.MoveToBottom="가장 아래로 내리기(&B)" Basic.MainMenu.Edit.AdvAudio="오디오 고급 설정(&A)" +Basic.MainMenu.View="보기(&V)" +Basic.MainMenu.View.Toolbars="도구 모음(&T)" +Basic.MainMenu.View.Toolbars.Listboxes="목록 상자(&L)" +Basic.MainMenu.View.SceneTransitions="장면 전환(&C)" +Basic.MainMenu.View.StatusBar="상태 표시줄(&S)" + Basic.MainMenu.SceneCollection="장면 모음(&S)" Basic.MainMenu.Profile="프로파일(&P)" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="테마" Basic.Settings.General.Language="언어" Basic.Settings.General.WarnBeforeStartingStream="방송을 시작할 때 확인 대화 상자 표시" Basic.Settings.General.WarnBeforeStoppingStream="방송을 중단할 때 확인 대화 상자 표시" +Basic.Settings.General.HideProjectorCursor="프로젝터 위 커서 숨기기" Basic.Settings.General.Snapping="소스를 자석처럼 달라붙여서 정렬" Basic.Settings.General.ScreenSnapping="소스를 화면 변두리에 붙임" Basic.Settings.General.CenterSnapping="소스를 수평과 수직 중앙에 붙임" Basic.Settings.General.SourceSnapping="소스를 다른 소스에 붙임" Basic.Settings.General.SnapDistance="자석 감도" +Basic.Settings.General.RecordWhenStreaming="방송 시 자동으로 녹화" +Basic.Settings.General.KeepRecordingWhenStreamStops="방송을 중단하더라도 녹화는 유지" Basic.Settings.Stream="방송" Basic.Settings.Stream.StreamType="방송 형식" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="누르며 말하기 지연" Basic.Settings.Audio.UnknownAudioDevice="[장치를 연결하지 않았거나 사용할 수 없음]" Basic.Settings.Advanced="고급" +Basic.Settings.Advanced.General.ProcessPriority="프로세스 우선순위 설정" +Basic.Settings.Advanced.General.ProcessPriority.High="실시간" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="높음" +Basic.Settings.Advanced.General.ProcessPriority.Normal="보통" +Basic.Settings.Advanced.General.ProcessPriority.Idle="낮음" Basic.Settings.Advanced.FormatWarning="경고: NV12 이외의 색상 형식은 주로 녹화를 위한 것이며, 방송에 적합하지 않습니다. 이대로 방송을 하게 되면 형식 전환을 위해 CPU 사용량이 증가할 수 있습니다." Basic.Settings.Advanced.Audio.BufferingTime="오디오 버퍼링 시간" Basic.Settings.Advanced.Video.ColorFormat="색상 형식" diff --git a/obs/data/locale/lt-LT.ini b/obs/data/locale/lt-LT.ini index b9e7f1a..00c8f27 100644 --- a/obs/data/locale/lt-LT.ini +++ b/obs/data/locale/lt-LT.ini @@ -52,23 +52,70 @@ Bottom="Iš apačios" QuickTransitions.SwapScenes="Sukeisti Peržiūros/Išvesties scenas po Perėjimo" QuickTransitions.SwapScenesTT="Sukeičia peržiūros ir išvesties scenas po perėjimo įvykdymo (jei originali išvesties scena vis dar egzistuoja).\nTai neatšauks jokių pakeitimų kurie galima buvo atlikti originalioje išvesties scenoje." QuickTransitions.DuplicateScene="Dubliuoti Sceną" +QuickTransitions.EditProperties="Dubliuoti šaltinius" +QuickTransitions.HotkeyName="Greitasis perėjimas: %1" +Basic.AddTransition="Pridėti konfigūruojamą perėjimą" +Basic.RemoveTransition="Pašalinti konfigūruojamą perėjimą" +Basic.TransitionProperties="Perėjimo savybės" +Basic.SceneTransitions="Scenos perėjimai" +Basic.TransitionDuration="Trukmė" +Basic.TogglePreviewProgramMode="Studijos režimas" +TransitionNameDlg.Text="Prašome įvesti perėjimo pavadinimą" +TransitionNameDlg.Title="Perėjimo pavadinimas" +TitleBar.Profile="Profilis" +TitleBar.Scenes="Scenos" +NameExists.Title="Pavadinimas jau egzistuoja" +NameExists.Text="Toks pavadinimas jau naudojamas." +NoNameEntered.Title="Prašome įvesti tinkamą pavadinimą" +NoNameEntered.Text="Negalite naudoti tuščio pavadinimo." +ConfirmStart.Title="Pradėti transliaciją?" +ConfirmStart.Text="Ar esate įsitikinęs, kad norite pradėti transliavimą?" +ConfirmStop.Title="Stabdyti transliaciją?" +ConfirmStop.Text="Ar esate įsitikinęs, kad norite stabdyti transliavimą?" +ConfirmExit.Title="Išeiti iš OBS?" +ConfirmExit.Text="OBS metu yra aktyvus. Visos transliacijos/įrašymai bus išjungti. Ar tikrai norite išeiti?" +ConfirmRemove.Title="Pašalinimo patvirtinimas" +ConfirmRemove.Text="Ar tikrai norite pašalinti '$1'?" +Output.ConnectFail.Title="Nepavyko prisijungti" +Output.ConnectFail.BadPath="Neteisingas kelias arba jungimosi URL. Prašome patikrinti nustatymus ir įsitikinti, kad jie teisingi." +Output.ConnectFail.ConnectFailed="Nepavyko prisijungti prie serverio" +Output.ConnectFail.Error="Netikėta klaida bandant jungtis į serverį. Daugiau informacijos žurnalo faile." +Output.ConnectFail.Disconnected="Atjungtas nuo serverio." +Output.RecordFail.Title="Nepavyko pradėti įrašo" +Output.RecordFail.Unsupported="Išvesties formatas nepalaikomas arba nepalaiko daugiau vieno garso takelio. Prašome patikrinti nustatymus ir bandyti vėl." +Output.RecordNoSpace.Title="Nepakanka vietos diske" +Output.RecordNoSpace.Msg="Nėra pakankamai vietos diske, kad tęsti įrašymą." +Output.RecordError.Title="Įrašymo klaida" +Output.RecordError.Msg="Nenustatyta klaida įrašant." +Output.BadPath.Title="Blogas failo kelias" +Output.BadPath.Text="Nustatytas išvesties failo kelias yra netinkamas. Prašome patikrinti nustatymus, kad įsitikintumėte, jog nustatytas failo kelias yra teisingas." +LogReturnDialog="Žurnalas įkeltas sėkmingai" +LogReturnDialog.CopyURL="Kopijuoti adresą" +LogReturnDialog.ErrorUploadingLog="Žurnalo failo įkėlimo klaida" +LicenseAgreement="Licencinė sutartis" +LicenseAgreement.PleaseReview="Prašome peržiūrėti licencijos sąlygas prieš OBS naudojimą. Naudodamasis šia programa, jūs pripažįstate, kad perskaitėte ir sutikite su GNU General Public License v2.0 (anglų k.) sąlygomis. Prašome slinkti žemyn, kad pamatytumėte likusią sutarties dalį." +LicenseAgreement.ClickIAgreeToContinue="Jei sutinkate su sutarties sąlygomis, pasirinkite Aš sutinku, kad tęsti. Jūs privalote sutikti su sutartimi, kad naudotis OBS." +LicenseAgreement.IAgree="Sutinku" +LicenseAgreement.Exit="Išeiti" +Remux.SourceFile="OBS įrašas" Remux.TargetFile="Paskirties failas" Remux.Remux="Permiksuoti" +Remux.OBSRecording="OBS įrašas" Remux.FinishedTitle="Permiksavimas baigtas" Remux.Finished="Įrašas permiksuotas" Remux.FinishedError="Įrašas permiksuotas, tačiau failas gali būti neužbaigtas" @@ -94,6 +141,12 @@ Basic.DisplayCapture="Ekrano perėmimas" Basic.Main.PreviewConextMenu.Enable="Įjungti peržiūrą" +ScaleFiltering="Mastelio filtras" +ScaleFiltering.Point="Taškas" +ScaleFiltering.Bilinear="Dvilinijinis" +ScaleFiltering.Bicubic="Bikubinis" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Perėjimo šalinimas" Deinterlacing.Discard="Atmesti" Deinterlacing.Retro="Retro" @@ -130,12 +183,32 @@ Basic.SourceSelect.AddVisible="Padaryti šaltinį matomu" Basic.PropertiesWindow="'%1' savybės" Basic.PropertiesWindow.AutoSelectFormat="%1 (automatinis pasirinkimas: %2)" +Basic.PropertiesWindow.SelectColor="Pasirinkti spalvą" +Basic.PropertiesWindow.SelectFont="Pasirinkti šriftą" +Basic.PropertiesWindow.ConfirmTitle="Nustatymai pakeisti" +Basic.PropertiesWindow.Confirm="Yra neišsaugotų pakeitimų. Ar norite juos išlaikyti?" +Basic.PropertiesWindow.NoProperties="Savybių nėra" +Basic.PropertiesWindow.AddFiles="Pridėti failus" +Basic.PropertiesWindow.AddDir="Pridėti katalogą" +Basic.PropertiesWindow.AddURL="Pridėti kelią/URL" +Basic.TransformWindow.Position="Padėtis" +Basic.TransformWindow.Rotation="Pasukimas" +Basic.TransformWindow.Size="Dydis" +Basic.TransformWindow.Alignment.TopLeft="Viršuje kairėje" +Basic.TransformWindow.Alignment.TopCenter="Viršuje per vidurį" +Basic.TransformWindow.Alignment.TopRight="Viršuje dešinėje" +Basic.TransformWindow.Alignment.CenterLeft="Centruota kairėje" +Basic.TransformWindow.Alignment.Center="Centre" +Basic.TransformWindow.Alignment.CenterRight="Centruoda dešinįje" +Basic.TransformWindow.Alignment.BottomLeft="Apačioje kairėje" +Basic.TransformWindow.Alignment.BottomCenter="Apačioje per vidurį" +Basic.TransformWindow.Alignment.BottomRight="Apačioje dešinėje" Basic.Main.AddSourceHelp.Text="Turite turėti bent 1 sceną, kad galėtumėte pridėti šaltinį." @@ -174,6 +247,12 @@ Basic.MainMenu.Edit.Transform.Rotate180="Pasukti 180 laipsnių" Basic.MainMenu.Edit.Transform.FlipHorizontal="Apversti &horizontaliai" Basic.MainMenu.Edit.Transform.FlipVertical="Apversti &vertikaliai" Basic.MainMenu.Edit.Transform.FitToScreen="Sutalpinti į ekraną" +Basic.MainMenu.Edit.Order="&Rikiavimas" +Basic.MainMenu.Edit.Order.MoveUp="Perkelti aukštyn" +Basic.MainMenu.Edit.Order.MoveDown="Perkelti žemyn" +Basic.MainMenu.Edit.Order.MoveToTop="Perkelti į viršų" +Basic.MainMenu.Edit.Order.MoveToBottom="Perkelti į apačią" + diff --git a/obs/data/locale/ms-MY.ini b/obs/data/locale/ms-MY.ini index 0d03804..924db64 100644 --- a/obs/data/locale/ms-MY.ini +++ b/obs/data/locale/ms-MY.ini @@ -116,6 +116,7 @@ LicenseAgreement.Exit="Keluar" Remux.SourceFile="Rakaman OBS" Remux.TargetFile="Fail sasaran" +Remux.OBSRecording="Rakaman OBS" Remux.SelectRecording="Pilih Rakaman OBS …" Remux.SelectTarget="Pilih fail sasaran …" Remux.FileExistsTitle="Fail sasaran wujud" @@ -136,6 +137,9 @@ Basic.Scene="Adegan" Basic.Main.PreviewConextMenu.Enable="Benarkan Pratonton" +Deinterlacing.Retro="Retro" +Deinterlacing.Linear="Linear" + Basic.Main.AddSceneDlg.Title="Tambah Adegan" Basic.Main.AddSceneDlg.Text="Sila taip nama adegan" @@ -193,6 +197,7 @@ Basic.Filters.Title="Penapis untuk %1" Basic.Filters.AddFilter.Title="Nama penapis" Basic.Filters.AddFilter.Text="Sila nyatakan nama penapis" +Basic.TransformWindow="Pengubahan Item Adegan" Basic.TransformWindow.Position="Kedudukan" Basic.TransformWindow.Rotation="Pusingan" Basic.TransformWindow.Size="Saiz" @@ -220,6 +225,7 @@ Basic.Main.StartRecording="Mula rakaman" Basic.Main.StartStreaming="Mula 'streaming'" Basic.Main.StopRecording="Hentikan rakaman" Basic.Main.StopStreaming="Hentikan 'streaming'" +Basic.Main.ForceStopStreaming="Berhenti 'streaming' (buang kelewatan)" Basic.MainMenu.File="&Fail" Basic.MainMenu.File.Export="&Export" @@ -228,20 +234,57 @@ Basic.MainMenu.File.ShowRecordings="Papar &Rakaman" Basic.MainMenu.File.Settings="&Tetapan" Basic.MainMenu.File.ShowSettingsFolder="Tunjukkan Folder Tetapan" Basic.MainMenu.File.ShowProfileFolder="Tunjukkan Folder Profil" +Basic.MainMenu.AlwaysOnTop="&Sentiasa Di Atas" Basic.MainMenu.File.Exit="Keluar (&X)" +Basic.MainMenu.Edit="&Sunting" Basic.MainMenu.Edit.Undo="&Ubah Balik" Basic.MainMenu.Edit.Redo="Buat Semula (&R)" Basic.MainMenu.Edit.UndoAction="&Ubah Balik $1" +Basic.MainMenu.Edit.RedoAction="&Ubah Semula $1" +Basic.MainMenu.Edit.Transform="&Ubah" +Basic.MainMenu.Edit.Transform.EditTransform="&Sunting Perubahan..." +Basic.MainMenu.Edit.Transform.Rotate90CW="Putarkan 90 darjah mengikut arah jam" +Basic.MainMenu.Edit.Transform.Rotate90CCW="Putarkan 90 darjah melawan arah jam" +Basic.MainMenu.Edit.Transform.Rotate180="Putarkan 180 darjah" +Basic.MainMenu.Edit.Transform.FitToScreen="&Muat di skrin" +Basic.MainMenu.Edit.Transform.StretchToScreen="&Regangkan ke skrin" +Basic.MainMenu.Edit.Order.MoveUp="Gerakkan ke &atas" +Basic.MainMenu.Edit.Order.MoveDown="Gerakkan ke &bawah" +Basic.MainMenu.Profile="&Profil" + +Basic.MainMenu.Help.Website="Lawat laman &Web" +Basic.MainMenu.Help.Logs="Fail &Log" +Basic.MainMenu.Help.Logs.ShowLogs="&Tunjukkan Fail-Fail Log" +Basic.MainMenu.Help.Logs.UploadCurrentLog="Muat naik Fail Log &Kini" +Basic.MainMenu.Help.CheckForUpdates="Semak Versi Baharu" Basic.Settings.ConfirmTitle="Sahkan Perubahan" +Basic.Settings.Confirm="Anda mempunyai perubahan yang tidak disimpan. Simpan perubahan?" + +Basic.Settings.General="Am" +Basic.Settings.General.Theme="Tema" +Basic.Settings.General.Language="Bahasa" +Basic.Settings.General.WarnBeforeStartingStream="Tunjukkan dialog pengesahan ketika memulakan 'stream'" +Basic.Settings.General.WarnBeforeStoppingStream="Tunjukkan dialog pengesahan ketika menghentikan 'stream'" + +Basic.Settings.Stream="'Stream'" +Basic.Settings.Stream.StreamType="Jenis 'Stream'" + +Basic.Settings.Output.Format="Format Rakaman" +Basic.Settings.Output.SelectFile="Pilih Fail Rakaman" +Basic.Settings.Output.Simple.RecordingQuality="Kualiti Rakaman" +Basic.Settings.Output.Simple.RecordingQuality.Stream="Sama seperti 'stream'" +Basic.Settings.Output.Simple.Encoder.Software="Perisian (x264)" +Basic.Settings.Output.Reconnect="Sambung semula secara automatik" +Basic.Settings.Output.RetryDelay="Kelewatan Percubaan (saat)" +Basic.Settings.Output.MaxRetries="Cubaan Maksimum" - - - +Basic.Settings.Output.Adv.Recording="Rakaman" +Basic.Settings.Output.Adv.Recording.Type="Jenis" Basic.Settings.Output.Adv.FFmpeg.Type="Jenis Output FFmpeg" diff --git a/obs/data/locale/nb-NO.ini b/obs/data/locale/nb-NO.ini index 8bf7b83..9683ab6 100644 --- a/obs/data/locale/nb-NO.ini +++ b/obs/data/locale/nb-NO.ini @@ -8,6 +8,7 @@ Cancel="Avbryt" Close="Lukk" Save="Lagre" Discard="Forkast" +Disable="Deaktiver" Yes="Ja" No="Nei" Add="Legg til" @@ -38,10 +39,33 @@ Untitled="Uten navn" New="Ny" Duplicate="Dupliser" Enable="Aktiver" +DisableOSXVSync="Deaktiver OS X v-sync" +ResetOSXVSyncOnExit="Tilbakestill OS X v-sync ved avsluttning" +HighResourceUsage="Koder overbelastet! Vurder nedjustering av videokvaliteten eller en raskere forhåndsinstilling for koderen." Transition="Overgang" +QuickTransitions="Raske overganger" +Left="Venstre" +Right="Høyre" +Top="Topp" +Bottom="Bunn" +QuickTransitions.SwapScenes="Bytt forhåndsvisnings-/utgangsscener etter overgang" +QuickTransitions.SwapScenesTT="Bytter forhåndsvisnings- og utgangsscenen etter overgang, hvis den originale utgangsscenen fortsatt eksisterer.\nDette vil ikke tilbakestille endringer på den originale utgangsscenen." +QuickTransitions.DuplicateScene="Dupliser scene" +QuickTransitions.DuplicateSceneTT="Når denne instillingen er aktiv kan du justere visningen av kilder i den samme scenen uten endring i utgangsvisningen.\nOm du vil endre kildenes instillinger må du aktivere «dupliserte kilder.»\nVed aktivering eller deaktivering av denne instillingen vil utgangsscenen omstilles, om den fortsatt eksisterer." +QuickTransitions.EditProperties="Dupliserte kilder" +QuickTransitions.EditPropertiesTT="Lar deg justere kilders egenskaper i samme scene uten å endre visning i utgangsscenen.\nKan bare brukes om instillingen «duplisert scene» er aktiv.\nEnkelte kilder (som opptak eller mediekilder) støtter ikke denne funksjonen og kan ikke endres separat.\nOm du aktiverer eller deaktiverer denne instillingen vil omstille utgangsscenen om den fortsatt eksisterer.\n\nAdvarsel: kan kreve mer systemressurser ettersom alle kildene i scenen blir duplisert." +QuickTransitions.HotkeyName="Hurtigovergang: %1" +Basic.AddTransition="Legg til konfigurerbar overgang" +Basic.RemoveTransition="Fjern konfigurerbar overgang" +Basic.TransitionProperties="Egenskaper" +Basic.SceneTransitions="Sceneoverganger" +Basic.TransitionDuration="Varighet" +Basic.TogglePreviewProgramMode="Studiomodus" +TransitionNameDlg.Text="Gi et navn til overgangen" +TransitionNameDlg.Title="Overgangsnavn" TitleBar.Profile="Profil" TitleBar.Scenes="Scener" @@ -52,7 +76,11 @@ NameExists.Text="Dette navnet er allerede i bruk." NoNameEntered.Title="Vennligst oppgi et gyldig navn" NoNameEntered.Text="Du kan ikke bruke et tomt navn." +ConfirmStart.Title="Begynn strømming?" +ConfirmStart.Text="Er du sikker på at du vil begynne strømming?" +ConfirmStop.Title="Stans strømming?" +ConfirmStop.Text="Er du sikker på at du vil stanse strømming?" ConfirmExit.Title="Avslutt OBS?" ConfirmExit.Text="OBS er aktiv. Alle strømmer og opptak vil bli stoppet. Er du sikker på at du vil avslutte?" @@ -63,11 +91,12 @@ ConfirmRemove.Text="Er du sikker på at du vil fjerne '$1'?" Output.ConnectFail.Title="Tilkobling misklytes" Output.ConnectFail.BadPath="Ugyldig filbane eller tilkoblings-URL. Vennligst bekreft at instillingene dine er riktige." Output.ConnectFail.ConnectFailed="Kunne ikke koble til tjener." +Output.ConnectFail.InvalidStream="Kunne ikke få adgang til den angitte kanalen eller strømmenøkkelen. Strømmenøkkelen kan være feil eller det kan være et problem med tilkoblingen til tjeneren." Output.ConnectFail.Error="En uventet feil oppstod ved tilkobling til serveren. Detaljert informasjon kan du finne i loggfila." Output.ConnectFail.Disconnected="Koblet fra tjeneren." Output.RecordFail.Title="Kunne ikke starte opptak" -Output.RecordFail.Unsupported="Utdataformatet støttes enten ikke eller støtter ikke mer enn ett lydspor. Kontroller innstillingene og prøv igjen." +Output.RecordFail.Unsupported="Utgangsformatet støttes enten ikke eller støtter ikke mer enn ett lydspor. Kontroller innstillingene og prøv igjen." Output.RecordNoSpace.Title="Ikke nok diskplass" Output.RecordNoSpace.Msg="Det er ikke nok diskplass til å fortsette opptaket." Output.RecordError.Title="Innspillingsfeil" @@ -89,6 +118,7 @@ LicenseAgreement.Exit="Avslutt" Remux.SourceFile="OBS-opptak" Remux.TargetFile="Målfil" Remux.Remux="Remuks" +Remux.OBSRecording="OBS-opptak" Remux.FinishedTitle="Remuksing ferdig" Remux.Finished="Opptak remukset" Remux.FinishedError="Opptak remukset, men filen kan være ufullstendig." @@ -115,6 +145,18 @@ Basic.DisplayCapture="Skjermopptak" Basic.Main.PreviewConextMenu.Enable="Aktiver forhåndsvisning" +Deinterlacing="Avsammenfletting" +Deinterlacing.Discard="Forkast" +Deinterlacing.Retro="Retro" +Deinterlacing.Blend="Bland" +Deinterlacing.Blend2x="Dobbelblanding" +Deinterlacing.Linear="Lineær" +Deinterlacing.Linear2x="Dobbellineær" +Deinterlacing.Yadif="Yadif" +Deinterlacing.Yadif2x="Dobbelyadif" +Deinterlacing.TopFieldFirst="Øverste felt først" +Deinterlacing.BottomFieldFirst="Nederste felt først" + Basic.Main.AddSceneDlg.Title="Ny Scene" Basic.Main.AddSceneDlg.Text="Vennligst gi et navn til scenen." @@ -138,17 +180,23 @@ Basic.SourceSelect.AddExisting="Legg til eksisterende" Basic.SourceSelect.AddVisible="Gjør kilden synlig" Basic.PropertiesWindow="Egenskaper for '%1'" +Basic.PropertiesWindow.AutoSelectFormat="%1 (selvvalg: %2)" Basic.PropertiesWindow.SelectColor="Velg farge" Basic.PropertiesWindow.SelectFont="Velg skrifttype" Basic.PropertiesWindow.ConfirmTitle="Innstillingene er endret" Basic.PropertiesWindow.Confirm="Det er ulagra endringer. Vil du beholde dem?" Basic.PropertiesWindow.NoProperties="Ingen egenskaper tilgjengelige" Basic.PropertiesWindow.AddFiles="Legg til filer" +Basic.PropertiesWindow.AddDir="Legg til mappe" Basic.PropertiesWindow.AddURL="Legg til fil- eller URL-adresse" +Basic.PropertiesWindow.AddEditableListDir="Legg mappe til «%1»" Basic.PropertiesWindow.AddEditableListFiles="Legg filer til '%1'" Basic.PropertiesWindow.AddEditableListEntry="Lag oppføring til '%1'" Basic.PropertiesWindow.EditEditableListEntry="Endre oppføring fra '%1'" +Basic.PropertiesView.FPS.Simple="Bildefrekvens (enkel)" +Basic.PropertiesView.FPS.Rational="Bildefrekvens (rasjonalt tall)" +Basic.PropertiesView.FPS.ValidFPSRanges="Gyldige bildefrekvensrekkevidder:" Basic.InteractionWindow="Kommuniserer med «%1»" @@ -176,6 +224,7 @@ Basic.TransformWindow.Alignment="Posisjonsoppstilling" Basic.TransformWindow.BoundsType="Innholdsavgrensningstype" Basic.TransformWindow.BoundsAlignment="Oppstilling i innholdsavgrensning" Basic.TransformWindow.Bounds="Innholdsavgrensningsstørrelse" +Basic.TransformWindow.Crop="Beskjæring" Basic.TransformWindow.Alignment.TopLeft="Øverst til venstre" Basic.TransformWindow.Alignment.TopCenter="Øverst i midten" @@ -204,7 +253,9 @@ Basic.Main.Connecting="Kobler til…" Basic.Main.StartRecording="Start Opptak" Basic.Main.StartStreaming="Start Strømming" Basic.Main.StopRecording="Stopp Opptak" +Basic.Main.StoppingRecording="Stanser innspilling…" Basic.Main.StopStreaming="Stopp Strømming" +Basic.Main.StoppingStreaming="Stanser strøm…" Basic.Main.ForceStopStreaming="Stopp strømming (forkast forsinkelse)" Basic.MainMenu.File="&Fil" @@ -215,6 +266,7 @@ Basic.MainMenu.File.Remux="Re&muks opptak" Basic.MainMenu.File.Settings="Inn&stillinger" Basic.MainMenu.File.ShowSettingsFolder="Vis innstillingsmappen" Basic.MainMenu.File.ShowProfileFolder="Vis profilmappen" +Basic.MainMenu.AlwaysOnTop="&Alltid synlig" Basic.MainMenu.File.Exit="&Avslutt" Basic.MainMenu.Edit="&Redigér" @@ -240,6 +292,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Legg på &toppen" Basic.MainMenu.Edit.Order.MoveToBottom="Legg på &bunnen" Basic.MainMenu.Edit.AdvAudio="&Avanserte lydinstillinger" + Basic.MainMenu.SceneCollection="&Scenesamling" Basic.MainMenu.Profile="&Profil" @@ -259,30 +312,46 @@ Basic.Settings.Confirm="Du har endringer som ikke er lagret. Vil du lagre?" Basic.Settings.General="Generelt" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Språk" +Basic.Settings.General.WarnBeforeStartingStream="Vis bekreftelsesdialogboks når du starter strømming" +Basic.Settings.General.WarnBeforeStoppingStream="Vis bekreftelsesdialogboks når stanser strømming" +Basic.Settings.General.HideProjectorCursor="Skjul musepekeren over projektorer" +Basic.Settings.General.Snapping="Festing ved kildejustering" +Basic.Settings.General.ScreenSnapping="Fest kilder til kanten av skjermen" +Basic.Settings.General.CenterSnapping="Fest kilder til vannrett og loddrett midtpunkt" +Basic.Settings.General.SourceSnapping="Fest kilder til andre kilder" +Basic.Settings.General.SnapDistance="Festingfølsomhet" +Basic.Settings.General.RecordWhenStreaming="Spill inn automatisk ved strømming" +Basic.Settings.General.KeepRecordingWhenStreamStops="Fortsett innspilling etter strømming" Basic.Settings.Stream="Strøm" Basic.Settings.Stream.StreamType="Strømmetype" -Basic.Settings.Output="Innspilling" +Basic.Settings.Output="Utgang" Basic.Settings.Output.Format="Opptaksformat" Basic.Settings.Output.Encoder="Koder" Basic.Settings.Output.SelectDirectory="Velg opptaksmappe" Basic.Settings.Output.SelectFile="Velg opptaksfil" -Basic.Settings.Output.Mode="Innspillingsmodus" +Basic.Settings.Output.EnforceBitrate="Håndhev strømmingtjenestens bitrategrense" +Basic.Settings.Output.Mode="Utgangsmodus" Basic.Settings.Output.Mode.Simple="Enkel" Basic.Settings.Output.Mode.Adv="Avansert" -Basic.Settings.Output.Mode.FFmpeg="FFmpeg produkt" +Basic.Settings.Output.Mode.FFmpeg="FFmpeg-utgang" Basic.Settings.Output.Simple.SavePath="Opptaksbane" Basic.Settings.Output.Simple.RecordingQuality="Opptakskvalitet" Basic.Settings.Output.Simple.RecordingQuality.Stream="Samme som strøm" Basic.Settings.Output.Simple.RecordingQuality.Small="Høy kvalitet, middels filstørrelse" Basic.Settings.Output.Simple.RecordingQuality.HQ="Veldig høy kvalitet, stor filstørrelse" Basic.Settings.Output.Simple.RecordingQuality.Lossless="Tapsfri kvalitet, veldig stor filstørrelse" +Basic.Settings.Output.Simple.Warn.VideoBitrate="Advarsel: strømmens bitrate vil settes til %1, som er den øvre grensen for gjeldende strømmetjeneste. Om du ønsker å gå over denne grensen må du aktivere avanserte koderinnstillinger og sørge for at valget for håndheving av strømmetjenestens grense ikke er huket av." +Basic.Settings.Output.Simple.Warn.AudioBitrate="Advarsel: strømmens lydbitrate vil settes til %1, som er den øvre grensen for gjeldende strømmetjeneste. Om du ønsker å gå over denne grensen må du aktivere avanserte koderinnstillinger og sørge for at valget for håndheving av strømmetjenestens grense ikke er huket av." Basic.Settings.Output.Simple.Warn.Encoder="Advarsel: Opptak med programvarekoder i en annen kvalitetsinnstilling enn strømmingen vil kreve ekstra prosessorressurser om du strømmer og tar opp på samme tid." Basic.Settings.Output.Simple.Warn.Lossless="Advarsel: Tapsfri kvalitet resulterer i enormt store filstørrelser! Denne innstillingen kan bruke oppimot 7 GB diskplass per minutt ved opptak med høy oppløsning og bildefrekvens. Tapsfri kvalitet anbefales ikke for lange opptak med mindre du har store mengder diskplass tilgjengelig." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Er du sikker på at du vil bruke tapsfri kvalitet?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Tapsfri kvalitet advarsel!" +Basic.Settings.Output.Simple.Warn.MultipleQSV="Advarsel: du kan ikke bruke flere separate QSV-kodere når du strømmer og tar opp samtidig. Hvis du ønsker gjøre begge på samme tid må du endre strømme- eller opptakskoderen." Basic.Settings.Output.Simple.Encoder.Software="Programvare (x264)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Maskinvare (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Maskinvare (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Programvare (x264 forhåndsinstilling for liten prosessorbruk, øker filstørrelsen)" Basic.Settings.Output.VideoBitrate="Bildeoverføringshastighet" Basic.Settings.Output.AudioBitrate="Lydoverføringshastighet" @@ -292,8 +361,10 @@ Basic.Settings.Output.MaxRetries="Høyst antall tilkoblingsforsøk" Basic.Settings.Output.Advanced="Aktiver Avanserte Koderinstillinger" Basic.Settings.Output.EncoderPreset="Koderforhåndsinstilling (raskere gir mindre prosessorbelastning)" Basic.Settings.Output.CustomEncoderSettings="Egendefinert koderinstilling" +Basic.Settings.Output.CustomMuxerSettings="Egenfinderte mukserinstillinger" +Basic.Settings.Output.NoSpaceFileName="Lag filnavn uten mellomrom" -Basic.Settings.Output.Adv.Rescale="Reskaler produkt" +Basic.Settings.Output.Adv.Rescale="Reskaler utgang" Basic.Settings.Output.Adv.AudioTrack="Lydspor" Basic.Settings.Output.Adv.Streaming="Strømming" Basic.Settings.Output.Adv.ApplyServiceSettings="Bruk strømmetjenerens koderinstillinger" @@ -305,11 +376,13 @@ Basic.Settings.Output.Adv.Audio.Track4="Spor 4" Basic.Settings.Output.Adv.Recording="Opptak" Basic.Settings.Output.Adv.Recording.Type="Type" Basic.Settings.Output.Adv.Recording.Type.Standard="Standard" -Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Egendefinert FFmpeg produkt" +Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Egendefinert FFmpeg-utgang" Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Bruk strømkoder)" -Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg-utdatatype" -Basic.Settings.Output.Adv.FFmpeg.Type.URL="Utdata til URL" -Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Utdata til fil" +Basic.Settings.Output.Adv.Recording.Filename="Filnavnformat" +Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Skriv over om filen eksisterer" +Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg-utgangstype" +Basic.Settings.Output.Adv.FFmpeg.Type.URL="Utgang til URL" +Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Utgang til fil" Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common="Vanlige opptaksformater" Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Alle filer" Basic.Settings.Output.Adv.FFmpeg.SavePathURL="Filbane eller URL" @@ -327,12 +400,14 @@ Basic.Settings.Output.Adv.FFmpeg.AEncoder="Lydkoder" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Lydkoderinstillinger (om noen)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Mukserinstillinger (om noen)" +FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z" +FilenameFormatting.TT="%CCYY år, fire sifre\n%YY år, siste to sifre (00-99)\n%MM måned, som tall (01-12)\n%DD dag i måneden, forranstillt med null (01-31)\n%hh time, i tjuefiretimersformat (00-23)\n%mm minutt (00-59)\n%ss sekund (00-61)\n%% et prosenttegn\n%a forkortet ukedagsnavn\n%A helt ukedagsnavn\n%b forkortet månedsnavn\n%B helt månedsnavn\n%d dag i måneden, forranstillt med null (01-31)\n%H time, i tjuefiretimersformat (00-23)\n%I time, i tolvtimersformat (01-12)\n%m måned, som tall (01-12)\n%M minutt (00-59)\n%p formiddags- og ettermiddagsymbol (AM, PM)\n%S sekund (00-61)\n%y år, siste to sifre (00-99)\n%Y år\n%z ISO 8601 forskyvd fra UTC eller tidssone-\n navn eller -forkortelse\n%Z Tidssonenavn eller -forkortelse\n" Basic.Settings.Video="Bilde" Basic.Settings.Video.Adapter="Bildeadapter:" Basic.Settings.Video.BaseResolution="Grunnoppløsning (lerret):" -Basic.Settings.Video.ScaledResolution="Utdataoppløsning (skalert):" +Basic.Settings.Video.ScaledResolution="Utgangsoppløsning (skalert):" Basic.Settings.Video.DownscaleFilter="Nedskaleringsfilter:" Basic.Settings.Video.DisableAeroWindows="Deaktiver Aero (kun Windows)" Basic.Settings.Video.FPS="FPS:" @@ -343,7 +418,7 @@ Basic.Settings.Video.Numerator="Teller:" Basic.Settings.Video.Denominator="Nevner:" Basic.Settings.Video.Renderer="Renderer:" Basic.Settings.Video.InvalidResolution="Ugyldig oppløsningsverdi. Må være [bredde]x[høyde] (f.eks. 1920x1080)" -Basic.Settings.Video.CurrentlyActive="Bildeinnspilling er aktiv. Vennligst skru av alle innspillinger for å endre bildeinstillingene" +Basic.Settings.Video.CurrentlyActive="Bildeutgang er aktiv. Vennligst stans alle utganger for å endre bildeinstillingene." Basic.Settings.Video.DisableAero="Skru av Aero" Basic.Settings.Video.DownscaleFilter.Bilinear="Bilineær (raskest, men uskarp ved skalering)" @@ -362,6 +437,7 @@ Basic.Settings.Audio.EnablePushToMute="Aktiver demp-ved-trykk" Basic.Settings.Audio.PushToMuteDelay="Demp-ved-trykk forsinkelse" Basic.Settings.Audio.EnablePushToTalk="Aktiver snakk-ved-trykk" Basic.Settings.Audio.PushToTalkDelay="Snakk-ved-trykk forsinkelse" +Basic.Settings.Audio.UnknownAudioDevice="[Enhet ikke tilkoblet eller ikke tilgjengelig]" Basic.Settings.Advanced="Avansert" Basic.Settings.Advanced.FormatWarning="Advarsel: Fargeformater andre enn NV12 er ment for opptak. Disse formatene anbefales ikke ved strømming, da det fører til økt prosessorbruk som følge av fargeformatkonvertering." diff --git a/obs/data/locale/nl-NL.ini b/obs/data/locale/nl-NL.ini index 020e032..cd6fe17 100644 --- a/obs/data/locale/nl-NL.ini +++ b/obs/data/locale/nl-NL.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS is momenteel actief. Alle streams/opnames zullen worden af ConfirmRemove.Title="Bevestig het verwijderen" ConfirmRemove.Text="Weet je zeker dat je '$1' wil verwijderen?" +ConfirmRemove.TextMultiple="Weet je zeker dat je %1 elementen wil verwijderen?" Output.ConnectFail.Title="Kan geen verbinding maken" Output.ConnectFail.BadPath="Ongeldig pad of verbindings-url. Controleer a.u.b. of je instellingen geldig zijn." @@ -144,6 +145,12 @@ Basic.DisplayCapture="Beeldschermcapture" Basic.Main.PreviewConextMenu.Enable="Preview inschakelen" +ScaleFiltering="Schaal-filter" +ScaleFiltering.Point="Point" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Deinterlacing" Deinterlacing.Discard="Verwerpen" Deinterlacing.Retro="Retro" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Instellingen Aangepast" Basic.PropertiesWindow.Confirm="Er zijn onopgeslagen aanpassingen. Wil je deze bewaren?" Basic.PropertiesWindow.NoProperties="Geen eigenschappen beschikbaar" Basic.PropertiesWindow.AddFiles="Bestanden Toevoegen" +Basic.PropertiesWindow.AddDir="Map Toevoegen" Basic.PropertiesWindow.AddURL="Pad/URL Toevoegen" +Basic.PropertiesWindow.AddEditableListDir="Voeg map toe aan '%1'" Basic.PropertiesWindow.AddEditableListFiles="Voeg bestanden toe aan '%1'" Basic.PropertiesWindow.AddEditableListEntry="Voeg toe aan '%1'" Basic.PropertiesWindow.EditEditableListEntry="Verwijder van '%1'" @@ -250,7 +259,9 @@ Basic.Main.Connecting="Verbinden..." Basic.Main.StartRecording="Opname Starten" Basic.Main.StartStreaming="Stream Starten" Basic.Main.StopRecording="Opname Stoppen" +Basic.Main.StoppingRecording="Opname Stoppen..." Basic.Main.StopStreaming="Stream Stoppen" +Basic.Main.StoppingStreaming="Stream Stoppen..." Basic.Main.ForceStopStreaming="Stop Stream (vertraging negeren)" Basic.MainMenu.File="&Bestand" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Naar Boven Schuiven (&T)" Basic.MainMenu.Edit.Order.MoveToBottom="Naar Onderen Schuiven (&B)" Basic.MainMenu.Edit.AdvAudio="Geavanceerde &Audioinstellingen" +Basic.MainMenu.View="&Beeld" +Basic.MainMenu.View.Toolbars="&Werkbalken" +Basic.MainMenu.View.Toolbars.Listboxes="&Lijsten" +Basic.MainMenu.View.SceneTransitions="S&cène-overgangen" +Basic.MainMenu.View.StatusBar="&Statusbalk" + Basic.MainMenu.SceneCollection="&Scèneverzameling" Basic.MainMenu.Profile="&Profiel" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Thema" Basic.Settings.General.Language="Taal" Basic.Settings.General.WarnBeforeStartingStream="Laat bevestigingsvenster zien bij het starten van streams" Basic.Settings.General.WarnBeforeStoppingStream="Laat bevestiginsvenster zien bij het stoppen van streams" +Basic.Settings.General.HideProjectorCursor="Verberg cursor boven projectors" Basic.Settings.General.Snapping="Bronuitlijning" Basic.Settings.General.ScreenSnapping="Bronnen uitlijnen op de rand van het scherm" Basic.Settings.General.CenterSnapping="Bronnen uitlijnen op het horizontale en verticale midden" Basic.Settings.General.SourceSnapping="Bronnen uitlijnen op andere bronnen" Basic.Settings.General.SnapDistance="Gevoeligheid" +Basic.Settings.General.RecordWhenStreaming="Stream automatisch opnemen" +Basic.Settings.General.KeepRecordingWhenStreamStops="Opname voortzetten als de stream stopt" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Stream Type" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Push-to-talk vertraging" Basic.Settings.Audio.UnknownAudioDevice="[Apparaat niet verbonden of niet beschikbaar]" Basic.Settings.Advanced="Geavanceerd" +Basic.Settings.Advanced.General.ProcessPriority="Procesprioriteit" +Basic.Settings.Advanced.General.ProcessPriority.High="Hoog" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Bovennormaal" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normaal" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Laagst" Basic.Settings.Advanced.FormatWarning="Waarschuwing: Andere kleurformaten dan NV12 zijn hoofdzakelijk bedoeld voor opnemen en worden niet aanbevolen om mee te streamen. Streamen kan verhoogd CPU-gebruik opleveren vanwege kleurformaatconversie." Basic.Settings.Advanced.Audio.BufferingTime="Audio Buffertijd" Basic.Settings.Advanced.Video.ColorFormat="Kleurindeling" diff --git a/obs/data/locale/pl-PL.ini b/obs/data/locale/pl-PL.ini index 1eec74f..fbc2f74 100644 --- a/obs/data/locale/pl-PL.ini +++ b/obs/data/locale/pl-PL.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS jest uruchomiony. Wszelkie transmisje czy nagrania zostan ConfirmRemove.Title="Potwierdź usunięcie" ConfirmRemove.Text="Czy na pewno chcesz usunąć '$1'?" +ConfirmRemove.TextMultiple="Liczba elementów do usunięcia: %1. Czy na pewno chcesz je usunąć?" Output.ConnectFail.Title="Nie udało się połączyć" Output.ConnectFail.BadPath="Nieprawidłowa ścieżka lub adres URL połączenia. Sprawdź poprawność ustawień." @@ -118,6 +119,7 @@ LicenseAgreement.Exit="Wyjście" Remux.SourceFile="Nagrywanie OBS" Remux.TargetFile="Plik docelowy" Remux.Remux="Przepakowanie (remux)" +Remux.OBSRecording="Plik nagrania z OBS" Remux.FinishedTitle="Przepakowanie zakończone" Remux.Finished="Nagranie przepakowane" Remux.FinishedError="Nagranie przepakowane ale może być niekompletne" @@ -143,6 +145,12 @@ Basic.DisplayCapture="Przechwytywanie obrazu" Basic.Main.PreviewConextMenu.Enable="Włącz podgląd" +ScaleFiltering="Filtrowanie skalowania" +ScaleFiltering.Point="Punktowe" +ScaleFiltering.Bilinear="Dwuliniowe" +ScaleFiltering.Bicubic="Dwusześcienne" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="Usuwanie przeplotu" Deinterlacing.Discard="Odrzuć" Deinterlacing.Retro="Retro" @@ -185,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Ustawienia zmienione" Basic.PropertiesWindow.Confirm="Czy chcesz zachować niezapisane zmiany?" Basic.PropertiesWindow.NoProperties="Brak właściwości" Basic.PropertiesWindow.AddFiles="Dodaj pliki" +Basic.PropertiesWindow.AddDir="Dodaj folder" Basic.PropertiesWindow.AddURL="Dodaj ścieżkę/adres URL" +Basic.PropertiesWindow.AddEditableListDir="Dodaj folder do '%1'" Basic.PropertiesWindow.AddEditableListFiles="Dodaj pliki do '%1'" Basic.PropertiesWindow.AddEditableListEntry="Dodaj wpis do '%1'" Basic.PropertiesWindow.EditEditableListEntry="Edytuj wpis w '%1'" @@ -249,7 +259,9 @@ Basic.Main.Connecting="Łączenie..." Basic.Main.StartRecording="Rozpocznij nagrywanie" Basic.Main.StartStreaming="Rozpocznij stream" Basic.Main.StopRecording="Zatrzymaj nagrywanie" +Basic.Main.StoppingRecording="Zatrzymywanie nagrywania..." Basic.Main.StopStreaming="Zatrzymaj stream" +Basic.Main.StoppingStreaming="Zatrzymywanie streamowania..." Basic.Main.ForceStopStreaming="Zatrzymaj stream (anuluj opóźnienie)" Basic.MainMenu.File="&Plik" @@ -286,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Przesuń na samą gó&rę" Basic.MainMenu.Edit.Order.MoveToBottom="Przesuń na sa&m dół" Basic.MainMenu.Edit.AdvAudio="Z&aawansowane ustawienia dźwięku" +Basic.MainMenu.View="&Widok" +Basic.MainMenu.View.Toolbars="Paski &narzędzi" +Basic.MainMenu.View.Toolbars.Listboxes="Panele kontrolne &list elementów" +Basic.MainMenu.View.SceneTransitions="Efekty &przejścia scen" +Basic.MainMenu.View.StatusBar="Pasek &stanu" + Basic.MainMenu.SceneCollection="Zbiór &scen" Basic.MainMenu.Profile="&Profil" @@ -307,11 +325,14 @@ Basic.Settings.General.Theme="Motyw" Basic.Settings.General.Language="Język" Basic.Settings.General.WarnBeforeStartingStream="Pokaż komunikat potwierdzenia uruchomienia streamowania" Basic.Settings.General.WarnBeforeStoppingStream="Pokaż komunikat potwierdzenia zatrzymania streamowania" +Basic.Settings.General.HideProjectorCursor="Ukryj kursor podglądu na pełnym ekranie" Basic.Settings.General.Snapping="Przyciąganie elementów źródłowych" Basic.Settings.General.ScreenSnapping="Przyciągaj do krawędzi ekranu" Basic.Settings.General.CenterSnapping="Przyciągaj do poziomego i pionowego środka" Basic.Settings.General.SourceSnapping="Przyciągaj źródła do innych źródeł" Basic.Settings.General.SnapDistance="Czułość przyciągania" +Basic.Settings.General.RecordWhenStreaming="Automatyczne nagrywanie streamu" +Basic.Settings.General.KeepRecordingWhenStreamStops="Zachowaj nagranie po zatrzymaniu streamu" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Typ streamu" @@ -430,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Opóźnienie \"naciśnij-aby-mówić\"" Basic.Settings.Audio.UnknownAudioDevice="[Urządzenie niedostępne lub niepodłączone]" Basic.Settings.Advanced="Zaawansowane" +Basic.Settings.Advanced.General.ProcessPriority="Priorytet procesu" +Basic.Settings.Advanced.General.ProcessPriority.High="Wysoki" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Powyżej normalnego" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normalny" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Bezczynny" Basic.Settings.Advanced.FormatWarning="Ostrzeżenie: Formaty koloru inne niż NV12 przeznaczone są głównie do nagrywania i nie są zalecane podczas przesyłania strumieniowego. Użycie innych formatów koloru podczas przesyłania strumieniowego wymagać będzie przekształcenia formatu w locie, co może znacząco zwiększyć obciążenie procesora." Basic.Settings.Advanced.Audio.BufferingTime="Czas buforowania dźwięku" Basic.Settings.Advanced.Video.ColorFormat="Format koloru" diff --git a/obs/data/locale/pt-BR.ini b/obs/data/locale/pt-BR.ini index 42b33e1..8f61f4c 100644 --- a/obs/data/locale/pt-BR.ini +++ b/obs/data/locale/pt-BR.ini @@ -54,6 +54,7 @@ QuickTransitions.SwapScenesTT="Troca a preview e a saída após transicionar (se QuickTransitions.DuplicateScene="Duplicar Cena" QuickTransitions.DuplicateSceneTT="Quando estiver editando a mesma cena, permite editar a visibilidade/transformação de fontes sem modificar a saída.\nPara editar as propriedades das fontes sem modificar a saída, habilite 'Fontes Duplicadas'.\nMudar este valor irá resetar a cena atual de saída (se ainda existir)." QuickTransitions.EditProperties="Duplicar Fontes" +QuickTransitions.EditPropertiesTT="Quando a editar a mesma cena, permite a edição das propriedades de fontes sem modificar a saida.\nIsto só pode ser usado se 'Duplicar Cena' está ativado.\nAlgumas fontes (como a captura e fontes de media) não suportam isto e não podem ser editados separadamente.\nModificar este valor vai redefinir a atual saída de cena (se ele ainda existir).\n\nAviso: Porque fontes vão ser duplicados, isto pode requisitar um sistema extra ou recursos de vídeo." QuickTransitions.HotkeyName="Transição Rápida: %1" Basic.AddTransition="Adicionar Transição Configurável" @@ -117,6 +118,7 @@ LicenseAgreement.Exit="Sair" Remux.SourceFile="OBS Gravando" Remux.TargetFile="Arquivo de destino" Remux.Remux="Remux" +Remux.OBSRecording="Gravação do OBS" Remux.FinishedTitle="Remux finalizado" Remux.Finished="Remux da gravação finalizado" Remux.FinishedError="Remux da gravação finalizado, mas o arquivo pode estar incompleto" @@ -142,12 +144,16 @@ Basic.DisplayCapture="Captura de tela" Basic.Main.PreviewConextMenu.Enable="Ativar pré-vizualização" + +Deinterlacing="Desentrelaçamento" Deinterlacing.Discard="Descartar" Deinterlacing.Retro="Retro" Deinterlacing.Blend="Misturar" Deinterlacing.Blend2x="Misturar 2x" Deinterlacing.Linear="Linear" Deinterlacing.Linear2x="Linear 2x" +Deinterlacing.Yadif="Yadif" +Deinterlacing.Yadif2x="Yadif 2x" Deinterlacing.TopFieldFirst="Campo Superior Primeiro" Deinterlacing.BottomFieldFirst="Campo Inferior Primeiro" @@ -282,6 +288,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Mover para o &Topo" Basic.MainMenu.Edit.Order.MoveToBottom="Mover para a &Base" Basic.MainMenu.Edit.AdvAudio="&Propriedades de áudio avançadas" + Basic.MainMenu.SceneCollection="&Coleção de cena" Basic.MainMenu.Profile="&Perfil" @@ -303,6 +310,7 @@ Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" Basic.Settings.General.WarnBeforeStartingStream="Mostrar diálogo de confirmação quando iniciar transmissões" Basic.Settings.General.WarnBeforeStoppingStream="Mostrar diálogo de confirmação quando terminar transmissões" +Basic.Settings.General.Snapping="Alinhamentos com encaixe na Cena" Basic.Settings.General.ScreenSnapping="Encaixar Fontes ás bordas da tela" Basic.Settings.General.CenterSnapping="Encaixar Fontes aos centros vertical e horizontal" Basic.Settings.General.SourceSnapping="Encaixar fontes com outras fontes" @@ -334,6 +342,8 @@ Basic.Settings.Output.Simple.Warn.Lossless="Aviso: Qualidade Lossless gera arqui Basic.Settings.Output.Simple.Warn.Lossless.Msg="Tem certeza que deseja usar qualidade lossless?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Aviso de qualidade lossless!" Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (preset x264 de baixa utilização de CPU, aumenta o tamanho do arquivo)" Basic.Settings.Output.VideoBitrate="Taxa de Bits do Vídeo" Basic.Settings.Output.AudioBitrate="Taxa de Bits do Áudio" @@ -343,6 +353,7 @@ Basic.Settings.Output.MaxRetries="Número Máximo de Tentativas" Basic.Settings.Output.Advanced="Ativar as configurações avançadas do encoder" Basic.Settings.Output.EncoderPreset="Predefinição de codificação (maior = menor uso de CPU)" Basic.Settings.Output.CustomEncoderSettings="Configurações de codificador personalizadas" +Basic.Settings.Output.CustomMuxerSettings="Configurações personalizadas do Muxer" Basic.Settings.Output.NoSpaceFileName="Gerar Nome de Arquivo sem Espaços" Basic.Settings.Output.Adv.Rescale="Redimensionar a saída" @@ -381,6 +392,7 @@ Basic.Settings.Output.Adv.FFmpeg.AEncoder="Codificador de áudio" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Configurações do codificador de áudio(se houver)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Configurações do Muxer (se houver)" +FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z" FilenameFormatting.TT="%CCYY Ano, quatro dígitos\n%YY Ano, dois dígitos (00-99)\n%MM Mês como um número decimal (01-12)\n%DD Dia do mês, começando com 0 (01-31)\n%hh Hora, em formato de 24h (00-23)\n%mm Minuto (00-59)\n%ss Segundo (00-61)\n%% A % sign\n%a Dia da Semana abreviado\n%A Nome da Semana completo\n%b Nome do Mês abreviado\n%B Nome do Mês completo\n%d Dia do Mês, começando com 0 (01-31)\n%H Hora, no formato de 24h (00-23)\n%I Hora no formato de 12h (01-12)\n%m Mês como um número decimal (01-12)\n%M Minuto (00-59)\n%p Designação AM ou PM\n%S Segundo (00-61)\n%y Ano, últimos dois dígitos (00-99)\n%Y Ano\n%z ISO 8601 diferença de fuso horário ou de UTC\n nome ou abreviação\n%Z Nome do Fuso Horário ou abreviação\n" diff --git a/obs/data/locale/pt-PT.ini b/obs/data/locale/pt-PT.ini index 95eb5cb..e22eb7a 100644 --- a/obs/data/locale/pt-PT.ini +++ b/obs/data/locale/pt-PT.ini @@ -39,6 +39,9 @@ Untitled="Sem título" New="Novo" Duplicate="Duplicar" Enable="Ativar" +DisableOSXVSync="Desabilitar o OSX V-Sync" +ResetOSXVSyncOnExit="Redefinir o OSX V-Sync na saída" +HighResourceUsage="Codificação sobrecarregado! Considere diminuir a configurações de vídeo ou usar uma predefinição mais rápido de codificação." Transition="Transição" QuickTransitions="Transições rápidas" Left="Esquerda" @@ -46,11 +49,21 @@ Right="Direita" Top="Cima" Bottom="Baixo" +QuickTransitions.SwapScenes="Trocar pré-visualização/saída de cenas Depois de uma Transição" QuickTransitions.DuplicateScene="Duplicar cena" +QuickTransitions.EditProperties="Fontes duplicadas" +QuickTransitions.EditPropertiesTT="Quando a editar a mesma cena, permite a edição das propriedades de fontes sem modificar a saida.\nIsto só pode ser usado se 'Duplicar Cena' está ativado.\nAlgumas fontes (como a captura e fontes de media) não suportam isto e não podem ser editados separadamente.\nModificar este valor vai redefinir a atual saída de cena (se ele ainda existir).\n\nAviso: Porque fontes vão ser duplicados, isto pode requisitar um sistema extra ou recursos de vídeo." +QuickTransitions.HotkeyName="Transição rápida: %1" +Basic.AddTransition="Adicionar Transição Configurável" Basic.RemoveTransition="Remover transição configurável" Basic.TransitionProperties="Propriedades da transição" +Basic.SceneTransitions="Transições de cena" +Basic.TransitionDuration="Duração" +Basic.TogglePreviewProgramMode="Modo de estúdio" +TransitionNameDlg.Text="Por favor, escreva o nome da transição" +TransitionNameDlg.Title="Nome da transição" TitleBar.Profile="Perfil" TitleBar.Scenes="Cenas" @@ -61,7 +74,11 @@ NameExists.Text="O nome já encontra-se em utilização." NoNameEntered.Title="Por favor, introduz um nome válido" NoNameEntered.Text="Não pode utilizar nomes vazios." +ConfirmStart.Title="Começar a transmitir?" +ConfirmStart.Text="Tem a certeza que quer começar a transmissão?" +ConfirmStop.Title="Parar a transmissão?" +ConfirmStop.Text="Tem a certeza que quer parar a transmissão?" ConfirmExit.Title="Sair do OBS?" ConfirmExit.Text="O OBS está ligado. Todas as transmissões e gravações serão paradas. Tem a certeza de que pretende sair?" @@ -72,6 +89,7 @@ ConfirmRemove.Text="Tem a certeza que quer remover '$1'?" Output.ConnectFail.Title="Falha ao ligar" Output.ConnectFail.BadPath="Caminho ou endereço de ligação inválido. Por favor, verifique as suas definições para confirmar que são válidas." Output.ConnectFail.ConnectFailed="Falhou a ligação ao servidor" +Output.ConnectFail.InvalidStream="Não foi possível acessar o canal especificado ou chave de transmissão, por favor ré-verifique a sua chave de transmissão. Se é correto, pode haver um problema de conexão com o servidor." Output.ConnectFail.Error="Ocurreu um erro inesperado ao ligar-se ao servidor. Mais informação no ficheiro Log." Output.ConnectFail.Disconnected="Desligado do servidor." @@ -98,6 +116,7 @@ LicenseAgreement.Exit="Sair" Remux.SourceFile="Gravação do OBS" Remux.TargetFile="Ficheiro de destino" Remux.Remux="Remisturar" +Remux.OBSRecording="Gravação do OBS" Remux.FinishedTitle="Remistura concluída" Remux.Finished="Gravação remisturada" Remux.FinishedError="Gravação remisturada, mas o ficheiro pode estar incompleto" @@ -124,6 +143,18 @@ Basic.DisplayCapture="Captura de Ecrã" Basic.Main.PreviewConextMenu.Enable="Ativar pré-visualização" +Deinterlacing="Desentrelaçamento" +Deinterlacing.Discard="Discartar" +Deinterlacing.Retro="Retro" +Deinterlacing.Blend="Misturar" +Deinterlacing.Blend2x="Misturar 2x" +Deinterlacing.Linear="Linear" +Deinterlacing.Linear2x="Linear 2x" +Deinterlacing.Yadif="Yadif" +Deinterlacing.Yadif2x="Yadif 2x" +Deinterlacing.TopFieldFirst="Campo Superior Primeiro" +Deinterlacing.BottomFieldFirst="Campo Inferior Primeiro" + Basic.Main.AddSceneDlg.Title="Adicionar Cena" Basic.Main.AddSceneDlg.Text="Por favor introduza o nome da cena" @@ -189,6 +220,7 @@ Basic.TransformWindow.Alignment="Alinhamento da Posição" Basic.TransformWindow.BoundsType="Tipo da Caixa de Rebordo" Basic.TransformWindow.BoundsAlignment="Alinhamento da Caixa de Rebordo" Basic.TransformWindow.Bounds="Tamanho da Caixa de Rebordo" +Basic.TransformWindow.Crop="Recortar" Basic.TransformWindow.Alignment.TopLeft="Superior Esquerdo" Basic.TransformWindow.Alignment.TopCenter="Superior Central" @@ -228,6 +260,7 @@ Basic.MainMenu.File.Remux="Re&misturar gravações" Basic.MainMenu.File.Settings="&Definições" Basic.MainMenu.File.ShowSettingsFolder="Mostrar pasta das definições" Basic.MainMenu.File.ShowProfileFolder="Mostrar pasta do perfil" +Basic.MainMenu.AlwaysOnTop="&Sempre em Cima" Basic.MainMenu.File.Exit="&Sair" Basic.MainMenu.Edit="&Editar" @@ -253,6 +286,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Mover para p &Topo" Basic.MainMenu.Edit.Order.MoveToBottom="Mover para o Fundo" Basic.MainMenu.Edit.AdvAudio="Propriedades &avançadas de áudio" + Basic.MainMenu.SceneCollection="Coleção de cena" Basic.MainMenu.Profile="&Perfil" @@ -272,6 +306,10 @@ Basic.Settings.Confirm="Voçê tem alterações não salvadas. Deseja salvar as Basic.Settings.General="Geral" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" +Basic.Settings.General.WarnBeforeStartingStream="Mostrar caixa de diálogo de confirmação ao iniciar transmissões" +Basic.Settings.General.WarnBeforeStoppingStream="Mostrar caixa de diálogo de confirmação ao parar transmissões" +Basic.Settings.General.Snapping="Alinhamentos com encaixe na Cena" +Basic.Settings.General.SnapDistance="Sensibilidade do Snap" Basic.Settings.Stream="Transmissão" Basic.Settings.Stream.StreamType="Tipo de transmissão" @@ -281,6 +319,7 @@ Basic.Settings.Output.Format="Formato de gravação" Basic.Settings.Output.Encoder="Codificador" Basic.Settings.Output.SelectDirectory="Selecione o diretório de gravação" Basic.Settings.Output.SelectFile="Selecione ficheiro de gravação" +Basic.Settings.Output.EnforceBitrate="Forçar limites de taxa de bits de serviço de transmissão" Basic.Settings.Output.Mode="Modo de saída" Basic.Settings.Output.Mode.Simple="Simples" Basic.Settings.Output.Mode.Adv="Avançado" @@ -289,8 +328,14 @@ Basic.Settings.Output.Simple.SavePath="Caminho da gravação" Basic.Settings.Output.Simple.RecordingQuality="Qualidade da gravação" Basic.Settings.Output.Simple.RecordingQuality.Stream="A mesma da transmissão" Basic.Settings.Output.Simple.RecordingQuality.Small="Alta qualidade, Tamanho médio" +Basic.Settings.Output.Simple.RecordingQuality.HQ="Qualidade indistinguível, Tamanho de Arquivo Grande" +Basic.Settings.Output.Simple.RecordingQuality.Lossless="Sem perda de qualidade, Tamanho de Arquivo Enorme" Basic.Settings.Output.Simple.Warn.Lossless.Msg="Tem a certeza de que pretende utilizar a qualidade sem perdas?" +Basic.Settings.Output.Simple.Warn.Lossless.Title="Aviso de qualidade sem perda!" Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" +Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 baixa utilização da CPU pré-ajustada, aumenta o tamanho do arquivo)" Basic.Settings.Output.VideoBitrate="Bitrate de Vídeo" Basic.Settings.Output.AudioBitrate="Bitrate de Áudio" Basic.Settings.Output.Reconnect="Religar Automaticamente" @@ -299,6 +344,8 @@ Basic.Settings.Output.MaxRetries="Número Máximo de Tentativas de Religação" Basic.Settings.Output.Advanced="Ativar definições avançadas de codificação" Basic.Settings.Output.EncoderPreset="Predefinição de codificação (maior = menor processamento)" Basic.Settings.Output.CustomEncoderSettings="Definições de codificação personalizadas" +Basic.Settings.Output.CustomMuxerSettings="Configurações personalizadas do Muxer" +Basic.Settings.Output.NoSpaceFileName="Gerar o Nome do Arquivo sem espaço" Basic.Settings.Output.Adv.Rescale="Redimensionar saída" Basic.Settings.Output.Adv.AudioTrack="Faixa de áudio" @@ -314,6 +361,8 @@ Basic.Settings.Output.Adv.Recording.Type="Tipo" Basic.Settings.Output.Adv.Recording.Type.Standard="Padrão" Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Saída personalizada (FFmpeg)" Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Utilizar o codificador da transmissão)" +Basic.Settings.Output.Adv.Recording.Filename="Formatação do nome do arquivo" +Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Substituir, se o arquivo existe" Basic.Settings.Output.Adv.FFmpeg.Type="Tipo de saída FFmpeg" Basic.Settings.Output.Adv.FFmpeg.Type.URL="Exportar para endereço" Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Exportar para ficheiro" @@ -332,11 +381,15 @@ Basic.Settings.Output.Adv.FFmpeg.VEncoder="Codificador de vídeo" Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Definições do codificador de vídeo (se houver)" Basic.Settings.Output.Adv.FFmpeg.AEncoder="Codificador de áudio" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Definições do codificador de áudio (se houver)" +Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Configurações Muxer (se houver)" +FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z" Basic.Settings.Video="Vídeo" Basic.Settings.Video.Adapter="Adaptador de Vídeo:" +Basic.Settings.Video.BaseResolution="Resolução de base (tela):" +Basic.Settings.Video.ScaledResolution="Resolução de saída (escalado):" Basic.Settings.Video.DownscaleFilter="Filtro de Escalamento:" Basic.Settings.Video.DisableAeroWindows="Desactivar Aero (apenas no Windows)" Basic.Settings.Video.FPS="FPS:" @@ -366,6 +419,7 @@ Basic.Settings.Audio.EnablePushToMute="Ativar o push-to-mute" Basic.Settings.Audio.PushToMuteDelay="Atrado do push-to-mute" Basic.Settings.Audio.EnablePushToTalk="Ativar o push-to-talk" Basic.Settings.Audio.PushToTalkDelay="Atraso do push-to-talk" +Basic.Settings.Audio.UnknownAudioDevice="[Dispositivo não conectado ou não disponível]" Basic.Settings.Advanced="Avançado" Basic.Settings.Advanced.FormatWarning="Aviso: Formatos de cor diferentes de NV12 destinam-se principalmente a gravação e não são recomendados durante a transmissão. A transmissão pode incorrer numa maior utilização do processador devido à conversão do formato de cor." @@ -377,6 +431,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Parcial" Basic.Settings.Advanced.Video.ColorRange.Full="Total" Basic.Settings.Advanced.StreamDelay="Atraso na trasmissão" Basic.Settings.Advanced.StreamDelay.Duration="Duração (segundos)" +Basic.Settings.Advanced.StreamDelay.Preserve="Preservar o ponto de corte (aumentar atraso) quando reconectar" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Utilização estimada de memória: %1 MB" Basic.AdvAudio="Propriedades avançadas de áudio" diff --git a/obs/data/locale/ro-RO.ini b/obs/data/locale/ro-RO.ini index 6b5b259..9abadd4 100644 --- a/obs/data/locale/ro-RO.ini +++ b/obs/data/locale/ro-RO.ini @@ -143,6 +143,7 @@ Basic.DisplayCapture="Captură de display" Basic.Main.PreviewConextMenu.Enable="Activează previzualizarea" + Deinterlacing="Deîntrețesere" Deinterlacing.Discard="Înlătură" Deinterlacing.Retro="Retro" @@ -286,6 +287,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Mu&tă în vârf" Basic.MainMenu.Edit.Order.MoveToBottom="Mută la fu&nd" Basic.MainMenu.Edit.AdvAudio="Proprietăți audio &avansate" + Basic.MainMenu.SceneCollection="Colecție de &scene" Basic.MainMenu.Profile="&Profil" diff --git a/obs/data/locale/ru-RU.ini b/obs/data/locale/ru-RU.ini index 66975d4..7dbb792 100644 --- a/obs/data/locale/ru-RU.ini +++ b/obs/data/locale/ru-RU.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="В настоящее время OBS активен. Все п ConfirmRemove.Title="Подтвердить удаление" ConfirmRemove.Text="Вы уверены, что хотите удалить '$1'?" +ConfirmRemove.TextMultiple="Вы уверены, что вы хотите удалить %1 элементов?" Output.ConnectFail.Title="Не удалось подключиться" Output.ConnectFail.BadPath="Неверный путь или URL соединения. Пожалуйста, проверьте настройки, чтобы подтвердить, что они являются действительными." @@ -144,6 +145,12 @@ Basic.DisplayCapture="Захват экрана" Basic.Main.PreviewConextMenu.Enable="Включить предпросмотр" +ScaleFiltering="Фильтрация масштаба" +ScaleFiltering.Point="Точечная" +ScaleFiltering.Bilinear="Билинейная" +ScaleFiltering.Bicubic="Бикубическая" +ScaleFiltering.Lanczos="Ланцошная" + Deinterlacing="Устранение чересстрочности" Deinterlacing.Discard="Отклонить" Deinterlacing.Retro="Ретро" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="Настройки изменены" Basic.PropertiesWindow.Confirm="Имеются несохранённые изменения. Желаете сохранить их?" Basic.PropertiesWindow.NoProperties="Нет доступных параметров" Basic.PropertiesWindow.AddFiles="Добавить файлы" +Basic.PropertiesWindow.AddDir="Добавить папку" Basic.PropertiesWindow.AddURL="Добавить путь/URL" +Basic.PropertiesWindow.AddEditableListDir="Добавить папку в '%1'" Basic.PropertiesWindow.AddEditableListFiles="Добавить файлы в '%1'" Basic.PropertiesWindow.AddEditableListEntry="Добавить запись в '%1'" Basic.PropertiesWindow.EditEditableListEntry="Редактировать запись из '%1'" @@ -250,7 +259,9 @@ Basic.Main.Connecting="Соединение..." Basic.Main.StartRecording="Начать запись" Basic.Main.StartStreaming="Запустить трансляцию" Basic.Main.StopRecording="Остановить запись" +Basic.Main.StoppingRecording="Остановка Записи..." Basic.Main.StopStreaming="Остановить трансляцию" +Basic.Main.StoppingStreaming="Остановка вещания..." Basic.Main.ForceStopStreaming="Остановить передачу (отменить задержку)" Basic.MainMenu.File="&Файл" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Переместить &Наверх" Basic.MainMenu.Edit.Order.MoveToBottom="Переместить &Вниз" Basic.MainMenu.Edit.AdvAudio="&Расширенные свойства аудио" +Basic.MainMenu.View="&Посмотреть" +Basic.MainMenu.View.Toolbars="&Панель инструментов" +Basic.MainMenu.View.Toolbars.Listboxes="&Списки" +Basic.MainMenu.View.SceneTransitions="Сцена переходов" +Basic.MainMenu.View.StatusBar="&Строка состояния" + Basic.MainMenu.SceneCollection="Коллекция сцен" Basic.MainMenu.Profile="Профиль" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="Тема" Basic.Settings.General.Language="Язык" Basic.Settings.General.WarnBeforeStartingStream="Показывать окно подтверждения при запуске трансляции" Basic.Settings.General.WarnBeforeStoppingStream="Показывать окно подтверждения при остановке трансляции" +Basic.Settings.General.HideProjectorCursor="Скрыть курсор за проекторы" Basic.Settings.General.Snapping="Привязка расположения источника" Basic.Settings.General.ScreenSnapping="Привязка к краю экрана" Basic.Settings.General.CenterSnapping="Привязка к центру по горизонтали и вертикали" Basic.Settings.General.SourceSnapping="Привязка к другим источникам" Basic.Settings.General.SnapDistance="Чувствительность привязки" +Basic.Settings.General.RecordWhenStreaming="Автоматическая запись при стриме" +Basic.Settings.General.KeepRecordingWhenStreamStops="Сохранить запись, когда стрим остановится" Basic.Settings.Stream="Вещание" Basic.Settings.Stream.StreamType="Тип вещания" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="Задержка включения мик Basic.Settings.Audio.UnknownAudioDevice="[Устройство не подключено или недоступно]" Basic.Settings.Advanced="Расширенные" +Basic.Settings.Advanced.General.ProcessPriority="Приоритет процесса" +Basic.Settings.Advanced.General.ProcessPriority.High="Высокий" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Выше нормального" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Средний" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Низкий" Basic.Settings.Advanced.FormatWarning="Предупреждение: цветовые форматы, отличные от NV12, прежде всего предназначены для записи и не рекомендуются для потокового вещания. Преобразование цветового формата во время вещания может потребовать дополнительную нагрузку на ЦП." Basic.Settings.Advanced.Audio.BufferingTime="Время буферизации аудио" Basic.Settings.Advanced.Video.ColorFormat="Цветовой формат" diff --git a/obs/data/locale/sk-SK.ini b/obs/data/locale/sk-SK.ini index b6c77a2..45db795 100644 --- a/obs/data/locale/sk-SK.ini +++ b/obs/data/locale/sk-SK.ini @@ -82,6 +82,7 @@ Basic.DisplayCapture="Zachytávanie monitora" + Basic.Main.AddSceneDlg.Title="Pridať scénu" Basic.Main.AddSceneDlg.Text="Prosím, zadajte názov scény" @@ -175,6 +176,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Premies&tniť navrch" Basic.MainMenu.Edit.Order.MoveToBottom="Premiestniť naspodok (&B)" + Basic.MainMenu.Help="Pomoc (&H)" Basic.MainMenu.Help.Logs="&Log súbory" Basic.MainMenu.Help.Logs.ShowLogs="Zobraziť log &súbory" diff --git a/obs/data/locale/sl-SI.ini b/obs/data/locale/sl-SI.ini index a7b5b61..adb56a3 100644 --- a/obs/data/locale/sl-SI.ini +++ b/obs/data/locale/sl-SI.ini @@ -80,6 +80,7 @@ Basic.DisplayCapture="Zajemanje zaslona" + Basic.Main.AddSceneDlg.Title="Dodaj seceno" Basic.Main.AddSceneDlg.Text="Prosimo, vnesite ime scene" @@ -172,6 +173,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Premakni na &Vrh" Basic.MainMenu.Edit.Order.MoveToBottom="Premakni na &Dno" + Basic.MainMenu.Help="&Pomoč" Basic.MainMenu.Help.Logs="&Dnevniki" Basic.MainMenu.Help.Logs.ShowLogs="&Pokaži Zapisnik" diff --git a/obs/data/locale/sr-CS.ini b/obs/data/locale/sr-CS.ini index e758e13..a184506 100644 --- a/obs/data/locale/sr-CS.ini +++ b/obs/data/locale/sr-CS.ini @@ -118,6 +118,7 @@ LicenseAgreement.Exit="Izlaz" Remux.SourceFile="OBS snimak" Remux.TargetFile="Datoteka" Remux.Remux="Remux" +Remux.OBSRecording="OBS snimak" Remux.FinishedTitle="Remux završen" Remux.Finished="Završen remux snimka" Remux.FinishedError="Remux završen, ali datoteka možda nije kompletirana" @@ -143,6 +144,7 @@ Basic.DisplayCapture="Prikaži ulaz" Basic.Main.PreviewConextMenu.Enable="Omogući pregled" + Deinterlacing="Deinterlejsing" Deinterlacing.Discard="Odbaci" Deinterlacing.Retro="Retro" @@ -185,7 +187,9 @@ Basic.PropertiesWindow.ConfirmTitle="Podešavanja promenjena" Basic.PropertiesWindow.Confirm="Postoje podešavanja koja nisu sačuvana. Da li želite da ih sačuvate?" Basic.PropertiesWindow.NoProperties="Nema dostupnih svojstava" Basic.PropertiesWindow.AddFiles="Dodaj datoteke" +Basic.PropertiesWindow.AddDir="Dodaj direktorijum" Basic.PropertiesWindow.AddURL="Dodaj putanju/URL" +Basic.PropertiesWindow.AddEditableListDir="Dodaj direktorijum u '%1'" Basic.PropertiesWindow.AddEditableListFiles="Dodaj datoteke u '%1'" Basic.PropertiesWindow.AddEditableListEntry="Dodaj zapise u '%1'" Basic.PropertiesWindow.EditEditableListEntry="Izmeni zapise za '%1'" @@ -249,7 +253,9 @@ Basic.Main.Connecting="Povezivanje..." Basic.Main.StartRecording="Počni snimanje" Basic.Main.StartStreaming="Počni strimovanje" Basic.Main.StopRecording="Zaustavi snimanje" +Basic.Main.StoppingRecording="Zaustavljanje snimanja..." Basic.Main.StopStreaming="Zaustavi strimovanje" +Basic.Main.StoppingStreaming="Zaustavljanje emitovanja..." Basic.Main.ForceStopStreaming="Zaustavi strimovanje (poništi odlaganje)" Basic.MainMenu.File="&Fajl" @@ -286,6 +292,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Pomeri na vrh (&T)" Basic.MainMenu.Edit.Order.MoveToBottom="Pomeri na dno (&B)" Basic.MainMenu.Edit.AdvAudio="N&apredna podešavanja zvuka" + Basic.MainMenu.SceneCollection="Kolekcija &scena" Basic.MainMenu.Profile="&Profil" @@ -307,6 +314,7 @@ Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Jezik" Basic.Settings.General.WarnBeforeStartingStream="Prikaži prozor za potvrdu kada se započinju strimovi" Basic.Settings.General.WarnBeforeStoppingStream="Prikaži prozor za potvrdu kada se zaustavljaju strimovi" +Basic.Settings.General.HideProjectorCursor="Sakrij pokazivač na projektorima" Basic.Settings.General.Snapping="Poravnavanje privlačenjem izvora" Basic.Settings.General.ScreenSnapping="Privuci izvore ivici ekrana" Basic.Settings.General.CenterSnapping="Privuci izvore horizontalnoj i vertikalnoj sredini" diff --git a/obs/data/locale/sr-SP.ini b/obs/data/locale/sr-SP.ini index 9181b64..d7f91a4 100644 --- a/obs/data/locale/sr-SP.ini +++ b/obs/data/locale/sr-SP.ini @@ -118,6 +118,7 @@ LicenseAgreement.Exit="Излаз" Remux.SourceFile="OBS снимак" Remux.TargetFile="Датотека" Remux.Remux="Remux" +Remux.OBSRecording="OBS снимак" Remux.FinishedTitle="Remux завршен" Remux.Finished="Завршен remux снимка" Remux.FinishedError="Remux завршен, али датотека можда није комплетирана" @@ -143,6 +144,7 @@ Basic.DisplayCapture="Прикажи улаз" Basic.Main.PreviewConextMenu.Enable="Омогући преглед" + Deinterlacing="Деинтерлејсинг" Deinterlacing.Discard="Одбаци" Deinterlacing.Retro="Ретро" @@ -185,7 +187,9 @@ Basic.PropertiesWindow.ConfirmTitle="Подешавања промењена" Basic.PropertiesWindow.Confirm="Постоје подешавања која нису сачувана. Да ли желите да их сачувате?" Basic.PropertiesWindow.NoProperties="Нема доступних својстава" Basic.PropertiesWindow.AddFiles="Додај датотеке" +Basic.PropertiesWindow.AddDir="Додај директоријум" Basic.PropertiesWindow.AddURL="Додај путању/URL" +Basic.PropertiesWindow.AddEditableListDir="Додај директоријум у '%1'" Basic.PropertiesWindow.AddEditableListFiles="Додај датотеке у '%1'" Basic.PropertiesWindow.AddEditableListEntry="Додај записе у '%1'" Basic.PropertiesWindow.EditEditableListEntry="Измени записе за '%1'" @@ -249,7 +253,9 @@ Basic.Main.Connecting="Повезивање..." Basic.Main.StartRecording="Почни снимање" Basic.Main.StartStreaming="Почни стримовање" Basic.Main.StopRecording="Заустави снимање" +Basic.Main.StoppingRecording="Заустављање снимања..." Basic.Main.StopStreaming="Заустави стримовање" +Basic.Main.StoppingStreaming="Заустављање емитовања..." Basic.Main.ForceStopStreaming="Заустави стримовање (поништи одлагање)" Basic.MainMenu.File="Фајл (&F)" @@ -286,6 +292,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Помери на врх (&T)" Basic.MainMenu.Edit.Order.MoveToBottom="Помери на дно (&B)" Basic.MainMenu.Edit.AdvAudio="Н&апредна подешавања звука" + Basic.MainMenu.SceneCollection="Колекција сцена (&S)" Basic.MainMenu.Profile="Профил (&P)" @@ -307,6 +314,7 @@ Basic.Settings.General.Theme="Тема" Basic.Settings.General.Language="Језик" Basic.Settings.General.WarnBeforeStartingStream="Прикажи прозор за потврду када се започињу стримови" Basic.Settings.General.WarnBeforeStoppingStream="Прикажи прозор за потврду када се заустављају стримови" +Basic.Settings.General.HideProjectorCursor="Сакриј показивач на пројекторима" Basic.Settings.General.Snapping="Поравнавање привлачењем извора" Basic.Settings.General.ScreenSnapping="Привуци изворе ивици екрана" Basic.Settings.General.CenterSnapping="Привуци изворе хоризонталној и вертикалној средини" diff --git a/obs/data/locale/sv-SE.ini b/obs/data/locale/sv-SE.ini index 56d1da6..d475c6c 100644 --- a/obs/data/locale/sv-SE.ini +++ b/obs/data/locale/sv-SE.ini @@ -82,10 +82,12 @@ ConfirmExit.Text="OBS är aktivt. Alla strömmar/inspelningar kommer att stäng ConfirmRemove.Title="Bekräfta borttagning" ConfirmRemove.Text="Vill du verkligen ta bort '$1'?" +ConfirmRemove.TextMultiple="Är du säker på att du vill ta bort %1 poster?" Output.ConnectFail.Title="Anslutning misslyckades" Output.ConnectFail.BadPath="Ogiltig sökväg eller anslutnings-URL. Kontrollera att dina inställningar är korrekta." Output.ConnectFail.ConnectFailed="Kunde inte ansluta till servern" +Output.ConnectFail.InvalidStream="Kunde inte komma åt den valda kanalen eller strömnyckeln, var god dubbelkolla din strömnyckel. Om den stämmer kan det finnas något anslutningsproblem till servern." Output.ConnectFail.Error="Ett oväntat fel uppstod vid anslutning till servern. Se loggfilen för ytterligare information." Output.ConnectFail.Disconnected="Nedkopplad från servern." @@ -138,10 +140,17 @@ Basic.DisplayCapture="Bildskärmskälla" Basic.Main.PreviewConextMenu.Enable="Förhandsvisa" +ScaleFiltering="Skalningsfiltrering" +ScaleFiltering.Bilinear="Bilinjär" +ScaleFiltering.Bicubic="Bikubisk" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing.Discard="Avfärda" Deinterlacing.Retro="Retro" Deinterlacing.Linear="Linjär" Deinterlacing.Linear2x="Linjär 2x" +Deinterlacing.Yadif="Yadif" +Deinterlacing.Yadif2x="Yadif 2x" Deinterlacing.TopFieldFirst="Övre fältet först" Deinterlacing.BottomFieldFirst="Nedre fältet först" @@ -175,7 +184,9 @@ Basic.PropertiesWindow.ConfirmTitle="Inställningarna har ändrats" Basic.PropertiesWindow.Confirm="Det finns osparade ändringar. Vill du behålla dem?" Basic.PropertiesWindow.NoProperties="Inga inställningar tillgängliga" Basic.PropertiesWindow.AddFiles="Lägg till Filer" +Basic.PropertiesWindow.AddDir="Lägg till mapp" Basic.PropertiesWindow.AddURL="Lägg till Sökväg/URL" +Basic.PropertiesWindow.AddEditableListDir="Lägg till mapp i '%1'" Basic.PropertiesWindow.AddEditableListFiles="Lägg till filer i '%1'" Basic.PropertiesView.FPS.Simple="Enkla bildfrekvensvärden" @@ -237,7 +248,9 @@ Basic.Main.Connecting="Ansluter..." Basic.Main.StartRecording="Starta inspelning" Basic.Main.StartStreaming="Börja strömma" Basic.Main.StopRecording="Stoppa inspelning" +Basic.Main.StoppingRecording="Stoppar inspelning..." Basic.Main.StopStreaming="Sluta strömma" +Basic.Main.StoppingStreaming="Stoppar ström..." Basic.Main.ForceStopStreaming="Sluta strömma (ignorera fördröjning)" Basic.MainMenu.File="&Arkiv" @@ -274,6 +287,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="Lägg &överst" Basic.MainMenu.Edit.Order.MoveToBottom="Lägg unders&t" Basic.MainMenu.Edit.AdvAudio="&Avancerade ljudinställningar" +Basic.MainMenu.View="&Visa" +Basic.MainMenu.View.Toolbars="&Verktygsfält" +Basic.MainMenu.View.Toolbars.Listboxes="&Listrutor" +Basic.MainMenu.View.SceneTransitions="S&cenövergångar" +Basic.MainMenu.View.StatusBar="&Statusfält" + Basic.MainMenu.SceneCollection="&Scensamling" Basic.MainMenu.Profile="&Profil" @@ -295,13 +314,16 @@ Basic.Settings.General.Theme=" Tema" Basic.Settings.General.Language="Språk" Basic.Settings.General.WarnBeforeStartingStream="Visa bekräftelsedialog när ström startas" Basic.Settings.General.WarnBeforeStoppingStream="Visa bekräftelsedialog när ström stoppas" +Basic.Settings.General.HideProjectorCursor="Dölj pekaren över projektorer" Basic.Settings.General.ScreenSnapping="Fäst källor till skärmens kant" Basic.Settings.General.CenterSnapping="Fäst källor till den horisontala och vertikala mittenlinjen" Basic.Settings.General.SourceSnapping="Fäst källor till andra källor" Basic.Settings.General.SnapDistance="Fästkänslighet" +Basic.Settings.General.RecordWhenStreaming="Spela automatiskt in vid strömning" +Basic.Settings.General.KeepRecordingWhenStreamStops="Fortsätt spela in när strömmen stoppas" -Basic.Settings.Stream="Stream" -Basic.Settings.Stream.StreamType="Streamtyp" +Basic.Settings.Stream="Ström" +Basic.Settings.Stream.StreamType="Strömtyp" Basic.Settings.Output="Utmatning" Basic.Settings.Output.Format="Inspelningsformat" @@ -371,6 +393,7 @@ Basic.Settings.Output.Adv.FFmpeg.AEncoder="Ljudkodare" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Ljudkodar-inställningar (om något)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muxerinställningar (om det finns)" +FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z" FilenameFormatting.TT="%CCYY År, fyra siffror\n%YY År, de två sista siffrorna (00-99)\n%MM Månad som en siffra (01-12)\n%DD Månadens dag, inledande nolla (01-31)\n%hh Timme i 24-timmarsformat (00-23)\n%mm Minut (00-59)\n%ss Sekund (00-61)\n%% Ett procenttecken\n%a Förkortad veckodag\n%A Fullständig veckodag\n%b Förkortat månadsnamn\n%B Fullständigt månadsnamn\n%d Månadens dag, inledande nolla (01-31)\n%H Timme i 24-timmarsformat (00-23)\n%I Timme i 12-timmarsformat (01-12)\n%m Månad som en siffra (01-12)\n%M Minut (00-59)\n%p AM eller PM\n%S Sekund (00-61)\n%y År, de två sista siffrorna (00-99)\n%Y År\n%z ISO 8601-offset från UTC eller tidszonens\n namn eller förkortning\n%Z Tidszonens namn eller förkortning\n" @@ -410,6 +433,11 @@ Basic.Settings.Audio.PushToTalkDelay="Fördröjning för tryck för att tala" Basic.Settings.Audio.UnknownAudioDevice="[Enheten är inte ansluten eller tillgänglig]" Basic.Settings.Advanced="Avancerat" +Basic.Settings.Advanced.General.ProcessPriority="Processprioritet" +Basic.Settings.Advanced.General.ProcessPriority.High="Hög" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Över normal" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normal" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Inaktiv" Basic.Settings.Advanced.FormatWarning="Varning: Andra färgformat än NV12 är avsedda för inspelning och rekommenderas inte för att strömma. Högre processoranvändning kan uppstå vid strömning p.g.a. konvertering av färgformat." Basic.Settings.Advanced.Audio.BufferingTime="Ljudbuffringstid" Basic.Settings.Advanced.Video.ColorFormat="Färgformat" diff --git a/obs/data/locale/ta-IN.ini b/obs/data/locale/ta-IN.ini index 777158e..5514eec 100644 --- a/obs/data/locale/ta-IN.ini +++ b/obs/data/locale/ta-IN.ini @@ -84,6 +84,8 @@ New="புதிய" + + diff --git a/obs/data/locale/th-TH.ini b/obs/data/locale/th-TH.ini index 5228d51..8e0c377 100644 --- a/obs/data/locale/th-TH.ini +++ b/obs/data/locale/th-TH.ini @@ -50,6 +50,7 @@ Basic.Scene="ฉาก" + Basic.Main.AddSceneDlg.Title="เพิ่มฉาก" Basic.Main.DefaultSceneName.Text="ฉาก %1" @@ -87,6 +88,7 @@ Basic.MainMenu.File.Exit="อ&อก" + Basic.MainMenu.Help.CheckForUpdates="ตรวจสอบการอัพเดต" Basic.Settings.ConfirmTitle="ยืนยันการเปลี่ยนแปลง" diff --git a/obs/data/locale/tr-TR.ini b/obs/data/locale/tr-TR.ini index da1061c..67cd2c5 100644 --- a/obs/data/locale/tr-TR.ini +++ b/obs/data/locale/tr-TR.ini @@ -121,6 +121,7 @@ Basic.DisplayCapture="Ekran Yakalama" Basic.Main.PreviewConextMenu.Enable="Önizlemeyi Etkinleştir" + Basic.Main.AddSceneDlg.Title="Sahne Ekle" Basic.Main.AddSceneDlg.Text="Lütfen sahne adını giriniz" @@ -246,6 +247,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="En &Üste Taşı" Basic.MainMenu.Edit.Order.MoveToBottom="En &Alta Taşı" Basic.MainMenu.Edit.AdvAudio="&Gelişmiş Ses Özellikleri" + Basic.MainMenu.SceneCollection="&Sahne Koleksiyonu" Basic.MainMenu.Profile="&Profil" diff --git a/obs/data/locale/uk-UA.ini b/obs/data/locale/uk-UA.ini index 07c0ca9..602a911 100644 --- a/obs/data/locale/uk-UA.ini +++ b/obs/data/locale/uk-UA.ini @@ -6,8 +6,9 @@ OK="Так" Apply="Застосувати" Cancel="Скасувати" Close="Закрити" -Save="зберегти" +Save="Зберегти" Discard="Відхилити" +Disable="Вимкнено" Yes="Так" No="Ні" Add="Додати" @@ -26,6 +27,10 @@ Mixer="Мікшер" Browse="Огляд" Mono="Моно" Stereo="Стерео" +DroppedFrames="Пропущено кадрів %1 (%2%)" +PreviewProjector="Повноекранний Проектор (вікно Перегляду)" +SceneProjector="Повноекранний Проектор (Сцена)" +SourceProjector="Повноекранний Проектор (Джерело)" Clear="Очистити" Revert="Відмінити" Show="Показати" @@ -34,9 +39,33 @@ Untitled="Без назви" New="Новий" Duplicate="Дублювати" Enable="Увімкнути" +DisableOSXVSync="Вимкнути OSX V-Sync" +ResetOSXVSyncOnExit="Відновити OSX V-Sync при виході" +HighResourceUsage="Кодування перевантажено! Треба знизити вимоги до налаштування відео або спробувати швидші налаштування енкодера." +Transition="Відео-перехід" +QuickTransitions="Швидкі відео-переходи" +Left="Зліва" +Right="Зправа" +Top="Зверху" +Bottom="Знизу" +QuickTransitions.SwapScenes="Поміняти місцями сцени Перегляд/Вивід після Відео-переходу" +QuickTransitions.SwapScenesTT="Міняє місцями сцени Перегляд та Вивід після закінчення Відео-переходу (якщо сцена Вивід ще існує).\nЗміні внесені до обох сцен залишаються." +QuickTransitions.DuplicateScene="Використовувати копію сцени" +QuickTransitions.DuplicateSceneTT="Під час редагування поточної сцени дозволяє зберегти Вивід без змін.\nДля можливості редагувати властивості Джерел, увімкніть також 'Використовувати копію Джерел'.\nЗміна цієї опції призведе до оновлення поточної сцени, яка йде на Вивід (якщо вона ще існує)." +QuickTransitions.EditProperties="Використовувати копію Джерел" +QuickTransitions.EditPropertiesTT="Під час редагування поточної сцени, дозволяє редагувати властивості Джерел не змінюючи Вивід.\nЦя опція можлива якщо 'Використовувати копію сцени' вибрана.\nДеякі джерела (наприклад медіа або прилади захвату зображень) не підтримують копіювання, тобто не можна внести окремі зміни у джерело.\nЗміна властивостей таких джерел миттєво змінить Вивід (якщо він ще існує).\n\nУвага: Так як джерела внутрішньо копіюються, то це може призвести до додаткового навантаження на систему та використання відео ресурсів." +QuickTransitions.HotkeyName="Швидкий відео-перехід: %1" +Basic.AddTransition="Додати Відео-перехід що конфігурується" +Basic.RemoveTransition="Видалити Відео-перехід що конфігурується" +Basic.TransitionProperties="Властивості Відео-переходу" +Basic.SceneTransitions="Відео-переходи між Сценами" +Basic.TransitionDuration="Тривалість" +Basic.TogglePreviewProgramMode="Режим Студії" +TransitionNameDlg.Text="Будь ласка, введіть назву для Відео-переходу" +TransitionNameDlg.Title="Назва Відео-переходу" TitleBar.Profile="Профіль" TitleBar.Scenes="Сцени" @@ -47,29 +76,139 @@ NameExists.Text="Ім'я вже використовується." NoNameEntered.Title="Невірно введено ім'я" NoNameEntered.Text="Не можна використовувати порожні назви." +ConfirmStart.Title="Почати трансляцію?" +ConfirmStart.Text="Ви впевнені, що хочете почати трансляцію?" +ConfirmStop.Title="Закінчити трансляцію?" +ConfirmStop.Text="Ви впевнені, що хочете закінчити трансляцію?" ConfirmExit.Title="Вийти з OBS?" +ConfirmExit.Text="OBS ще працює. Всі розпочаті трансляції та записи буде припинено. Ви дійсно хочете вийти?" ConfirmRemove.Title="Підтвердження видалення" ConfirmRemove.Text="Ви дійсно бажаєте видалити \"$1\"?" +ConfirmRemove.TextMultiple="Ви впевнені, що хочете видалити %1 елементів?" Output.ConnectFail.Title="Не вдалося підключитися" +Output.ConnectFail.BadPath="Шлях або URL-адреса недосяжні. Будь ласка, перевірте налаштування програмного забезпечення." Output.ConnectFail.ConnectFailed="Не вдалося підключитися до сервера" +Output.ConnectFail.InvalidStream="Немає доступу до вказаного каналу або до ключа трансляції, будь ласка перевірте свій ключ для трансляцій. Якщо він дійсний, то можливо є проблеми зв'язку з сервером." +Output.ConnectFail.Error="Під час зв'язку з сервером відбулася несподівана помилка. Подробиці знаходяться у лог-файлі." Output.ConnectFail.Disconnected="Від'єднаний від серверу." Output.RecordFail.Title="Не вдалося розпочати запис" +Output.RecordFail.Unsupported="Формат виводу на жаль не підтримується або форматом не підтримується більш однієї звукової доріжки. Будь ласка, перевірте налаштування та повторіть спробу." +Output.RecordNoSpace.Title="Недостатньо простору на диску" +Output.RecordNoSpace.Msg="На диску недостатньо простору для запису." +Output.RecordError.Title="Помилка запису" +Output.RecordError.Msg="Під час запису відбулася несподівана помилка." +Output.BadPath.Title="Недійсний шлях до файлу" +Output.BadPath.Text="Шлях вказаний для виводу файлу недійсний. Будь ласка, перевірте у налаштуваннях, що шлях було вказано вірно." +LogReturnDialog="Лог успішно завантажено" LogReturnDialog.CopyURL="Копіювати посилання" LogReturnDialog.ErrorUploadingLog="Помилка завантаження файлу журналу" +LicenseAgreement="Ліцензійна угода" +LicenseAgreement.PleaseReview="Будь ласка, ознайомтеся з умовами ліцензії, перш ніж використовувати OBS. Використовуючи цю програму, ви підтверджуєте, що ви прочитали і згодні з умовами GNU General Public License v 2.0. Будь ласка, прокрутіть вниз, щоб побачити весь текст угоди." +LicenseAgreement.ClickIAgreeToContinue="Якщо ви згодні з умовами угоди, натисніть кнопку Я згоден(-а), щоб продовжити. Ви повинні прийняти угоду для використання OBS." +LicenseAgreement.IAgree="Я згоден(-а)" LicenseAgreement.Exit="Вихід" +Remux.SourceFile="OBS запис" +Remux.TargetFile="Кінцевий файл" +Remux.Remux="Ремультиплексація" +Remux.OBSRecording="OBS запис" +Remux.FinishedTitle="Ремультиплексацію завершено" +Remux.Finished="Запис ремультиплексовано" +Remux.FinishedError="Запис ремультиплексовано, але схоже файл неповний" +Remux.SelectRecording="Виберіть OBS запис …" +Remux.SelectTarget="Виберіть кінцевий файл …" +Remux.FileExistsTitle="Кінцевий файл існує" +Remux.FileExists="Кінцевий файл існує, ви хочете його замінити?" +Remux.ExitUnfinishedTitle="Ремультиплексація триває" +Remux.ExitUnfinished="Ремультиплексацію не завершено, якщо зупинити зараз, то файл може лишитись пошкодженим.\nВи впевнені, що хочете зупинити ремультиплексацію?" +UpdateAvailable="Доступне оновлення" +UpdateAvailable.Text="Доступна версія %1.%2.%3. Натисніть тут, щоб завантажити" Basic.Scene="Сцена" +Basic.DisplayCapture="Захват екрану" + +Basic.Main.PreviewConextMenu.Enable="Увімкнути вікно Перегляду" + +ScaleFiltering="Фільтр масштабування" +ScaleFiltering.Point="Ступінчастий" +ScaleFiltering.Bilinear="Білінійний" +ScaleFiltering.Bicubic="Бікубічний" +ScaleFiltering.Lanczos="Ланцош" + +Deinterlacing="Деінтерлейсинг" +Deinterlacing.Discard="Discard" +Deinterlacing.Retro="Retro" +Deinterlacing.Blend="Blend" +Deinterlacing.Blend2x="Blend 2x" +Deinterlacing.Linear="Linear" +Deinterlacing.Linear2x="Linear 2x" +Deinterlacing.Yadif="Yadif" +Deinterlacing.Yadif2x="Yadif 2x" +Deinterlacing.TopFieldFirst="Спочатку непарні рядки" +Deinterlacing.BottomFieldFirst="Спочатку парні рядки" + +Basic.Main.AddSceneDlg.Title="Додати Сцену" +Basic.Main.AddSceneDlg.Text="Будь ласка, введіть назву для Сцени" + +Basic.Main.DefaultSceneName.Text="Сцена %1" + +Basic.Main.AddSceneCollection.Title="Додати Набір Сцен" +Basic.Main.AddSceneCollection.Text="Будь ласка, введіть назву для Набору Сцен" + +Basic.Main.RenameSceneCollection.Title="Перейменування Набору Сцен" + +AddProfile.Title="Додати Профіль" +AddProfile.Text="Будь ласка, введіть назву для Профілю" + +RenameProfile.Title="Перейменування Профілю" + +Basic.Main.PreviewDisabled="Вікно Перегляду вимкнено" + +Basic.SourceSelect="Створити/Вибрати Джерело" +Basic.SourceSelect.CreateNew="Створити нове" +Basic.SourceSelect.AddExisting="Додати вже існуюче" +Basic.SourceSelect.AddVisible="Зробити джерело видимим" + +Basic.PropertiesWindow="Властивості для '%1'" +Basic.PropertiesWindow.SelectColor="Вибір кольору" +Basic.PropertiesWindow.SelectFont="Вибір шрифту" +Basic.PropertiesWindow.ConfirmTitle="Змінено налаштування" +Basic.PropertiesWindow.Confirm="Зміни незбережені. Ви хочете їх зберегти?" +Basic.PropertiesWindow.NoProperties="Немає доступних властивостей" + + + + + +Basic.TransformWindow.BoundsType="Тип рамки розміру" +Basic.TransformWindow.BoundsAlignment="Вирівнювання у рамці розміру" + + +Basic.TransformWindow.BoundsType.ScaleInner="Масштабувати до внутрішніх границь" +Basic.TransformWindow.BoundsType.ScaleOuter="Масштабувати до зовнішніх границь" +Basic.TransformWindow.BoundsType.ScaleToWidth="Масштабувати в ширину границь" +Basic.TransformWindow.BoundsType.ScaleToHeight="Масштабувати в висоту границь" +Basic.TransformWindow.BoundsType.Stretch="Розтягнути до границь" + + +Basic.Main.Scenes="Сцени" +Basic.Main.Sources="Джерела" +Basic.Main.Connecting="З'єднання..." +Basic.Main.StopRecording="Зупинити запис" +Basic.Main.StoppingRecording="Запис зупиняється..." +Basic.Main.StopStreaming="Закінчити трансляцію" +Basic.Main.StoppingStreaming="Припинення трансляції..." +Basic.Main.ForceStopStreaming="Закінчити трансляцію (миттєво)" @@ -83,6 +222,7 @@ Basic.Scene="Сцена" +FilenameFormatting.TT="%CCYY Рік, чотири цифри\n%YY Рік, останні дві цифри (00-99)\n%MM Місяць за номером (01-12)\n%DD День місяця, ноль попереду (01-31)\n%hh Години у 24-год. форматі (00-23)\n%mm Мінути (00-59)\n%ss Секунди (00-61)\n%% Знак % \n%a Абревіатура дня тижня\n%A День тижня повністю\n%b Абревіатура місяця\n%B Місяць повністю\n%d День місяця, ноль попереду (01-31)\n%H Години у 24-год. форматі (00-23)\n%I Години у 12-год. форматі (01-12)\n%m Місяць за номером (01-12)\n%M Мінути (00-59)\n%p ДП або ПП позначення\n%S Секунди (00-61)\n%y Рік, останні дві цифри (00-99)\n%Y Рік\n%z ISO 8601 поправка від UTC або часовий\n пояс чи абревіатура\n%Z Часовий пояс чи абревіатура\n" @@ -90,26 +230,44 @@ Basic.Scene="Сцена" - - - - - - - - - - - - - - - - - - - - +Basic.Hotkeys.StopStreaming="Закінчити трансляцію" +Basic.Hotkeys.StartRecording="Почати запис" +Basic.Hotkeys.StopRecording="Зупинити запис" +Basic.Hotkeys.SelectScene="Перейти до сцени" + +Hotkeys.Insert="Insert" +Hotkeys.Delete="Delete" +Hotkeys.Home="Home" +Hotkeys.End="End" +Hotkeys.PageUp="Page Up" +Hotkeys.PageDown="Page Down" +Hotkeys.NumLock="Num Lock" +Hotkeys.ScrollLock="Scroll Lock" +Hotkeys.CapsLock="Caps Lock" +Hotkeys.Backspace="Backspace" +Hotkeys.Tab="Tab" +Hotkeys.Print="Print" +Hotkeys.Pause="Pause" +Hotkeys.Left="Left" +Hotkeys.Right="Right" +Hotkeys.Up="Up" +Hotkeys.Down="Down" +Hotkeys.Windows="Windows" +Hotkeys.Super="Super" +Hotkeys.Menu="Menu" +Hotkeys.Space="Пробіл" +Hotkeys.NumpadNum="Numpad %1" +Hotkeys.NumpadMultiply="Numpad Multiply" +Hotkeys.NumpadDivide="Numpad Divide" +Hotkeys.NumpadAdd="Numpad Add" +Hotkeys.NumpadSubtract="Numpad Subtract" +Hotkeys.NumpadDecimal="Numpad Decimal" +Hotkeys.AppleKeypadNum="%1 (Keypad)" +Hotkeys.AppleKeypadMultiply="* (Keypad)" +Hotkeys.AppleKeypadDivide="/ (Keypad)" +Hotkeys.AppleKeypadAdd="+ (Keypad)" +Hotkeys.AppleKeypadSubtract="- (Keypad)" +Hotkeys.AppleKeypadDecimal=". (Keypad)" diff --git a/obs/data/locale/vi-VN.ini b/obs/data/locale/vi-VN.ini index d00db1c..59b22de 100644 --- a/obs/data/locale/vi-VN.ini +++ b/obs/data/locale/vi-VN.ini @@ -7,7 +7,8 @@ Apply="Áp dụng" Cancel="Hùy" Close="Đóng" Save="Lưu" -Discard="Loại bỏ" +Discard="Bỏ" +Disable="Tắt" Yes="Có" No="Không" Add="Thêm" @@ -26,29 +27,40 @@ Mixer="Máy trộn" Browse="trình duyệt" Mono="Âm thanh đơn" Stereo="Âm thanh nổi" -DroppedFrames="Khung bị bỏ %1 (%2%)" +DroppedFrames="Khung rớt %1 (%2%)" PreviewProjector="Toàn màn hình (xem trước)" SceneProjector="Toàn màn hình (cảnh)" -SourceProjector="Toàn màn hình (mã nguồn)" +SourceProjector="Toàn màn hình (nguồn)" Clear="Xóa" Revert="Phục hồi" Show="Hiện" Hide="Ẩn" Untitled="Chưa Đặt Tên" -New="Tài liệu mới" -Duplicate="tạo bản sao" +New="Mới" +Duplicate="Tạo bản sao" Enable="Kích hoạt" +DisableOSXVSync="Tắt OSX V-Sync" +ResetOSXVSyncOnExit="Đặt lại OSX V-Sync khi Thoát" +Transition="Chuyển tiếp" +QuickTransitions="C. cảnh nhanh" +Left="Trái" +Right="Phải" +Top="Trên" +Bottom="Dưới" + +QuickTransitions.SwapScenes="Hoán đổi cảnh Xem trước/Đầu ra sau khi Chuyển cảnh" +QuickTransitions.SwapScenesTT="Hoán đổi cảnh xem trước và cảnh đầu ra sau khi chuyển cảnh (nếu cảnh đầu ra gốc vẫn tồn tại).\nMọi thay đổi với cảnh đầu ra gốc sẽ không hoàn tác." +QuickTransitions.DuplicateScene="Tạo bản sao cảnh" - -TitleBar.Profile="Hồ sơ cá nhân" +TitleBar.Profile="Cấu hình" TitleBar.Scenes="Cảnh" NameExists.Title="Tên đã tồn tại" NameExists.Text="Tên hiện đang sử dụng." -NoNameEntered.Title="Vui lòng nhập tuổi hợp lệ" +NoNameEntered.Title="Vui lòng nhập tên hợp lệ" NoNameEntered.Text="Bạn không thể sử dụng tên có sản phẩm nào." @@ -114,6 +126,7 @@ Basic.DisplayCapture="Chụp màn hình" Basic.Main.PreviewConextMenu.Enable="Bật xem trước" + Basic.Main.AddSceneDlg.Title="Thêm cảnh" Basic.Main.AddSceneDlg.Text="Vui lòng nhập tên của cảnh" @@ -243,6 +256,7 @@ Basic.MainMenu.Edit.Order.MoveToTop="Chuyển đến đầu trang" Basic.MainMenu.Edit.Order.MoveToBottom="Chuyển đến dưới cùng" Basic.MainMenu.Edit.AdvAudio="Thuộc tính âm thanh nâng cao" + Basic.MainMenu.SceneCollection="& Cảnh bộ sưu tập" Basic.MainMenu.Profile="& Hồ sơ" diff --git a/obs/data/locale/zh-CN.ini b/obs/data/locale/zh-CN.ini index be86f53..e0ec6b4 100644 --- a/obs/data/locale/zh-CN.ini +++ b/obs/data/locale/zh-CN.ini @@ -87,6 +87,7 @@ ConfirmExit.Text="OBS工作中. 所有的流/录像将会停止. 你确定想要 ConfirmRemove.Title="确认移除" ConfirmRemove.Text="确定要删除 '$1' 吗?" +ConfirmRemove.TextMultiple="您确定要删除 %1 项目吗?" Output.ConnectFail.Title="连接失败" Output.ConnectFail.BadPath="无效的路径或URL。请检查您的设置以确认它们是有效的。" @@ -144,6 +145,12 @@ Basic.DisplayCapture="显示捕获" Basic.Main.PreviewConextMenu.Enable="开启预览" +ScaleFiltering="尺度滤波" +ScaleFiltering.Point="点" +ScaleFiltering.Bilinear="双线性算法" +ScaleFiltering.Bicubic="双立方算法" +ScaleFiltering.Lanczos="兰索斯算法" + Deinterlacing="去隔行扫描" Deinterlacing.Discard="舍弃" Deinterlacing.Retro="复古" @@ -186,7 +193,9 @@ Basic.PropertiesWindow.ConfirmTitle="设置已更改" Basic.PropertiesWindow.Confirm="有未保存的更改. 你想要保存他们吗?" Basic.PropertiesWindow.NoProperties="没有可用的属性" Basic.PropertiesWindow.AddFiles="添加文件" +Basic.PropertiesWindow.AddDir="添加目录" Basic.PropertiesWindow.AddURL="添加路径 /URL" +Basic.PropertiesWindow.AddEditableListDir="将目录添加到 '%1'" Basic.PropertiesWindow.AddEditableListFiles="将文件添加到 '%1'" Basic.PropertiesWindow.AddEditableListEntry="将条目添加到 '%1'" Basic.PropertiesWindow.EditEditableListEntry="编辑条目从 '%1'" @@ -250,7 +259,9 @@ Basic.Main.Connecting="连接中..." Basic.Main.StartRecording="开始录制" Basic.Main.StartStreaming="开始串流" Basic.Main.StopRecording="停止录制" +Basic.Main.StoppingRecording="停止录制..." Basic.Main.StopStreaming="停止串流" +Basic.Main.StoppingStreaming="停止推流..." Basic.Main.ForceStopStreaming="停止流 (放弃延迟)" Basic.MainMenu.File="& 文件" @@ -287,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="移至顶部" Basic.MainMenu.Edit.Order.MoveToBottom="移至底部" Basic.MainMenu.Edit.AdvAudio="&高级音频属性" +Basic.MainMenu.View="查看(&V)" +Basic.MainMenu.View.Toolbars="工具栏(&T)" +Basic.MainMenu.View.Toolbars.Listboxes="列表框(&L)" +Basic.MainMenu.View.SceneTransitions="场景过渡(&C)" +Basic.MainMenu.View.StatusBar="状态栏(&S)" + Basic.MainMenu.SceneCollection="&场景集合" Basic.MainMenu.Profile="&档案" @@ -308,11 +325,14 @@ Basic.Settings.General.Theme="主题" Basic.Settings.General.Language="语言" Basic.Settings.General.WarnBeforeStartingStream="启动流时显示确认对话框" Basic.Settings.General.WarnBeforeStoppingStream="停止流时显示确认对话框" +Basic.Settings.General.HideProjectorCursor="隐藏投影仪上的光标" Basic.Settings.General.Snapping="源对齐方式" Basic.Settings.General.ScreenSnapping="对齐源到屏幕边缘" Basic.Settings.General.CenterSnapping="水平和垂直居中对齐源" Basic.Settings.General.SourceSnapping="对齐源跟其他的源" Basic.Settings.General.SnapDistance="对齐的敏感性" +Basic.Settings.General.RecordWhenStreaming="当推流时自动录像" +Basic.Settings.General.KeepRecordingWhenStreamStops="当推流停止时保持录像" Basic.Settings.Stream="串流" Basic.Settings.Stream.StreamType="串流类型" @@ -431,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="按住讲话延时" Basic.Settings.Audio.UnknownAudioDevice="[设备未连接或不可用]" Basic.Settings.Advanced="高级" +Basic.Settings.Advanced.General.ProcessPriority="进程优先级" +Basic.Settings.Advanced.General.ProcessPriority.High="高" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="高于正常" +Basic.Settings.Advanced.General.ProcessPriority.Normal="常规" +Basic.Settings.Advanced.General.ProcessPriority.Idle="空闲" Basic.Settings.Advanced.FormatWarning="警告: NV12 之外的颜色格式主要用于录像, 并不推荐在流式传输时使用. 流可能导致更多的 CPU 使用率, 因为需要颜色格式转换." Basic.Settings.Advanced.Audio.BufferingTime="音频缓冲时间" Basic.Settings.Advanced.Video.ColorFormat="颜色格式" diff --git a/obs/data/locale/zh-TW.ini b/obs/data/locale/zh-TW.ini index c6f71f0..62e80f4 100644 --- a/obs/data/locale/zh-TW.ini +++ b/obs/data/locale/zh-TW.ini @@ -20,10 +20,10 @@ Properties="屬性" MoveUp="向上移動" MoveDown="向下移動" Settings="設定" -Display="顯示器" +Display="螢幕" Name="名稱" Exit="離開" -Mixer="混合器" +Mixer="混音器" Browse="瀏覽" Mono="單聲道" Stereo="立體聲" @@ -40,7 +40,7 @@ New="新增" Duplicate="複製" Enable="啟用" DisableOSXVSync="關閉OSX垂直同步" -ResetOSXVSyncOnExit="離開時重製OSX垂直同步" +ResetOSXVSyncOnExit="離開時重置OSX垂直同步" HighResourceUsage="編碼過載!考慮降低影像設定或使用更快的編碼預設。" Transition="轉場" QuickTransitions="快速轉場" @@ -57,15 +57,15 @@ QuickTransitions.EditProperties="複製來源" QuickTransitions.EditPropertiesTT="在修改同樣的場景時,讓修改影像來源的屬性時不需要修改輸出。\n這只能在'複製場景'被啟動時使用。\n某些來源 (如擷取或是媒體來源) 並不支援此功能且無法被單獨修改。\n變更這個值將會 (如果還存在的話) 重置目前的輸出場景。\n\n警告:由於來源會被複製,這功能可能會使用額外的系統或是影像資源。" QuickTransitions.HotkeyName="快速轉場:%1" -Basic.AddTransition="添加設置轉換動畫" -Basic.RemoveTransition="移除設置轉換動畫" -Basic.TransitionProperties="轉換動畫屬性" +Basic.AddTransition="添加設置轉換" +Basic.RemoveTransition="移除可選的轉場特效" +Basic.TransitionProperties="轉場特效屬性" Basic.SceneTransitions="轉場特效" Basic.TransitionDuration="持續時間" Basic.TogglePreviewProgramMode="工作室模式" -TransitionNameDlg.Text="請輸入轉換動畫名稱" -TransitionNameDlg.Title="轉換動畫名稱" +TransitionNameDlg.Text="請輸入轉場特效名稱" +TransitionNameDlg.Title="轉場特效名稱" TitleBar.Profile="設定檔" TitleBar.Scenes="場景" @@ -87,6 +87,7 @@ ConfirmExit.Text="正在使用 OBS,所有的串流/錄製都將被關閉。您 ConfirmRemove.Title="確認刪除?" ConfirmRemove.Text="您確定要刪除「$1」?" +ConfirmRemove.TextMultiple="您確定要移除這%1個項目嘛?" Output.ConnectFail.Title="連線失敗" Output.ConnectFail.BadPath="無效的路徑或 URL。 請確認您的設定是正確的。" @@ -118,6 +119,7 @@ LicenseAgreement.Exit="離開" Remux.SourceFile="OBS 錄影" Remux.TargetFile="目標檔案" Remux.Remux="重新封裝" +Remux.OBSRecording="OBS錄影" Remux.FinishedTitle="重新封裝完成" Remux.Finished="錄影已被重新封裝" Remux.FinishedError="錄影已被重新封裝,但檔案可能不完整" @@ -143,6 +145,12 @@ Basic.DisplayCapture="截取螢幕" Basic.Main.PreviewConextMenu.Enable="啟用預覽" +ScaleFiltering="縮放濾鏡" +ScaleFiltering.Point="點" +ScaleFiltering.Bilinear="雙線性插值" +ScaleFiltering.Bicubic="雙三次插值" +ScaleFiltering.Lanczos="Lanczos" + Deinterlacing="去交錯" Deinterlacing.Discard="捨棄" Deinterlacing.Retro="復古" @@ -152,6 +160,8 @@ Deinterlacing.Linear="線性" Deinterlacing.Linear2x="線性 2x" Deinterlacing.Yadif="Yadif" Deinterlacing.Yadif2x="Yadif 2x" +Deinterlacing.TopFieldFirst="由偶數場開始" +Deinterlacing.BottomFieldFirst="由奇數場開始" Basic.Main.AddSceneDlg.Title="新增場景" Basic.Main.AddSceneDlg.Text="請輸入場景名稱" @@ -183,12 +193,14 @@ Basic.PropertiesWindow.ConfirmTitle="設定值已變更" Basic.PropertiesWindow.Confirm="有尚未儲存的變更。要保存他們嗎?" Basic.PropertiesWindow.NoProperties="無可用的屬性" Basic.PropertiesWindow.AddFiles="新增檔案" +Basic.PropertiesWindow.AddDir="新增目錄" Basic.PropertiesWindow.AddURL="新增路徑/URL" +Basic.PropertiesWindow.AddEditableListDir="將目錄新增至「%1」" Basic.PropertiesWindow.AddEditableListFiles="將檔案新增至「%1」" Basic.PropertiesWindow.AddEditableListEntry="將項目新增至「%1」" Basic.PropertiesWindow.EditEditableListEntry="編輯「%1」的項目" -Basic.PropertiesView.FPS.Simple="簡單的 FPS 值" +Basic.PropertiesView.FPS.Simple="常用 FPS 值" Basic.PropertiesView.FPS.Rational="有理數 FPS 值" Basic.PropertiesView.FPS.ValidFPSRanges="FPS 的有效範圍:" @@ -214,23 +226,23 @@ Basic.TransformWindow="場景物件變型" Basic.TransformWindow.Position="位置" Basic.TransformWindow.Rotation="旋轉" Basic.TransformWindow.Size="大小" -Basic.TransformWindow.Alignment="位置原點" -Basic.TransformWindow.BoundsType="外框類型" -Basic.TransformWindow.BoundsAlignment="對齊外框" -Basic.TransformWindow.Bounds="外框大小" +Basic.TransformWindow.Alignment="對齊基準" +Basic.TransformWindow.BoundsType="邊框類型" +Basic.TransformWindow.BoundsAlignment="邊框內對齊" +Basic.TransformWindow.Bounds="邊框大小" Basic.TransformWindow.Crop="剪裁" -Basic.TransformWindow.Alignment.TopLeft="左上角" -Basic.TransformWindow.Alignment.TopCenter="水平靠上" -Basic.TransformWindow.Alignment.TopRight="右上角" -Basic.TransformWindow.Alignment.CenterLeft="垂直靠左" -Basic.TransformWindow.Alignment.Center="正中心" -Basic.TransformWindow.Alignment.CenterRight="垂直靠右" -Basic.TransformWindow.Alignment.BottomLeft="左下角" -Basic.TransformWindow.Alignment.BottomCenter="水平靠下" -Basic.TransformWindow.Alignment.BottomRight="右下角" +Basic.TransformWindow.Alignment.TopLeft="置頂靠左" +Basic.TransformWindow.Alignment.TopCenter="置頂居中" +Basic.TransformWindow.Alignment.TopRight="置頂靠右" +Basic.TransformWindow.Alignment.CenterLeft="靠左居中" +Basic.TransformWindow.Alignment.Center="置中" +Basic.TransformWindow.Alignment.CenterRight="靠右居中" +Basic.TransformWindow.Alignment.BottomLeft="置底靠左" +Basic.TransformWindow.Alignment.BottomCenter="置底居中" +Basic.TransformWindow.Alignment.BottomRight="置底靠右" -Basic.TransformWindow.BoundsType.None="無外框" +Basic.TransformWindow.BoundsType.None="無邊框" Basic.TransformWindow.BoundsType.MaxOnly="等比例縮放至框內,不超過原始大小" Basic.TransformWindow.BoundsType.ScaleInner="等比例縮放至框內" Basic.TransformWindow.BoundsType.ScaleOuter="等比例縮放至框外" @@ -247,7 +259,9 @@ Basic.Main.Connecting="連線中……" Basic.Main.StartRecording="開始錄製" Basic.Main.StartStreaming="開始串流" Basic.Main.StopRecording="停止錄製" +Basic.Main.StoppingRecording="停止錄製..." Basic.Main.StopStreaming="停止串流" +Basic.Main.StoppingStreaming="停止串流..." Basic.Main.ForceStopStreaming="停止實況(丟棄延遲)" Basic.MainMenu.File="檔案 (&F)" @@ -274,9 +288,9 @@ Basic.MainMenu.Edit.Transform.Rotate90CCW="逆時針旋轉 90°" Basic.MainMenu.Edit.Transform.Rotate180="旋轉 180°" Basic.MainMenu.Edit.Transform.FlipHorizontal="水平翻轉 (&H)" Basic.MainMenu.Edit.Transform.FlipVertical="垂直翻轉 (&V)" -Basic.MainMenu.Edit.Transform.FitToScreen="適合螢幕 (&F)" -Basic.MainMenu.Edit.Transform.StretchToScreen="拉伸到螢幕 (&S)" -Basic.MainMenu.Edit.Transform.CenterToScreen="垂直至中畫面 (&C)" +Basic.MainMenu.Edit.Transform.FitToScreen="縮放到螢幕大小,不改變比例 (&F)" +Basic.MainMenu.Edit.Transform.StretchToScreen="拉伸到螢幕大小 (&S)" +Basic.MainMenu.Edit.Transform.CenterToScreen="置中畫面 (&C)" Basic.MainMenu.Edit.Order="排序 (&O)" Basic.MainMenu.Edit.Order.MoveUp="向上移動 (&U)" Basic.MainMenu.Edit.Order.MoveDown="向下移動 (&D)" @@ -284,6 +298,12 @@ Basic.MainMenu.Edit.Order.MoveToTop="移至最上層 (&T)" Basic.MainMenu.Edit.Order.MoveToBottom="移至最下層 (&B)" Basic.MainMenu.Edit.AdvAudio="進階音訊屬性(&A)" +Basic.MainMenu.View="檢視(&V)" +Basic.MainMenu.View.Toolbars="工具列(&T)" +Basic.MainMenu.View.Toolbars.Listboxes="列表控制項(&L)" +Basic.MainMenu.View.SceneTransitions="轉場特效(&c)" +Basic.MainMenu.View.StatusBar="狀態列(&S)" + Basic.MainMenu.SceneCollection="場景群組 (&S)" Basic.MainMenu.Profile="設定檔 (&P)" @@ -305,6 +325,14 @@ Basic.Settings.General.Theme="佈景主題" Basic.Settings.General.Language="語言" Basic.Settings.General.WarnBeforeStartingStream="啟動串流時顯示確認對話框" Basic.Settings.General.WarnBeforeStoppingStream="停止串流時顯示確認對話框" +Basic.Settings.General.HideProjectorCursor="當游標在投影上時隱藏游標" +Basic.Settings.General.Snapping="貼齊對準來源" +Basic.Settings.General.ScreenSnapping="來源與螢幕邊緣貼齊" +Basic.Settings.General.CenterSnapping="來源與水平中央以及垂直中央貼齊" +Basic.Settings.General.SourceSnapping="來源與其他來源貼齊" +Basic.Settings.General.SnapDistance="貼齊敏感度" +Basic.Settings.General.RecordWhenStreaming="串流時自動錄製" +Basic.Settings.General.KeepRecordingWhenStreamStops="串流停止時繼續錄製" Basic.Settings.Stream="串流" Basic.Settings.Stream.StreamType="串流類型" @@ -314,30 +342,30 @@ Basic.Settings.Output.Format="錄影格式" Basic.Settings.Output.Encoder="編碼器" Basic.Settings.Output.SelectDirectory="選擇錄影資料夾" Basic.Settings.Output.SelectFile="選擇錄影檔案" -Basic.Settings.Output.EnforceBitrate="強制設定實況流量上限" +Basic.Settings.Output.EnforceBitrate="強制設定串流位元率上限" Basic.Settings.Output.Mode="輸出模式" Basic.Settings.Output.Mode.Simple="簡易" Basic.Settings.Output.Mode.Adv="進階" Basic.Settings.Output.Mode.FFmpeg="FFmpeg 輸出" Basic.Settings.Output.Simple.SavePath="錄影路徑" Basic.Settings.Output.Simple.RecordingQuality="錄影畫質" -Basic.Settings.Output.Simple.RecordingQuality.Stream="與實況同等畫質" +Basic.Settings.Output.Simple.RecordingQuality.Stream="與串流同等畫質" Basic.Settings.Output.Simple.RecordingQuality.Small="高畫質,檔案大小中等" Basic.Settings.Output.Simple.RecordingQuality.HQ="近乎無損畫質,檔案大小巨大" Basic.Settings.Output.Simple.RecordingQuality.Lossless="無損畫質,非常大的檔案" -Basic.Settings.Output.Simple.Warn.VideoBitrate="警告:實況影像流量將會被設為 %1,他是目前實況流量的上限。如果您確定想要超過 %1 的限制,請開啟進階編碼器選項,並取消「強制設定實況流量上限」。" -Basic.Settings.Output.Simple.Warn.AudioBitrate="警告:實況音訊流量將會被設為 %1,他是目前實況流量的上限。如果您確定想要超過 %1 的限制,請開啟進階編碼器選項,並取消「強制設定實況流量上限」。" -Basic.Settings.Output.Simple.Warn.Encoder="警告:如果錄影與實況同時運作,並使用與實況不同的編碼品質設定將會增加額外的CPU使用量" -Basic.Settings.Output.Simple.Warn.Lossless="警告:無損畫質將會產生大容量的暫存檔!無損畫質在高解析度或高幀率時,可能會每分鐘使用高達 7GB(gigabytes)的容量。除非您擁有海量的硬碟空間,否則不建議使用無損畫質錄製長時間的影片。" +Basic.Settings.Output.Simple.Warn.VideoBitrate="警告:串流影像位元率將會被設為 %1,他是目前串流位元率的上限。如果您確定想要超過 %1 的限制,請開啟進階編碼器選項,並取消「強制設定串流位元率上限」。" +Basic.Settings.Output.Simple.Warn.AudioBitrate="警告:實況串流位元率將會被設為 %1,他是目前串流位元率的上限。如果您確定想要超過 %1 的限制,請開啟進階編碼器選項,並取消「強制設定串流位元率上限」。" +Basic.Settings.Output.Simple.Warn.Encoder="警告:如果錄影與串流同時運作,並使用與串流不同的編碼品質設定將會增加額外的CPU使用量" +Basic.Settings.Output.Simple.Warn.Lossless="警告:無損畫質將會產生非常大的檔案!無損畫質在高解析度或高影格率時,可能會每分鐘使用高達 7GB(gigabytes)的容量。除非您擁有非常大量的硬碟空間,否則不建議使用無損畫質錄製長時間的影片。" Basic.Settings.Output.Simple.Warn.Lossless.Msg="你確定你想要使用無損畫質?" Basic.Settings.Output.Simple.Warn.Lossless.Title="無損畫質警告!" -Basic.Settings.Output.Simple.Warn.MultipleQSV="警告 ︰ 在同一時間串流和錄像,您不能使用多個單獨的 QSV 編碼器。 如果你想要在同一時間串流和錄像,請更改錄像編碼器或串流編碼器。" +Basic.Settings.Output.Simple.Warn.MultipleQSV="警告︰同時串流和錄影時,不能使用多個不同的 QSV 編碼器。如果你想要在同一時間串流和錄影,請更改錄影編碼器或串流編碼器。" Basic.Settings.Output.Simple.Encoder.Software="軟體編碼( x264 )" -Basic.Settings.Output.Simple.Encoder.Hardware.QSV="硬體 (QSV)" -Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="硬體 (NVENC)" +Basic.Settings.Output.Simple.Encoder.Hardware.QSV="硬體編碼 (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="硬體編碼 (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="軟體編碼( x264 預設低 CPU 使用率,將增加檔案容量 )" -Basic.Settings.Output.VideoBitrate="影像頻率(kbit/s)" -Basic.Settings.Output.AudioBitrate="音效頻率(kbit/s)" +Basic.Settings.Output.VideoBitrate="影像位元率(kbit/s)" +Basic.Settings.Output.AudioBitrate="音效位元率(kbit/s)" Basic.Settings.Output.Reconnect="自動重新連線" Basic.Settings.Output.RetryDelay="重試間隔(秒)" Basic.Settings.Output.MaxRetries="最大嘗試次數" @@ -385,6 +413,7 @@ Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muxer 設定(如果有才啟 FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z" +FilenameFormatting.TT="%CCYY 年, 四位數\n%YY 年, 末兩位數 (00-99)\n%MM 月(01-12)\n%DD 日(01-31)\n%hh 時(24小時格式, 00-23)\n%mm 分(00-59)\n%ss 秒(00-61)\n%% %符號\n%a 星期, 縮寫\n%A 星期\n%b 月, 縮寫\n%B 月\n%d 日(01-31)\n%H 時(24小時格式, 00-23)\n%I 時(12小時格式, 01-12)\n%m 月(01-12)\n%M 分(00-59)\n%p AM或PM\n%S 秒(00-61)\n%y 年, 末兩位數 (00-99)\n%Y 年\n%z 與UTC時區的時差(ISO 8601格式)或\n 時區名或時區縮寫\n%Z 時區名或縮寫\n" Basic.Settings.Video="影像" Basic.Settings.Video.Adapter="顯示卡:" @@ -392,11 +421,11 @@ Basic.Settings.Video.BaseResolution="來源(全畫面)解析度:" Basic.Settings.Video.ScaledResolution="輸出(縮放)解析度:" Basic.Settings.Video.DownscaleFilter="壓縮方式:" Basic.Settings.Video.DisableAeroWindows="暫時關閉 Aero 特效(僅限 Windows)" -Basic.Settings.Video.FPS="FPS(幀率):" +Basic.Settings.Video.FPS="FPS (影格率):" Basic.Settings.Video.FPSCommon="常用 FPS:" Basic.Settings.Video.FPSInteger="自訂 FPS:" Basic.Settings.Video.FPSFraction="自訂 FPS 比率:" -Basic.Settings.Video.Numerator="幀數(分子):" +Basic.Settings.Video.Numerator="影格數(分子):" Basic.Settings.Video.Denominator="秒數(分母):" Basic.Settings.Video.Renderer="渲染器:" Basic.Settings.Video.InvalidResolution="無效的解析度。 必須表達為 [寬]x[高] (例如:1920x1080)" @@ -422,6 +451,11 @@ Basic.Settings.Audio.PushToTalkDelay="按壓時說話 延遲" Basic.Settings.Audio.UnknownAudioDevice="[設備未連接或不可用]" Basic.Settings.Advanced="進階" +Basic.Settings.Advanced.General.ProcessPriority="程序優先順序" +Basic.Settings.Advanced.General.ProcessPriority.High="高" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="在標準以上" +Basic.Settings.Advanced.General.ProcessPriority.Normal="標準" +Basic.Settings.Advanced.General.ProcessPriority.Idle="閒置" Basic.Settings.Advanced.FormatWarning="警告: NV12以外的色彩格式主要用於錄影,並不推薦在串流時使用。串流時可能會因需要轉換色彩格式而增加CPU使用率。" Basic.Settings.Advanced.Audio.BufferingTime="音訊緩衝時間" Basic.Settings.Advanced.Video.ColorFormat="色彩格式" @@ -472,25 +506,25 @@ Hotkeys.Windows="Windows" Hotkeys.Super="超級" Hotkeys.Menu="選單" Hotkeys.Space="空格" -Hotkeys.NumpadNum="數字鍵盤 %1" -Hotkeys.NumpadMultiply="數字鍵 乘號" -Hotkeys.NumpadDivide="數字鍵 除號" -Hotkeys.NumpadAdd="數字鍵 加號" -Hotkeys.NumpadSubtract="數字鍵 減號" -Hotkeys.NumpadDecimal="數字鍵 十進位" +Hotkeys.NumpadNum="九宮鍵 %1" +Hotkeys.NumpadMultiply="九宮鍵 乘號" +Hotkeys.NumpadDivide="九宮鍵 除號" +Hotkeys.NumpadAdd="九宮鍵 加號" +Hotkeys.NumpadSubtract="九宮鍵 減號" +Hotkeys.NumpadDecimal="九宮鍵 十進位" Hotkeys.AppleKeypadNum="%1 (九宮鍵)" Hotkeys.AppleKeypadMultiply="* (九宮鍵)" Hotkeys.AppleKeypadDivide="/ (九宮鍵)" -Hotkeys.AppleKeypadAdd="+ (鍵盤)" -Hotkeys.AppleKeypadSubtract="+ (鍵盤)" -Hotkeys.AppleKeypadDecimal=". (鍵盤)" -Hotkeys.AppleKeypadEqual="+ (鍵盤)" +Hotkeys.AppleKeypadAdd="+ (九宮鍵)" +Hotkeys.AppleKeypadSubtract="+ (九宮鍵)" +Hotkeys.AppleKeypadDecimal=". (九宮鍵)" +Hotkeys.AppleKeypadEqual="+ (九宮鍵)" Hotkeys.MouseButton="滑鼠 %1" Mute="靜音" Unmute="取消靜音" Push-to-mute="按壓時靜音" -Push-to-talk="點擊即說" +Push-to-talk="按壓時說話" SceneItemShow="顯示 「%1」" SceneItemHide="隱藏 「%1」" diff --git a/obs/forms/OBSBasic.ui b/obs/forms/OBSBasic.ui index 0bd0191..76d8e55 100644 --- a/obs/forms/OBSBasic.ui +++ b/obs/forms/OBSBasic.ui @@ -7,7 +7,7 @@ 0 0 - 957 + 1110 724 @@ -575,210 +575,224 @@ - - - - 4 - - - - - - 120 - 0 - - - - - - - - 4 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 22 - 22 - - - - Basic.AddTransition - - - Basic.AddTransition - - - - - - - :/res/images/add.png:/res/images/add.png - - - true - - - addIconSmall - - - - - - - - 0 - 0 - - - - - 22 - 22 - - - - Basic.RemoveTransition - - - Basic.RemoveTransition - - - - - - - :/res/images/list_remove.png:/res/images/list_remove.png - - - true - - - removeIconSmall - - - - - - - - 0 - 0 - - - - - 22 - 22 - - - - Basic.TransitionProperties - - - Basic.TransitionProperties - - - - - - - :/res/images/configuration21_16.png:/res/images/configuration21_16.png - - - true - - - configIconSmall - - - - - - - - - 4 - - - - - - 0 - 0 - - - - Basic.TransitionDuration - - - transitionDuration - - - - - - - ms - - - 2 - - - 10000 - - - 50 - - - 300 - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - + Basic.SceneTransitions + + + + + 4 + + + 1 + + + 1 + + + 1 + + + 2 + + + + + + 120 + 0 + + + + + + + + 4 + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 22 + 22 + + + + Basic.AddTransition + + + Basic.AddTransition + + + + + + + :/res/images/add.png:/res/images/add.png + + + true + + + addIconSmall + + + + + + + + 0 + 0 + + + + + 22 + 22 + + + + Basic.RemoveTransition + + + Basic.RemoveTransition + + + + + + + :/res/images/list_remove.png:/res/images/list_remove.png + + + true + + + removeIconSmall + + + + + + + + 0 + 0 + + + + + 22 + 22 + + + + Basic.TransitionProperties + + + Basic.TransitionProperties + + + + + + + :/res/images/configuration21_16.png:/res/images/configuration21_16.png + + + true + + + configIconSmall + + + + + + + + + 4 + + + + + + 0 + 0 + + + + Basic.TransitionDuration + + + + + + + ms + + + 2 + + + 10000 + + + 50 + + + 300 + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + @@ -790,7 +804,7 @@ 0 0 - 957 + 1110 21 @@ -861,6 +875,7 @@ + @@ -884,8 +899,23 @@ + + + Basic.MainMenu.View + + + + Basic.MainMenu.View.Toolbars + + + + + + + + @@ -1248,6 +1278,47 @@ Basic.MainMenu.AlwaysOnTop + + + true + + + true + + + Basic.MainMenu.View.Toolbars.Listboxes + + + + + true + + + true + + + Basic.MainMenu.View.SceneTransitions + + + + + true + + + true + + + Basic.MainMenu.View.StatusBar + + + + + true + + + Basic.MainMenu.Edit.LockPreview + + diff --git a/obs/forms/OBSBasicSettings.ui b/obs/forms/OBSBasicSettings.ui index 736b75e..be10b2f 100644 --- a/obs/forms/OBSBasicSettings.ui +++ b/obs/forms/OBSBasicSettings.ui @@ -6,7 +6,7 @@ 0 0 - 896 + 981 667 @@ -185,14 +185,21 @@ - + + + + Basic.Settings.General.RecordWhenStreaming + + + + Qt::Horizontal - + true @@ -285,6 +292,23 @@ + + + + Basic.Settings.General.HideProjectorCursor + + + + + + + false + + + Basic.Settings.General.KeepRecordingWhenStreamStops + + + @@ -2497,8 +2521,8 @@ 0 0 - 80 - 16 + 98 + 28 @@ -2862,9 +2886,9 @@ 0 - 0 - 559 - 563 + -206 + 803 + 820 @@ -2883,6 +2907,28 @@ + + + + Basic.Settings.General + + + + + + Basic.Settings.Advanced.General.ProcessPriority + + + processPriority + + + + + + + + + @@ -3212,6 +3258,28 @@ + + + + Basic.Settings.Advanced.Network + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + + + Basic.Settings.Advanced.Network.BindToIP + + + + + + @@ -3292,8 +3360,8 @@ setCurrentIndex(int) - 159 - 34 + 310 + 29 241 @@ -3308,8 +3376,8 @@ setCurrentIndex(int) - 586 - 38 + 250 + 39 250 @@ -3324,12 +3392,12 @@ setVisible(bool) - 875 - 360 + 250 + 39 - 875 - 427 + 250 + 39 @@ -3340,12 +3408,12 @@ setVisible(bool) - 875 - 360 + 250 + 39 - 466 - 427 + 250 + 39 @@ -3356,12 +3424,12 @@ setVisible(bool) - 875 - 360 + 250 + 39 - 875 - 503 + 250 + 39 @@ -3372,12 +3440,12 @@ setVisible(bool) - 875 - 360 + 250 + 39 - 466 - 503 + 250 + 39 @@ -3388,12 +3456,12 @@ setCurrentIndex(int) - 705 - 144 + 259 + 48 - 396 - 245 + 241 + 30 @@ -3404,12 +3472,12 @@ setEnabled(bool) - 514 - 344 + 259 + 60 - 748 - 344 + 228 + 50 @@ -3420,12 +3488,12 @@ setEnabled(bool) - 864 - 141 + 259 + 39 - 427 - 178 + 228 + 29 @@ -3436,12 +3504,12 @@ setEnabled(bool) - 864 - 141 + 259 + 60 - 864 - 178 + 228 + 50 @@ -3452,12 +3520,12 @@ setCurrentIndex(int) - 864 - 141 + 259 + 60 - 427 - 215 + 259 + 60 @@ -3468,12 +3536,12 @@ setEnabled(bool) - 864 - 141 + 250 + 39 - 864 - 215 + 250 + 39 @@ -3484,12 +3552,12 @@ setEnabled(bool) - 427 - 355 + 250 + 39 - 862 - 355 + 250 + 39 @@ -3500,12 +3568,12 @@ setEnabled(bool) - 424 - 331 + 250 + 39 - 658 - 331 + 250 + 39 @@ -3516,12 +3584,12 @@ setEnabled(bool) - 875 - 254 + 250 + 39 - 466 - 291 + 250 + 39 @@ -3532,12 +3600,12 @@ setEnabled(bool) - 516 - 411 + 250 + 39 250 - 92 + 39 @@ -3548,12 +3616,12 @@ setEnabled(bool) - 533 - 273 + 250 + 39 - 449 - 301 + 250 + 39 @@ -3564,12 +3632,12 @@ setEnabled(bool) - 690 - 454 + 250 + 39 - 690 - 506 + 250 + 39 @@ -3580,12 +3648,12 @@ setEnabled(bool) - 376 - 196 + 720 + 280 - 305 - 224 + 346 + 306 @@ -3596,12 +3664,12 @@ setEnabled(bool) - 417 - 204 + 761 + 280 - 434 - 234 + 778 + 306 @@ -3612,12 +3680,12 @@ setEnabled(bool) - 476 - 202 + 820 + 280 - 466 - 253 + 810 + 329 @@ -3628,12 +3696,12 @@ setEnabled(bool) - 518 - 204 + 862 + 280 - 515 - 277 + 859 + 352 @@ -3644,12 +3712,12 @@ setEnabled(bool) - 557 - 207 + 866 + 280 866 - 306 + 375 @@ -3660,12 +3728,28 @@ setVisible(bool) - 484 - 147 + 250 + 39 - 483 - 170 + 250 + 39 + + + + + recordWhenStreaming + toggled(bool) + keepRecordStreamStops + setEnabled(bool) + + + 404 + 193 + + + 404 + 219 diff --git a/obs/forms/OBSBasicSourceSelect.ui b/obs/forms/OBSBasicSourceSelect.ui index 455cfd8..07f64ab 100644 --- a/obs/forms/OBSBasicSourceSelect.ui +++ b/obs/forms/OBSBasicSourceSelect.ui @@ -20,7 +20,7 @@ - + Basic.SourceSelect.CreateNew @@ -73,7 +73,7 @@ - radioButton + createNew toggled(bool) sourceName setEnabled(bool) diff --git a/obs/obs-app.cpp b/obs/obs-app.cpp index 0422027..232f8c8 100644 --- a/obs/obs-app.cpp +++ b/obs/obs-app.cpp @@ -328,6 +328,8 @@ bool OBSApp::InitGlobalConfigDefaults() config_set_default_string(globalConfig, "General", "Language", DEFAULT_LANG); config_set_default_uint(globalConfig, "General", "MaxLogs", 10); + config_set_default_string(globalConfig, "General", "ProcessPriority", + "Normal"); #if _WIN32 config_set_default_string(globalConfig, "Video", "Renderer", @@ -354,6 +356,16 @@ bool OBSApp::InitGlobalConfigDefaults() "CenterSnapping", false); config_set_default_double(globalConfig, "BasicWindow", "SnapDistance", 10.0); + config_set_default_bool(globalConfig, "BasicWindow", + "RecordWhenStreaming", false); + config_set_default_bool(globalConfig, "BasicWindow", + "KeepRecordingWhenStreamStops", false); + config_set_default_bool(globalConfig, "BasicWindow", + "ShowTransitions", true); + config_set_default_bool(globalConfig, "BasicWindow", + "ShowListboxToolbars", true); + config_set_default_bool(globalConfig, "BasicWindow", + "ShowStatusBar", true); #ifdef __APPLE__ config_set_default_bool(globalConfig, "Video", "DisableOSXVSync", true); @@ -1298,7 +1310,6 @@ static int run_program(fstream &logFile, int argc, char *argv[]) return 0; prof.Stop(); - PrintInitProfile(); return program.exec(); @@ -1424,6 +1435,16 @@ char *GetConfigPathPtr(const char *name) } } +int GetProgramDataPath(char *path, size_t size, const char *name) +{ + return os_get_program_data_path(path, size, name); +} + +char *GetProgramDataPathPtr(const char *name) +{ + return os_get_program_data_path_ptr(name); +} + bool GetFileSafeName(const char *name, std::string &file) { size_t base_len = strlen(name); diff --git a/obs/obs-app.hpp b/obs/obs-app.hpp index 8eac175..a341a82 100644 --- a/obs/obs-app.hpp +++ b/obs/obs-app.hpp @@ -136,6 +136,9 @@ public: int GetConfigPath(char *path, size_t size, const char *name); char *GetConfigPathPtr(const char *name); +int GetProgramDataPath(char *path, size_t size, const char *name); +char *GetProgramDataPathPtr(const char *name); + inline OBSApp *App() {return static_cast(qApp);} inline config_t *GetGlobalConfig() {return App()->GlobalConfig();} diff --git a/obs/platform-windows.cpp b/obs/platform-windows.cpp index c34bbd5..fb4c997 100644 --- a/obs/platform-windows.cpp +++ b/obs/platform-windows.cpp @@ -215,3 +215,18 @@ void SetAlwaysOnTop(QMainWindow *window, bool enable) SetWindowPos(hwnd, enable ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } + +void SetProcessPriority(const char *priority) +{ + if (!priority) + return; + + if (strcmp(priority, "High") == 0) + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); + else if (strcmp(priority, "AboveNormal") == 0) + SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); + else if (strcmp(priority, "Normal") == 0) + SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); + else if (strcmp(priority, "Idle") == 0) + SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS); +} diff --git a/obs/platform.hpp b/obs/platform.hpp index de9d1c6..a25e4a8 100644 --- a/obs/platform.hpp +++ b/obs/platform.hpp @@ -51,6 +51,7 @@ void SetAlwaysOnTop(QMainWindow *window, bool enable); #ifdef _WIN32 uint32_t GetWindowsVersion(); void SetAeroEnabled(bool enable); +void SetProcessPriority(const char *priority); #endif #ifdef __APPLE__ diff --git a/obs/properties-view.cpp b/obs/properties-view.cpp index 1d16b32..14cd3ff 100644 --- a/obs/properties-view.cpp +++ b/obs/properties-view.cpp @@ -401,7 +401,7 @@ static void AddComboItem(QComboBox *combo, obs_property_t *prop, var = QVariant::fromValue(val); } else if (format == OBS_COMBO_FORMAT_STRING) { - var = obs_property_list_item_string(prop, idx); + var = QByteArray(obs_property_list_item_string(prop, idx)); } combo->addItem(QT_UTF8(name), var); @@ -470,13 +470,16 @@ QWidget *OBSPropertiesView::AddList(obs_property_t *prop, bool &warning) if (type == OBS_COMBO_TYPE_EDITABLE) combo->setEditable(true); + combo->setMaxVisibleItems(40); + string value = from_obs_data(settings, name, format); if (format == OBS_COMBO_FORMAT_STRING && - type == OBS_COMBO_TYPE_EDITABLE) + type == OBS_COMBO_TYPE_EDITABLE) { combo->lineEdit()->setText(QT_UTF8(value.c_str())); - else - idx = combo->findData(QT_UTF8(value.c_str())); + } else { + idx = combo->findData(QByteArray(value.c_str())); + } if (type == OBS_COMBO_TYPE_EDITABLE) return NewWidget(prop, combo, @@ -1518,7 +1521,7 @@ void WidgetInfo::ListChanged(const char *setting) QVariant data; if (type == OBS_COMBO_TYPE_EDITABLE) { - data = combo->currentText(); + data = combo->currentText().toUtf8(); } else { int index = combo->currentIndex(); if (index != -1) @@ -1540,7 +1543,7 @@ void WidgetInfo::ListChanged(const char *setting) break; case OBS_COMBO_FORMAT_STRING: obs_data_set_string(view->settings, setting, - QT_TO_UTF8(data.toString())); + data.toByteArray().constData()); break; } } @@ -1632,6 +1635,8 @@ void WidgetInfo::EditableListChanged() obs_data_set_array(view->settings, setting, array); obs_data_array_release(array); + + ControlChanged(); } void WidgetInfo::ButtonClicked() @@ -1673,7 +1678,7 @@ void WidgetInfo::ControlChanged() if (!PathChanged(setting)) return; break; - case OBS_PROPERTY_EDITABLE_LIST: return; + case OBS_PROPERTY_EDITABLE_LIST: break; case OBS_PROPERTY_FRAME_RATE: if (!FrameRateChanged(widget, setting, view->settings)) return; @@ -1761,12 +1766,15 @@ public: void WidgetInfo::EditListAdd() { - bool allow_files = obs_property_editable_list_allow_files(property); - if (!allow_files) { + enum obs_editable_list_type type = obs_property_editable_list_type( + property); + + if (type == OBS_EDITABLE_LIST_TYPE_STRINGS) { EditListAddText(); return; } + /* Files and URLs */ QMenu popup(view->window()); QAction *action; @@ -1776,11 +1784,19 @@ void WidgetInfo::EditListAdd() this, &WidgetInfo::EditListAddFiles); popup.addAction(action); - action = new QAction(QTStr("Basic.PropertiesWindow.AddURL"), this); + action = new QAction(QTStr("Basic.PropertiesWindow.AddDir"), this); connect(action, &QAction::triggered, - this, &WidgetInfo::EditListAddText); + this, &WidgetInfo::EditListAddDir); popup.addAction(action); + if (type == OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS) { + action = new QAction(QTStr("Basic.PropertiesWindow.AddURL"), + this); + connect(action, &QAction::triggered, + this, &WidgetInfo::EditListAddText); + popup.addAction(action); + } + popup.exec(QCursor::pos()); } @@ -1826,6 +1842,26 @@ void WidgetInfo::EditListAddFiles() EditableListChanged(); } +void WidgetInfo::EditListAddDir() +{ + QListWidget *list = reinterpret_cast(widget); + const char *desc = obs_property_description(property); + const char *default_path = + obs_property_editable_list_default_path(property); + + QString title = QTStr("Basic.PropertiesWindow.AddEditableListDir") + .arg(QT_UTF8(desc)); + + QString dir = QFileDialog::getExistingDirectory( + App()->GetMainWindow(), title, QT_UTF8(default_path)); + + if (dir.isEmpty()) + return; + + list->addItem(dir); + EditableListChanged(); +} + void WidgetInfo::EditListRemove() { QListWidget *list = reinterpret_cast(widget); @@ -1839,7 +1875,8 @@ void WidgetInfo::EditListRemove() void WidgetInfo::EditListEdit() { QListWidget *list = reinterpret_cast(widget); - bool allow_files = obs_property_editable_list_allow_files(property); + enum obs_editable_list_type type = obs_property_editable_list_type( + property); const char *desc = obs_property_description(property); const char *filter = obs_property_editable_list_filter(property); QList selectedItems = list->selectedItems(); @@ -1848,8 +1885,21 @@ void WidgetInfo::EditListEdit() return; QListWidgetItem *item = selectedItems[0]; - EditableItemDialog dialog(widget->window(), item->text(), allow_files, - filter); + + if (type == OBS_EDITABLE_LIST_TYPE_FILES) { + QString path = QFileDialog::getOpenFileName( + App()->GetMainWindow(), QTStr("Browse"), + item->text(), QT_UTF8(filter)); + if (path.isEmpty()) + return; + + item->setText(path); + EditableListChanged(); + return; + } + + EditableItemDialog dialog(widget->window(), item->text(), + type != OBS_EDITABLE_LIST_TYPE_STRINGS, filter); auto title = QTStr("Basic.PropertiesWindow.EditEditableListEntry").arg( QT_UTF8(desc)); dialog.setWindowTitle(title); diff --git a/obs/properties-view.hpp b/obs/properties-view.hpp index 2d58d7c..4a153c7 100644 --- a/obs/properties-view.hpp +++ b/obs/properties-view.hpp @@ -52,6 +52,7 @@ public slots: void EditListAdd(); void EditListAddText(); void EditListAddFiles(); + void EditListAddDir(); void EditListRemove(); void EditListEdit(); void EditListUp(); diff --git a/obs/window-basic-filters.cpp b/obs/window-basic-filters.cpp index 2eeb437..f055d61 100644 --- a/obs/window-basic-filters.cpp +++ b/obs/window-basic-filters.cpp @@ -201,6 +201,16 @@ void OBSBasicFilters::RemoveFilter(OBSSource filter) } } + const char *filterName = obs_source_get_name(filter); + const char *sourceName = obs_source_get_name(source); + if (!sourceName || !filterName) + return; + + const char *filterId = obs_source_get_id(filter); + + blog(LOG_INFO, "User removed filter '%s' (%s) from source '%s'", + filterName, filterId, sourceName); + main->SaveProject(); } @@ -373,6 +383,12 @@ void OBSBasicFilters::AddNewFilter(const char *id) obs_source_t *filter = obs_source_create(id, name.c_str(), nullptr, nullptr); if (filter) { + const char *sourceName = obs_source_get_name(source); + + blog(LOG_INFO, "User added filter '%s' (%s) " + "to source '%s'", + name.c_str(), id, sourceName); + obs_source_filter_add(source, filter); obs_source_release(filter); } @@ -670,6 +686,11 @@ void OBSBasicFilters::FilterNameEdited(QWidget *editor, QListWidget *list) QTStr("NoNameEntered.Text")); } } else { + const char *sourceName = obs_source_get_name(source); + + blog(LOG_INFO, "User renamed filter '%s' on source '%s' to '%s'", + prevName, sourceName, name.c_str()); + listItem->setText(QT_UTF8(name.c_str())); obs_source_set_name(filter, name.c_str()); } diff --git a/obs/window-basic-main-outputs.cpp b/obs/window-basic-main-outputs.cpp index 8dcd875..e94503b 100644 --- a/obs/window-basic-main-outputs.cpp +++ b/obs/window-basic-main-outputs.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "audio-encoders.hpp" #include "window-basic-main.hpp" @@ -27,10 +28,10 @@ static void OBSStreamStopping(void *data, calldata_t *params) int sec = (int)obs_output_get_active_delay(obj); if (sec == 0) - return; - - QMetaObject::invokeMethod(output->main, - "StreamDelayStopping", Q_ARG(int, sec)); + QMetaObject::invokeMethod(output->main, "StreamStopping"); + else + QMetaObject::invokeMethod(output->main, + "StreamDelayStopping", Q_ARG(int, sec)); } static void OBSStartStreaming(void *data, calldata_t *params) @@ -75,6 +76,14 @@ static void OBSStopRecording(void *data, calldata_t *params) UNUSED_PARAMETER(params); } +static void OBSRecordStopping(void *data, calldata_t *params) +{ + BasicOutputHandler *output = static_cast(data); + QMetaObject::invokeMethod(output->main, "RecordStopping"); + + UNUSED_PARAMETER(params); +} + static void FindBestFilename(string &strPath, bool noSpace) { int num = 2; @@ -281,7 +290,7 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) streamDelayStarting.Connect(obs_output_get_signal_handler(streamOutput), "starting", OBSStreamStarting, this); - streamDelayStopping.Connect(obs_output_get_signal_handler(streamOutput), + streamStopping.Connect(obs_output_get_signal_handler(streamOutput), "stopping", OBSStreamStopping, this); startStreaming.Connect(obs_output_get_signal_handler(streamOutput), @@ -304,6 +313,8 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) "start", OBSStartRecording, this); stopRecording.Connect(obs_output_get_signal_handler(fileOutput), "stop", OBSStopRecording, this); + recordStopping.Connect(obs_output_get_signal_handler(fileOutput), + "stopping", OBSRecordStopping, this); } int SimpleOutput::GetAudioBitrate() const @@ -342,6 +353,7 @@ void SimpleOutput::Update() preset = config_get_string(main->Config(), "SimpleOutput", presetType); + obs_data_set_string(h264Settings, "rate_control", "CBR"); obs_data_set_int(h264Settings, "bitrate", videoBitrate); if (advanced) { @@ -528,6 +540,14 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) "DelaySec"); bool preserveDelay = config_get_bool(main->Config(), "Output", "DelayPreserve"); + const char *bindIP = config_get_string(main->Config(), "Output", + "BindIP"); + + obs_data_t *settings = obs_data_create(); + obs_data_set_string(settings, "bind_ip", bindIP); + obs_output_update(streamOutput, settings); + obs_data_release(settings); + if (!reconnect) maxRetries = 0; @@ -544,6 +564,18 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) return false; } +static void ensure_directory_exists(string &path) +{ + replace(path.begin(), path.end(), '\\', '/'); + + size_t last = path.rfind('/'); + if (last == string::npos) + return; + + string directory = path.substr(0, last); + os_mkdirs(directory.c_str()); +} + bool SimpleOutput::StartRecording() { if (usingRecordingPreset) { @@ -589,11 +621,10 @@ bool SimpleOutput::StartRecording() strPath += GenerateSpecifiedFilename(ffmpegOutput ? "avi" : format, noSpace, filenameFormat); + ensure_directory_exists(strPath); if (!overwriteIfExists) FindBestFilename(strPath, noSpace); - SetupOutputs(); - if (!ffmpegOutput) { obs_output_set_video_encoder(fileOutput, h264Recording); obs_output_set_audio_encoder(fileOutput, aacRecording, 0); @@ -761,7 +792,7 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) streamDelayStarting.Connect(obs_output_get_signal_handler(streamOutput), "starting", OBSStreamStarting, this); - streamDelayStopping.Connect(obs_output_get_signal_handler(streamOutput), + streamStopping.Connect(obs_output_get_signal_handler(streamOutput), "stopping", OBSStreamStopping, this); startStreaming.Connect(obs_output_get_signal_handler(streamOutput), @@ -773,6 +804,8 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) "start", OBSStartRecording, this); stopRecording.Connect(obs_output_get_signal_handler(fileOutput), "stop", OBSStopRecording, this); + recordStopping.Connect(obs_output_get_signal_handler(fileOutput), + "stopping", OBSRecordStopping, this); } void AdvancedOutput::UpdateStreamSettings() @@ -1052,6 +1085,14 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service) "DelaySec"); bool preserveDelay = config_get_bool(main->Config(), "Output", "DelayPreserve"); + const char *bindIP = config_get_string(main->Config(), "Output", + "BindIP"); + + obs_data_t *settings = obs_data_create(); + obs_data_set_string(settings, "bind_ip", bindIP); + obs_output_update(streamOutput, settings); + obs_data_release(settings); + if (!reconnect) maxRetries = 0; @@ -1123,6 +1164,7 @@ bool AdvancedOutput::StartRecording() strPath += GenerateSpecifiedFilename(recFormat, noSpace, filenameFormat); + ensure_directory_exists(strPath); if (!overwriteIfExists) FindBestFilename(strPath, noSpace); diff --git a/obs/window-basic-main-outputs.hpp b/obs/window-basic-main-outputs.hpp index ec041c0..186d3b4 100644 --- a/obs/window-basic-main-outputs.hpp +++ b/obs/window-basic-main-outputs.hpp @@ -15,7 +15,8 @@ struct BasicOutputHandler { OBSSignal startStreaming; OBSSignal stopStreaming; OBSSignal streamDelayStarting; - OBSSignal streamDelayStopping; + OBSSignal streamStopping; + OBSSignal recordStopping; inline BasicOutputHandler(OBSBasic *main_) : main(main_) {} diff --git a/obs/window-basic-main-transitions.cpp b/obs/window-basic-main-transitions.cpp index 212b316..1467866 100644 --- a/obs/window-basic-main-transitions.cpp +++ b/obs/window-basic-main-transitions.cpp @@ -562,6 +562,11 @@ void OBSBasic::SetCurrentScene(obs_source_t *scene, bool force) } UpdateSceneSelection(scene); + + bool userSwitched = (!force && !disableSaving); + blog(LOG_INFO, "%s to scene '%s'", + userSwitched ? "User switched" : "Switched", + obs_source_get_name(scene)); } void OBSBasic::CreateProgramDisplay() diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 20eb765..5b8b654 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -90,8 +91,13 @@ static void SetOBSRef(QListWidgetItem *item, T &&val) static void AddExtraModulePaths() { char base_module_dir[512]; +#if defined(_WIN32) || defined(__APPLE__) + int ret = GetProgramDataPath(base_module_dir, sizeof(base_module_dir), + "obs-studio/plugins/%module%"); +#else int ret = GetConfigPath(base_module_dir, sizeof(base_module_dir), "obs-studio/plugins/%module%"); +#endif if (ret <= 0) return; @@ -132,10 +138,15 @@ OBSBasic::OBSBasic(QWidget *parent) int posy = config_get_int(App()->GlobalConfig(), "BasicWindow", "posy"); - if (!WindowPositionValid(posx, posy)) - posx = posy = 0; - - setGeometry(posx, posy, width, height); + if (!WindowPositionValid(posx, posy)) { + QRect rect = App()->desktop()->availableGeometry(); + setGeometry(QStyle::alignedRect( + Qt::LeftToRight, + Qt::AlignCenter, + size(), rect)); + } else { + setGeometry(posx, posy, width, height); + } } char styleSheetPath[512]; @@ -363,6 +374,8 @@ void OBSBasic::Save(const char *file) ui->transitionDuration->value(), transitions, scene, curProgramScene); + obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked()); + if (!obs_data_save_json_safe(saveData, file, "tmp", "bak")) blog(LOG_ERROR, "Could not save scene data to %s", file); @@ -469,6 +482,51 @@ void OBSBasic::LoadSceneListOrder(obs_data_array_t *array) } } +static void LogFilter(obs_source_t*, obs_source_t *filter, void *v_val) +{ + const char *name = obs_source_get_name(filter); + const char *id = obs_source_get_id(filter); + int val = (int)(intptr_t)v_val; + string indent; + + for (int i = 0; i < val; i++) + indent += " "; + + blog(LOG_INFO, "%s- filter: '%s' (%s)", indent.c_str(), name, id); +} + +static bool LogSceneItem(obs_scene_t*, obs_sceneitem_t *item, void*) +{ + obs_source_t *source = obs_sceneitem_get_source(item); + const char *name = obs_source_get_name(source); + const char *id = obs_source_get_id(source); + + blog(LOG_INFO, " - source: '%s' (%s)", name, id); + + obs_source_enum_filters(source, LogFilter, (void*)(intptr_t)2); + return true; +} + +void OBSBasic::LogScenes() +{ + blog(LOG_INFO, "------------------------------------------------"); + blog(LOG_INFO, "Loaded scenes:"); + + for (int i = 0; i < ui->scenes->count(); i++) { + QListWidgetItem *item = ui->scenes->item(i); + OBSScene scene = GetOBSRef(item); + + obs_source_t *source = obs_scene_get_source(scene); + const char *name = obs_source_get_name(source); + + blog(LOG_INFO, "- scene '%s':", name); + obs_scene_enum_items(scene, LogSceneItem, nullptr); + obs_source_enum_filters(source, LogFilter, (void*)(intptr_t)1); + } + + blog(LOG_INFO, "------------------------------------------------"); +} + void OBSBasic::Load(const char *file) { if (!file || !os_file_exists(file)) { @@ -596,6 +654,10 @@ retryScene: RefreshQuickTransitions(); + bool previewLocked = obs_data_get_bool(data, "preview_locked"); + ui->preview->SetLocked(previewLocked); + ui->actionLockPreview->setChecked(previewLocked); + obs_data_release(data); if (!opt_starting_scene.empty()) @@ -613,6 +675,8 @@ retryScene: opt_start_recording = false; } + LogScenes(); + disableSaving--; } @@ -802,6 +866,8 @@ bool OBSBasic::InitBasicConfigDefaults() config_set_default_uint (basicConfig, "Output", "RetryDelay", 10); config_set_default_uint (basicConfig, "Output", "MaxRetries", 20); + config_set_default_string(basicConfig, "Output", "BindIP", "default"); + int i = 0; uint32_t scale_cx = cx; uint32_t scale_cy = cy; @@ -951,8 +1017,10 @@ void OBSBasic::ResetOutputs() } } -#define MAIN_SEPARATOR \ - "=====================================================================" +#define STARTUP_SEPARATOR \ + "==== Startup complete ===============================================" +#define SHUTDOWN_SEPARATOR \ + "==== Shutting down ==================================================" void OBSBasic::OBSInit() { @@ -1001,9 +1069,12 @@ void OBSBasic::OBSInit() InitHotkeys(); AddExtraModulePaths(); + blog(LOG_INFO, "---------------------------------"); obs_load_all_modules(); + blog(LOG_INFO, "---------------------------------"); + obs_log_loaded_modules(); - blog(LOG_INFO, MAIN_SEPARATOR); + blog(LOG_INFO, STARTUP_SEPARATOR); ResetOutputs(); CreateHotkeys(); @@ -1022,6 +1093,21 @@ void OBSBasic::OBSInit() SetPreviewProgramMode(config_get_bool(App()->GlobalConfig(), "BasicWindow", "PreviewProgramMode")); +#define SET_VISIBILITY(name, control) \ + do { \ + if (config_has_user_value(App()->GlobalConfig(), \ + "BasicWindow", name)) { \ + bool visible = config_get_bool(App()->GlobalConfig(), \ + "BasicWindow", name); \ + ui->control->setChecked(visible); \ + } \ + } while (false) + + SET_VISIBILITY("ShowTransitions", toggleSceneTransitions); + SET_VISIBILITY("ShowListboxToolbars", toggleListboxToolbars); + SET_VISIBILITY("ShowStatusBar", toggleStatusBar); +#undef SET_VISIBILITY + { ProfileScope("OBSBasic::Load"); disableSaving--; @@ -1588,6 +1674,12 @@ void OBSBasic::AddScene(OBSSource source) }, &addSceneItem); SaveProject(); + + if (!disableSaving) { + obs_source_t *source = obs_scene_get_source(scene); + blog(LOG_INFO, "User added scene '%s'", + obs_source_get_name(source)); + } } void OBSBasic::RemoveScene(OBSSource source) @@ -1613,6 +1705,11 @@ void OBSBasic::RemoveScene(OBSSource source) } SaveProject(); + + if (!disableSaving) { + blog(LOG_INFO, "User Removed scene '%s'", + obs_source_get_name(source)); + } } void OBSBasic::AddSceneItem(OBSSceneItem item) @@ -1623,24 +1720,39 @@ void OBSBasic::AddSceneItem(OBSSceneItem item) InsertSceneItem(item); SaveProject(); + + if (!disableSaving) { + obs_source_t *sceneSource = obs_scene_get_source(scene); + obs_source_t *itemSource = obs_sceneitem_get_source(item); + blog(LOG_INFO, "User added source '%s' (%s) to scene '%s'", + obs_source_get_name(itemSource), + obs_source_get_id(itemSource), + obs_source_get_name(sceneSource)); + } } void OBSBasic::RemoveSceneItem(OBSSceneItem item) { - obs_scene_t *scene = obs_sceneitem_get_scene(item); + for (int i = 0; i < ui->sources->count(); i++) { + QListWidgetItem *listItem = ui->sources->item(i); - if (GetCurrentScene() == scene) { - for (int i = 0; i < ui->sources->count(); i++) { - QListWidgetItem *listItem = ui->sources->item(i); - - if (GetOBSRef(listItem) == item) { - DeleteListItem(ui->sources, listItem); - break; - } + if (GetOBSRef(listItem) == item) { + DeleteListItem(ui->sources, listItem); + break; } } SaveProject(); + + if (!disableSaving) { + obs_scene_t *scene = obs_sceneitem_get_scene(item); + obs_source_t *sceneSource = obs_scene_get_source(scene); + obs_source_t *itemSource = obs_sceneitem_get_source(item); + blog(LOG_INFO, "User Removed source '%s' (%s) from scene '%s'", + obs_source_get_name(itemSource), + obs_source_get_id(itemSource), + obs_source_get_name(sceneSource)); + } } void OBSBasic::UpdateSceneSelection(OBSSource source) @@ -2149,6 +2261,8 @@ void OBSBasic::SourceRenamed(void *data, calldata_t *params) "RenameSources", Q_ARG(QString, QT_UTF8(newName)), Q_ARG(QString, QT_UTF8(prevName))); + + blog(LOG_INFO, "Source '%s' renamed to '%s'", prevName, newName); } void OBSBasic::DrawBackdrop(float cx, float cy) @@ -2252,13 +2366,20 @@ void OBSBasic::SetService(obs_service_t *newService) service = newService; } -bool OBSBasic::StreamingActive() +bool OBSBasic::StreamingActive() const { if (!outputHandler) return false; return outputHandler->StreamingActive(); } +bool OBSBasic::Active() const +{ + if (!outputHandler) + return false; + return outputHandler->Active(); +} + #ifdef _WIN32 #define IS_WIN32 1 #else @@ -2495,6 +2616,8 @@ void OBSBasic::ClearSceneData() void OBSBasic::closeEvent(QCloseEvent *event) { + blog(LOG_INFO, SHUTDOWN_SEPARATOR); + if (outputHandler && outputHandler->Active()) { QMessageBox::StandardButton button = QMessageBox::question( this, QTStr("ConfirmExit.Title"), @@ -2881,6 +3004,38 @@ QMenu *OBSBasic::AddDeinterlacingMenu(obs_source_t *source) return menu; } +void OBSBasic::SetScaleFilter() +{ + QAction *action = reinterpret_cast(sender()); + obs_scale_type mode = (obs_scale_type)action->property("mode").toInt(); + OBSSceneItem sceneItem = GetCurrentSceneItem(); + + obs_sceneitem_set_scale_filter(sceneItem, mode); +} + +QMenu *OBSBasic::AddScaleFilteringMenu(obs_sceneitem_t *item) +{ + QMenu *menu = new QMenu(QTStr("ScaleFiltering")); + obs_scale_type scaleFilter = obs_sceneitem_get_scale_filter(item); + QAction *action; + +#define ADD_MODE(name, mode) \ + action = menu->addAction(QTStr("" name), this, \ + SLOT(SetScaleFilter())); \ + action->setProperty("mode", (int)mode); \ + action->setCheckable(true); \ + action->setChecked(scaleFilter == mode); + + ADD_MODE("Disable", OBS_SCALE_DISABLE); + ADD_MODE("ScaleFiltering.Point", OBS_SCALE_POINT); + ADD_MODE("ScaleFiltering.Bilinear", OBS_SCALE_BILINEAR); + ADD_MODE("ScaleFiltering.Bicubic", OBS_SCALE_BICUBIC); + ADD_MODE("ScaleFiltering.Lanczos", OBS_SCALE_LANCZOS); +#undef ADD_MODE + + return menu; +} + void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) { QMenu popup(this); @@ -2897,6 +3052,12 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) if (IsPreviewProgramMode()) action->setEnabled(false); + action = popup.addAction( + QTStr("Basic.MainMenu.Edit.LockPreview"), + this, SLOT(on_actionLockPreview_triggered())); + action->setCheckable(true); + action->setChecked(ui->preview->Locked()); + previewProjector = new QMenu(QTStr("PreviewProjector")); AddProjectorMenuMonitors(previewProjector, this, SLOT(OpenPreviewProjector())); @@ -2939,6 +3100,10 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) popup.addMenu(AddDeinterlacingMenu(source)); popup.addSeparator(); } + + popup.addMenu(AddScaleFilteringMenu(sceneItem)); + popup.addSeparator(); + popup.addMenu(sourceProjector); popup.addSeparator(); @@ -2992,21 +3157,24 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu() size_t idx = 0; QMenu *popup = new QMenu(QTStr("Add"), this); - while (obs_enum_input_types(idx++, &type)) { - const char *name = obs_source_get_display_name(type); - - if (strcmp(type, "scene") == 0) - continue; + auto addSource = [this, popup] (const char *type, const char *name) { QAction *popupItem = new QAction(QT_UTF8(name), this); popupItem->setData(QT_UTF8(type)); connect(popupItem, SIGNAL(triggered(bool)), this, SLOT(AddSourceFromAction())); popup->addAction(popupItem); + }; + while (obs_enum_input_types(idx++, &type)) { + const char *name = obs_source_get_display_name(type); + + addSource(type, name); foundValues = true; } + addSource("scene", Str("Basic.Scene")); + if (!foundValues) { delete popup; popup = nullptr; @@ -3046,11 +3214,51 @@ void OBSBasic::on_actionAddSource_triggered() void OBSBasic::on_actionRemoveSource_triggered() { - OBSSceneItem item = GetCurrentSceneItem(); - obs_source_t *source = obs_sceneitem_get_source(item); + vector items; - if (source && QueryRemoveSource(source)) - obs_sceneitem_remove(item); + auto func = [] (obs_scene_t *, obs_sceneitem_t *item, void *param) + { + vector &items = + *reinterpret_cast*>(param); + if (obs_sceneitem_selected(item)) + items.emplace_back(item); + return true; + }; + + obs_scene_enum_items(GetCurrentScene(), func, &items); + + if (!items.size()) + return; + + auto removeMultiple = [this] (size_t count) + { + QString text = QTStr("ConfirmRemove.TextMultiple") + .arg(QString::number(count)); + + QMessageBox remove_items(this); + remove_items.setText(text); + QAbstractButton *Yes = remove_items.addButton(QTStr("Yes"), + QMessageBox::YesRole); + remove_items.addButton(QTStr("No"), QMessageBox::NoRole); + remove_items.setIcon(QMessageBox::Question); + remove_items.setWindowTitle(QTStr("ConfirmRemove.Title")); + remove_items.exec(); + + return Yes == remove_items.clickedButton(); + }; + + if (items.size() == 1) { + OBSSceneItem &item = items[0]; + obs_source_t *source = obs_sceneitem_get_source(item); + + if (source && QueryRemoveSource(source)) + obs_sceneitem_remove(item); + } else { + if (removeMultiple(items.size())) { + for (auto &item : items) + obs_sceneitem_remove(item); + } + } } void OBSBasic::on_actionInteract_triggered() @@ -3339,8 +3547,34 @@ void OBSBasic::StartStreaming() ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); } + + bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), + "BasicWindow", "RecordWhenStreaming"); + if (recordWhenStreaming) + StartRecording(); } +#ifdef _WIN32 +static inline void UpdateProcessPriority() +{ + const char *priority = config_get_string(App()->GlobalConfig(), + "General", "ProcessPriority"); + if (priority && strcmp(priority, "Normal") != 0) + SetProcessPriority(priority); +} + +static inline void ClearProcessPriority() +{ + const char *priority = config_get_string(App()->GlobalConfig(), + "General", "ProcessPriority"); + if (priority && strcmp(priority, "Normal") != 0) + SetProcessPriority("Normal"); +} +#else +#define UpdateProcessPriority() do {} while(false) +#define ClearProcessPriority() do {} while(false) +#endif + void OBSBasic::StopStreaming() { SaveProject(); @@ -3351,7 +3585,15 @@ void OBSBasic::StopStreaming() if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(true); App()->DecrementSleepInhibition(); + ClearProcessPriority(); } + + bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), + "BasicWindow", "RecordWhenStreaming"); + bool keepRecordingWhenStreamStops = config_get_bool(GetGlobalConfig(), + "BasicWindow", "KeepRecordingWhenStreamStops"); + if (recordWhenStreaming && !keepRecordingWhenStreamStops) + StopRecording(); } void OBSBasic::ForceStopStreaming() @@ -3364,7 +3606,15 @@ void OBSBasic::ForceStopStreaming() if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(true); App()->DecrementSleepInhibition(); + ClearProcessPriority(); } + + bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), + "BasicWindow", "RecordWhenStreaming"); + bool keepRecordingWhenStreamStops = config_get_bool(GetGlobalConfig(), + "BasicWindow", "KeepRecordingWhenStreamStops"); + if (recordWhenStreaming && !keepRecordingWhenStreamStops) + StopRecording(); } void OBSBasic::StreamDelayStarting(int sec) @@ -3387,6 +3637,7 @@ void OBSBasic::StreamDelayStarting(int sec) if (ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(false); App()->IncrementSleepInhibition(); + UpdateProcessPriority(); } } @@ -3417,11 +3668,17 @@ void OBSBasic::StreamingStart() if (ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(false); App()->IncrementSleepInhibition(); + UpdateProcessPriority(); } blog(LOG_INFO, STREAMING_START); } +void OBSBasic::StreamStopping() +{ + ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming")); +} + void OBSBasic::StreamingStop(int code) { const char *errorMessage; @@ -3458,6 +3715,7 @@ void OBSBasic::StreamingStop(int code) if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(true); App()->DecrementSleepInhibition(); + ClearProcessPriority(); } blog(LOG_INFO, STREAMING_STOP); @@ -3476,10 +3734,16 @@ void OBSBasic::StreamingStop(int code) void OBSBasic::StartRecording() { - SaveProject(); + if (outputHandler->RecordingActive()) + return; - if (!outputHandler->RecordingActive()) - outputHandler->StartRecording(); + SaveProject(); + outputHandler->StartRecording(); +} + +void OBSBasic::RecordStopping() +{ + ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); } void OBSBasic::StopRecording() @@ -3492,6 +3756,7 @@ void OBSBasic::StopRecording() if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(true); App()->DecrementSleepInhibition(); + ClearProcessPriority(); } } @@ -3503,6 +3768,7 @@ void OBSBasic::RecordingStart() if (ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(false); App()->IncrementSleepInhibition(); + UpdateProcessPriority(); } blog(LOG_INFO, RECORDING_START); @@ -3533,6 +3799,7 @@ void OBSBasic::RecordingStop(int code) if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(true); App()->DecrementSleepInhibition(); + ClearProcessPriority(); } } @@ -4113,3 +4380,35 @@ int OBSBasic::GetProfilePath(char *path, size_t size, const char *file) const return snprintf(path, size, "%s/%s/%s", profiles_path, profile, file); } + +void OBSBasic::on_toggleSceneTransitions_toggled(bool visible) +{ + ui->sceneTransitionsLabel->setVisible(visible); + ui->transitionsContainer->setVisible(visible); + + config_set_bool(App()->GlobalConfig(), "BasicWindow", + "ShowTransitions", visible); +} + +void OBSBasic::on_toggleListboxToolbars_toggled(bool visible) +{ + ui->sourcesToolbar->setVisible(visible); + ui->scenesToolbar->setVisible(visible); + + config_set_bool(App()->GlobalConfig(), "BasicWindow", + "ShowListboxToolbars", visible); +} + +void OBSBasic::on_toggleStatusBar_toggled(bool visible) +{ + ui->statusbar->setVisible(visible); + + config_set_bool(App()->GlobalConfig(), "BasicWindow", + "ShowStatusBar", visible); +} + +void OBSBasic::on_actionLockPreview_triggered() +{ + ui->preview->ToggleLocked(); + ui->actionLockPreview->setChecked(ui->preview->Locked()); +} diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index f75feb5..c8d6feb 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -81,6 +81,7 @@ class OBSBasic : public OBSMainWindow { friend class OBSBasicPreview; friend class OBSBasicStatusBar; + friend class OBSBasicSourceSelect; enum class MoveDir { Up, @@ -203,6 +204,7 @@ private: void AddSceneCollection(bool create_new); void RefreshSceneCollections(); void ChangeSceneCollection(); + void LogScenes(); void LoadProfile(); void ResetProfileData(); @@ -289,12 +291,14 @@ public slots: void StreamDelayStopping(int sec); void StreamingStart(); + void StreamStopping(); void StreamingStop(int errorcode); void StartRecording(); void StopRecording(); void RecordingStart(); + void RecordStopping(); void RecordingStop(int code); void SaveProjectDeferred(); @@ -331,6 +335,8 @@ private slots: void SetDeinterlacingMode(); void SetDeinterlacingOrder(); + void SetScaleFilter(); + private: /* OBS Callbacks */ static void SceneReordered(void *data, calldata_t *params); @@ -366,7 +372,8 @@ public: obs_service_t *GetService(); void SetService(obs_service_t *service); - bool StreamingActive(); + bool StreamingActive() const; + bool Active() const; int ResetVideo(); bool ResetAudio(); @@ -398,6 +405,7 @@ public: void ReorderSceneItem(obs_sceneitem_t *item, size_t idx); QMenu *AddDeinterlacingMenu(obs_source_t *source); + QMenu *AddScaleFilteringMenu(obs_sceneitem_t *item); void CreateSourcePopupMenu(QListWidgetItem *item, bool preview); void UpdateTitleBar(); @@ -453,6 +461,8 @@ private slots: void on_actionMoveToTop_triggered(); void on_actionMoveToBottom_triggered(); + void on_actionLockPreview_triggered(); + void on_streamButton_clicked(); void on_recordButton_clicked(); void on_settingsButton_clicked(); @@ -478,6 +488,10 @@ private slots: void on_actionAlwaysOnTop_triggered(); + void on_toggleSceneTransitions_toggled(bool visible); + void on_toggleListboxToolbars_toggled(bool visible); + void on_toggleStatusBar_toggled(bool visible); + void on_transitions_currentIndexChanged(int index); void on_transitionAdd_clicked(); void on_transitionRemove_clicked(); diff --git a/obs/window-basic-preview.cpp b/obs/window-basic-preview.cpp index 8e7725c..dc2c4f3 100644 --- a/obs/window-basic-preview.cpp +++ b/obs/window-basic-preview.cpp @@ -379,6 +379,11 @@ void OBSBasicPreview::GetStretchHandleData(const vec2 &pos) void OBSBasicPreview::mousePressEvent(QMouseEvent *event) { + if (locked) { + OBSQTDisplay::mousePressEvent(event); + return; + } + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); float pixelRatio = main->devicePixelRatio(); float x = float(event->x()) - main->previewX / pixelRatio; @@ -451,6 +456,11 @@ void OBSBasicPreview::ProcessClick(const vec2 &pos) void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event) { + if (locked) { + OBSQTDisplay::mouseReleaseEvent(event); + return; + } + if (mouseDown) { vec2 pos = GetMouseEventPos(event); @@ -941,6 +951,9 @@ void OBSBasicPreview::StretchItem(const vec2 &pos) void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event) { + if (locked) + return; + if (mouseDown) { vec2 pos = GetMouseEventPos(event); @@ -1076,6 +1089,9 @@ bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene, void OBSBasicPreview::DrawSceneEditing() { + if (locked) + return; + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID); diff --git a/obs/window-basic-preview.hpp b/obs/window-basic-preview.hpp index 1a25245..c11581b 100644 --- a/obs/window-basic-preview.hpp +++ b/obs/window-basic-preview.hpp @@ -45,6 +45,7 @@ private: bool mouseMoved = false; bool mouseOverItems = false; bool cropping = false; + bool locked = false; static vec2 GetMouseEventPos(QMouseEvent *event); static bool DrawSelectedItem(obs_scene_t *scene, obs_sceneitem_t *item, @@ -80,6 +81,10 @@ public: void DrawSceneEditing(); + inline void SetLocked(bool newLockedVal) {locked = newLockedVal;} + inline void ToggleLocked() {locked = !locked;} + inline bool Locked() const {return locked;} + /* use libobs allocator for alignment because the matrices itemToScreen * and screenToItem may contain SSE data, which will cause SSE * instructions to crash if the data is not aligned to at least a 16 diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp index 3fa2dda..fef022b 100644 --- a/obs/window-basic-settings.cpp +++ b/obs/window-basic-settings.cpp @@ -259,7 +259,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) PopulateAACBitrates({ui->simpleOutputABitrate, ui->advOutTrack1Bitrate, ui->advOutTrack2Bitrate, - ui->advOutTrack3Bitrate, ui->advOutTrack3Bitrate}); + ui->advOutTrack3Bitrate, ui->advOutTrack4Bitrate}); ui->listWidget->setAttribute(Qt::WA_MacShowFocusRect, false); @@ -271,6 +271,9 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->theme, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->warnBeforeStreamStart,CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->warnBeforeStreamStop, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->hideProjectorCursor, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->recordWhenStreaming, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->keepRecordStreamStops,CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->snappingEnabled, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->screenSnapping, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->centerSnapping, CHECK_CHANGED, GENERAL_CHANGED); @@ -368,6 +371,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->reconnectEnable, CHECK_CHANGED, ADV_CHANGED); HookWidget(ui->reconnectRetryDelay, SCROLL_CHANGED, ADV_CHANGED); HookWidget(ui->reconnectMaxRetries, SCROLL_CHANGED, ADV_CHANGED); + HookWidget(ui->processPriority, COMBO_CHANGED, ADV_CHANGED); + HookWidget(ui->bindToIP, COMBO_CHANGED, ADV_CHANGED); #ifdef _WIN32 uint32_t winVer = GetWindowsVersion(); @@ -383,15 +388,39 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) connect(toggleAero, &QAbstractButton::toggled, this, &OBSBasicSettings::ToggleDisableAero); } + +#define PROCESS_PRIORITY(val) \ + {"Basic.Settings.Advanced.General.ProcessPriority." ## val , val} + + static struct ProcessPriority { + const char *name; + const char *val; + } processPriorities[] = { + PROCESS_PRIORITY("High"), + PROCESS_PRIORITY("AboveNormal"), + PROCESS_PRIORITY("Normal"), + PROCESS_PRIORITY("Idle") + }; +#undef PROCESS_PRIORITY + + for (ProcessPriority pri : processPriorities) + ui->processPriority->addItem(QTStr(pri.name), pri.val); + #else delete ui->rendererLabel; delete ui->renderer; delete ui->adapterLabel; delete ui->adapter; + delete ui->processPriorityLabel; + delete ui->processPriority; + delete ui->advancedGeneralGroupBox; ui->rendererLabel = nullptr; ui->renderer = nullptr; ui->adapterLabel = nullptr; ui->adapter = nullptr; + ui->processPriorityLabel = nullptr; + ui->processPriority = nullptr; + ui->advancedGeneralGroupBox = nullptr; #endif #ifndef __APPLE__ @@ -494,6 +523,20 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) connect(ui->listWidget, SIGNAL(currentRowChanged(int)), this, SLOT(SimpleRecordingEncoderChanged())); + // Get Bind to IP Addresses + obs_properties_t *ppts = obs_get_output_properties("rtmp_output"); + obs_property_t *p = obs_properties_get(ppts, "bind_ip"); + + size_t count = obs_property_list_item_count(p); + for (size_t i = 0; i < count; i++) { + const char *name = obs_property_list_item_name(p, i); + const char *val = obs_property_list_item_string(p, i); + + ui->bindToIP->addItem(QT_UTF8(name), val); + } + + obs_properties_destroy(ppts); + LoadSettings(false); // Add warning checks to advanced output recording section controls @@ -794,6 +837,14 @@ void OBSBasicSettings::LoadGeneralSettings() LoadLanguageList(); LoadThemeList(); + bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), + "BasicWindow", "RecordWhenStreaming"); + ui->recordWhenStreaming->setChecked(recordWhenStreaming); + + bool keepRecordStreamStops = config_get_bool(GetGlobalConfig(), + "BasicWindow", "KeepRecordingWhenStreamStops"); + ui->keepRecordStreamStops->setChecked(keepRecordStreamStops); + bool snappingEnabled = config_get_bool(GetGlobalConfig(), "BasicWindow", "SnappingEnabled"); ui->snappingEnabled->setChecked(snappingEnabled); @@ -814,7 +865,6 @@ void OBSBasicSettings::LoadGeneralSettings() "BasicWindow", "SnapDistance"); ui->snapDistance->setValue(snapDistance); - bool warnBeforeStreamStart = config_get_bool(GetGlobalConfig(), "BasicWindow", "WarnBeforeStartingStream"); ui->warnBeforeStreamStart->setChecked(warnBeforeStreamStart); @@ -823,6 +873,10 @@ void OBSBasicSettings::LoadGeneralSettings() "BasicWindow", "WarnBeforeStoppingStream"); ui->warnBeforeStreamStop->setChecked(warnBeforeStreamStop); + bool hideProjectorCursor = config_get_bool(GetGlobalConfig(), + "BasicWindow", "HideProjectorCursor"); + ui->hideProjectorCursor->setChecked(hideProjectorCursor); + loading = false; } @@ -1771,6 +1825,8 @@ void OBSBasicSettings::LoadAdvancedSettings() "FilenameFormatting"); bool overwriteIfExists = config_get_bool(main->Config(), "Output", "OverwriteIfExists"); + const char *bindIP = config_get_string(main->Config(), "Output", + "BindIP"); loading = true; @@ -1791,6 +1847,8 @@ void OBSBasicSettings::LoadAdvancedSettings() SetComboByName(ui->colorSpace, videoColorSpace); SetComboByValue(ui->colorRange, videoColorRange); + SetComboByValue(ui->bindToIP, bindIP); + if (video_output_active(obs_get_video())) { ui->advancedVideoContainer->setEnabled(false); } @@ -1803,6 +1861,13 @@ void OBSBasicSettings::LoadAdvancedSettings() ui->disableOSXVSync->setChecked(disableOSXVSync); ui->resetOSXVSync->setChecked(resetOSXVSync); ui->resetOSXVSync->setEnabled(disableOSXVSync); +#elif _WIN32 + const char *processPriority = config_get_string(App()->GlobalConfig(), + "General", "ProcessPriority"); + int idx = ui->processPriority->findData(processPriority); + if (idx == -1) + idx = ui->processPriority->findData("Normal"); + ui->processPriority->setCurrentIndex(idx); #endif loading = false; @@ -2157,6 +2222,19 @@ void OBSBasicSettings::SaveGeneralSettings() config_set_bool(GetGlobalConfig(), "BasicWindow", "WarnBeforeStoppingStream", ui->warnBeforeStreamStop->isChecked()); + + config_set_bool(GetGlobalConfig(), "BasicWindow", + "HideProjectorCursor", + ui->hideProjectorCursor->isChecked()); + + if (WidgetChanged(ui->recordWhenStreaming)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "RecordWhenStreaming", + ui->recordWhenStreaming->isChecked()); + if (WidgetChanged(ui->keepRecordStreamStops)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "KeepRecordingWhenStreamStops", + ui->keepRecordStreamStops->isChecked()); } void OBSBasicSettings::SaveStream1Settings() @@ -2223,6 +2301,13 @@ void OBSBasicSettings::SaveAdvancedSettings() if (WidgetChanged(ui->renderer)) config_set_string(App()->GlobalConfig(), "Video", "Renderer", QT_TO_UTF8(ui->renderer->currentText())); + + std::string priority = + QT_TO_UTF8(ui->processPriority->currentData().toString()); + config_set_string(App()->GlobalConfig(), "General", "ProcessPriority", + priority.c_str()); + if (main->Active()) + SetProcessPriority(priority.c_str()); #endif #ifdef __APPLE__ @@ -2249,6 +2334,7 @@ void OBSBasicSettings::SaveAdvancedSettings() SaveCheckBox(ui->reconnectEnable, "Output", "Reconnect"); SaveSpinBox(ui->reconnectRetryDelay, "Output", "RetryDelay"); SaveSpinBox(ui->reconnectMaxRetries, "Output", "MaxRetries"); + SaveComboData(ui->bindToIP, "Output", "BindIP"); } static inline const char *OutputModeFromIdx(int idx) @@ -2853,12 +2939,12 @@ void OBSBasicSettings::on_filenameFormatting_textEdited(const QString &text) { #ifdef __APPLE__ size_t invalidLocation = - text.toStdString().find_first_of(":/\\"); + text.toStdString().find_first_of(":"); #elif _WIN32 size_t invalidLocation = - text.toStdString().find_first_of("<>:\"/\\|?*"); + text.toStdString().find_first_of("<>:\"|?*"); #else - size_t invalidLocation = text.toStdString().find_first_of("/"); + size_t invalidLocation = string::npos; #endif if (invalidLocation != string::npos) diff --git a/obs/window-basic-source-select.cpp b/obs/window-basic-source-select.cpp index c27727a..826fb8b 100644 --- a/obs/window-basic-source-select.cpp +++ b/obs/window-basic-source-select.cpp @@ -177,6 +177,21 @@ void OBSBasicSourceSelect::on_buttonBox_rejected() done(DialogCode::Rejected); } +static inline const char *GetSourceDisplayName(const char *id) +{ + if (strcmp(id, "scene") == 0) + return Str("Basic.Scene"); + return obs_source_get_display_name(id); +} + +Q_DECLARE_METATYPE(OBSScene); + +template +static inline T GetOBSRef(QListWidgetItem *item) +{ + return item->data(static_cast(QtDataRole::OBSRef)).value(); +} + OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) : QDialog (parent), ui (new Ui::OBSBasicSourceSelect), @@ -186,7 +201,7 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) ui->sourceList->setAttribute(Qt::WA_MacShowFocusRect, false); - QString placeHolderText{QT_UTF8(obs_source_get_display_name(id))}; + QString placeHolderText{QT_UTF8(GetSourceDisplayName(id))}; QString text{placeHolderText}; int i = 1; @@ -202,5 +217,29 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) installEventFilter(CreateShortcutFilter()); - obs_enum_sources(EnumSources, this); + if (strcmp(id_, "scene") == 0) { + OBSBasic *main = reinterpret_cast( + App()->GetMainWindow()); + OBSSource curSceneSource = main->GetCurrentSceneSource(); + + ui->selectExisting->setChecked(true); + ui->createNew->setChecked(false); + ui->createNew->setEnabled(false); + ui->sourceName->setEnabled(false); + + int count = main->ui->scenes->count(); + for (int i = 0; i < count; i++) { + QListWidgetItem *item = main->ui->scenes->item(i); + OBSScene scene = GetOBSRef(item); + OBSSource sceneSource = obs_scene_get_source(scene); + + if (curSceneSource == sceneSource) + continue; + + const char *name = obs_source_get_name(sceneSource); + ui->sourceList->addItem(QT_UTF8(name)); + } + } else { + obs_enum_sources(EnumSources, this); + } } diff --git a/obs/window-basic-status-bar.cpp b/obs/window-basic-status-bar.cpp index 660544f..64015ef 100644 --- a/obs/window-basic-status-bar.cpp +++ b/obs/window-basic-status-bar.cpp @@ -114,6 +114,8 @@ void OBSBasicStatusBar::UpdateBandwidth() uint64_t bytesSent = obs_output_get_total_bytes(streamOutput); uint64_t bytesSentTime = os_gettime_ns(); + if (bytesSent < lastBytesSent) + bytesSent = 0; if (bytesSent == 0) lastBytesSent = 0; @@ -231,14 +233,21 @@ void OBSBasicStatusBar::Reconnect(int seconds) } } -void OBSBasicStatusBar::ReconnectSuccess() +void OBSBasicStatusBar::ReconnectClear() { - showMessage(QTStr("Basic.StatusBar.ReconnectSuccessful"), 4000); retries = 0; reconnectTimeout = 0; bitrateUpdateSeconds = -1; lastBytesSent = 0; lastBytesSentTime = os_gettime_ns(); + delaySecTotal = 0; + UpdateDelayMsg(); +} + +void OBSBasicStatusBar::ReconnectSuccess() +{ + showMessage(QTStr("Basic.StatusBar.ReconnectSuccessful"), 4000); + ReconnectClear(); if (streamOutput) { delaySecTotal = obs_output_get_active_delay(streamOutput); @@ -312,6 +321,7 @@ void OBSBasicStatusBar::StreamStopped() "reconnect_success", OBSOutputReconnectSuccess, this); + ReconnectClear(); streamOutput = nullptr; clearMessage(); Deactivate(); diff --git a/obs/window-basic-status-bar.hpp b/obs/window-basic-status-bar.hpp index 8fb2850..3f9bc8f 100644 --- a/obs/window-basic-status-bar.hpp +++ b/obs/window-basic-status-bar.hpp @@ -69,4 +69,6 @@ public: void StreamStopped(); void RecordingStarted(obs_output_t *output); void RecordingStopped(); + + void ReconnectClear(); }; diff --git a/obs/window-projector.cpp b/obs/window-projector.cpp index b6091d9..172e7be 100644 --- a/obs/window-projector.cpp +++ b/obs/window-projector.cpp @@ -9,7 +9,8 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_) : OBSQTDisplay (widget, - Qt::Window | Qt::FramelessWindowHint), + Qt::Window | Qt::FramelessWindowHint | + Qt::X11BypassWindowManagerHint), source (source_), removedSignal (obs_source_get_signal_handler(source), "remove", OBSSourceRemoved, this) @@ -26,6 +27,14 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_) connect(this, &OBSQTDisplay::DisplayCreated, addDrawCallback); + bool hideCursor = config_get_bool(GetGlobalConfig(), + "BasicWindow", "HideProjectorCursor"); + if (hideCursor) { + QPixmap empty(16, 16); + empty.fill(Qt::transparent); + setCursor(QCursor(empty)); + } + App()->IncrementSleepInhibition(); } diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 8791183..d178acc 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -10,6 +10,7 @@ if(WIN32) add_subdirectory(decklink/win) add_subdirectory(win-mf) add_subdirectory(obs-qsv11) + add_subdirectory(vlc-video) elseif(APPLE) add_subdirectory(coreaudio-encoder) add_subdirectory(mac-avcapture) @@ -17,6 +18,7 @@ elseif(APPLE) add_subdirectory(mac-vth264) add_subdirectory(mac-syphon) add_subdirectory(decklink/mac) + add_subdirectory(vlc-video) elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") add_subdirectory(linux-capture) add_subdirectory(linux-pulseaudio) @@ -24,6 +26,7 @@ elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") add_subdirectory(linux-jack) add_subdirectory(linux-alsa) add_subdirectory(decklink/linux) + add_subdirectory(vlc-video) elseif("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") add_subdirectory(linux-capture) add_subdirectory(linux-pulseaudio) diff --git a/plugins/coreaudio-encoder/data/locale/fi-FI.ini b/plugins/coreaudio-encoder/data/locale/fi-FI.ini index 0666ac0..0a92d20 100644 --- a/plugins/coreaudio-encoder/data/locale/fi-FI.ini +++ b/plugins/coreaudio-encoder/data/locale/fi-FI.ini @@ -2,5 +2,5 @@ CoreAudioAAC="CoreAudio AAC -enkooderi" Bitrate="Bitrate" AllowHEAAC="Salli HE-AAC" OutputSamplerate="Ulostulon näytetaajuus" -UseInputSampleRate="Käytä sisääntulon näytetaajuutta (saattaa sisältää myös epäsopivia arvoja)" +UseInputSampleRate="Käytä sisääntulon näytetaajuutta (saattaa sisältää myös ei tuettuja arvoja)" diff --git a/plugins/coreaudio-encoder/data/locale/it-IT.ini b/plugins/coreaudio-encoder/data/locale/it-IT.ini index 959543b..dc4ea1e 100644 --- a/plugins/coreaudio-encoder/data/locale/it-IT.ini +++ b/plugins/coreaudio-encoder/data/locale/it-IT.ini @@ -1,4 +1,6 @@ CoreAudioAAC="Encoder CoreAudio AAC" Bitrate="Bitrate" AllowHEAAC="Consenti l'HE-AAC" +OutputSamplerate="Output Sample Rate" +UseInputSampleRate="Usa frequenza di campionamento input (OBS) (potrebbe elencare bitrate non supportati)" diff --git a/plugins/coreaudio-encoder/data/locale/nb-NO.ini b/plugins/coreaudio-encoder/data/locale/nb-NO.ini index 59d98a4..415ebd0 100644 --- a/plugins/coreaudio-encoder/data/locale/nb-NO.ini +++ b/plugins/coreaudio-encoder/data/locale/nb-NO.ini @@ -1,4 +1,6 @@ CoreAudioAAC="CoreAudio AAC-koder" Bitrate="Bitrate" AllowHEAAC="Tillat HE-AAC-koding" +OutputSamplerate="Prøvefrekvens for utgang" +UseInputSampleRate="Bruk prøvefrekvens for inngang (OBS). Kan vise bitrater som ikke er støttet." diff --git a/plugins/coreaudio-encoder/data/locale/pt-PT.ini b/plugins/coreaudio-encoder/data/locale/pt-PT.ini index f1171f2..e2b503c 100644 --- a/plugins/coreaudio-encoder/data/locale/pt-PT.ini +++ b/plugins/coreaudio-encoder/data/locale/pt-PT.ini @@ -1,4 +1,6 @@ CoreAudioAAC="Codificador CoreAudio AAC" Bitrate="Bitrate" AllowHEAAC="Permitir HE-AAC" +OutputSamplerate="Taxa de amostragem de saída" +UseInputSampleRate="Taxa de amostragem de entrada (OBS) Taxa Amostra (pode listar taxas de bit sem suporte)" diff --git a/plugins/coreaudio-encoder/encoder.cpp b/plugins/coreaudio-encoder/encoder.cpp index bd9f9ed..9d6117a 100644 --- a/plugins/coreaudio-encoder/encoder.cpp +++ b/plugins/coreaudio-encoder/encoder.cpp @@ -1346,7 +1346,8 @@ bool obs_module_load(void) { #ifdef _WIN32 if (!load_core_audio()) { - CA_LOG(LOG_WARNING, "Couldn't load CoreAudio AAC encoder"); + CA_LOG(LOG_WARNING, "CoreAudio AAC encoder not installed on " + "the system or couldn't be loaded"); return true; } diff --git a/plugins/coreaudio-encoder/windows-imports.h b/plugins/coreaudio-encoder/windows-imports.h index 37e31b1..695229a 100644 --- a/plugins/coreaudio-encoder/windows-imports.h +++ b/plugins/coreaudio-encoder/windows-imports.h @@ -413,7 +413,7 @@ static bool load_lib(void) #define LOAD_LIB(x, n) x = LoadLibrary(TEXT(n)); \ if (!x) \ - CA_LOG(LOG_WARNING, "Failed loading library '" n "'"); + CA_LOG(LOG_DEBUG, "Failed loading library '" n "'"); LOAD_LIB(audio_toolbox, "CoreAudioToolbox.dll"); #undef LOAD_LIB diff --git a/plugins/decklink/data/locale/pt-PT.ini b/plugins/decklink/data/locale/pt-PT.ini index 9ad1547..18afbb8 100644 --- a/plugins/decklink/data/locale/pt-PT.ini +++ b/plugins/decklink/data/locale/pt-PT.ini @@ -2,4 +2,5 @@ BlackmagicDevice="Dispositivo Blackmagic" Device="Dispositivo" Mode="Modo" Buffering="Utilizar Buffering" +PixelFormat="Formato de pixel" diff --git a/plugins/decklink/decklink-device-discovery.cpp b/plugins/decklink/decklink-device-discovery.cpp index e08f485..d785388 100644 --- a/plugins/decklink/decklink-device-discovery.cpp +++ b/plugins/decklink/decklink-device-discovery.cpp @@ -32,7 +32,7 @@ bool DeckLinkDeviceDiscovery::Init(void) initialized = result == S_OK; if (!initialized) - blog(LOG_INFO, "Failed to start search for DeckLink devices"); + blog(LOG_DEBUG, "Failed to start search for DeckLink devices"); return initialized; } diff --git a/plugins/image-source/CMakeLists.txt b/plugins/image-source/CMakeLists.txt index 9966d04..4159cf5 100644 --- a/plugins/image-source/CMakeLists.txt +++ b/plugins/image-source/CMakeLists.txt @@ -1,11 +1,18 @@ project(image-source) +if(MSVC) + set(image-source_PLATFORM_DEPS + w32-pthreads) +endif() + set(image-source_SOURCES - image-source.c) + image-source.c + obs-slideshow.c) add_library(image-source MODULE ${image-source_SOURCES}) target_link_libraries(image-source - libobs) + libobs + ${image-source_PLATFORM_DEPS}) install_obs_plugin_with_data(image-source data) diff --git a/plugins/image-source/data/locale/ar-SA.ini b/plugins/image-source/data/locale/ar-SA.ini index d75aa98..cba4b29 100644 --- a/plugins/image-source/data/locale/ar-SA.ini +++ b/plugins/image-source/data/locale/ar-SA.ini @@ -2,3 +2,4 @@ ImageInput="الصورة" File="ملف الصورة" UnloadWhenNotShowing="إلغاء تحميل الصورة إذا لم تظهر" + diff --git a/plugins/image-source/data/locale/bg-BG.ini b/plugins/image-source/data/locale/bg-BG.ini index 0788b9f..05cd321 100644 --- a/plugins/image-source/data/locale/bg-BG.ini +++ b/plugins/image-source/data/locale/bg-BG.ini @@ -1,3 +1,4 @@ ImageInput="Изображение" File="Файл с изображение" + diff --git a/plugins/image-source/data/locale/ca-ES.ini b/plugins/image-source/data/locale/ca-ES.ini index e4d2dec..e476e39 100644 --- a/plugins/image-source/data/locale/ca-ES.ini +++ b/plugins/image-source/data/locale/ca-ES.ini @@ -2,3 +2,14 @@ ImageInput="Imatge" File="Fitxer de la imatge" UnloadWhenNotShowing="Descarrega la imatge de la memòria quan no es mostri" +SlideShow="Presentació de diapositives" +SlideShow.TransitionSpeed="Velocitat de la transició (mil·lisegons)" +SlideShow.SlideTime="Temps entre diapositives (mil·lisegons)" +SlideShow.Files="Fitxers d'imatge" +SlideShow.Randomize="Reproducció aleatòria" +SlideShow.Transition="Transició" +SlideShow.Transition.Cut="Tall" +SlideShow.Transition.Fade="Desaparèixer" +SlideShow.Transition.Swipe="De cop" +SlideShow.Transition.Slide="Diapositiva" + diff --git a/plugins/image-source/data/locale/cs-CZ.ini b/plugins/image-source/data/locale/cs-CZ.ini index 7dcfbf9..bcb8f60 100644 --- a/plugins/image-source/data/locale/cs-CZ.ini +++ b/plugins/image-source/data/locale/cs-CZ.ini @@ -2,3 +2,14 @@ ImageInput="Obrázek" File="Soubor obrázku" UnloadWhenNotShowing="Nenačítat při skrytém" +SlideShow="Obrázková prezentace" +SlideShow.TransitionSpeed="Rychlost přechodu (milisekundy)" +SlideShow.SlideTime="Čas mezi snímky (milisekundy)" +SlideShow.Files="Soubory obrázků" +SlideShow.Randomize="Náhodné přehrávání" +SlideShow.Transition="Přechod" +SlideShow.Transition.Cut="Střih" +SlideShow.Transition.Fade="Slábnutí" +SlideShow.Transition.Swipe="Tažení" +SlideShow.Transition.Slide="Sklouznutí" + diff --git a/plugins/image-source/data/locale/da-DK.ini b/plugins/image-source/data/locale/da-DK.ini index 3f43659..75de5c0 100644 --- a/plugins/image-source/data/locale/da-DK.ini +++ b/plugins/image-source/data/locale/da-DK.ini @@ -2,3 +2,4 @@ ImageInput="Billede" File="Billedfil" UnloadWhenNotShowing="Fjern billede fra hukommelsen når det ikke vises" + diff --git a/plugins/image-source/data/locale/de-DE.ini b/plugins/image-source/data/locale/de-DE.ini index b7b5f87..1336cb7 100644 --- a/plugins/image-source/data/locale/de-DE.ini +++ b/plugins/image-source/data/locale/de-DE.ini @@ -2,3 +2,14 @@ ImageInput="Bild" File="Bilddatei" UnloadWhenNotShowing="Entlade Bild, wenn es nicht angezeigt wird" +SlideShow="Diashow" +SlideShow.TransitionSpeed="Geschwindigkeit des Übergangs (Millisekunden)" +SlideShow.SlideTime="Zeit zwischen Bildern (Millisekunden)" +SlideShow.Files="Bilddateien" +SlideShow.Randomize="Zufällige Wiedergabe" +SlideShow.Transition="Übergang" +SlideShow.Transition.Cut="Schnitt" +SlideShow.Transition.Fade="Überblenden" +SlideShow.Transition.Swipe="Swipe" +SlideShow.Transition.Slide="Slide" + diff --git a/plugins/image-source/data/locale/el-GR.ini b/plugins/image-source/data/locale/el-GR.ini index 228453b..dd23c11 100644 --- a/plugins/image-source/data/locale/el-GR.ini +++ b/plugins/image-source/data/locale/el-GR.ini @@ -2,3 +2,4 @@ ImageInput="Εικόνα" File="Αρχείο εικόνας" UnloadWhenNotShowing="Ξεφόρτωση εικόνας όταν δεν εμφανίζεται" + diff --git a/plugins/image-source/data/locale/en-US.ini b/plugins/image-source/data/locale/en-US.ini index 6b0d155..eb5ab61 100644 --- a/plugins/image-source/data/locale/en-US.ini +++ b/plugins/image-source/data/locale/en-US.ini @@ -1,3 +1,14 @@ ImageInput="Image" File="Image File" UnloadWhenNotShowing="Unload image when not showing" + +SlideShow="Image Slide Show" +SlideShow.TransitionSpeed="Transition Speed (milliseconds)" +SlideShow.SlideTime="Time Between Slides (milliseconds)" +SlideShow.Files="Image Files" +SlideShow.Randomize="Randomize Playback" +SlideShow.Transition="Transition" +SlideShow.Transition.Cut="Cut" +SlideShow.Transition.Fade="Fade" +SlideShow.Transition.Swipe="Swipe" +SlideShow.Transition.Slide="Slide" diff --git a/plugins/image-source/data/locale/es-ES.ini b/plugins/image-source/data/locale/es-ES.ini index b7e28b4..d5756b2 100644 --- a/plugins/image-source/data/locale/es-ES.ini +++ b/plugins/image-source/data/locale/es-ES.ini @@ -2,3 +2,14 @@ ImageInput="Imagen" File="Archivo de imagen" UnloadWhenNotShowing="Descargar la imagen mientras no se muestre" +SlideShow="Galería de imágenes" +SlideShow.TransitionSpeed="Velocidad de la transición (milisegundos)" +SlideShow.SlideTime="Tiempo entre diapositivas (milisegundos)" +SlideShow.Files="Archivo de imagen" +SlideShow.Randomize="Reproducción aleatoria" +SlideShow.Transition="Transición" +SlideShow.Transition.Cut="Corte" +SlideShow.Transition.Fade="Desvanecimiento" +SlideShow.Transition.Swipe="Deslizar Rapido" +SlideShow.Transition.Slide="Deslizar" + diff --git a/plugins/image-source/data/locale/eu-ES.ini b/plugins/image-source/data/locale/eu-ES.ini index ab9a648..725fd53 100644 --- a/plugins/image-source/data/locale/eu-ES.ini +++ b/plugins/image-source/data/locale/eu-ES.ini @@ -2,3 +2,14 @@ ImageInput="Irudia" File="Irudi-fitxategia" UnloadWhenNotShowing="Ez kargatu irudia erakusten ez denean" +SlideShow="Irudien diaporama" +SlideShow.TransitionSpeed="Trantsizioaren abiadura (milisegundotan)" +SlideShow.SlideTime="Diapositiben arteko denbora (milisegundotan)" +SlideShow.Files="Irudi fitxategiak" +SlideShow.Randomize="Ausazko erreprodukzioa" +SlideShow.Transition="Trantsizioa" +SlideShow.Transition.Cut="Ebaki" +SlideShow.Transition.Fade="Iraungi" +SlideShow.Transition.Swipe="Korritu" +SlideShow.Transition.Slide="Irristatu" + diff --git a/plugins/image-source/data/locale/fi-FI.ini b/plugins/image-source/data/locale/fi-FI.ini index 6e8ed5b..3ccfcd7 100644 --- a/plugins/image-source/data/locale/fi-FI.ini +++ b/plugins/image-source/data/locale/fi-FI.ini @@ -2,3 +2,14 @@ ImageInput="Kuva" File="Kuvatiedosto" UnloadWhenNotShowing="Vapauta kuva muistista, kun se ei ole näkyvissä" +SlideShow="Diaesitys" +SlideShow.TransitionSpeed="Siirtymän nopeus (millisekuntia)" +SlideShow.SlideTime="Kesto kuvien välissä (millisekunteina)" +SlideShow.Files="Kuvatiedostot" +SlideShow.Randomize="Toista satunnaisesti" +SlideShow.Transition="Siirtymä" +SlideShow.Transition.Cut="Leikkaa" +SlideShow.Transition.Fade="Häivytä" +SlideShow.Transition.Swipe="Pyyhkäise" +SlideShow.Transition.Slide="Liu'uta" + diff --git a/plugins/image-source/data/locale/fr-FR.ini b/plugins/image-source/data/locale/fr-FR.ini index eece777..c7f2b10 100644 --- a/plugins/image-source/data/locale/fr-FR.ini +++ b/plugins/image-source/data/locale/fr-FR.ini @@ -2,3 +2,14 @@ ImageInput="Image" File="Fichier image" UnloadWhenNotShowing="Décharger l'image quand elle n'est pas affichée" +SlideShow="Diaporama" +SlideShow.TransitionSpeed="Vitesse de transition (millisecondes)" +SlideShow.SlideTime="Temps entre chaque diapositive (millisecondes)" +SlideShow.Files="Fichiers image" +SlideShow.Randomize="Lecture aléatoire" +SlideShow.Transition="Transition" +SlideShow.Transition.Cut="Coupure" +SlideShow.Transition.Fade="Fondu" +SlideShow.Transition.Swipe="Balayage" +SlideShow.Transition.Slide="Glissement" + diff --git a/plugins/image-source/data/locale/gl-ES.ini b/plugins/image-source/data/locale/gl-ES.ini index 9f52ac2..e197c31 100644 --- a/plugins/image-source/data/locale/gl-ES.ini +++ b/plugins/image-source/data/locale/gl-ES.ini @@ -2,3 +2,4 @@ ImageInput="Imaxe" File="Ficheiro de imaxe" UnloadWhenNotShowing="Non descargar a imaxe cando non se mostre" + diff --git a/plugins/image-source/data/locale/he-IL.ini b/plugins/image-source/data/locale/he-IL.ini index 92732e6..f482eba 100644 --- a/plugins/image-source/data/locale/he-IL.ini +++ b/plugins/image-source/data/locale/he-IL.ini @@ -2,3 +2,14 @@ ImageInput="תמונה" File="קובץ תמונה" UnloadWhenNotShowing="הסר טעינת תמונה כאשר לא נראה" +SlideShow="מצגת תמונות" +SlideShow.TransitionSpeed="מהירות מעבר (אלפיות שניה)" +SlideShow.SlideTime="זמן בין שקופיות (אלפיות שניה)" +SlideShow.Files="קבצי תמונה" +SlideShow.Randomize="סדר נגינה אקראי" +SlideShow.Transition="מעבר" +SlideShow.Transition.Cut="חתוך" +SlideShow.Transition.Fade="עמעום" +SlideShow.Transition.Swipe="החלקה" +SlideShow.Transition.Slide="הסט" + diff --git a/plugins/image-source/data/locale/hr-HR.ini b/plugins/image-source/data/locale/hr-HR.ini index ebf3736..1a64603 100644 --- a/plugins/image-source/data/locale/hr-HR.ini +++ b/plugins/image-source/data/locale/hr-HR.ini @@ -2,3 +2,14 @@ ImageInput="Slika" File="Datoteka slike" UnloadWhenNotShowing="Ukloni sliku iz memorije kada se ne prikazuje" +SlideShow="Prikazivanje slajdova" +SlideShow.TransitionSpeed="Brzina prelaza (milisekunde)" +SlideShow.SlideTime="Vreme između slajdova (milisekunde)" +SlideShow.Files="Datoteke slika" +SlideShow.Randomize="Nasumična reprodukcija" +SlideShow.Transition="Prelaz" +SlideShow.Transition.Cut="Isecanje" +SlideShow.Transition.Fade="Zatamnjenje" +SlideShow.Transition.Swipe="Prevlačenje" +SlideShow.Transition.Slide="Klizanje" + diff --git a/plugins/image-source/data/locale/hu-HU.ini b/plugins/image-source/data/locale/hu-HU.ini index 876cadf..7bf84f2 100644 --- a/plugins/image-source/data/locale/hu-HU.ini +++ b/plugins/image-source/data/locale/hu-HU.ini @@ -2,3 +2,14 @@ ImageInput="Kép" File="Képfájl" UnloadWhenNotShowing="Kép kitöltése mikor nem jelenik meg" +SlideShow="Képvetítő" +SlideShow.TransitionSpeed="Áttűnési sebesség (ezredmásodperc)" +SlideShow.SlideTime="Diák közti idő (ezredmásodperc)" +SlideShow.Files="Képfájlok" +SlideShow.Randomize="Véletlenszerű lejátszás" +SlideShow.Transition="Átmenet" +SlideShow.Transition.Cut="Kivágás" +SlideShow.Transition.Fade="Áttűnés" +SlideShow.Transition.Swipe="Lapozás" +SlideShow.Transition.Slide="Csúsztatás" + diff --git a/plugins/image-source/data/locale/it-IT.ini b/plugins/image-source/data/locale/it-IT.ini index bbeb964..8824432 100644 --- a/plugins/image-source/data/locale/it-IT.ini +++ b/plugins/image-source/data/locale/it-IT.ini @@ -2,3 +2,14 @@ ImageInput="Immagine" File="File Immagine" UnloadWhenNotShowing="Non caricare immagine se non si vede" +SlideShow="Presentazione immagini" +SlideShow.TransitionSpeed="Velocità di transizione (millisecondi)" +SlideShow.SlideTime="Tempo tra le diapositive (millisecondi)" +SlideShow.Files="Files Immagini" +SlideShow.Randomize="Randomizzare la riproduzione" +SlideShow.Transition="Transizione" +SlideShow.Transition.Cut="Taglio" +SlideShow.Transition.Fade="Dissolvenza" +SlideShow.Transition.Swipe="Scorri" +SlideShow.Transition.Slide="Scivola" + diff --git a/plugins/image-source/data/locale/ja-JP.ini b/plugins/image-source/data/locale/ja-JP.ini index 6a70959..4fa9b81 100644 --- a/plugins/image-source/data/locale/ja-JP.ini +++ b/plugins/image-source/data/locale/ja-JP.ini @@ -2,3 +2,14 @@ ImageInput="画像" File="画像ファイル" UnloadWhenNotShowing="表示中でない画像は読み込まない" +SlideShow="画像スライドショー" +SlideShow.TransitionSpeed="画面切替速度 (ミリ秒)" +SlideShow.SlideTime="スライド時間間隔 (ミリ秒)" +SlideShow.Files="画像ファイル" +SlideShow.Randomize="ランダム再生" +SlideShow.Transition="トランジション" +SlideShow.Transition.Cut="カット" +SlideShow.Transition.Fade="フェード" +SlideShow.Transition.Swipe="スワイプ" +SlideShow.Transition.Slide="スライド" + diff --git a/plugins/image-source/data/locale/ko-KR.ini b/plugins/image-source/data/locale/ko-KR.ini index f81df75..47145cb 100644 --- a/plugins/image-source/data/locale/ko-KR.ini +++ b/plugins/image-source/data/locale/ko-KR.ini @@ -2,3 +2,14 @@ ImageInput="이미지" File="이미지 파일" UnloadWhenNotShowing="이미지가 표시되지 않을 경우 비디오 메모리에서 읽지 않기" +SlideShow="이미지 슬라이드 쇼" +SlideShow.TransitionSpeed="전환 속도 (밀리초)" +SlideShow.SlideTime="슬라이드 간격 (밀리초)" +SlideShow.Files="이미지 파일 형식" +SlideShow.Randomize="무작위 재생" +SlideShow.Transition="전환 방식" +SlideShow.Transition.Cut="자르기" +SlideShow.Transition.Fade="서서히 사라지기" +SlideShow.Transition.Swipe="밀어내기" +SlideShow.Transition.Slide="슬라이드" + diff --git a/plugins/image-source/data/locale/nb-NO.ini b/plugins/image-source/data/locale/nb-NO.ini index 65daf16..4f5f101 100644 --- a/plugins/image-source/data/locale/nb-NO.ini +++ b/plugins/image-source/data/locale/nb-NO.ini @@ -2,3 +2,14 @@ ImageInput="Bilde" File="Bildefil" UnloadWhenNotShowing="Avlast bilde når det ikke vises" +SlideShow="Lysbildefremvisning" +SlideShow.TransitionSpeed="Overgangshastighet (millisekunder)" +SlideShow.SlideTime="Tid mellom lysbilder (millisekunder)" +SlideShow.Files="Bildefiler" +SlideShow.Randomize="Tilfeldig avspilling" +SlideShow.Transition="Overgang" +SlideShow.Transition.Cut="Kutt" +SlideShow.Transition.Fade="Forløpning" +SlideShow.Transition.Swipe="Sveip" +SlideShow.Transition.Slide="Skyv" + diff --git a/plugins/image-source/data/locale/nl-NL.ini b/plugins/image-source/data/locale/nl-NL.ini index e44004b..282474b 100644 --- a/plugins/image-source/data/locale/nl-NL.ini +++ b/plugins/image-source/data/locale/nl-NL.ini @@ -2,3 +2,14 @@ ImageInput="Afbeelding" File="Afbeeldingsbestand" UnloadWhenNotShowing="Niet getoonde afbeeldingen uit het geheugen verwijderen" +SlideShow="Diashow" +SlideShow.TransitionSpeed="Overgangssnelheid (milliseconden)" +SlideShow.SlideTime="Tijd Tussen Dia's (milliseconden)" +SlideShow.Files="Afbeeldingsbestanden" +SlideShow.Randomize="Willekeurige Volgorde" +SlideShow.Transition="Overgang" +SlideShow.Transition.Cut="Knippen" +SlideShow.Transition.Fade="Vervagen" +SlideShow.Transition.Swipe="Vegen" +SlideShow.Transition.Slide="Slide" + diff --git a/plugins/image-source/data/locale/pl-PL.ini b/plugins/image-source/data/locale/pl-PL.ini index 4022954..61ba68a 100644 --- a/plugins/image-source/data/locale/pl-PL.ini +++ b/plugins/image-source/data/locale/pl-PL.ini @@ -2,3 +2,14 @@ ImageInput="Obraz" File="Plik obrazu" UnloadWhenNotShowing="Usuń obraz z pamięci, gdy nie jest pokazywany" +SlideShow="Pokaz slajdów" +SlideShow.TransitionSpeed="Prędkość efektu przejścia (ms)" +SlideShow.SlideTime="Czas wyświetlania slajdu (ms)" +SlideShow.Files="Pliki graficzne" +SlideShow.Randomize="Odtwarzanie losowe" +SlideShow.Transition="Efekt przejścia" +SlideShow.Transition.Cut="Cięcie" +SlideShow.Transition.Fade="Zanikanie" +SlideShow.Transition.Swipe="Przeciągnięcie" +SlideShow.Transition.Slide="Przesunięcie" + diff --git a/plugins/image-source/data/locale/pt-BR.ini b/plugins/image-source/data/locale/pt-BR.ini index 9f45f1c..1de7fc3 100644 --- a/plugins/image-source/data/locale/pt-BR.ini +++ b/plugins/image-source/data/locale/pt-BR.ini @@ -2,3 +2,14 @@ ImageInput="Imagem" File="Arquivo de Imagem" UnloadWhenNotShowing="Descarregar imagem quando não estiver em exibição" +SlideShow="Imagens em Deslize" +SlideShow.TransitionSpeed="Velocidade de transição (milissegundos)" +SlideShow.SlideTime="Tempo entre Deslizes (milissegundos)" +SlideShow.Files="Arquivos de Imagem" +SlideShow.Randomize="Reprodução aleatória" +SlideShow.Transition="Transição" +SlideShow.Transition.Cut="Cortar" +SlideShow.Transition.Fade="Desvanecer" +SlideShow.Transition.Swipe="Deslizar" +SlideShow.Transition.Slide="Deslize" + diff --git a/plugins/image-source/data/locale/pt-PT.ini b/plugins/image-source/data/locale/pt-PT.ini index fc12920..6ae3cb7 100644 --- a/plugins/image-source/data/locale/pt-PT.ini +++ b/plugins/image-source/data/locale/pt-PT.ini @@ -2,3 +2,14 @@ ImageInput="Imagem" File="Ficheiro de imagem" UnloadWhenNotShowing="Descarregar imagem quando não estiver em visualização" +SlideShow="Imagens em Deslize" +SlideShow.TransitionSpeed="Velocidade de transição (milissegundos)" +SlideShow.SlideTime="Tempo entre Deslizes (milissegundos)" +SlideShow.Files="Arquivos de Imagem" +SlideShow.Randomize="Reprodução aleatória" +SlideShow.Transition="Transição" +SlideShow.Transition.Cut="Cortar" +SlideShow.Transition.Fade="Desvanecer" +SlideShow.Transition.Swipe="Deslizar" +SlideShow.Transition.Slide="Deslize" + diff --git a/plugins/image-source/data/locale/ro-RO.ini b/plugins/image-source/data/locale/ro-RO.ini index c93f400..0a262c9 100644 --- a/plugins/image-source/data/locale/ro-RO.ini +++ b/plugins/image-source/data/locale/ro-RO.ini @@ -2,3 +2,4 @@ ImageInput="Imagine" File="Fișier imagine" UnloadWhenNotShowing="Eliberează din memorie imaginea când nu este afișată" + diff --git a/plugins/image-source/data/locale/ru-RU.ini b/plugins/image-source/data/locale/ru-RU.ini index a384665..562e412 100644 --- a/plugins/image-source/data/locale/ru-RU.ini +++ b/plugins/image-source/data/locale/ru-RU.ini @@ -2,3 +2,14 @@ ImageInput="Изображение" File="Файл изображения" UnloadWhenNotShowing="Выгружать изображения, которые не показываются" +SlideShow="Слайдшоу" +SlideShow.TransitionSpeed="Скорость перехода (миллисекунды)" +SlideShow.SlideTime="Время между слайдами (миллисекунды)" +SlideShow.Files="Файлы изображений" +SlideShow.Randomize="Случайное воспроизведение" +SlideShow.Transition="Переход" +SlideShow.Transition.Cut="Обрезать" +SlideShow.Transition.Fade="Затухание" +SlideShow.Transition.Swipe="Перемещение" +SlideShow.Transition.Slide="Сдвиг" + diff --git a/plugins/image-source/data/locale/sk-SK.ini b/plugins/image-source/data/locale/sk-SK.ini index 872ec47..e735524 100644 --- a/plugins/image-source/data/locale/sk-SK.ini +++ b/plugins/image-source/data/locale/sk-SK.ini @@ -2,3 +2,4 @@ ImageInput="Obrázok" File="Súbor s obrázkom" UnloadWhenNotShowing="Uvoľniť obrázok, ak nie je zobrazený" + diff --git a/plugins/image-source/data/locale/sl-SI.ini b/plugins/image-source/data/locale/sl-SI.ini index 25554a8..1c579f1 100644 --- a/plugins/image-source/data/locale/sl-SI.ini +++ b/plugins/image-source/data/locale/sl-SI.ini @@ -2,3 +2,4 @@ ImageInput="Slika" File="Slikovna datoteka" UnloadWhenNotShowing="Ne naloži slike, ko ni prikazana" + diff --git a/plugins/image-source/data/locale/sr-CS.ini b/plugins/image-source/data/locale/sr-CS.ini index ebf3736..1a64603 100644 --- a/plugins/image-source/data/locale/sr-CS.ini +++ b/plugins/image-source/data/locale/sr-CS.ini @@ -2,3 +2,14 @@ ImageInput="Slika" File="Datoteka slike" UnloadWhenNotShowing="Ukloni sliku iz memorije kada se ne prikazuje" +SlideShow="Prikazivanje slajdova" +SlideShow.TransitionSpeed="Brzina prelaza (milisekunde)" +SlideShow.SlideTime="Vreme između slajdova (milisekunde)" +SlideShow.Files="Datoteke slika" +SlideShow.Randomize="Nasumična reprodukcija" +SlideShow.Transition="Prelaz" +SlideShow.Transition.Cut="Isecanje" +SlideShow.Transition.Fade="Zatamnjenje" +SlideShow.Transition.Swipe="Prevlačenje" +SlideShow.Transition.Slide="Klizanje" + diff --git a/plugins/image-source/data/locale/sr-SP.ini b/plugins/image-source/data/locale/sr-SP.ini index 3e7bc80..46d20e8 100644 --- a/plugins/image-source/data/locale/sr-SP.ini +++ b/plugins/image-source/data/locale/sr-SP.ini @@ -2,3 +2,14 @@ ImageInput="Слика" File="Датотека слике" UnloadWhenNotShowing="Уклони слику из меморије када се не приказује" +SlideShow="Приказивање слајдова" +SlideShow.TransitionSpeed="Брзина прелаза (милисекунде)" +SlideShow.SlideTime="Време између слајдова (милисекунде)" +SlideShow.Files="Датотеке слика" +SlideShow.Randomize="Насумична репродукција" +SlideShow.Transition="Прелаз" +SlideShow.Transition.Cut="Исецање" +SlideShow.Transition.Fade="Затамњење" +SlideShow.Transition.Swipe="Превлачење" +SlideShow.Transition.Slide="Клизање" + diff --git a/plugins/image-source/data/locale/sv-SE.ini b/plugins/image-source/data/locale/sv-SE.ini index 4727f33..edd84ab 100644 --- a/plugins/image-source/data/locale/sv-SE.ini +++ b/plugins/image-source/data/locale/sv-SE.ini @@ -2,3 +2,14 @@ ImageInput="Bild" File="Bildfil" UnloadWhenNotShowing="Ta bort bild när den inte visas" +SlideShow="Bildspel" +SlideShow.TransitionSpeed="Övergångshastighet (millisekunder)" +SlideShow.SlideTime="Tid mellan bilder (millisekunder)" +SlideShow.Files="Bildfiler" +SlideShow.Randomize="Slumpa uppspelning" +SlideShow.Transition="Övergång" +SlideShow.Transition.Cut="Klipp" +SlideShow.Transition.Fade="Tona" +SlideShow.Transition.Swipe="Svep" +SlideShow.Transition.Slide="Glid" + diff --git a/plugins/image-source/data/locale/th-TH.ini b/plugins/image-source/data/locale/th-TH.ini index 983783b..b2c3514 100644 --- a/plugins/image-source/data/locale/th-TH.ini +++ b/plugins/image-source/data/locale/th-TH.ini @@ -1,3 +1,4 @@ ImageInput="รูปภาพ" File="ไฟล์รูปภาพ" + diff --git a/plugins/image-source/data/locale/tr-TR.ini b/plugins/image-source/data/locale/tr-TR.ini index 4774a2c..d5ef434 100644 --- a/plugins/image-source/data/locale/tr-TR.ini +++ b/plugins/image-source/data/locale/tr-TR.ini @@ -2,3 +2,4 @@ ImageInput="Görüntü" File="Görüntü Dosyası" UnloadWhenNotShowing="Resim gösterilmediğinde bellekten kaldır" + diff --git a/plugins/image-source/data/locale/zh-CN.ini b/plugins/image-source/data/locale/zh-CN.ini index f569552..14946c0 100644 --- a/plugins/image-source/data/locale/zh-CN.ini +++ b/plugins/image-source/data/locale/zh-CN.ini @@ -2,3 +2,14 @@ ImageInput="图像" File="图像文件" UnloadWhenNotShowing="当不显示时卸载图像" +SlideShow="图像幻灯片放映" +SlideShow.TransitionSpeed="过渡速度(毫秒)" +SlideShow.SlideTime="幻灯片之间时间(毫秒)" +SlideShow.Files="图像文件" +SlideShow.Randomize="随机播放" +SlideShow.Transition="转换" +SlideShow.Transition.Cut="剪切" +SlideShow.Transition.Fade="淡出" +SlideShow.Transition.Swipe="滑动" +SlideShow.Transition.Slide="幻灯片" + diff --git a/plugins/image-source/data/locale/zh-TW.ini b/plugins/image-source/data/locale/zh-TW.ini index 39c928f..6c40828 100644 --- a/plugins/image-source/data/locale/zh-TW.ini +++ b/plugins/image-source/data/locale/zh-TW.ini @@ -2,3 +2,14 @@ ImageInput="圖片" File="圖片檔案" UnloadWhenNotShowing="當不顯示圖片時卸載" +SlideShow="投影片放映" +SlideShow.TransitionSpeed="變更速度 (毫秒)" +SlideShow.SlideTime="圖片間隔 (毫秒)" +SlideShow.Files="圖片檔案" +SlideShow.Randomize="隨機播放" +SlideShow.Transition="變更特效" +SlideShow.Transition.Cut="直接變更" +SlideShow.Transition.Fade="淡入淡出" +SlideShow.Transition.Swipe="滑出" +SlideShow.Transition.Slide="推出" + diff --git a/plugins/image-source/image-source.c b/plugins/image-source/image-source.c index f47d1dc..91ced6c 100644 --- a/plugins/image-source/image-source.c +++ b/plugins/image-source/image-source.c @@ -23,6 +23,7 @@ struct image_source { time_t file_timestamp; float update_time_elapsed; uint64_t last_time; + bool active; gs_image_file_t image; }; @@ -162,6 +163,31 @@ static void image_source_tick(void *data, float seconds) struct image_source *context = data; uint64_t frame_time = obs_get_video_frame_time(); + if (obs_source_active(context->source)) { + if (!context->active) { + if (context->image.is_animated_gif) + context->last_time = frame_time; + context->active = true; + } + + } else { + if (context->active) { + if (context->image.is_animated_gif) { + context->image.cur_frame = 0; + context->image.cur_loop = 0; + context->image.cur_time = 0; + + obs_enter_graphics(); + gs_image_file_update_texture(&context->image); + obs_leave_graphics(); + } + + context->active = false; + } + + return; + } + if (context->last_time && context->image.is_animated_gif) { uint64_t elapsed = frame_time - context->last_time; bool updated = gs_image_file_tick(&context->image, elapsed); @@ -175,8 +201,6 @@ static void image_source_tick(void *data, float seconds) context->last_time = frame_time; - if (!obs_source_showing(context->source)) return; - context->update_time_elapsed += seconds; if (context->update_time_elapsed >= 1.0f) { @@ -246,8 +270,11 @@ static struct obs_source_info image_source_info = { OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("image-source", "en-US") +extern struct obs_source_info slideshow_info; + bool obs_module_load(void) { obs_register_source(&image_source_info); + obs_register_source(&slideshow_info); return true; } diff --git a/plugins/image-source/obs-slideshow.c b/plugins/image-source/obs-slideshow.c new file mode 100644 index 0000000..3fa7b67 --- /dev/null +++ b/plugins/image-source/obs-slideshow.c @@ -0,0 +1,528 @@ +#include +#include +#include +#include +#include + +#define do_log(level, format, ...) \ + blog(level, "[slideshow: '%s'] " format, \ + obs_source_get_name(ss->source), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) + +#define S_TR_SPEED "transition_speed" +#define S_SLIDE_TIME "slide_time" +#define S_TRANSITION "transition" +#define S_RANDOMIZE "randomize" +#define S_FILES "files" + +#define TR_CUT "cut" +#define TR_FADE "fade" +#define TR_SWIPE "swipe" +#define TR_SLIDE "slide" + +#define T_(text) obs_module_text("SlideShow." text) +#define T_TR_SPEED T_("TransitionSpeed") +#define T_SLIDE_TIME T_("SlideTime") +#define T_TRANSITION T_("Transition") +#define T_RANDOMIZE T_("Randomize") +#define T_FILES T_("Files") + +#define T_TR_(text) obs_module_text("SlideShow.Transition." text) +#define T_TR_CUT T_TR_("Cut") +#define T_TR_FADE T_TR_("Fade") +#define T_TR_SWIPE T_TR_("Swipe") +#define T_TR_SLIDE T_TR_("Slide") + +/* ------------------------------------------------------------------------- */ + +struct image_file_data { + char *path; + obs_source_t *source; +}; + +struct slideshow { + obs_source_t *source; + + bool randomize; + float slide_time; + uint32_t tr_speed; + const char *tr_name; + obs_source_t *transition; + + float elapsed; + size_t cur_item; + + uint32_t cx; + uint32_t cy; + + pthread_mutex_t mutex; + DARRAY(struct image_file_data) files; +}; + +static obs_source_t *get_transition(struct slideshow *ss) +{ + obs_source_t *tr; + + pthread_mutex_lock(&ss->mutex); + tr = ss->transition; + obs_source_addref(tr); + pthread_mutex_unlock(&ss->mutex); + + return tr; +} + +static obs_source_t *get_source(struct darray *array, const char *path) +{ + DARRAY(struct image_file_data) files; + obs_source_t *source = NULL; + + files.da = *array; + + for (size_t i = 0; i < files.num; i++) { + const char *cur_path = files.array[i].path; + + if (strcmp(path, cur_path) == 0) { + source = files.array[i].source; + obs_source_addref(source); + break; + } + } + + return source; +} + +static obs_source_t *create_source_from_file(const char *file) +{ + obs_data_t *settings = obs_data_create(); + obs_source_t *source; + + obs_data_set_string(settings, "file", file); + obs_data_set_bool(settings, "unload", false); + source = obs_source_create_private("image_source", NULL, settings); + + obs_data_release(settings); + return source; +} + +static void free_files(struct darray *array) +{ + DARRAY(struct image_file_data) files; + files.da = *array; + + for (size_t i = 0; i < files.num; i++) { + bfree(files.array[i].path); + obs_source_release(files.array[i].source); + } + + da_free(files); +} + +static inline size_t random_file(struct slideshow *ss) +{ + return (size_t)rand() % ss->files.num; +} + +/* ------------------------------------------------------------------------- */ + +static const char *ss_getname(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("SlideShow"); +} + +static void add_file(struct slideshow *ss, struct darray *array, + const char *path, uint32_t *cx, uint32_t *cy) +{ + DARRAY(struct image_file_data) new_files; + struct image_file_data data; + obs_source_t *new_source; + + new_files.da = *array; + + pthread_mutex_lock(&ss->mutex); + new_source = get_source(&ss->files.da, path); + pthread_mutex_unlock(&ss->mutex); + + if (!new_source) + new_source = get_source(&new_files.da, path); + if (!new_source) + new_source = create_source_from_file(path); + + if (new_source) { + uint32_t new_cx = obs_source_get_width(new_source); + uint32_t new_cy = obs_source_get_height(new_source); + + data.path = bstrdup(path); + data.source = new_source; + da_push_back(new_files, &data); + + if (new_cx > *cx) *cx = new_cx; + if (new_cy > *cy) *cy = new_cy; + } + + *array = new_files.da; +} + +static bool valid_extension(const char *ext) +{ + if (!ext) + return false; + return astrcmpi(ext, ".bmp") == 0 || + astrcmpi(ext, ".tga") == 0 || + astrcmpi(ext, ".png") == 0 || + astrcmpi(ext, ".jpeg") == 0 || + astrcmpi(ext, ".jpg") == 0 || + astrcmpi(ext, ".gif") == 0; +} + +static void ss_update(void *data, obs_data_t *settings) +{ + DARRAY(struct image_file_data) new_files; + DARRAY(struct image_file_data) old_files; + obs_source_t *new_tr = NULL; + obs_source_t *old_tr = NULL; + struct slideshow *ss = data; + obs_data_array_t *array; + const char *tr_name; + uint32_t new_duration; + uint32_t new_speed; + uint32_t cx = 0; + uint32_t cy = 0; + size_t count; + + /* ------------------------------------- */ + /* get settings data */ + + da_init(new_files); + + tr_name = obs_data_get_string(settings, S_TRANSITION); + if (astrcmpi(tr_name, TR_CUT) == 0) + tr_name = "cut_transition"; + else if (astrcmpi(tr_name, TR_SWIPE) == 0) + tr_name = "swipe_transition"; + else if (astrcmpi(tr_name, TR_SLIDE) == 0) + tr_name = "slide_transition"; + else + tr_name = "fade_transition"; + + ss->randomize = obs_data_get_bool(settings, S_RANDOMIZE); + + if (!ss->tr_name || strcmp(tr_name, ss->tr_name) != 0) + new_tr = obs_source_create_private(tr_name, NULL, NULL); + + new_duration = (uint32_t)obs_data_get_int(settings, S_SLIDE_TIME); + new_speed = (uint32_t)obs_data_get_int(settings, S_TR_SPEED); + + array = obs_data_get_array(settings, S_FILES); + count = obs_data_array_count(array); + + /* ------------------------------------- */ + /* create new list of sources */ + + for (size_t i = 0; i < count; i++) { + obs_data_t *item = obs_data_array_item(array, i); + const char *path = obs_data_get_string(item, "value"); + os_dir_t *dir = os_opendir(path); + + if (dir) { + struct dstr dir_path = {0}; + struct os_dirent *ent; + + for (;;) { + const char *ext; + + ent = os_readdir(dir); + if (!ent) + break; + if (ent->directory) + continue; + + ext = os_get_path_extension(ent->d_name); + if (!valid_extension(ext)) + continue; + + dstr_copy(&dir_path, path); + dstr_cat_ch(&dir_path, '/'); + dstr_cat(&dir_path, ent->d_name); + add_file(ss, &new_files.da, dir_path.array, + &cx, &cy); + } + + dstr_free(&dir_path); + os_closedir(dir); + } else { + add_file(ss, &new_files.da, path, &cx, &cy); + } + + obs_data_release(item); + } + + /* ------------------------------------- */ + /* update settings data */ + + pthread_mutex_lock(&ss->mutex); + + old_files.da = ss->files.da; + ss->files.da = new_files.da; + if (new_tr) { + old_tr = ss->transition; + ss->transition = new_tr; + } + + if (new_duration < 50) + new_duration = 50; + if (new_speed > (new_duration - 50)) + new_speed = new_duration - 50; + + ss->tr_speed = new_speed; + ss->tr_name = tr_name; + ss->slide_time = (float)new_duration / 1000.0f; + + pthread_mutex_unlock(&ss->mutex); + + /* ------------------------------------- */ + /* clean up and restart transition */ + + if (old_tr) + obs_source_release(old_tr); + free_files(&old_files.da); + + ss->cx = cx; + ss->cy = cy; + ss->cur_item = 0; + ss->elapsed = 0.0f; + obs_transition_set_size(ss->transition, cx, cy); + obs_transition_set_alignment(ss->transition, OBS_ALIGN_CENTER); + obs_transition_set_scale_type(ss->transition, + OBS_TRANSITION_SCALE_ASPECT); + + if (ss->randomize && ss->files.num) + ss->cur_item = random_file(ss); + if (new_tr) + obs_source_add_active_child(ss->source, new_tr); + if (ss->files.num) + obs_transition_start(ss->transition, OBS_TRANSITION_MODE_AUTO, + ss->tr_speed, + ss->files.array[ss->cur_item].source); + + obs_data_array_release(array); +} + +static void ss_destroy(void *data) +{ + struct slideshow *ss = data; + + obs_source_release(ss->transition); + free_files(&ss->files.da); + pthread_mutex_destroy(&ss->mutex); + bfree(ss); +} + +static void *ss_create(obs_data_t *settings, obs_source_t *source) +{ + struct slideshow *ss = bzalloc(sizeof(*ss)); + ss->source = source; + + pthread_mutex_init_value(&ss->mutex); + if (pthread_mutex_init(&ss->mutex, NULL) != 0) + goto error; + + obs_source_update(source, NULL); + + UNUSED_PARAMETER(settings); + return ss; + +error: + ss_destroy(ss); + return NULL; +} + +static void ss_video_render(void *data, gs_effect_t *effect) +{ + struct slideshow *ss = data; + obs_source_t *transition = get_transition(ss); + + if (transition) { + obs_source_video_render(transition); + obs_source_release(transition); + } + + UNUSED_PARAMETER(effect); +} + +static void ss_video_tick(void *data, float seconds) +{ + struct slideshow *ss = data; + + if (!ss->transition || !ss->slide_time) + return; + + ss->elapsed += seconds; + if (ss->elapsed > ss->slide_time) { + ss->elapsed -= ss->slide_time; + + if (ss->randomize) { + size_t next = ss->cur_item; + if (ss->files.num > 1) { + while (next == ss->cur_item) + next = random_file(ss); + } + ss->cur_item = next; + + } else if (++ss->cur_item >= ss->files.num) { + ss->cur_item = 0; + } + + if (ss->files.num) + obs_transition_start(ss->transition, + OBS_TRANSITION_MODE_AUTO, ss->tr_speed, + ss->files.array[ss->cur_item].source); + } +} + +static inline bool ss_audio_render_(obs_source_t *transition, uint64_t *ts_out, + struct obs_source_audio_mix *audio_output, + uint32_t mixers, size_t channels, size_t sample_rate) +{ + struct obs_source_audio_mix child_audio; + uint64_t source_ts; + + if (obs_source_audio_pending(transition)) + return false; + + source_ts = obs_source_get_audio_timestamp(transition); + if (!source_ts) + return false; + + obs_source_get_audio_mix(transition, &child_audio); + for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) { + if ((mixers & (1 << mix)) == 0) + continue; + + for (size_t ch = 0; ch < channels; ch++) { + float *out = audio_output->output[mix].data[ch]; + float *in = child_audio.output[mix].data[ch]; + + memcpy(out, in, AUDIO_OUTPUT_FRAMES * + MAX_AUDIO_CHANNELS * sizeof(float)); + } + } + + *ts_out = source_ts; + + UNUSED_PARAMETER(sample_rate); + return true; +} + +static bool ss_audio_render(void *data, uint64_t *ts_out, + struct obs_source_audio_mix *audio_output, + uint32_t mixers, size_t channels, size_t sample_rate) +{ + struct slideshow *ss = data; + obs_source_t *transition = get_transition(ss); + bool success; + + if (!transition) + return false; + + success = ss_audio_render_(transition, ts_out, audio_output, mixers, + channels, sample_rate); + + obs_source_release(transition); + return success; +} + +static void ss_enum_sources(void *data, obs_source_enum_proc_t cb, void *param) +{ + struct slideshow *ss = data; + + pthread_mutex_lock(&ss->mutex); + if (ss->transition) + cb(ss->source, ss->transition, param); + pthread_mutex_unlock(&ss->mutex); +} + +static uint32_t ss_width(void *data) +{ + struct slideshow *ss = data; + return ss->transition ? ss->cx : 0; +} + +static uint32_t ss_height(void *data) +{ + struct slideshow *ss = data; + return ss->transition ? ss->cy : 0; +} + +static void ss_defaults(obs_data_t *settings) +{ + obs_data_set_default_string(settings, S_TRANSITION, "fade"); + obs_data_set_default_int(settings, S_SLIDE_TIME, 8000); + obs_data_set_default_int(settings, S_TR_SPEED, 700); +} + +static const char *file_filter = + "Image files (*.bmp *.tga *.png *.jpeg *.jpg *.gif)"; + +static obs_properties_t *ss_properties(void *data) +{ + obs_properties_t *ppts = obs_properties_create(); + struct slideshow *ss = data; + struct dstr path = {0}; + obs_property_t *p; + + p = obs_properties_add_list(ppts, S_TRANSITION, T_TRANSITION, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, T_TR_CUT, TR_CUT); + obs_property_list_add_string(p, T_TR_FADE, TR_FADE); + obs_property_list_add_string(p, T_TR_SWIPE, TR_SWIPE); + obs_property_list_add_string(p, T_TR_SLIDE, TR_SLIDE); + + obs_properties_add_int(ppts, S_SLIDE_TIME, T_SLIDE_TIME, + 50, 3600000, 50); + obs_properties_add_int(ppts, S_TR_SPEED, T_TR_SPEED, + 0, 3600000, 50); + obs_properties_add_bool(ppts, S_RANDOMIZE, T_RANDOMIZE); + + if (ss) { + pthread_mutex_lock(&ss->mutex); + if (ss->files.num) { + struct image_file_data *last = da_end(ss->files); + const char *slash; + + dstr_copy(&path, last->path); + dstr_replace(&path, "\\", "/"); + slash = strrchr(path.array, '/'); + if (slash) + dstr_resize(&path, slash - path.array + 1); + } + pthread_mutex_unlock(&ss->mutex); + } + + obs_properties_add_editable_list(ppts, S_FILES, T_FILES, + OBS_EDITABLE_LIST_TYPE_FILES, file_filter, path.array); + dstr_free(&path); + + return ppts; +} + +struct obs_source_info slideshow_info = { + .id = "slideshow", + .type = OBS_SOURCE_TYPE_INPUT, + .output_flags = OBS_SOURCE_VIDEO | + OBS_SOURCE_CUSTOM_DRAW | + OBS_SOURCE_COMPOSITE, + .get_name = ss_getname, + .create = ss_create, + .destroy = ss_destroy, + .update = ss_update, + .video_render = ss_video_render, + .video_tick = ss_video_tick, + .audio_render = ss_audio_render, + .enum_active_sources = ss_enum_sources, + .get_width = ss_width, + .get_height = ss_height, + .get_defaults = ss_defaults, + .get_properties = ss_properties +}; diff --git a/plugins/linux-alsa/data/locale/cs-CZ.ini b/plugins/linux-alsa/data/locale/cs-CZ.ini index abd61a4..a9bdfcd 100644 --- a/plugins/linux-alsa/data/locale/cs-CZ.ini +++ b/plugins/linux-alsa/data/locale/cs-CZ.ini @@ -1,3 +1,3 @@ -AlsaInput="Zaznamenávací zařízení zvuku (ALSA)" +AlsaInput="Zařízení pro záznam zvuku (ALSA)" Device="Zařízení" diff --git a/plugins/linux-alsa/data/locale/he-IL.ini b/plugins/linux-alsa/data/locale/he-IL.ini new file mode 100644 index 0000000..7433b99 --- /dev/null +++ b/plugins/linux-alsa/data/locale/he-IL.ini @@ -0,0 +1,3 @@ +AlsaInput="התקן לכידת שמע (ALSA)" +Device="התקן" + diff --git a/plugins/linux-alsa/data/locale/it-IT.ini b/plugins/linux-alsa/data/locale/it-IT.ini new file mode 100644 index 0000000..15b9a46 --- /dev/null +++ b/plugins/linux-alsa/data/locale/it-IT.ini @@ -0,0 +1,3 @@ +AlsaInput="Dispositivo di acquisizione audio (ALSA)" +Device="Dispositivo" + diff --git a/plugins/linux-alsa/data/locale/nb-NO.ini b/plugins/linux-alsa/data/locale/nb-NO.ini new file mode 100644 index 0000000..7e1cfb8 --- /dev/null +++ b/plugins/linux-alsa/data/locale/nb-NO.ini @@ -0,0 +1,3 @@ +AlsaInput="Lydopptaksenhet (ALSA)" +Device="Enhet" + diff --git a/plugins/linux-alsa/data/locale/pt-PT.ini b/plugins/linux-alsa/data/locale/pt-PT.ini new file mode 100644 index 0000000..d272f9b --- /dev/null +++ b/plugins/linux-alsa/data/locale/pt-PT.ini @@ -0,0 +1,3 @@ +AlsaInput="Dispositivo de captura de áudio (ALSA)" +Device="Dispositivo" + diff --git a/plugins/linux-capture/data/locale/ar-SA.ini b/plugins/linux-capture/data/locale/ar-SA.ini index 547458e..da365c6 100644 --- a/plugins/linux-capture/data/locale/ar-SA.ini +++ b/plugins/linux-capture/data/locale/ar-SA.ini @@ -1,6 +1,6 @@ X11SharedMemoryScreenInput="التقاط الشاشة (XSHM)" Screen="الشاشة" -CaptureCursor="مؤشر الالتقاط" +CaptureCursor="التقاط المؤشر" AdvancedSettings="إعدادات متقدمة" XServer="X Server" XCCapture="التقاط النافذة (Xcomposite)" @@ -11,4 +11,5 @@ CropRight="الاقتطاع من اليمين (بكسل)" CropBottom="الاقتطاع من الأسفل (بكسل)" SwapRedBlue="مبادلة بين الأحمر و الأزرق" LockX="قفل السيرفر X عند الالتقاط" +IncludeXBorder="تضمين حواف X" diff --git a/plugins/linux-capture/data/locale/fi-FI.ini b/plugins/linux-capture/data/locale/fi-FI.ini index 129eddd..02cd436 100644 --- a/plugins/linux-capture/data/locale/fi-FI.ini +++ b/plugins/linux-capture/data/locale/fi-FI.ini @@ -3,7 +3,7 @@ Screen="Kuvaruutu" CaptureCursor="Kaappaa kursori" AdvancedSettings="Lisäasetukset" XServer="X Server" -XCCapture="Ikkunakaappaus (Xcomposite)" +XCCapture="Ikkuna (Xcomposite)" Window="Ikkuna" CropTop="Rajaa ylhäältä (pikselit)" CropLeft="Rajaa vasemmalta (pikselit)" diff --git a/plugins/linux-capture/data/locale/he-IL.ini b/plugins/linux-capture/data/locale/he-IL.ini index 3e7b532..96b1a8f 100644 --- a/plugins/linux-capture/data/locale/he-IL.ini +++ b/plugins/linux-capture/data/locale/he-IL.ini @@ -12,4 +12,5 @@ CropBottom="חיתוך תחתון (פיקסלים)" SwapRedBlue="החלף אדום וכחול" LockX="נעל שרת X בעת לכידה" IncludeXBorder="כלול קצה שרת X" +ExcludeAlpha="השתמש בתבנית מרקם ללא אלפא (עקיפת בעיית Mesa)" diff --git a/plugins/linux-capture/data/locale/it-IT.ini b/plugins/linux-capture/data/locale/it-IT.ini index 2e5aba6..a2f3307 100644 --- a/plugins/linux-capture/data/locale/it-IT.ini +++ b/plugins/linux-capture/data/locale/it-IT.ini @@ -12,4 +12,5 @@ CropBottom="Crop Inferiore (pixels)" SwapRedBlue="Inverti rosso e blu" LockX="Blocca l' X Server durante l'acquisizione" IncludeXBorder="Includi X bordi" +ExcludeAlpha="Usa formato texture alfa-less (soluzione di Mesa)" diff --git a/plugins/linux-capture/data/locale/nb-NO.ini b/plugins/linux-capture/data/locale/nb-NO.ini index 211df01..b320b0e 100644 --- a/plugins/linux-capture/data/locale/nb-NO.ini +++ b/plugins/linux-capture/data/locale/nb-NO.ini @@ -10,6 +10,7 @@ CropLeft="Beskjær venstre (piksler)" CropRight="Beskjær høyre (piksler)" CropBottom="Beskjær bunnen (piksler)" SwapRedBlue="Bytt rød og blå" -LockX="Lås X-server under opptak" +LockX="Lås X-tjener under opptak" IncludeXBorder="Inkludér X11-rammen" +ExcludeAlpha="Bruk teksturformat uten alfakanal (Mesa løsning)" diff --git a/plugins/linux-capture/data/locale/pt-PT.ini b/plugins/linux-capture/data/locale/pt-PT.ini index 7ed2b91..4eaec6d 100644 --- a/plugins/linux-capture/data/locale/pt-PT.ini +++ b/plugins/linux-capture/data/locale/pt-PT.ini @@ -12,4 +12,5 @@ CropBottom="Cortar em baixo (píxeis)" SwapRedBlue="Trocar vermelho e azul" LockX="Bloquear X Server durante a captura" IncludeXBorder="Incluir X Border" +ExcludeAlpha="Uso da textura Formato de alfa-menos (solução de Mesa)" diff --git a/plugins/linux-capture/data/locale/zh-TW.ini b/plugins/linux-capture/data/locale/zh-TW.ini index a2af34e..7d02276 100644 --- a/plugins/linux-capture/data/locale/zh-TW.ini +++ b/plugins/linux-capture/data/locale/zh-TW.ini @@ -12,4 +12,5 @@ CropBottom="剪裁底部(px)" SwapRedBlue="交換紅藍顏色" LockX="截取時鎖定 X server" IncludeXBorder="包含X邊框" +ExcludeAlpha="使用不帶有alpha值的材質格式 (Mesa暫時解決方案)" diff --git a/plugins/linux-capture/xcompcap-helper.cpp b/plugins/linux-capture/xcompcap-helper.cpp index 5922a48..cf4f0f6 100644 --- a/plugins/linux-capture/xcompcap-helper.cpp +++ b/plugins/linux-capture/xcompcap-helper.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "xcompcap-helper.hpp" @@ -171,9 +172,9 @@ namespace XCompcap return XScreenNumberOfScreen(attr.screen); } - std::string getWindowName(Window win) + std::string getWindowAtom(Window win, const char *atom) { - Atom netWmName = XInternAtom(disp(), "_NET_WM_NAME", false); + Atom netWmName = XInternAtom(disp(), atom, false); int n; char **list = 0; XTextProperty tp; @@ -199,6 +200,11 @@ namespace XCompcap } } + char *conv = nullptr; + if (os_mbs_to_utf8_ptr(res.c_str(), 0, &conv)) + res = conv; + bfree(conv); + XFree(tp.value); return res; @@ -409,6 +415,39 @@ void XErrorLock::resetError() curErrorText[0] = 0; } +XDisplayLock::XDisplayLock() +{ + islock = false; + lock(); +} + +XDisplayLock::~XDisplayLock() +{ + unlock(); +} + +bool XDisplayLock::isLocked() +{ + return islock; +} + +void XDisplayLock::lock() +{ + if (!islock) { + XLockDisplay(XCompcap::disp()); + islock = true; + } +} + +void XDisplayLock::unlock() +{ + if (islock) { + XSync(XCompcap::disp(), 0); + XUnlockDisplay(XCompcap::disp()); + islock = false; + } +} + ObsGsContextHolder::ObsGsContextHolder() { diff --git a/plugins/linux-capture/xcompcap-helper.hpp b/plugins/linux-capture/xcompcap-helper.hpp index ceb992e..838d84f 100644 --- a/plugins/linux-capture/xcompcap-helper.hpp +++ b/plugins/linux-capture/xcompcap-helper.hpp @@ -48,6 +48,23 @@ class XErrorLock void resetError(); }; +class XDisplayLock +{ + bool islock; + + public: + XDisplayLock(const XDisplayLock&) = delete; + XDisplayLock& operator=(const XDisplayLock&) = delete; + + XDisplayLock(); + ~XDisplayLock(); + + bool isLocked(); + + void unlock(); + void lock(); +}; + class ObsGsContextHolder { public: @@ -65,12 +82,22 @@ namespace XCompcap std::string getWindowCommand(Window win); int getRootWindowScreen(Window root); - std::string getWindowName(Window win); + std::string getWindowAtom(Window win, const char *atom); int getWindowPid(Window win); bool ewmhIsSupported(); std::list getTopLevelWindows(); std::list getAllWindows(); + inline std::string getWindowName(Window win) + { + return getWindowAtom(win, "_NET_WM_NAME"); + } + + inline std::string getWindowClass(Window win) + { + return getWindowAtom(win, "WM_CLASS"); + } + void processEvents(); bool windowWasReconfigured(Window win); } diff --git a/plugins/linux-capture/xcompcap-main.cpp b/plugins/linux-capture/xcompcap-main.cpp index f9cf00c..7a09ecb 100644 --- a/plugins/linux-capture/xcompcap-main.cpp +++ b/plugins/linux-capture/xcompcap-main.cpp @@ -57,11 +57,11 @@ obs_properties_t *XCompcapMain::properties() for (Window win: XCompcap::getTopLevelWindows()) { std::string wname = XCompcap::getWindowName(win); - std::string progpath = XCompcap::getWindowCommand(win); + std::string cls = XCompcap::getWindowClass(win); std::string winid = std::to_string((long long)win); std::string desc = (winid + WIN_STRING_DIV + wname + - WIN_STRING_DIV + progpath); + WIN_STRING_DIV + cls); obs_property_list_add_string(wins, wname.c_str(), desc.c_str()); @@ -106,6 +106,7 @@ void XCompcapMain::defaults(obs_data_t *settings) obs_data_set_default_bool(settings, "exclude_alpha", false); } +#define FIND_WINDOW_INTERVAL 2.0 struct XCompcapMain_private { @@ -137,7 +138,7 @@ struct XCompcapMain_private obs_source_t *source; std::string windowName; - Window win; + Window win = 0; int cut_top, cur_cut_top; int cut_left, cur_cut_left; int cut_right, cur_cut_right; @@ -148,6 +149,8 @@ struct XCompcapMain_private bool include_border; bool exclude_alpha; + double window_check_time = 0.0; + uint32_t width; uint32_t height; uint32_t border; @@ -201,6 +204,8 @@ XCompcapMain::~XCompcapMain() static Window getWindowFromString(std::string wstr) { + XErrorLock xlock; + if (wstr == "") { return XCompcap::getTopLevelWindows().front(); } @@ -211,26 +216,29 @@ static Window getWindowFromString(std::string wstr) } size_t firstMark = wstr.find(WIN_STRING_DIV); + size_t markSize = strlen(WIN_STRING_DIV); if (firstMark == std::string::npos) return (Window)std::stol(wstr); - std::string widstr = wstr.substr(0, firstMark); - Window wid = (Window)std::stol(widstr); + Window wid = 0; - wstr = wstr.substr(firstMark + strlen(WIN_STRING_DIV)); + wstr = wstr.substr(firstMark + markSize); size_t lastMark = wstr.rfind(WIN_STRING_DIV); std::string wname = wstr.substr(0, lastMark); + std::string wcls = wstr.substr(lastMark + markSize); Window matchedNameWin = wid; for (Window cwin: XCompcap::getTopLevelWindows()) { std::string cwinname = XCompcap::getWindowName(cwin); + std::string ccls = XCompcap::getWindowClass(cwin); - if (cwin == wid && wname == cwinname) + if (cwin == wid && wname == cwinname && wcls == ccls) return wid; - if (wname == cwinname) + if (wname == cwinname || + (!matchedNameWin && !wcls.empty() && wcls == ccls)) matchedNameWin = cwin; } @@ -240,7 +248,7 @@ static Window getWindowFromString(std::string wstr) static void xcc_cleanup(XCompcapMain_private *p) { PLock lock(&p->lock); - XErrorLock xlock; + XDisplayLock xlock; if (p->gltex) { gs_texture_destroy(p->gltex); @@ -299,7 +307,9 @@ void XCompcapMain::updateSettings(obs_data_t *settings) xlock.resetError(); - XCompositeRedirectWindow(xdisp, p->win, CompositeRedirectAutomatic); + if (p->win) + XCompositeRedirectWindow(xdisp, p->win, + CompositeRedirectAutomatic); if (xlock.gotError()) { blog(LOG_ERROR, "XCompositeRedirectWindow failed: %s", @@ -307,18 +317,19 @@ void XCompcapMain::updateSettings(obs_data_t *settings) return; } - XSelectInput(xdisp, p->win, StructureNotifyMask | ExposureMask); + if (p->win) + XSelectInput(xdisp, p->win, StructureNotifyMask | ExposureMask); XSync(xdisp, 0); XWindowAttributes attr; - if (!XGetWindowAttributes(xdisp, p->win, &attr)) { + if (!p->win || !XGetWindowAttributes(xdisp, p->win, &attr)) { p->win = 0; p->width = 0; p->height = 0; return; } - if (p->cursor && p->show_cursor) { + if (p->win && p->cursor && p->show_cursor) { Window child; int x, y; @@ -451,8 +462,6 @@ void XCompcapMain::updateSettings(obs_data_t *settings) void XCompcapMain::tick(float seconds) { - UNUSED_PARAMETER(seconds); - if (!obs_source_showing(p->source)) return; @@ -463,21 +472,30 @@ void XCompcapMain::tick(float seconds) XCompcap::processEvents(); - if (XCompcap::windowWasReconfigured(p->win)) - updateSettings(0); + if (p->win && XCompcap::windowWasReconfigured(p->win)) { + p->window_check_time = FIND_WINDOW_INTERVAL; + p->win = 0; + } - XErrorLock xlock; - xlock.resetError(); + XDisplayLock xlock; XWindowAttributes attr; - if (!XGetWindowAttributes(xdisp, p->win, &attr)) { + if (!p->win || !XGetWindowAttributes(xdisp, p->win, &attr)) { + p->window_check_time += (double)seconds; + + if (p->window_check_time < FIND_WINDOW_INTERVAL) + return; + Window newWin = getWindowFromString(p->windowName); - if (XGetWindowAttributes(xdisp, newWin, &attr)) { + p->window_check_time = 0.0; + + if (newWin && XGetWindowAttributes(xdisp, newWin, &attr)) { p->win = newWin; updateSettings(0); + } else { + return; } - return; } if (!p->tex || !p->gltex) @@ -524,11 +542,11 @@ void XCompcapMain::tick(float seconds) void XCompcapMain::render(gs_effect_t *effect) { - PLock lock(&p->lock, true); - if (!p->win) return; + PLock lock(&p->lock, true); + effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); if (!lock.isLocked() || !p->tex) @@ -552,10 +570,16 @@ void XCompcapMain::render(gs_effect_t *effect) uint32_t XCompcapMain::width() { + if (!p->win) + return 0; + return p->width - p->cur_cut_left - p->cur_cut_right; } uint32_t XCompcapMain::height() { + if (!p->win) + return 0; + return p->height - p->cur_cut_bot - p->cur_cut_top; } diff --git a/plugins/linux-capture/xcomposite-main.cpp b/plugins/linux-capture/xcomposite-main.cpp index f27132b..c553fb2 100644 --- a/plugins/linux-capture/xcomposite-main.cpp +++ b/plugins/linux-capture/xcomposite-main.cpp @@ -70,7 +70,8 @@ extern "C" void xcomposite_load(void) sinfo.id = "xcomposite_input"; sinfo.output_flags = OBS_SOURCE_VIDEO | - OBS_SOURCE_CUSTOM_DRAW; + OBS_SOURCE_CUSTOM_DRAW | + OBS_SOURCE_DO_NOT_DUPLICATE; sinfo.get_name = xcompcap_getname; sinfo.create = xcompcap_create; diff --git a/plugins/linux-jack/data/locale/nb-NO.ini b/plugins/linux-jack/data/locale/nb-NO.ini index 432dfef..f5e6f1d 100644 --- a/plugins/linux-jack/data/locale/nb-NO.ini +++ b/plugins/linux-jack/data/locale/nb-NO.ini @@ -1,4 +1,4 @@ StartJACKServer="Start JACK server" Channels="Antall kanaler" -JACKInput="JACK innputtklient" +JACKInput="JACK inngangsklient" diff --git a/plugins/linux-pulseaudio/data/locale/cs-CZ.ini b/plugins/linux-pulseaudio/data/locale/cs-CZ.ini index c5e4498..c42899a 100644 --- a/plugins/linux-pulseaudio/data/locale/cs-CZ.ini +++ b/plugins/linux-pulseaudio/data/locale/cs-CZ.ini @@ -1,4 +1,4 @@ -PulseInput="Záznam vstupu zvuku (PulseAudio)" -PulseOutput="Záznam výstupu zvuku (PulseAudio)" +PulseInput="Záznam zvukového vstupu (PulseAudio)" +PulseOutput="Záznam zvukového výstupu (PulseAudio)" Device="Zařízení" diff --git a/plugins/linux-pulseaudio/data/locale/fi-FI.ini b/plugins/linux-pulseaudio/data/locale/fi-FI.ini index a14b7e6..cee9618 100644 --- a/plugins/linux-pulseaudio/data/locale/fi-FI.ini +++ b/plugins/linux-pulseaudio/data/locale/fi-FI.ini @@ -1,4 +1,4 @@ -PulseInput="Äänen sisääntulo (PulseAudio)" -PulseOutput="Äänen ulostulo (PulseAudio)" +PulseInput="Äänitulo (PulseAudio)" +PulseOutput="Äänilähtö (PulseAudio)" Device="Laite" diff --git a/plugins/linux-pulseaudio/pulse-wrapper.c b/plugins/linux-pulseaudio/pulse-wrapper.c index 6af459a..bbda717 100644 --- a/plugins/linux-pulseaudio/pulse-wrapper.c +++ b/plugins/linux-pulseaudio/pulse-wrapper.c @@ -172,6 +172,10 @@ int_fast32_t pulse_get_source_info_list(pa_source_info_cb_t cb, void* userdata) pa_operation *op = pa_context_get_source_info_list( pulse_context, cb, userdata); + if (!op) { + pulse_unlock(); + return -1; + } while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) pulse_wait(); pa_operation_unref(op); @@ -191,6 +195,10 @@ int_fast32_t pulse_get_source_info(pa_source_info_cb_t cb, const char *name, pa_operation *op = pa_context_get_source_info_by_name( pulse_context, name, cb, userdata); + if (!op) { + pulse_unlock(); + return -1; + } while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) pulse_wait(); pa_operation_unref(op); @@ -209,6 +217,10 @@ int_fast32_t pulse_get_server_info(pa_server_info_cb_t cb, void* userdata) pa_operation *op = pa_context_get_server_info( pulse_context, cb, userdata); + if (!op) { + pulse_unlock(); + return -1; + } while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) pulse_wait(); pa_operation_unref(op); diff --git a/plugins/linux-v4l2/data/locale/ar-SA.ini b/plugins/linux-v4l2/data/locale/ar-SA.ini index 7e269d8..988037c 100644 --- a/plugins/linux-v4l2/data/locale/ar-SA.ini +++ b/plugins/linux-v4l2/data/locale/ar-SA.ini @@ -1,5 +1,8 @@ V4L2Input="جهاز التقاط الفيديو (V4L2)" Device="الجهاز" +VideoFormat="تنسيق الفيديو" +VideoStandard="معيار الفيديو" +DVTiming="توقيت DV" Resolution="الأبعاد" FrameRate="الإطار" diff --git a/plugins/linux-v4l2/data/locale/zh-TW.ini b/plugins/linux-v4l2/data/locale/zh-TW.ini index 9f66f22..66a8387 100644 --- a/plugins/linux-v4l2/data/locale/zh-TW.ini +++ b/plugins/linux-v4l2/data/locale/zh-TW.ini @@ -3,6 +3,7 @@ Device="裝置" Input="輸入" VideoFormat="影像格式" VideoStandard="影像標準" +DVTiming="數位視頻時序值" Resolution="解析度" FrameRate="影格率" LeaveUnchanged="不改變並離開" diff --git a/plugins/mac-avcapture/data/locale/cs-CZ.ini b/plugins/mac-avcapture/data/locale/cs-CZ.ini index ad03e4f..df3a5b2 100644 --- a/plugins/mac-avcapture/data/locale/cs-CZ.ini +++ b/plugins/mac-avcapture/data/locale/cs-CZ.ini @@ -1,4 +1,4 @@ -AVCapture="Zaznamenávací zařízení obrazu" +AVCapture="Zařízení pro záznam obrazu" Device="Zařízení" UsePreset="Použít předvolbu" Preset="Předvolba" diff --git a/plugins/mac-avcapture/data/locale/it-IT.ini b/plugins/mac-avcapture/data/locale/it-IT.ini index 1aab549..42d5dc7 100644 --- a/plugins/mac-avcapture/data/locale/it-IT.ini +++ b/plugins/mac-avcapture/data/locale/it-IT.ini @@ -3,4 +3,12 @@ Device="Dispositivo" UsePreset="Usa il pre impostato" Preset="Preimpostato" Buffering="Usa Buffer" +FrameRate="Frame rate" +InputFormat="Formato di input" +ColorSpace="Spazio colore" +VideoRange="Gamma video" +VideoRange.Partial="Parziale" +VideoRange.Full="Intero" +Auto="Autom." +Unknown="Sconosciuto ($1)" diff --git a/plugins/mac-avcapture/data/locale/nb-NO.ini b/plugins/mac-avcapture/data/locale/nb-NO.ini index 0b0b60c..897f4f0 100644 --- a/plugins/mac-avcapture/data/locale/nb-NO.ini +++ b/plugins/mac-avcapture/data/locale/nb-NO.ini @@ -3,4 +3,12 @@ Device="Enhet" UsePreset="Bruk forhåndsinnstilling" Preset="Forhåndsinnstilling" Buffering="Bruk bufring" +FrameRate="Bildefrekvens" +InputFormat="Inngangsformat" +ColorSpace="Fargeområde" +VideoRange="Videoområde" +VideoRange.Partial="Delvis" +VideoRange.Full="Hel" +Auto="Automatisk" +Unknown="Ukjent (%1)" diff --git a/plugins/mac-avcapture/data/locale/pt-PT.ini b/plugins/mac-avcapture/data/locale/pt-PT.ini index 6c9b2c9..262f16a 100644 --- a/plugins/mac-avcapture/data/locale/pt-PT.ini +++ b/plugins/mac-avcapture/data/locale/pt-PT.ini @@ -3,4 +3,11 @@ Device="Dispositivo" UsePreset="Utilizar predefinição" Preset="Predefinição" Buffering="Utilizar Buffering" +FrameRate="Taxa de frames (frame rate)" +InputFormat="Formato de entrada" +ColorSpace="Espaço de cor" +VideoRange.Partial="Parcial" +VideoRange.Full="Completo" +Auto="Auto" +Unknown="Desconhecido (%1)" diff --git a/plugins/mac-avcapture/data/locale/zh-TW.ini b/plugins/mac-avcapture/data/locale/zh-TW.ini index fe3127c..f51e675 100644 --- a/plugins/mac-avcapture/data/locale/zh-TW.ini +++ b/plugins/mac-avcapture/data/locale/zh-TW.ini @@ -1,4 +1,14 @@ +AVCapture="視訊擷取裝置" Device="裝置" UsePreset="使用預設值" Preset="預設值" +Buffering="使用緩衝" +FrameRate="畫面播放速率" +InputFormat="輸入格式" +ColorSpace="色彩空間" +VideoRange="色彩範圍" +VideoRange.Partial="部分" +VideoRange.Full="全部" +Auto="自動" +Unknown="未知 (%1)" diff --git a/plugins/mac-capture/data/locale/fi-FI.ini b/plugins/mac-capture/data/locale/fi-FI.ini index b0db996..29cffdf 100644 --- a/plugins/mac-capture/data/locale/fi-FI.ini +++ b/plugins/mac-capture/data/locale/fi-FI.ini @@ -1,11 +1,11 @@ -CoreAudio.InputCapture="Äänen sisääntulon kaappaus" -CoreAudio.OutputCapture="Äänen ulostulon kaappaus" +CoreAudio.InputCapture="Äänitulo" +CoreAudio.OutputCapture="Äänilähtö" CoreAudio.Device="Laite" CoreAudio.Device.Default="Oletusarvo" -DisplayCapture="Ruudunkaappaus" +DisplayCapture="Monitori" DisplayCapture.Display="Näyttö" DisplayCapture.ShowCursor="Näytä kursori" -WindowCapture="Ikkunakaappaus" +WindowCapture="Ikkuna" WindowCapture.ShowShadow="Näytä ikkunan varjo" WindowUtils.Window="Ikkuna" WindowUtils.ShowEmptyNames="Näytä ikkunat joilla on tyhjä nimi" diff --git a/plugins/mac-capture/data/locale/nl-NL.ini b/plugins/mac-capture/data/locale/nl-NL.ini index 56210fd..2c540af 100644 --- a/plugins/mac-capture/data/locale/nl-NL.ini +++ b/plugins/mac-capture/data/locale/nl-NL.ini @@ -1,8 +1,8 @@ -CoreAudio.InputCapture="Audioinvoer Opname" -CoreAudio.OutputCapture="Audiouitvoer Opname" +CoreAudio.InputCapture="Audioinvoer Capture" +CoreAudio.OutputCapture="Audiouitvoer Capture" CoreAudio.Device="Apparaat" CoreAudio.Device.Default="Standaardinstellingen" -DisplayCapture="Beeldschermopname" +DisplayCapture="Beeldschermcapture" DisplayCapture.Display="Beeldscherm" DisplayCapture.ShowCursor="Cursor Weergeven" WindowCapture="Venstercapture" diff --git a/plugins/mac-capture/data/locale/zh-TW.ini b/plugins/mac-capture/data/locale/zh-TW.ini index 3eb14e9..a09f63c 100644 --- a/plugins/mac-capture/data/locale/zh-TW.ini +++ b/plugins/mac-capture/data/locale/zh-TW.ini @@ -1,8 +1,8 @@ -CoreAudio.InputCapture="截取音效輸入" -CoreAudio.OutputCapture="截取音效輸出" +CoreAudio.InputCapture="擷取音效輸入" +CoreAudio.OutputCapture="擷取音效輸出" CoreAudio.Device="裝置" CoreAudio.Device.Default="預設" -DisplayCapture="截取螢幕輸出" +DisplayCapture="擷取螢幕輸出" DisplayCapture.Display="螢幕" DisplayCapture.ShowCursor="顯示游標" WindowCapture="視窗擷取" @@ -12,6 +12,8 @@ WindowUtils.ShowEmptyNames="顯示無標題視窗" CropMode="裁剪" CropMode.None="無" CropMode.Manual="手動" +CropMode.ToWindow="到視窗" +CropMode.ToWindowAndManual="到視窗並手動" Crop.origin.x="左邊界" Crop.origin.y="上邊界" Crop.size.width="右邊界" diff --git a/plugins/mac-syphon/data/locale/zh-TW.ini b/plugins/mac-syphon/data/locale/zh-TW.ini new file mode 100644 index 0000000..34028b2 --- /dev/null +++ b/plugins/mac-syphon/data/locale/zh-TW.ini @@ -0,0 +1,13 @@ +Syphon="遊戲擷取 (Syphon)" +Source="來源" +LaunchSyphonInject="啟動 SyphonInject" +Inject="注入" +Application="應用程式" +SyphonLicense="Syphon 授權" +Crop="剪裁" +Crop.origin.x="左邊界" +Crop.origin.y="上邊界" +Crop.size.width="右邊界" +Crop.size.height="下邊界" +AllowTransparency="允許透明" + diff --git a/plugins/mac-vth264/data/locale/fi-FI.ini b/plugins/mac-vth264/data/locale/fi-FI.ini index 79c42d0..da41201 100644 --- a/plugins/mac-vth264/data/locale/fi-FI.ini +++ b/plugins/mac-vth264/data/locale/fi-FI.ini @@ -1,11 +1,11 @@ -VTH264EncHW="Apple VT H264 Hardware Encoder" -VTH264EncSW="Apple VT H264 Software Encoder" -VTEncoder="VideoToolbox Encoder" +VTH264EncHW="Apple VT H264 Laitteistopohjainen enkooderi" +VTH264EncSW="Apple VT H264 Ohjelmistopohjainen enkooderi" +VTEncoder="VideoToolbox Enkooderi" Bitrate="Bitrate" UseMaxBitrate="Rajoita bitratea" -MaxBitrate="Bitrate-maksimi" -MaxBitrateWindow="Bitrate-maksimin ikkuna (sekuntia)" -KeyframeIntervalSec="Keyframe-väli (sec, 0=auto)" +MaxBitrate="Maksimi bitrate" +MaxBitrateWindow="Maksimi bitraten ikkuna (sekunteina)" +KeyframeIntervalSec="Keyframe-väli (sekunteina, 0=automaattinen)" Profile="Profiili" None="(Ei mitään)" DefaultEncoder="(Oletusenkooderi)" diff --git a/plugins/mac-vth264/data/locale/pt-PT.ini b/plugins/mac-vth264/data/locale/pt-PT.ini new file mode 100644 index 0000000..a8fb8a9 --- /dev/null +++ b/plugins/mac-vth264/data/locale/pt-PT.ini @@ -0,0 +1,14 @@ +VTH264EncHW="Codificador de Hardware Apple VT H264" +VTH264EncSW="Codificador de Apple VT H264 Software" +VTEncoder="VideoToolbox Codificador" +Bitrate="Taxa de bits" +UseMaxBitrate="Limite taxa de bits" +MaxBitrate="Taxa de bits máxima" +MaxBitrateWindow="Máxima janela taxa de bits (segundos)" +KeyframeIntervalSec="Intervalo do keyframe (segundos, 0=automático)" +Profile="Perfil" +None="(Nenhum)" +DefaultEncoder="(Codificador padrão)" +UseBFrames="Usar B-Frames" + + diff --git a/plugins/mac-vth264/data/locale/zh-TW.ini b/plugins/mac-vth264/data/locale/zh-TW.ini new file mode 100644 index 0000000..a9984cd --- /dev/null +++ b/plugins/mac-vth264/data/locale/zh-TW.ini @@ -0,0 +1,14 @@ +VTH264EncHW="蘋果 VT H264 硬體編碼器" +VTH264EncSW="蘋果 VT H264 軟體編碼器" +VTEncoder="VideoToolbox 編碼器" +Bitrate="位元率" +UseMaxBitrate="限制位元率" +MaxBitrate="最大位元率" +MaxBitrateWindow="最大位元率窗 (秒)" +KeyframeIntervalSec="關鍵訊框間隔 (秒,0 = 自動)" +Profile="設定檔" +None="(無)" +DefaultEncoder="(預設編碼器)" +UseBFrames="使用 B 訊框" + + diff --git a/plugins/obs-ffmpeg/data/locale/fi-FI.ini b/plugins/obs-ffmpeg/data/locale/fi-FI.ini index 2ea579c..53cfea4 100644 --- a/plugins/obs-ffmpeg/data/locale/fi-FI.ini +++ b/plugins/obs-ffmpeg/data/locale/fi-FI.ini @@ -2,11 +2,11 @@ FFmpegOutput="FFmpeg ulostulo" FFmpegAAC="FFmpeg oletus AAC-enkooderi" Bitrate="Bitrate" Preset="Esiasetus" -RateControl="Nopeudensäädin" -KeyframeIntervalSec="Keyframe-väli (sec, 0=auto)" +RateControl="Rate Control -tila" +KeyframeIntervalSec="Keyframe-väli (sekunteina, 0=automaattinen)" Lossless="Häviötön" -NVENC.Use2Pass="Käytä kaksivaiheista enkoodausta" +NVENC.Use2Pass="Käytä Two-Pass enkoodausta" NVENC.Preset.default="Oletusarvo" NVENC.Preset.hq="Korkea laatu" NVENC.Preset.hp="Korkea suorituskyky" @@ -22,7 +22,7 @@ Looping="Toista jatkuvasti" Input="Sisääntulo" InputFormat="Sisääntulon muoto" ForceFormat="Pakota muodon muuntaminen" -HardwareDecode="Käytä laitteistoa purkamiseen, kun mahdollista" +HardwareDecode="Käytä laitteistotason purkua, kun mahdollista" ClearOnMediaEnd="Piilota lähde kun toisto päättyy" Advanced="Lisäasetukset" AudioBufferSize="Äänipuskurin koko (ruutua)" diff --git a/plugins/obs-ffmpeg/data/locale/he-IL.ini b/plugins/obs-ffmpeg/data/locale/he-IL.ini index 5523c3c..e5ed371 100644 --- a/plugins/obs-ffmpeg/data/locale/he-IL.ini +++ b/plugins/obs-ffmpeg/data/locale/he-IL.ini @@ -1,7 +1,20 @@ FFmpegOutput="פלט FFmpeg" FFmpegAAC="FFmpeg מקודד AAC ברירת מחדל" Bitrate="קצב ביטים" +Preset="קבוע מראש" +RateControl="בקרת קצב" +KeyframeIntervalSec="מרווח ערך ה keyframe בשניות (0=אוטומטי)" +Lossless="ללא אובדן נתונים" +NVENC.Use2Pass="השתמש בקידוד שני מעברים" +NVENC.Preset.default="ברירת מחדל" +NVENC.Preset.hq="איכות גבוהה" +NVENC.Preset.hp="ביצועים גבוהים" +NVENC.Preset.bd="בלוריי" +NVENC.Preset.ll="השהיה נמוכה" +NVENC.Preset.llhq="השהיה נמוכה איכות גבוהה" +NVENC.Preset.llhp="השהיה נמוכה ביצועים גבוהים" +NVENC.Level="רמה" FFmpegSource="מקור מדיה" LocalFile="קובץ מקומי" @@ -23,6 +36,10 @@ DiscardNonIntra="פריימים לא ביינים" DiscardNonKey="מסגרות שאינן מפתח" DiscardAll="כל הפריימים (זהירות!)" RestartWhenActivated="הפעל מחדש השמעה כאשר מקור הופך לפעיל" +ColorRange="טווח צבעים YUV" +ColorRange.Auto="אוטומטי" +ColorRange.Partial="חלקי" +ColorRange.Full="מלא" MediaFileFilter.AllMediaFiles="כל קבצי המדיה" diff --git a/plugins/obs-ffmpeg/data/locale/hr-HR.ini b/plugins/obs-ffmpeg/data/locale/hr-HR.ini index 4a8a315..a5a2b9c 100644 --- a/plugins/obs-ffmpeg/data/locale/hr-HR.ini +++ b/plugins/obs-ffmpeg/data/locale/hr-HR.ini @@ -2,7 +2,9 @@ FFmpegOutput="FFmpeg izlaz" FFmpegAAC="FFmpeg podrazumevani AAC enkoder" Bitrate="Protok" Preset="Šablon" +RateControl="Kontrola protoka" KeyframeIntervalSec="Interval ključnih frejmova (sekunde, 0=automatski)" +Lossless="Bez gubitka" NVENC.Use2Pass="Koristi enkoding duplog prolaza" NVENC.Preset.default="Podrazumevani" diff --git a/plugins/obs-ffmpeg/data/locale/it-IT.ini b/plugins/obs-ffmpeg/data/locale/it-IT.ini index 53864f9..2f7d252 100644 --- a/plugins/obs-ffmpeg/data/locale/it-IT.ini +++ b/plugins/obs-ffmpeg/data/locale/it-IT.ini @@ -1,7 +1,20 @@ FFmpegOutput="Uscita FFmpeg" FFmpegAAC="Codificatore FFmpeg predefinito AAC" Bitrate="Bitrate" +Preset="Preset" +RateControl="Controllo frequenza" +KeyframeIntervalSec="Intervallo Keyframe (secondi, 0=automatico)" +Lossless="Lossless" +NVENC.Use2Pass="Usa codifica in due passaggi" +NVENC.Preset.default="Predefinito" +NVENC.Preset.hq="Alta Qualità" +NVENC.Preset.hp="Alte Prestazioni" +NVENC.Preset.bd="Bluray" +NVENC.Preset.ll="Bassa latenza" +NVENC.Preset.llhq="Bassa latenza Alta Qualità" +NVENC.Preset.llhp="Bassa latenza ad Alte Prestazioni" +NVENC.Level="Livello" FFmpegSource="Origine multimediale" LocalFile="File locale" @@ -23,6 +36,14 @@ DiscardNonIntra="Frame non interposti" DiscardNonKey="Frame non di chiave" DiscardAll="Tutti i Frame (opzione per utenti più esperti)" RestartWhenActivated="Riattiva playback quando la fonte torna attiva" +ColorRange="Gamma di colore YUV" +ColorRange.Auto="Autom." +ColorRange.Partial="Parziale" +ColorRange.Full="Intero" +MediaFileFilter.AllMediaFiles="Tutti i file media" +MediaFileFilter.VideoFiles="File video" +MediaFileFilter.AudioFiles="File audio" +MediaFileFilter.AllFiles="Tutti i file" diff --git a/plugins/obs-ffmpeg/data/locale/nb-NO.ini b/plugins/obs-ffmpeg/data/locale/nb-NO.ini index 965826f..05c8d46 100644 --- a/plugins/obs-ffmpeg/data/locale/nb-NO.ini +++ b/plugins/obs-ffmpeg/data/locale/nb-NO.ini @@ -1,13 +1,26 @@ FFmpegOutput="FFmpeg utdata" FFmpegAAC="Standard FFmpeg AAC-koder" Bitrate="Bitrate" +Preset="Forhåndsinnstilling" +RateControl="Hastighetskontroll" +KeyframeIntervalSec="Nøkkelbildeintervall (sekunder, 0 = automatisk)" +Lossless="Tapsfri" +NVENC.Use2Pass="Bruk tostegskoding" +NVENC.Preset.default="Standard" +NVENC.Preset.hq="Høy kvalitet" +NVENC.Preset.hp="Høy ytelse" +NVENC.Preset.bd="Bluray" +NVENC.Preset.ll="Lav latens" +NVENC.Preset.llhq="Lav latens, høy kvalitet" +NVENC.Preset.llhp="Lav latens, høy ytelse" +NVENC.Level="Nivå" FFmpegSource="Mediekilde" LocalFile="Lokal fil" Looping="Repeter" -Input="Inndata" -InputFormat="Inndataformat" +Input="Inngang" +InputFormat="Inngangsformat" ForceFormat="Tving formatkonvertering" HardwareDecode="Bruk maskinvaredekoding når tilgjengelig" ClearOnMediaEnd="Skjul kilde når avspilling ender" @@ -22,6 +35,15 @@ DiscardBiDir="Toveisbilder" DiscardNonIntra="Non-intra bilder" DiscardNonKey="Ikkenøkkelbilder" DiscardAll="Alle bilder (forsiktig!)" +RestartWhenActivated="Start avspilling omigjen når kilde blir aktiv" +ColorRange="YUV fargerom" +ColorRange.Auto="Automatisk" +ColorRange.Partial="Delvis" +ColorRange.Full="Hel" +MediaFileFilter.AllMediaFiles="Alle mediefiler" +MediaFileFilter.VideoFiles="Videofiler" +MediaFileFilter.AudioFiles="Lydfiler" +MediaFileFilter.AllFiles="Alle filer" diff --git a/plugins/obs-ffmpeg/data/locale/pl-PL.ini b/plugins/obs-ffmpeg/data/locale/pl-PL.ini index 7b0381f..01f1f76 100644 --- a/plugins/obs-ffmpeg/data/locale/pl-PL.ini +++ b/plugins/obs-ffmpeg/data/locale/pl-PL.ini @@ -2,7 +2,9 @@ FFmpegOutput="Wyjście FFmpeg" FFmpegAAC="Domyślny enkoder AAC w FFmpeg" Bitrate="Przepływność bitowa" Preset="Profil ustawień" +RateControl="Typ przepływności" KeyframeIntervalSec="Odstęp między klatkami kluczowymi (sekundy, 0=automatyczny)" +Lossless="Bezstratny" NVENC.Use2Pass="Użyj enkodowania dwuprzebiegowego" NVENC.Preset.default="Domyślny" diff --git a/plugins/obs-ffmpeg/data/locale/pt-BR.ini b/plugins/obs-ffmpeg/data/locale/pt-BR.ini index f6f8c5c..b869ea9 100644 --- a/plugins/obs-ffmpeg/data/locale/pt-BR.ini +++ b/plugins/obs-ffmpeg/data/locale/pt-BR.ini @@ -1,7 +1,19 @@ FFmpegOutput="Saída do FFmpeg" FFmpegAAC="Codificador AAC Padrão do FFmpeg" Bitrate="Taxa de Bits" +Preset="Predefinição" +RateControl="Controle de Taxa" +KeyframeIntervalSec="Intervalo do keyframe (segundos, 0=automático)" +Lossless="Sem perdas" +NVENC.Preset.default="Predefinido" +NVENC.Preset.hq="Alta Qualidade" +NVENC.Preset.hp="Alto Desempenho" +NVENC.Preset.bd="Bluray" +NVENC.Preset.ll="Baixa latência" +NVENC.Preset.llhq="Baixa latência Alta Qualidade" +NVENC.Preset.llhp="Baixa latência Alto Desempenho" +NVENC.Level="Nível" FFmpegSource="Fonte de mídia" LocalFile="Arquivo Local" diff --git a/plugins/obs-ffmpeg/data/locale/pt-PT.ini b/plugins/obs-ffmpeg/data/locale/pt-PT.ini index 30f5001..826d9ce 100644 --- a/plugins/obs-ffmpeg/data/locale/pt-PT.ini +++ b/plugins/obs-ffmpeg/data/locale/pt-PT.ini @@ -1,7 +1,19 @@ FFmpegOutput="Saída de FFmpeg" FFmpegAAC="Codificador AAC padrão do FFmpeg" Bitrate="Bitrate" +Preset="Predefinição" +RateControl="Controle de Taxa" +KeyframeIntervalSec="Intervalo do keyframe (segundos, 0=automático)" +Lossless="Sem perdas" +NVENC.Preset.default="Predefinido" +NVENC.Preset.hq="Alta Qualidade" +NVENC.Preset.hp="Alto Desempenho" +NVENC.Preset.bd="Bluray" +NVENC.Preset.ll="Baixa latência" +NVENC.Preset.llhq="Baixa latência Alta Qualidade" +NVENC.Preset.llhp="Baixa latência Alto Desempenho" +NVENC.Level="Nível" FFmpegSource="Fonte de multimédia" LocalFile="Ficheiro local" @@ -22,6 +34,15 @@ DiscardBiDir="Fotogramas bidirecionais" DiscardNonIntra="Fotogramas não internos" DiscardNonKey="Fotogramas não registados" DiscardAll="Todos os fotogramas (cuidado!)" +RestartWhenActivated="Reiniciar reprodução quando a fonte se torna ativo" +ColorRange="Gama de cor YUV" +ColorRange.Auto="Auto" +ColorRange.Partial="Parcial" +ColorRange.Full="Completo" +MediaFileFilter.AllMediaFiles="Todos os Arquivos de Media" +MediaFileFilter.VideoFiles="Arquivos de Vídeo" +MediaFileFilter.AudioFiles="Arquivos de Áudio" +MediaFileFilter.AllFiles="Todos os ficheiros" diff --git a/plugins/obs-ffmpeg/data/locale/ru-RU.ini b/plugins/obs-ffmpeg/data/locale/ru-RU.ini index 6cd426b..07b3941 100644 --- a/plugins/obs-ffmpeg/data/locale/ru-RU.ini +++ b/plugins/obs-ffmpeg/data/locale/ru-RU.ini @@ -2,7 +2,9 @@ FFmpegOutput="Вывод FFmpeg" FFmpegAAC="Стандартный AAC-кодер FFmpeg" Bitrate="Битрейт" Preset="Пресет" +RateControl="Управление битрейтом" KeyframeIntervalSec="Интервал ключевых кадров (сек, 0=авто)" +Lossless="Без потерь" NVENC.Use2Pass="Использовать двухпроходное кодирование" NVENC.Preset.default="По умолчанию" diff --git a/plugins/obs-ffmpeg/data/locale/sr-CS.ini b/plugins/obs-ffmpeg/data/locale/sr-CS.ini index 4a8a315..a5a2b9c 100644 --- a/plugins/obs-ffmpeg/data/locale/sr-CS.ini +++ b/plugins/obs-ffmpeg/data/locale/sr-CS.ini @@ -2,7 +2,9 @@ FFmpegOutput="FFmpeg izlaz" FFmpegAAC="FFmpeg podrazumevani AAC enkoder" Bitrate="Protok" Preset="Šablon" +RateControl="Kontrola protoka" KeyframeIntervalSec="Interval ključnih frejmova (sekunde, 0=automatski)" +Lossless="Bez gubitka" NVENC.Use2Pass="Koristi enkoding duplog prolaza" NVENC.Preset.default="Podrazumevani" diff --git a/plugins/obs-ffmpeg/data/locale/sr-SP.ini b/plugins/obs-ffmpeg/data/locale/sr-SP.ini index f4606c7..1af21ed 100644 --- a/plugins/obs-ffmpeg/data/locale/sr-SP.ini +++ b/plugins/obs-ffmpeg/data/locale/sr-SP.ini @@ -2,7 +2,9 @@ FFmpegOutput="FFmpeg излаз" FFmpegAAC="FFmpeg подразумевани AAC енкодер" Bitrate="Проток" Preset="Шаблон" +RateControl="Контрола протока" KeyframeIntervalSec="Интервал кључних фрејмова (секунде, 0=аутоматски)" +Lossless="Без губитка" NVENC.Use2Pass="Користи енкодинг дуплог пролаза" NVENC.Preset.default="Подразумевани" diff --git a/plugins/obs-ffmpeg/data/locale/sv-SE.ini b/plugins/obs-ffmpeg/data/locale/sv-SE.ini index 0d1426f..8f2485d 100644 --- a/plugins/obs-ffmpeg/data/locale/sv-SE.ini +++ b/plugins/obs-ffmpeg/data/locale/sv-SE.ini @@ -2,6 +2,8 @@ FFmpegOutput="FFmpeg-utmatning" FFmpegAAC="AAC-kodare (FFmpeg standard)" Bitrate="Bithastighet" Preset="Förinställning" +RateControl="Hastighetskontroll" +Lossless="Förlustfri" NVENC.Use2Pass="Använd tvåpassavkodning" NVENC.Preset.default="Standard" diff --git a/plugins/obs-ffmpeg/data/locale/zh-TW.ini b/plugins/obs-ffmpeg/data/locale/zh-TW.ini index 447224c..efe35cf 100644 --- a/plugins/obs-ffmpeg/data/locale/zh-TW.ini +++ b/plugins/obs-ffmpeg/data/locale/zh-TW.ini @@ -1,7 +1,20 @@ FFmpegOutput="FFmpeg 輸出" FFmpegAAC="FFmpeg 預設 AAC 編碼器" -Bitrate="流量" +Bitrate="位元率" +Preset="預置" +RateControl="位元率控制" +KeyframeIntervalSec="關鍵訊框間隔 (秒,0 = 自動)" +Lossless="無損" +NVENC.Use2Pass="使用 Two-Pass 編碼" +NVENC.Preset.default="預設" +NVENC.Preset.hq="高品質" +NVENC.Preset.hp="高性能" +NVENC.Preset.bd="藍光" +NVENC.Preset.ll="低延遲" +NVENC.Preset.llhq="低延遲高品質" +NVENC.Preset.llhp="低延遲高性能" +NVENC.Level="级别" FFmpegSource="媒體來源" LocalFile="本機檔案" @@ -12,15 +25,21 @@ ForceFormat="強制格式轉換" HardwareDecode="盡可能使用硬體解碼" ClearOnMediaEnd="當播放結束時隱藏來源" Advanced="進階" -AudioBufferSize="音訊緩衝區大小 (幀)" -VideoBufferSize="影像緩衝區大小 (幀)" -FrameDropping="掉幀程度" +AudioBufferSize="音訊緩衝區大小 (訊框)" +VideoBufferSize="影像緩衝區大小 (訊框)" +FrameDropping="丟棄訊框级别" DiscardNone="無" DiscardDefault="預設 (無效封包)" -DiscardNonRef="非參考幀" -DiscardNonIntra="非內部框架" -DiscardNonKey="非關鍵幀" -DiscardAll="所有幀(小心!)" +DiscardNonRef="非參考訊框" +DiscardBiDir="雙向訊框" +DiscardNonIntra="非內部訊框" +DiscardNonKey="非關鍵訊框" +DiscardAll="所有訊框(小心!)" +RestartWhenActivated="當來源可使用時重新播放" +ColorRange="YUV 色彩空間" +ColorRange.Auto="自動" +ColorRange.Partial="部分" +ColorRange.Full="全部" MediaFileFilter.AllMediaFiles="所有媒體檔案" diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index 404ad7f..65212bc 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "ffmpeg-mux/ffmpeg-mux.h" #include @@ -33,10 +34,12 @@ struct ffmpeg_muxer { obs_output_t *output; os_process_pipe_t *pipe; + int64_t stop_ts; struct dstr path; bool sent_headers; - bool active; - bool capturing; + volatile bool active; + volatile bool stopping; + volatile bool capturing; }; static const char *ffmpeg_mux_getname(void *unused) @@ -72,6 +75,21 @@ static void *ffmpeg_mux_create(obs_data_t *settings, obs_output_t *output) #define FFMPEG_MUX "ffmpeg-mux" #endif +static inline bool capturing(struct ffmpeg_muxer *stream) +{ + return os_atomic_load_bool(&stream->capturing); +} + +static inline bool stopping(struct ffmpeg_muxer *stream) +{ + return os_atomic_load_bool(&stream->stopping); +} + +static inline bool active(struct ffmpeg_muxer *stream) +{ + return os_atomic_load_bool(&stream->active); +} + /* TODO: allow codecs other than h264 whenever we start using them */ static void add_video_encoder_params(struct ffmpeg_muxer *stream, @@ -223,8 +241,8 @@ static bool ffmpeg_mux_start(void *data) } /* write headers and start capture */ - stream->active = true; - stream->capturing = true; + os_atomic_set_bool(&stream->active, true); + os_atomic_set_bool(&stream->capturing, true); obs_output_begin_data_capture(stream->output, 0); info("Writing file '%s'...", stream->path.array); @@ -235,29 +253,32 @@ static int deactivate(struct ffmpeg_muxer *stream) { int ret = -1; - if (stream->active) { + if (active(stream)) { ret = os_process_pipe_destroy(stream->pipe); stream->pipe = NULL; - stream->active = false; - stream->sent_headers = false; + os_atomic_set_bool(&stream->active, false); + os_atomic_set_bool(&stream->sent_headers, false); info("Output of file '%s' stopped", stream->path.array); } + if (stopping(stream)) + obs_output_end_data_capture(stream->output); + + os_atomic_set_bool(&stream->stopping, false); return ret; } -static void ffmpeg_mux_stop(void *data) +static void ffmpeg_mux_stop(void *data, uint64_t ts) { struct ffmpeg_muxer *stream = data; - if (stream->capturing) { - obs_output_end_data_capture(stream->output); - stream->capturing = false; + if (capturing(stream)) { + stream->stop_ts = (int64_t)ts / 1000LL; + os_atomic_set_bool(&stream->stopping, true); + os_atomic_set_bool(&stream->capturing, false); } - - deactivate(stream); } static void signal_failure(struct ffmpeg_muxer *stream) @@ -271,7 +292,7 @@ static void signal_failure(struct ffmpeg_muxer *stream) } obs_output_signal_stop(stream->output, code); - stream->capturing = false; + os_atomic_set_bool(&stream->capturing, false); } static bool write_packet(struct ffmpeg_muxer *stream, @@ -358,7 +379,7 @@ static void ffmpeg_mux_data(void *data, struct encoder_packet *packet) { struct ffmpeg_muxer *stream = data; - if (!stream->active) + if (!active(stream)) return; if (!stream->sent_headers) { @@ -368,6 +389,13 @@ static void ffmpeg_mux_data(void *data, struct encoder_packet *packet) stream->sent_headers = true; } + if (stopping(stream)) { + if (packet->sys_dts_usec >= stream->stop_ts) { + deactivate(stream); + return; + } + } + write_packet(stream, packet); } diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c index 9c38143..5ad1daf 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c @@ -160,6 +160,8 @@ static bool nvenc_update(void *data, obs_data_t *settings) nvenc_video_info(enc, &info); av_opt_set_int(enc->context->priv_data, "cbr", false, 0); + av_opt_set(enc->context->priv_data, "preset", preset, 0); + if (astrcmpi(rc, "cqp") == 0) { bitrate = 0; enc->context->global_quality = cqp; @@ -171,7 +173,7 @@ static bool nvenc_update(void *data, obs_data_t *settings) bool hp = (astrcmpi(preset, "hp") == 0 || astrcmpi(preset, "llhp") == 0); - av_opt_set(enc->context->priv_data, "profile", + av_opt_set(enc->context->priv_data, "preset", hp ? "losslesshp" : "lossless", 0); } else if (astrcmpi(rc, "vbr") != 0) { /* CBR by default */ @@ -182,7 +184,6 @@ static bool nvenc_update(void *data, obs_data_t *settings) } - av_opt_set(enc->context->priv_data, "preset", preset, 0); av_opt_set(enc->context->priv_data, "level", level, 0); av_opt_set_int(enc->context->priv_data, "2pass", twopass, 0); av_opt_set_int(enc->context->priv_data, "gpu", gpu, 0); diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 87eac91..f08bd7c 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -89,6 +89,11 @@ struct ffmpeg_output { bool connecting; pthread_t start_thread; + uint64_t audio_start_ts; + uint64_t video_start_ts; + uint64_t stop_ts; + volatile bool stopping; + bool write_thread_active; pthread_mutex_t write_mutex; pthread_t write_thread; @@ -237,6 +242,7 @@ static bool create_video_stream(struct ffmpeg_data *data) context->pix_fmt = closest_format; context->colorspace = data->config.color_space; context->color_range = data->config.color_range; + context->thread_count = 0; data->video->time_base = context->time_base; @@ -548,6 +554,11 @@ fail: /* ------------------------------------------------------------------------- */ +static inline bool stopping(struct ffmpeg_output *output) +{ + return os_atomic_load_bool(&output->stopping); +} + static const char *ffmpeg_output_getname(void *unused) { UNUSED_PARAMETER(unused); @@ -588,7 +599,7 @@ fail: return NULL; } -static void ffmpeg_output_stop(void *data); +static void ffmpeg_output_full_stop(void *data); static void ffmpeg_deactivate(struct ffmpeg_output *output); static void ffmpeg_output_destroy(void *data) @@ -599,7 +610,7 @@ static void ffmpeg_output_destroy(void *data) if (output->connecting) pthread_join(output->start_thread, NULL); - ffmpeg_output_stop(output); + ffmpeg_output_full_stop(output); pthread_mutex_destroy(&output->write_mutex); os_sem_destroy(output->write_sem); @@ -647,6 +658,8 @@ static void receive_video(void *param, struct video_data *frame) av_init_packet(&packet); + if (!output->video_start_ts) + output->video_start_ts = frame->timestamp; if (!data->start_timestamp) data->start_timestamp = frame->timestamp; @@ -768,6 +781,8 @@ static bool prepare_audio(struct ffmpeg_data *data, return false; cutoff = data->start_timestamp - frame->timestamp; + output->timestamp += cutoff; + cutoff = cutoff * (uint64_t)data->audio_samplerate / 1000000000; @@ -797,6 +812,9 @@ static void receive_audio(void *param, struct audio_data *frame) if (!prepare_audio(data, frame, &in)) return; + if (!output->audio_start_ts) + output->audio_start_ts = in.timestamp; + frame_size_bytes = (size_t)data->frame_size * data->audio_size; for (size_t i = 0; i < data->audio_planes; i++) @@ -812,6 +830,26 @@ static void receive_audio(void *param, struct audio_data *frame) } } +static uint64_t get_packet_sys_dts(struct ffmpeg_output *output, + AVPacket *packet) +{ + struct ffmpeg_data *data = &output->ff_data; + uint64_t start_ts; + + AVRational time_base; + + if (data->video && data->video->index == packet->stream_index) { + time_base = data->video->time_base; + start_ts = output->video_start_ts; + } else { + time_base = data->audio->time_base; + start_ts = output->audio_start_ts; + } + + return start_ts + (uint64_t)av_rescale_q(packet->dts, + time_base, (AVRational){1, 1000000000}); +} + static int process_packet(struct ffmpeg_output *output) { AVPacket packet; @@ -834,6 +872,14 @@ static int process_packet(struct ffmpeg_output *output) packet.size, packet.flags, packet.stream_index, output->packets.num);*/ + if (stopping(output)) { + uint64_t sys_ts = get_packet_sys_dts(output, &packet); + if (sys_ts >= output->stop_ts) { + ffmpeg_output_full_stop(output); + return 0; + } + } + ret = av_interleaved_write_frame(output->ff_data.output, &packet); if (ret < 0) { av_free_packet(&packet); @@ -954,7 +1000,7 @@ static bool try_connect(struct ffmpeg_output *output) if (ret != 0) { blog(LOG_WARNING, "ffmpeg_output_start: failed to create write " "thread."); - ffmpeg_output_stop(output); + ffmpeg_output_full_stop(output); return false; } @@ -985,11 +1031,15 @@ static bool ffmpeg_output_start(void *data) if (output->connecting) return false; + os_atomic_set_bool(&output->stopping, false); + output->audio_start_ts = 0; + output->video_start_ts = 0; + ret = pthread_create(&output->start_thread, NULL, start_thread, output); return (output->connecting = (ret == 0)); } -static void ffmpeg_output_stop(void *data) +static void ffmpeg_output_full_stop(void *data) { struct ffmpeg_output *output = data; @@ -999,6 +1049,20 @@ static void ffmpeg_output_stop(void *data) } } +static void ffmpeg_output_stop(void *data, uint64_t ts) +{ + struct ffmpeg_output *output = data; + + if (output->active) { + if (ts == 0) { + ffmpeg_output_full_stop(output); + } else { + os_atomic_set_bool(&output->stopping, true); + output->stop_ts = ts; + } + } +} + static void ffmpeg_deactivate(struct ffmpeg_output *output) { if (output->write_thread_active) { diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index 3d71f9c..253be97 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -1,10 +1,29 @@ project(obs-filters) +find_package(Libspeexdsp QUIET) +if(LIBSPEEXDSP_FOUND) + set(obs-filters_LIBSPEEXDSP_SOURCES + noise-suppress-filter.c) + set(obs-filters_LIBSPEEXDSP_LIBRARIES + ${LIBSPEEXDSP_LIBRARIES}) +else() + message(STATUS "Speexdsp library not found, speexdsp filters disabled") +endif() + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/obs-filters-config.h.in" + "${CMAKE_BINARY_DIR}/plugins/obs-filters/config/obs-filters-config.h") + +set(obs-filters_config_HEADERS + "${CMAKE_BINARY_DIR}/plugins/obs-filters/config/obs-filters-config.h") +include_directories(${LIBSPEEXDSP_INCLUDE_DIRS} + "${CMAKE_BINARY_DIR}/plugins/obs-filters/config") + set(obs-filters_SOURCES obs-filters.c color-filter.c async-delay-filter.c crop-filter.c + scale-filter.c scroll-filter.c chroma-key-filter.c color-key-filter.c @@ -14,8 +33,11 @@ set(obs-filters_SOURCES mask-filter.c) add_library(obs-filters MODULE - ${obs-filters_SOURCES}) + ${obs-filters_SOURCES} + ${obs-filters_config_HEADERS} + ${obs-filters_LIBSPEEXDSP_SOURCES}) target_link_libraries(obs-filters - libobs) + libobs + ${obs-filters_LIBSPEEXDSP_LIBRARIES}) install_obs_plugin_with_data(obs-filters data) diff --git a/plugins/obs-filters/async-delay-filter.c b/plugins/obs-filters/async-delay-filter.c index cbbd866..3baff63 100644 --- a/plugins/obs-filters/async-delay-filter.c +++ b/plugins/obs-filters/async-delay-filter.c @@ -115,7 +115,7 @@ static obs_properties_t *async_delay_filter_properties(void *data) obs_properties_t *props = obs_properties_create(); obs_properties_add_int(props, SETTING_DELAY_MS, TEXT_DELAY_MS, - 0, 6000, 1); + 0, 20000, 1); UNUSED_PARAMETER(data); return props; diff --git a/plugins/obs-filters/crop-filter.c b/plugins/obs-filters/crop-filter.c index 11070fa..09839ec 100644 --- a/plugins/obs-filters/crop-filter.c +++ b/plugins/obs-filters/crop-filter.c @@ -12,10 +12,10 @@ struct crop_filter_data { int right; int top; int bottom; - uint32_t abs_cx; - uint32_t abs_cy; - uint32_t width; - uint32_t height; + int abs_cx; + int abs_cy; + int width; + int height; bool absolute; struct vec2 mul_val; @@ -108,13 +108,13 @@ static obs_properties_t *crop_filter_properties(void *data) obs_property_set_modified_callback(p, relative_clicked); obs_properties_add_int(props, "left", obs_module_text("Crop.Left"), - 0, 8192, 1); + -8192, 8192, 1); obs_properties_add_int(props, "top", obs_module_text("Crop.Top"), - 0, 8192, 1); + -8192, 8192, 1); obs_properties_add_int(props, "right", obs_module_text("Crop.Right"), - 0, 8192, 1); + -8192, 8192, 1); obs_properties_add_int(props, "bottom", obs_module_text("Crop.Bottom"), - 0, 8192, 1); + -8192, 8192, 1); obs_properties_add_int(props, "cx", obs_module_text("Crop.Width"), 0, 8192, 1); obs_properties_add_int(props, "cy", obs_module_text("Crop.Height"), @@ -135,37 +135,26 @@ static void calc_crop_dimensions(struct crop_filter_data *filter, obs_source_t *target = obs_filter_get_target(filter->context); uint32_t width; uint32_t height; - uint32_t total; if (!target) { width = 0; height = 0; + return; } else { width = obs_source_get_base_width(target); height = obs_source_get_base_height(target); } if (filter->absolute) { - uint32_t max_abs_cx = (filter->left + filter->abs_cx); - if (max_abs_cx > width) max_abs_cx = width; - max_abs_cx -= filter->left; - - total = max_abs_cx < width ? (width - max_abs_cx) : 0; + filter->width = filter->abs_cx; + filter->height = filter->abs_cy; } else { - total = filter->left + filter->right; + filter->width = (int)width - filter->left - filter->right; + filter->height = (int)height - filter->top - filter->bottom; } - filter->width = total > width ? 0 : (width - total); - if (filter->absolute) { - uint32_t max_abs_cy = (filter->top + filter->abs_cy); - if (max_abs_cy > height) max_abs_cy = height; - max_abs_cy -= filter->top; - - total = max_abs_cy < height ? (height - max_abs_cy) : 0; - } else { - total = filter->top + filter->bottom; - } - filter->height = total > height ? 0 : (height - total); + if (filter->width < 1) filter->width = 1; + if (filter->height < 1) filter->height = 1; if (width && filter->width) { mul_val->x = (float)filter->width / (float)width; @@ -209,13 +198,13 @@ static void crop_filter_render(void *data, gs_effect_t *effect) static uint32_t crop_filter_width(void *data) { struct crop_filter_data *crop = data; - return crop->width; + return (uint32_t)crop->width; } static uint32_t crop_filter_height(void *data) { struct crop_filter_data *crop = data; - return crop->height; + return (uint32_t)crop->height; } struct obs_source_info crop_filter = { diff --git a/plugins/obs-filters/data/crop_filter.effect b/plugins/obs-filters/data/crop_filter.effect index 3c0e9f3..5be66ad 100644 --- a/plugins/obs-filters/data/crop_filter.effect +++ b/plugins/obs-filters/data/crop_filter.effect @@ -6,8 +6,9 @@ uniform float2 add_val; sampler_state textureSampler { Filter = Linear; - AddressU = Wrap; - AddressV = Wrap; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; }; struct VertData { diff --git a/plugins/obs-filters/data/locale/ar-SA.ini b/plugins/obs-filters/data/locale/ar-SA.ini index d1c53ff..c3d934e 100644 --- a/plugins/obs-filters/data/locale/ar-SA.ini +++ b/plugins/obs-filters/data/locale/ar-SA.ini @@ -1,4 +1,4 @@ -CropFilter="إقتطاع" +ColorFilter="التصحيح اللوني" ScrollFilter="التمرير" DelayMs="التأخير (مللي ثانية)" Type="النّوع" diff --git a/plugins/obs-filters/data/locale/ca-ES.ini b/plugins/obs-filters/data/locale/ca-ES.ini index b418a1e..adc9e29 100644 --- a/plugins/obs-filters/data/locale/ca-ES.ini +++ b/plugins/obs-filters/data/locale/ca-ES.ini @@ -1,11 +1,12 @@ ColorFilter="Correcció de color" MaskFilter="Màscara d'imatge o barreja" AsyncDelayFilter="Retard del vídeo (asíncron)" -CropFilter="Escapça" +CropFilter="Escapça/Encoixinar" ScrollFilter="Desplaçament" ChromaKeyFilter="Clau croma" ColorKeyFilter="Clau de color" SharpnessFilter="Agudesa" +ScaleFilter="Escala/Relació d'Aspecte" NoiseGate="Porta de soroll" Gain="Guany" DelayMs="Retard (en mil·lisegons)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Temps de mantenir (mil·lisegons)" NoiseGate.ReleaseTime="Temps d'alliberar (mil·lisegons)" Gain.GainDB="Guany (dB)" StretchImage="Expandir imatge (descarta la relació d'aspecte d'imatge)" +Resolution="Resolució" +None="Cap" +ScaleFiltering="Escala de filtratge" +ScaleFiltering.Point="Punt" +ScaleFiltering.Bilinear="Bilineal" +ScaleFiltering.Bicubic="Bicúbic" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/cs-CZ.ini b/plugins/obs-filters/data/locale/cs-CZ.ini index f2891c6..28a68de 100644 --- a/plugins/obs-filters/data/locale/cs-CZ.ini +++ b/plugins/obs-filters/data/locale/cs-CZ.ini @@ -1,11 +1,12 @@ ColorFilter="Korekce barev" MaskFilter="Maska obrazu/prolnutí" AsyncDelayFilter="Zpoždění obrazu" -CropFilter="Oříznutí" +CropFilter="Oříznutí/odsazení" ScrollFilter="Rolování" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Klíč barvy" SharpnessFilter="Ostření" +ScaleFilter="Škálování/poměr stran" NoiseGate="Šumová brána" Gain="Zisk" DelayMs="Zpoždění (ms)" @@ -34,7 +35,7 @@ Crop.Top="Nahoře" Crop.Bottom="Dole" Crop.Width="Šířka" Crop.Height="Výška" -Crop.Relative="Relativní" +Crop.Relative="Relativně" ScrollFilter.SpeedX="Rychlost - vodorovně" ScrollFilter.SpeedY="Rychlost - svisle" ScrollFilter.LimitWidth="Omezit šířku" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Čas držení (ms)" NoiseGate.ReleaseTime="Čas uvolnění (ms)" Gain.GainDB="Zisk (dB)" StretchImage="Roztáhnout obrázek (ignorovat poměr stran)" +Resolution="Rozlišení" +None="Žádné" +ScaleFiltering="Filtrování rozsahu" +ScaleFiltering.Point="Bod" +ScaleFiltering.Bilinear="Bilineární" +ScaleFiltering.Bicubic="Bikubický" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/da-DK.ini b/plugins/obs-filters/data/locale/da-DK.ini index da181b5..11a7dec 100644 --- a/plugins/obs-filters/data/locale/da-DK.ini +++ b/plugins/obs-filters/data/locale/da-DK.ini @@ -1,7 +1,6 @@ ColorFilter="Farvekorrektion" MaskFilter="Billede maske/blanding" AsyncDelayFilter="Video forsinkelse (asynkron)" -CropFilter="Beskær" ScrollFilter="Rul" ChromaKeyFilter="Chroma nøgle" ColorKeyFilter="Farvenøgle" diff --git a/plugins/obs-filters/data/locale/de-DE.ini b/plugins/obs-filters/data/locale/de-DE.ini index 014f0e4..2ad2cdc 100644 --- a/plugins/obs-filters/data/locale/de-DE.ini +++ b/plugins/obs-filters/data/locale/de-DE.ini @@ -1,11 +1,12 @@ ColorFilter="Farbkorrektur" MaskFilter="Bild Maske/Blend" AsyncDelayFilter="Videoverzögerung (Asynchron)" -CropFilter="Zuschneiden" +CropFilter="Zuschneiden/Pad" ScrollFilter="Bewegung" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" SharpnessFilter="Schärfen" +ScaleFilter="Skalierung/Seitenverhältnis" NoiseGate="Noise Gate" Gain="Gain" DelayMs="Verzögerung (Millisekunden)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Hold-Zeit (Millisekunden)" NoiseGate.ReleaseTime="Release-Zeit (Millisekunden)" Gain.GainDB="Gain (dB)" StretchImage="Bild strecken (Bildseitenverhältnis verwerfen)" +Resolution="Auflösung" +None="Keine" +ScaleFiltering="Skalierungsfilterung" +ScaleFiltering.Point="Point" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/el-GR.ini b/plugins/obs-filters/data/locale/el-GR.ini index 38268a4..dce045b 100644 --- a/plugins/obs-filters/data/locale/el-GR.ini +++ b/plugins/obs-filters/data/locale/el-GR.ini @@ -1,6 +1,5 @@ ColorFilter="Διόρθωση Χρώματος" AsyncDelayFilter="Καθυστέρηση Βίντεο (Ασύγχρονη)" -CropFilter="Περικοπή" ScrollFilter="Κύλιση" ChromaKeyFilter="Κλειδί Chroma" ColorKeyFilter="Κλειδί Χρώματος" diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index a3d9cca..95b8c43 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -1,12 +1,14 @@ ColorFilter="Color Correction" MaskFilter="Image Mask/Blend" AsyncDelayFilter="Video Delay (Async)" -CropFilter="Crop" +CropFilter="Crop/Pad" ScrollFilter="Scroll" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" SharpnessFilter="Sharpen" +ScaleFilter="Scaling/Aspect Ratio" NoiseGate="Noise Gate" +NoiseSuppress="Noise Suppression" Gain="Gain" DelayMs="Delay (milliseconds)" Type="Type" @@ -51,3 +53,11 @@ NoiseGate.HoldTime="Hold Time (milliseconds)" NoiseGate.ReleaseTime="Release Time (milliseconds)" Gain.GainDB="Gain (dB)" StretchImage="Stretch Image (discard image aspect ratio)" +Resolution="Resolution" +None="None" +ScaleFiltering="Scale Filtering" +ScaleFiltering.Point="Point" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" +NoiseSuppress.SuppressLevel="Suppression Level (dB)" diff --git a/plugins/obs-filters/data/locale/es-ES.ini b/plugins/obs-filters/data/locale/es-ES.ini index 6789683..9654c46 100644 --- a/plugins/obs-filters/data/locale/es-ES.ini +++ b/plugins/obs-filters/data/locale/es-ES.ini @@ -1,11 +1,12 @@ ColorFilter="Corrección de color" MaskFilter="Imagen máscara/mezcla" AsyncDelayFilter="Demora de Video (asincróno)" -CropFilter="Filtro de Recorte" +CropFilter="Recortar/Acolchar" ScrollFilter="desplazamiento" ChromaKeyFilter="Fondro croma" ColorKeyFilter="Filtro de color" SharpnessFilter="Filtro de enfoque" +ScaleFilter="Escala/Relación de Aspecto" NoiseGate="Puerta anti-ruidos" Gain="Ganancia" DelayMs="Retardo (milisegundos)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Tiempo (en milisegundos) de espera" NoiseGate.ReleaseTime="Tiempo (en milisegundos) de liberacion" Gain.GainDB="Ganancia (dB)" StretchImage="Expandir imagen (descartar relación de aspecto de imagen)" +Resolution="Resolución" +None="Ninguno" +ScaleFiltering="Escala de filtrado" +ScaleFiltering.Point="Punto" +ScaleFiltering.Bilinear="Bilineal" +ScaleFiltering.Bicubic="Bicúbico" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/eu-ES.ini b/plugins/obs-filters/data/locale/eu-ES.ini index 1b7c39c..3523c20 100644 --- a/plugins/obs-filters/data/locale/eu-ES.ini +++ b/plugins/obs-filters/data/locale/eu-ES.ini @@ -1,11 +1,12 @@ ColorFilter="Kolore-zuzenketa" MaskFilter="Irudi maskara/nahasketa" AsyncDelayFilter="Bideo atzerapena (Async)" -CropFilter="Moztu" +CropFilter="Moztu/Bete" ScrollFilter="Korritu" ChromaKeyFilter="Kroma gakoa" ColorKeyFilter="Kolore gakoa" SharpnessFilter="Enfokea" +ScaleFilter="Eskala/Aspektu-erlazioa" NoiseGate="Zarata atalasea" Gain="Irabazia" DelayMs="Atzerapena (milisegundo)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Euste denbora (milisegundo)" NoiseGate.ReleaseTime="Askatze denbora (milisegundo)" Gain.GainDB="Irabazia (dB)" StretchImage="Luzatu irudia (baztertu irudiaren aspektu-erlazioa)" +Resolution="Bereizmena" +None="Ezer ez" +ScaleFiltering="Iragazketa-eskala" +ScaleFiltering.Point="Puntua" +ScaleFiltering.Bilinear="Bilineala" +ScaleFiltering.Bicubic="Bikubikoa" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/fi-FI.ini b/plugins/obs-filters/data/locale/fi-FI.ini index 0dbcee5..c16e6fe 100644 --- a/plugins/obs-filters/data/locale/fi-FI.ini +++ b/plugins/obs-filters/data/locale/fi-FI.ini @@ -1,14 +1,13 @@ ColorFilter="Värinkorjaus" MaskFilter="Kuvamaski/Sekoitus" -AsyncDelayFilter="Kuvan viide (Async)" -CropFilter="Rajaa" +AsyncDelayFilter="Kuvan viive (Async)" ScrollFilter="Vieritä" -ChromaKeyFilter="Chroma Key" +ChromaKeyFilter="Läpinäkyvä tausta" ColorKeyFilter="Väriavain" SharpnessFilter="Terävöitä" NoiseGate="Noise Gate" Gain="Vahvistus" -DelayMs="Viive (MS)" +DelayMs="Viive (millisekuntia)" Type="Tyyppi" MaskBlendType.MaskColor="Alpha-maski (värikanava)" MaskBlendType.MaskAlpha="Alpha-maski (alfa-kanava)" @@ -40,9 +39,9 @@ ScrollFilter.SpeedY="Pystynopeus" ScrollFilter.LimitWidth="Rajoita leveyttä" ScrollFilter.LimitHeight="Rajoita korkeutta" CustomColor="Mukautettu väri" -Red="Red" -Green="Green" -Blue="Blue" +Red="Punainen" +Green="Vihreä" +Blue="Sininen" Magenta="Magenta" NoiseGate.OpenThreshold="Avautumiskynnys (dB)" NoiseGate.CloseThreshold="Sulkeutumiskynnys (dB)" @@ -50,5 +49,5 @@ NoiseGate.AttackTime="Reagointiviive (millisekuntia)" NoiseGate.HoldTime="Pitoaika (millisekuntia)" NoiseGate.ReleaseTime="Vapautumisaika (millisekuntia)" Gain.GainDB="Vahvistus (dB)" -StretchImage="Venytä kuvaa (hylkää kuvasuhde)" +StretchImage="Venytä kuvaa (Ohita kuvasuhde)" diff --git a/plugins/obs-filters/data/locale/fr-FR.ini b/plugins/obs-filters/data/locale/fr-FR.ini index f44a0a0..8aebc66 100644 --- a/plugins/obs-filters/data/locale/fr-FR.ini +++ b/plugins/obs-filters/data/locale/fr-FR.ini @@ -1,14 +1,15 @@ ColorFilter="Corrections colorimétrique" MaskFilter="Masque d'image/mélange" -AsyncDelayFilter="Délai vidéo (async.)" -CropFilter="Rogner" +AsyncDelayFilter="Retard vidéo (async.)" +CropFilter="Rogner / Encadrer" ScrollFilter="Défilement" ChromaKeyFilter="Clé chromatique" ColorKeyFilter="Couleur d'incrustation" SharpnessFilter="Accentuer" +ScaleFilter="Mise à l’échelle / Ratio d'affichage" NoiseGate="Noise Gate" Gain="Gain" -DelayMs="Délai (en millisecondes)" +DelayMs="Retard (en millisecondes)" Type="Type " MaskBlendType.MaskColor="Masque alpha (canal de couleur)" MaskBlendType.MaskAlpha="Masque alpha (canal alpha)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Temps de maintien (millisecondes)" NoiseGate.ReleaseTime="Temps d'arrêt (millisecondes)" Gain.GainDB="Gain (dB)" StretchImage="Étirer l'Image (ignorer ses proportions)" +Resolution="Résolution" +None="Aucune" +ScaleFiltering="Échelle de filtrage" +ScaleFiltering.Point="Point" +ScaleFiltering.Bilinear="Bilinéaire" +ScaleFiltering.Bicubic="Bicubique" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/gl-ES.ini b/plugins/obs-filters/data/locale/gl-ES.ini index 2430114..de5dd7b 100644 --- a/plugins/obs-filters/data/locale/gl-ES.ini +++ b/plugins/obs-filters/data/locale/gl-ES.ini @@ -1,7 +1,6 @@ ColorFilter="Corrección da cor" MaskFilter="Máscara/mestura de imaxes" AsyncDelayFilter="Atraso do vídeo (asíncrono)" -CropFilter="Recortar" ScrollFilter="Desprazamento" DelayMs="Atraso (milisegundos)" Type="Tipo" diff --git a/plugins/obs-filters/data/locale/he-IL.ini b/plugins/obs-filters/data/locale/he-IL.ini index 28b9161..5234249 100644 --- a/plugins/obs-filters/data/locale/he-IL.ini +++ b/plugins/obs-filters/data/locale/he-IL.ini @@ -1,11 +1,12 @@ ColorFilter="תיקון צבע" MaskFilter="מסכה/עירבוב תמונה" AsyncDelayFilter="השהיית וידאו (אסינכרונית)" -CropFilter="חתוך" +CropFilter="חיתוך/ריפוד" ScrollFilter="גלול" ChromaKeyFilter="מסך כחול" ColorKeyFilter="מפתח צבע" SharpnessFilter="חידוד" +ScaleFilter="יחס גובה רוחב/קנה מידה" NoiseGate="שער רעש" Gain="הגברה" DelayMs="השהייה (אלפיות שניה)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="זמן החזקה (אלפיות שניה)" NoiseGate.ReleaseTime="זמן שחרור (אלפיות שניה)" Gain.GainDB="הגברה (dB)" StretchImage="מתח תמונה (יחס הגובה-רוחב של התמונה לא ישמר)" +Resolution="רזולוציה" +None="ללא" +ScaleFiltering="מסנן קנה מידה" +ScaleFiltering.Point="נקודה" +ScaleFiltering.Bilinear="ביליניארי" +ScaleFiltering.Bicubic="ביקיוביק" +ScaleFiltering.Lanczos="לנזוס" diff --git a/plugins/obs-filters/data/locale/hr-HR.ini b/plugins/obs-filters/data/locale/hr-HR.ini index c81390d..eca3457 100644 --- a/plugins/obs-filters/data/locale/hr-HR.ini +++ b/plugins/obs-filters/data/locale/hr-HR.ini @@ -1,7 +1,6 @@ ColorFilter="Promena boja" MaskFilter="Slika maske i stapanja" AsyncDelayFilter="Video pauza (asinhrono)" -CropFilter="Odsecanje" ScrollFilter="Pomeranje" ChromaKeyFilter="Ključ providnosti" ColorKeyFilter="Ključ boje" diff --git a/plugins/obs-filters/data/locale/hu-HU.ini b/plugins/obs-filters/data/locale/hu-HU.ini index 4a74a88..b25833f 100644 --- a/plugins/obs-filters/data/locale/hu-HU.ini +++ b/plugins/obs-filters/data/locale/hu-HU.ini @@ -1,11 +1,12 @@ ColorFilter="Színkorrekció" MaskFilter="Képmaszk/Keverés" AsyncDelayFilter="Videó késleltetés (Async)" -CropFilter="Vágás" +CropFilter="Vágás/Margó" ScrollFilter="Görgetés" ChromaKeyFilter="Chroma kulcs" ColorKeyFilter="Színkulcs" SharpnessFilter="Élesítés" +ScaleFilter="Méretezés/Képarány" NoiseGate="Zajgát" Gain="Erősítés" DelayMs="Késleltetés (ezredmásodperc)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Kitartás ideje (ezredmásodperc)" NoiseGate.ReleaseTime="Felengedés ideje (ezredmásodperc)" Gain.GainDB="Erősítés (dB)" StretchImage="Kép nyújtása (képarány elvetésével)" +Resolution="Felbontás" +None="Nincs" +ScaleFiltering="Skála-szűrés" +ScaleFiltering.Point="Pont" +ScaleFiltering.Bilinear="Bilineáris" +ScaleFiltering.Bicubic="Kettős köbös" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/it-IT.ini b/plugins/obs-filters/data/locale/it-IT.ini index c4f171a..0e48464 100644 --- a/plugins/obs-filters/data/locale/it-IT.ini +++ b/plugins/obs-filters/data/locale/it-IT.ini @@ -1,11 +1,12 @@ ColorFilter="Correzione colore" MaskFilter="Immagine maschera/miscela" AsyncDelayFilter="Ritardo video (Asincrono)" -CropFilter="Ritaglia" +CropFilter="Crop/Pad" ScrollFilter="Scorrimento" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Chiave Colore" SharpnessFilter="Nitidizza" +ScaleFilter="Ridimensionamento/Aspect Ratio" NoiseGate="Noise Gate" Gain="Incremento" DelayMs="Ritardo (millisecondi)" @@ -50,4 +51,12 @@ NoiseGate.AttackTime="Tempo d'inizio (millisecondi)" NoiseGate.HoldTime="Tempo d'attesa (millisecondi)" NoiseGate.ReleaseTime="Tempo di rilascio (millisecondi)" Gain.GainDB="Incremento (dB)" +StretchImage="Stendi immagine (scarta proporzioni immagine)" +Resolution="Risoluzione" +None="Nessuno" +ScaleFiltering="Scala di filtraggio" +ScaleFiltering.Point="Punto" +ScaleFiltering.Bilinear="Bilineare" +ScaleFiltering.Bicubic="Bicubico" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/ja-JP.ini b/plugins/obs-filters/data/locale/ja-JP.ini index 1464b2c..1c45f3b 100644 --- a/plugins/obs-filters/data/locale/ja-JP.ini +++ b/plugins/obs-filters/data/locale/ja-JP.ini @@ -1,11 +1,12 @@ ColorFilter="色補正" MaskFilter="イメージ マスク/ブレンド" AsyncDelayFilter="映像の遅延 (非同期)" -CropFilter="クロップ" +CropFilter="クロップ/パッド" ScrollFilter="スクロール" ChromaKeyFilter="クロマキー" ColorKeyFilter="カラーキー" SharpnessFilter="シャープ" +ScaleFilter="スケーリング/アスペクト比" NoiseGate="ノイズゲート" Gain="ゲイン" DelayMs="遅延時間 (ミリ秒)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="保持時間 (ミリ秒)" NoiseGate.ReleaseTime="解除時間 (ミリ秒)" Gain.GainDB="ゲイン (dB)" StretchImage="画像を拡大 (アスペクト比を破棄)" +Resolution="解像度" +None="なし" +ScaleFiltering="スケールフィルタ" +ScaleFiltering.Point="ポイント" +ScaleFiltering.Bilinear="バイリニア" +ScaleFiltering.Bicubic="バイキュービック" +ScaleFiltering.Lanczos="ランチョス" diff --git a/plugins/obs-filters/data/locale/ko-KR.ini b/plugins/obs-filters/data/locale/ko-KR.ini index 27da049..5ed6331 100644 --- a/plugins/obs-filters/data/locale/ko-KR.ini +++ b/plugins/obs-filters/data/locale/ko-KR.ini @@ -1,11 +1,12 @@ ColorFilter="색상 보정" MaskFilter="이미지 마스크/혼합" AsyncDelayFilter="비디오 지연 (비동기)" -CropFilter="자르기" +CropFilter="자르기/덧대기" ScrollFilter="스크롤" ChromaKeyFilter="크로마 키" ColorKeyFilter="색상 키" SharpnessFilter="선명하게" +ScaleFilter="비례축소/가로세로 비율" NoiseGate="노이즈 게이트" Gain="증폭" DelayMs="지연 (밀리초)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="개방 유지 시간 (밀리세컨드)" NoiseGate.ReleaseTime="폐쇄 준비 시간 (밀리세컨드)" Gain.GainDB="증폭 (dB)" StretchImage="이미지 늘리기 (이미지 가로 세로 비율 포기)" +Resolution="해상도" +None="없음" +ScaleFiltering="비율 필터링" +ScaleFiltering.Point="점" +ScaleFiltering.Bilinear="이중선형" +ScaleFiltering.Bicubic="쌍삼차" +ScaleFiltering.Lanczos="란초스" diff --git a/plugins/obs-filters/data/locale/nb-NO.ini b/plugins/obs-filters/data/locale/nb-NO.ini index 9291daf..2c517b3 100644 --- a/plugins/obs-filters/data/locale/nb-NO.ini +++ b/plugins/obs-filters/data/locale/nb-NO.ini @@ -1,7 +1,7 @@ ColorFilter="Fargekorrigering" MaskFilter="Bildemaske/-blanding" AsyncDelayFilter="Videoforsinkelse (asynkron)" -CropFilter="Beskjæring" +CropFilter="Beskjæring/utfall" ScrollFilter="Rull" ChromaKeyFilter="Chromafilter" ColorKeyFilter="Fargefilter" @@ -50,4 +50,5 @@ NoiseGate.AttackTime="Angrepstid (millisekunder)" NoiseGate.HoldTime="Holdetid (millisekunder)" NoiseGate.ReleaseTime="Løslatelsestid (millisekunder)" Gain.GainDB="Forsterkning (dB)" +StretchImage="Strekk bilde (ignorer bildets sideforhold)" diff --git a/plugins/obs-filters/data/locale/nl-NL.ini b/plugins/obs-filters/data/locale/nl-NL.ini index a5420d3..f97a8ed 100644 --- a/plugins/obs-filters/data/locale/nl-NL.ini +++ b/plugins/obs-filters/data/locale/nl-NL.ini @@ -1,11 +1,12 @@ ColorFilter="Kleurcorrectie" MaskFilter="Afbeeldingsmasker/Mengen" AsyncDelayFilter="Videovertraging (Async)" -CropFilter="Bijsnijden" +CropFilter="Bijsnijden/Aanvullen" ScrollFilter="Scrollen" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" SharpnessFilter="Verscherpen" +ScaleFilter="Schalen/Aspect Ratio" NoiseGate="Noise Gate" Gain="Gain" DelayMs="Vertraging (milliseconden)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Hold-tijd (milliseconden)" NoiseGate.ReleaseTime="Release-tijd (milliseconden)" Gain.GainDB="Gain (dB)" StretchImage="Afbeelding uitrekken (negeer beeldverhouding van de afbeelding)" +Resolution="Resolutie" +None="Geen" +ScaleFiltering="Schaal-filter" +ScaleFiltering.Point="Point" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/pl-PL.ini b/plugins/obs-filters/data/locale/pl-PL.ini index 3798779..fb93a94 100644 --- a/plugins/obs-filters/data/locale/pl-PL.ini +++ b/plugins/obs-filters/data/locale/pl-PL.ini @@ -1,11 +1,12 @@ ColorFilter="Korekcja Kolorów" MaskFilter="Maskowanie/nakładanie obrazu" AsyncDelayFilter="Opóźnienie wideo (asynchronicznie)" -CropFilter="Kadrowanie" +CropFilter="Przytnij/Uzupełnij" ScrollFilter="Przewijanie" ChromaKeyFilter="Kluczowanie koloru (chroma key)" ColorKeyFilter="Kluczowanie koloru (kolor)" SharpnessFilter="Wyostrzanie" +ScaleFilter="Skalowanie/proporcje" NoiseGate="Bramka szumów" Gain="Poziom" DelayMs="Opóźnienie (milisekundy)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Czas wstrzymania (milisekundy)" NoiseGate.ReleaseTime="Czas zwolnienia (milisekundy)" Gain.GainDB="Poziom (dB)" StretchImage="Rozciągnięcie obrazu (ignoruj proporcje)" +Resolution="Rozdzielczość" +None="Brak" +ScaleFiltering="Filtrowanie" +ScaleFiltering.Point="Punktowe" +ScaleFiltering.Bilinear="Dwuliniowe" +ScaleFiltering.Bicubic="Dwusześcienne" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/pt-BR.ini b/plugins/obs-filters/data/locale/pt-BR.ini index c55ce0c..ebb6a81 100644 --- a/plugins/obs-filters/data/locale/pt-BR.ini +++ b/plugins/obs-filters/data/locale/pt-BR.ini @@ -1,7 +1,6 @@ ColorFilter="Correção de cor" MaskFilter="Máscara/mistura de imagem" AsyncDelayFilter="Atraso de vídeo (Async)" -CropFilter="Cortar" ScrollFilter="Rolagem" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" diff --git a/plugins/obs-filters/data/locale/pt-PT.ini b/plugins/obs-filters/data/locale/pt-PT.ini index 1bbc821..8a2f817 100644 --- a/plugins/obs-filters/data/locale/pt-PT.ini +++ b/plugins/obs-filters/data/locale/pt-PT.ini @@ -1,7 +1,6 @@ ColorFilter="Correção de cor" MaskFilter="Máscara/mistura de imagem" AsyncDelayFilter="Atraso de vídeo (Async)" -CropFilter="Corte" ScrollFilter="Percorre" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" @@ -50,4 +49,5 @@ NoiseGate.AttackTime="Tempo de ataque (milissegundos)" NoiseGate.HoldTime="Tempo de bloqueio (milissegundos)" NoiseGate.ReleaseTime="Tempo de libertação (milissegundos)" Gain.GainDB="Ganho (dB)" +StretchImage="Esticar a imagem (relação de aspeto de imagem de descarte)" diff --git a/plugins/obs-filters/data/locale/ro-RO.ini b/plugins/obs-filters/data/locale/ro-RO.ini index d8ff402..901947c 100644 --- a/plugins/obs-filters/data/locale/ro-RO.ini +++ b/plugins/obs-filters/data/locale/ro-RO.ini @@ -1,7 +1,6 @@ ColorFilter="Corecție de culoare" MaskFilter="Mască/amestec de imagine" AsyncDelayFilter="Întârziere video (asincron)" -CropFilter="Trunchiere" ScrollFilter="Derulare" ChromaKeyFilter="Cheie chroma" ColorKeyFilter="Culoare cheie" diff --git a/plugins/obs-filters/data/locale/ru-RU.ini b/plugins/obs-filters/data/locale/ru-RU.ini index 8c86b7b..6bb28bf 100644 --- a/plugins/obs-filters/data/locale/ru-RU.ini +++ b/plugins/obs-filters/data/locale/ru-RU.ini @@ -1,11 +1,12 @@ ColorFilter="Коррекция цвета" MaskFilter="Маска изображения/Смешивание" AsyncDelayFilter="Задержка видео (асинхронность)" -CropFilter="Обрезка" +CropFilter="Кадрировать" ScrollFilter="Прокрутка" ChromaKeyFilter="Хромакей" ColorKeyFilter="Цветовой ключ" SharpnessFilter="Увеличить резкость" +ScaleFilter="Коэффициент Масштабирования/Аспект" NoiseGate="Подавление шума" Gain="Усиление" DelayMs="Задержка (миллисекунд)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="Длительность задержки (миллисек NoiseGate.ReleaseTime="Длительность затухания (миллисекунд)" Gain.GainDB="Усиление (дБ)" StretchImage="Растянуть изображение (игнорировать пропорции изображения)" +Resolution="Разрешение" +None="Нет" +ScaleFiltering="Масштаб Фильтрации" +ScaleFiltering.Point="Точечная" +ScaleFiltering.Bilinear="Билинейная" +ScaleFiltering.Bicubic="Бикубическая" +ScaleFiltering.Lanczos="Ланцошная" diff --git a/plugins/obs-filters/data/locale/sr-CS.ini b/plugins/obs-filters/data/locale/sr-CS.ini index 33f484a..6a487db 100644 --- a/plugins/obs-filters/data/locale/sr-CS.ini +++ b/plugins/obs-filters/data/locale/sr-CS.ini @@ -1,7 +1,6 @@ ColorFilter="Promena boja" MaskFilter="Slika maske i stapanja" AsyncDelayFilter="Video pauza (asinhrono)" -CropFilter="Odsecanje" ScrollFilter="Pomeranje" ChromaKeyFilter="Ključ providnosti" ColorKeyFilter="Ključ boje" diff --git a/plugins/obs-filters/data/locale/sr-SP.ini b/plugins/obs-filters/data/locale/sr-SP.ini index 0819432..2d635e8 100644 --- a/plugins/obs-filters/data/locale/sr-SP.ini +++ b/plugins/obs-filters/data/locale/sr-SP.ini @@ -1,7 +1,6 @@ ColorFilter="Промена боја" MaskFilter="Слика маске и стапања" AsyncDelayFilter="Видео пауза (асинхроно)" -CropFilter="Одсецање" ScrollFilter="Померање" ChromaKeyFilter="Кључ провидности" ColorKeyFilter="Кључ боје" diff --git a/plugins/obs-filters/data/locale/sv-SE.ini b/plugins/obs-filters/data/locale/sv-SE.ini index 7219081..df634a8 100644 --- a/plugins/obs-filters/data/locale/sv-SE.ini +++ b/plugins/obs-filters/data/locale/sv-SE.ini @@ -1,11 +1,11 @@ ColorFilter="Färgkorrigering" MaskFilter="Bild Mask/Blandning" AsyncDelayFilter="Videofördröjning (Async)" -CropFilter="Beskär" ScrollFilter="Scrollning" ChromaKeyFilter="Kromafilter" ColorKeyFilter="Färgfilter" SharpnessFilter="Skärpa" +ScaleFilter="Skalning/Bildförhållande" NoiseGate="Brusblockering" Gain="Förstärkning" DelayMs="Fördröjning (millisekunder)" @@ -51,4 +51,10 @@ NoiseGate.HoldTime="Hålltid (millisekunder)" NoiseGate.ReleaseTime="Släpptid (millisekunder)" Gain.GainDB="Förstärkning (dB)" StretchImage="Sträck bild (ignorera bildförhållandet)" +Resolution="Upplösning" +None="Ingen" +ScaleFiltering="Skalningsfiltrering" +ScaleFiltering.Bilinear="Bilinjär" +ScaleFiltering.Bicubic="Bikubisk" +ScaleFiltering.Lanczos="Lanczos" diff --git a/plugins/obs-filters/data/locale/tr-TR.ini b/plugins/obs-filters/data/locale/tr-TR.ini index be55167..30a9870 100644 --- a/plugins/obs-filters/data/locale/tr-TR.ini +++ b/plugins/obs-filters/data/locale/tr-TR.ini @@ -1,7 +1,6 @@ ColorFilter="Renk Düzeltme" MaskFilter="Görüntü Maskesi/Blend" AsyncDelayFilter="Görüntü Gecikmesi (Async)" -CropFilter="Kırpma" ScrollFilter="Kaydır" ChromaKeyFilter="Chroma Anahtarı" ColorKeyFilter="Renk Anahtarı" diff --git a/plugins/obs-filters/data/locale/zh-CN.ini b/plugins/obs-filters/data/locale/zh-CN.ini index 65ed05e..48033b4 100644 --- a/plugins/obs-filters/data/locale/zh-CN.ini +++ b/plugins/obs-filters/data/locale/zh-CN.ini @@ -1,11 +1,12 @@ ColorFilter="色彩校正" MaskFilter="图像掩码/混合" AsyncDelayFilter="视频延迟(异步)" -CropFilter="剪裁" +CropFilter="裁剪/填充" ScrollFilter="滚动" ChromaKeyFilter="色度键" ColorKeyFilter="色值" SharpnessFilter="锐化" +ScaleFilter="缩放比例" NoiseGate="噪音阈值" Gain="增益" DelayMs="延迟(毫秒)" @@ -51,4 +52,11 @@ NoiseGate.HoldTime="保持时间(毫秒)" NoiseGate.ReleaseTime="释放时间(毫秒)" Gain.GainDB="增益 (dB)" StretchImage="伸展图像 (丢弃图像纵横比)" +Resolution="分辨率" +None="无" +ScaleFiltering="尺度滤波" +ScaleFiltering.Point="点" +ScaleFiltering.Bilinear="双线性算法" +ScaleFiltering.Bicubic="双立方算法" +ScaleFiltering.Lanczos="兰索斯算法" diff --git a/plugins/obs-filters/data/locale/zh-TW.ini b/plugins/obs-filters/data/locale/zh-TW.ini new file mode 100644 index 0000000..e8b0776 --- /dev/null +++ b/plugins/obs-filters/data/locale/zh-TW.ini @@ -0,0 +1,62 @@ +ColorFilter="色彩校正" +MaskFilter="影像遮罩/混合" +AsyncDelayFilter="視頻延遲 (非同步)" +CropFilter="剪裁/填充" +ScrollFilter="捲動" +ChromaKeyFilter="色度鍵" +ColorKeyFilter="色彩鍵" +SharpnessFilter="銳化" +ScaleFilter="縮放/長寬比" +NoiseGate="噪音閾" +Gain="增益" +DelayMs="延遲 (毫秒)" +Type="類型" +MaskBlendType.MaskColor="Alpha 遮罩 (顏色通道)" +MaskBlendType.MaskAlpha="Alpha 遮罩 (Alpha 通道)" +MaskBlendType.BlendMultiply="混合 (乘法)" +MaskBlendType.BlendAddition="混合 (加法)" +MaskBlendType.BlendSubtraction="混合 (減法)" +Path="檔案路徑" +Color="顏色" +Opacity="不透明度" +Contrast="對比" +Brightness="亮度" +Gamma="伽瑪" +BrowsePath.Images="影像檔案" +BrowsePath.AllFiles="所有檔案" +KeyColorType="關鍵顏色類型" +KeyColor="關鍵顏色" +Similarity="相似性 (1-1000)" +Smoothness="平滑度 (1-1000)" +ColorSpillReduction="鍵色溢出減少 (1-1000)" +Crop.Left="左側" +Crop.Right="右側" +Crop.Top="上方" +Crop.Bottom="下方" +Crop.Width="寬度" +Crop.Height="高度" +Crop.Relative="相對" +ScrollFilter.SpeedX="水平速度" +ScrollFilter.SpeedY="垂直速度" +ScrollFilter.LimitWidth="限制寬度" +ScrollFilter.LimitHeight="限制高度" +CustomColor="自訂色彩" +Red="红" +Green="綠" +Blue="藍" +Magenta="洋紅" +NoiseGate.OpenThreshold="開啟閾值 (dB)" +NoiseGate.CloseThreshold="關閉閾值 (dB)" +NoiseGate.AttackTime="起音時間 (Attack time)(毫秒)" +NoiseGate.HoldTime="持續時間 (Hold time)(毫秒)" +NoiseGate.ReleaseTime="釋音時間 (Release time)(毫秒)" +Gain.GainDB="增益 (dB)" +StretchImage="伸展圖像 (無視圖像比例)" +Resolution="解析度" +None="無" +ScaleFiltering="縮放濾鏡" +ScaleFiltering.Point="點" +ScaleFiltering.Bilinear="雙線性插值" +ScaleFiltering.Bicubic="雙三次插值" +ScaleFiltering.Lanczos="Lanczos" + diff --git a/plugins/obs-filters/noise-suppress-filter.c b/plugins/obs-filters/noise-suppress-filter.c new file mode 100644 index 0000000..99cdb3a --- /dev/null +++ b/plugins/obs-filters/noise-suppress-filter.c @@ -0,0 +1,297 @@ +#include +#include + +#include +#include +#include + +/* -------------------------------------------------------- */ + +#define do_log(level, format, ...) \ + blog(level, "[noise suppress: '%s'] " format, \ + obs_source_get_name(ng->context), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) + +#ifdef _DEBUG +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) +#else +#define debug(format, ...) +#endif + +/* -------------------------------------------------------- */ + +#define S_SUPPRESS_LEVEL "suppress_level" + +#define MT_ obs_module_text +#define TEXT_SUPPRESS_LEVEL MT_("NoiseSuppress.SuppressLevel") + +#define MAX_PREPROC_CHANNELS 2 + +/* -------------------------------------------------------- */ + +struct noise_suppress_data { + obs_source_t *context; + int suppress_level; + + uint64_t last_timestamp; + + size_t frames; + size_t channels; + + struct circlebuf info_buffer; + struct circlebuf input_buffers[MAX_PREPROC_CHANNELS]; + struct circlebuf output_buffers[MAX_PREPROC_CHANNELS]; + + /* Speex preprocessor state */ + SpeexPreprocessState *states[MAX_PREPROC_CHANNELS]; + + /* 16 bit PCM buffers */ + float *copy_buffers[MAX_PREPROC_CHANNELS]; + spx_int16_t *segment_buffers[MAX_PREPROC_CHANNELS]; + + /* output data */ + struct obs_audio_data output_audio; + DARRAY(float) output_data; +}; + +/* -------------------------------------------------------- */ + +#define SUP_MIN -60 +#define SUP_MAX 0 + +static const float c_32_to_16 = (float)INT16_MAX; +static const float c_16_to_32 = ((float)INT16_MAX + 1.0f); + +/* -------------------------------------------------------- */ + +static const char *noise_suppress_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("NoiseSuppress"); +} + +static void noise_suppress_destroy(void *data) +{ + struct noise_suppress_data *ng = data; + + for (size_t i = 0; i < ng->channels; i++) { + speex_preprocess_state_destroy(ng->states[i]); + circlebuf_free(&ng->input_buffers[i]); + circlebuf_free(&ng->output_buffers[i]); + } + + bfree(ng->segment_buffers[0]); + bfree(ng->copy_buffers[0]); + circlebuf_free(&ng->info_buffer); + da_free(ng->output_data); + bfree(ng); +} + +static inline void alloc_channel(struct noise_suppress_data *ng, + uint32_t sample_rate, size_t channel, size_t frames) +{ + ng->states[channel] = speex_preprocess_state_init((int)frames, + sample_rate); + + circlebuf_reserve(&ng->input_buffers[channel], frames * sizeof(float)); + circlebuf_reserve(&ng->output_buffers[channel], frames * sizeof(float)); +} + +static void noise_suppress_update(void *data, obs_data_t *s) +{ + struct noise_suppress_data *ng = data; + + uint32_t sample_rate = audio_output_get_sample_rate(obs_get_audio()); + size_t channels = audio_output_get_channels(obs_get_audio()); + size_t frames = (size_t)sample_rate / 100; + + ng->suppress_level = (int)obs_data_get_int(s, S_SUPPRESS_LEVEL); + + /* Process 10 millisecond segments to keep latency low */ + ng->frames = frames; + ng->channels = channels; + + /* Ignore if already allocated */ + if (ng->states[0]) + return; + + /* One speex state for each channel (limit 2) */ + ng->copy_buffers[0] = bmalloc(frames * channels * sizeof(float)); + ng->segment_buffers[0] = bmalloc(frames * channels * sizeof(spx_int16_t)); + + if (channels == 2) { + ng->copy_buffers[1] = ng->copy_buffers[0] + frames; + ng->segment_buffers[1] = ng->segment_buffers[0] + frames; + } + + for (size_t i = 0; i < channels; i++) + alloc_channel(ng, sample_rate, i, frames); +} + +static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter) +{ + struct noise_suppress_data *ng = + bzalloc(sizeof(struct noise_suppress_data)); + + ng->context = filter; + noise_suppress_update(ng, settings); + return ng; +} + +static inline void process(struct noise_suppress_data *ng) +{ + /* Pop from input circlebuf */ + for (size_t i = 0; i < ng->channels; i++) + circlebuf_pop_front(&ng->input_buffers[i], ng->copy_buffers[i], + ng->frames * sizeof(float)); + + /* Set args */ + for (size_t i = 0; i < ng->channels; i++) + speex_preprocess_ctl(ng->states[i], + SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, + &ng->suppress_level); + + /* Convert to 16bit */ + for (size_t i = 0; i < ng->channels; i++) + for (size_t j = 0; j < ng->frames; j++) + ng->segment_buffers[i][j] = (spx_int16_t) + (ng->copy_buffers[i][j] * c_32_to_16); + + /* Execute */ + for (size_t i = 0; i < ng->channels; i++) + speex_preprocess_run(ng->states[i], ng->segment_buffers[i]); + + /* Convert back to 32bit */ + for (size_t i = 0; i < ng->channels; i++) + for (size_t j = 0; j < ng->frames; j++) + ng->copy_buffers[i][j] = + (float)ng->segment_buffers[i][j] / c_16_to_32; + + /* Push to output circlebuf */ + for (size_t i = 0; i < ng->channels; i++) + circlebuf_push_back(&ng->output_buffers[i], ng->copy_buffers[i], + ng->frames * sizeof(float)); +} + +struct ng_audio_info { + uint32_t frames; + uint64_t timestamp; +}; + +static inline void clear_circlebuf(struct circlebuf *buf) +{ + circlebuf_pop_front(buf, NULL, buf->size); +} + +static void reset_data(struct noise_suppress_data *ng) +{ + for (size_t i = 0; i < ng->channels; i++) { + clear_circlebuf(&ng->input_buffers[i]); + clear_circlebuf(&ng->output_buffers[i]); + } + + clear_circlebuf(&ng->info_buffer); +} + +static struct obs_audio_data *noise_suppress_filter_audio(void *data, + struct obs_audio_data *audio) +{ + struct noise_suppress_data *ng = data; + struct ng_audio_info info; + size_t segment_size = ng->frames * sizeof(float); + size_t out_size; + + if (!ng->states[0]) + return audio; + + /* ----------------------------------------------- + * if timestamp has dramatically changed, consider it a new stream of + * audio data. clear all circular buffers to prevent old audio data + * from being processed as part of the new data. */ + if (ng->last_timestamp) { + int64_t diff = llabs((int64_t)ng->last_timestamp - + (int64_t)audio->timestamp); + + if (diff > 1000000000LL) + reset_data(ng); + } + + ng->last_timestamp = audio->timestamp; + + /* ----------------------------------------------- + * push audio packet info (timestamp/frame count) to info circlebuf */ + info.frames = audio->frames; + info.timestamp = audio->timestamp; + circlebuf_push_back(&ng->info_buffer, &info, sizeof(info)); + + /* ----------------------------------------------- + * push back current audio data to input circlebuf */ + for (size_t i = 0; i < ng->channels; i++) + circlebuf_push_back(&ng->input_buffers[i], audio->data[i], + audio->frames * sizeof(float)); + + /* ----------------------------------------------- + * pop/process each 10ms segments, push back to output circlebuf */ + while (ng->input_buffers[0].size >= segment_size) + process(ng); + + /* ----------------------------------------------- + * peek front of info circlebuf, check to see if we have enough to + * pop the expected packet size, if not, return null */ + memset(&info, 0, sizeof(info)); + circlebuf_peek_front(&ng->info_buffer, &info, sizeof(info)); + out_size = info.frames * sizeof(float); + + if (ng->output_buffers[0].size < out_size) + return NULL; + + /* ----------------------------------------------- + * if there's enough audio data buffered in the output circlebuf, + * pop and return a packet */ + circlebuf_pop_front(&ng->info_buffer, NULL, sizeof(info)); + da_resize(ng->output_data, out_size * ng->channels); + + for (size_t i = 0; i < ng->channels; i++) { + ng->output_audio.data[i] = + (uint8_t*)&ng->output_data.array[i * out_size]; + + circlebuf_pop_front(&ng->output_buffers[i], + ng->output_audio.data[i], + out_size); + } + + ng->output_audio.frames = info.frames; + ng->output_audio.timestamp = info.timestamp; + return &ng->output_audio; +} + +static void noise_suppress_defaults(obs_data_t *s) +{ + obs_data_set_default_int(s, S_SUPPRESS_LEVEL, -30); +} + +static obs_properties_t *noise_suppress_properties(void *data) +{ + obs_properties_t *ppts = obs_properties_create(); + + obs_properties_add_int_slider(ppts, S_SUPPRESS_LEVEL, + TEXT_SUPPRESS_LEVEL, SUP_MIN, SUP_MAX, 0); + + UNUSED_PARAMETER(data); + return ppts; +} + +struct obs_source_info noise_suppress_filter = { + .id = "noise_suppress_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_AUDIO, + .get_name = noise_suppress_name, + .create = noise_suppress_create, + .destroy = noise_suppress_destroy, + .update = noise_suppress_update, + .filter_audio = noise_suppress_filter_audio, + .get_defaults = noise_suppress_defaults, + .get_properties = noise_suppress_properties, +}; diff --git a/plugins/obs-filters/obs-filters-config.h.in b/plugins/obs-filters/obs-filters-config.h.in new file mode 100644 index 0000000..3e560c8 --- /dev/null +++ b/plugins/obs-filters/obs-filters-config.h.in @@ -0,0 +1,11 @@ +#pragma once + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define SPEEXDSP_ENABLED @LIBSPEEXDSP_FOUND@ diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index e412de3..fbcdb91 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -1,4 +1,5 @@ #include +#include "obs-filters-config.h" OBS_DECLARE_MODULE() @@ -8,11 +9,15 @@ extern struct obs_source_info mask_filter; extern struct obs_source_info crop_filter; extern struct obs_source_info gain_filter; extern struct obs_source_info color_filter; +extern struct obs_source_info scale_filter; extern struct obs_source_info scroll_filter; extern struct obs_source_info color_key_filter; extern struct obs_source_info sharpness_filter; extern struct obs_source_info chroma_key_filter; extern struct obs_source_info async_delay_filter; +#if SPEEXDSP_ENABLED +extern struct obs_source_info noise_suppress_filter; +#endif extern struct obs_source_info noise_gate_filter; bool obs_module_load(void) @@ -21,11 +26,15 @@ bool obs_module_load(void) obs_register_source(&crop_filter); obs_register_source(&gain_filter); obs_register_source(&color_filter); + obs_register_source(&scale_filter); obs_register_source(&scroll_filter); obs_register_source(&color_key_filter); obs_register_source(&sharpness_filter); obs_register_source(&chroma_key_filter); obs_register_source(&async_delay_filter); +#if SPEEXDSP_ENABLED + obs_register_source(&noise_suppress_filter); +#endif obs_register_source(&noise_gate_filter); return true; } diff --git a/plugins/obs-filters/scale-filter.c b/plugins/obs-filters/scale-filter.c new file mode 100644 index 0000000..e8edfc5 --- /dev/null +++ b/plugins/obs-filters/scale-filter.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#include +#include + +#define S_RESOLUTION "resolution" +#define S_SAMPLING "sampling" + +#define T_RESOLUTION obs_module_text("Resolution") +#define T_NONE obs_module_text("None") +#define T_SAMPLING obs_module_text("ScaleFiltering") +#define T_SAMPLING_POINT obs_module_text("ScaleFiltering.Point") +#define T_SAMPLING_BILINEAR obs_module_text("ScaleFiltering.Bilinear") +#define T_SAMPLING_BICUBIC obs_module_text("ScaleFiltering.Bicubic") +#define T_SAMPLING_LANCZOS obs_module_text("ScaleFiltering.Lanczos") + +#define S_SAMPLING_POINT "point" +#define S_SAMPLING_BILINEAR "bilinear" +#define S_SAMPLING_BICUBIC "bicubic" +#define S_SAMPLING_LANCZOS "lanczos" + +struct scale_filter_data { + obs_source_t *context; + gs_effect_t *effect; + gs_eparam_t *image_param; + gs_eparam_t *dimension_param; + struct vec2 dimension_i; + int cx_in; + int cy_in; + int cx_out; + int cy_out; + enum obs_scale_type sampling; + gs_samplerstate_t *point_sampler; + bool aspect_ratio_only : 1; + bool target_valid : 1; + bool valid : 1; +}; + +static const char *scale_filter_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("ScaleFilter"); +} + +static void scale_filter_update(void *data, obs_data_t *settings) +{ + struct scale_filter_data *filter = data; + int ret; + + const char *res_str = obs_data_get_string(settings, S_RESOLUTION); + const char *sampling = obs_data_get_string(settings, S_SAMPLING); + + filter->valid = true; + + ret = sscanf(res_str, "%dx%d", &filter->cx_in, &filter->cy_in); + if (ret == 2) { + filter->aspect_ratio_only = false; + } else { + ret = sscanf(res_str, "%d:%d", &filter->cx_in, &filter->cy_in); + if (ret != 2) { + filter->valid = false; + return; + } + + filter->aspect_ratio_only = true; + } + + if (astrcmpi(sampling, S_SAMPLING_POINT) == 0) { + filter->sampling = OBS_SCALE_POINT; + + } else if (astrcmpi(sampling, S_SAMPLING_BILINEAR) == 0) { + filter->sampling = OBS_SCALE_BILINEAR; + + } else if (astrcmpi(sampling, S_SAMPLING_LANCZOS) == 0) { + filter->sampling = OBS_SCALE_LANCZOS; + + } else { /* S_SAMPLING_BICUBIC */ + filter->sampling = OBS_SCALE_BICUBIC; + } +} + +static void scale_filter_destroy(void *data) +{ + struct scale_filter_data *filter = data; + + obs_enter_graphics(); + gs_samplerstate_destroy(filter->point_sampler); + obs_leave_graphics(); + bfree(data); +} + +static void *scale_filter_create(obs_data_t *settings, obs_source_t *context) +{ + struct scale_filter_data *filter = + bzalloc(sizeof(struct scale_filter_data)); + struct gs_sampler_info sampler_info = {0}; + + filter->context = context; + + obs_enter_graphics(); + filter->point_sampler = gs_samplerstate_create(&sampler_info); + obs_leave_graphics(); + + scale_filter_update(filter, settings); + return filter; +} + +static void scale_filter_tick(void *data, float seconds) +{ + struct scale_filter_data *filter = data; + enum obs_base_effect type; + obs_source_t *target; + bool lower_than_2x; + double cx_f; + double cy_f; + int cx; + int cy; + + target = obs_filter_get_target(filter->context); + filter->cx_out = 0; + filter->cy_out = 0; + + filter->target_valid = !!target; + if (!filter->target_valid) + return; + + cx = obs_source_get_base_width(target); + cy = obs_source_get_base_height(target); + + if (!cx || !cy) { + filter->target_valid = false; + return; + } + + filter->cx_out = cx; + filter->cy_out = cy; + + if (!filter->valid) + return; + + /* ------------------------- */ + + cx_f = (double)cx; + cy_f = (double)cy; + + if (filter->aspect_ratio_only) { + double old_aspect = cx_f / cy_f; + double new_aspect = + (double)filter->cx_in / (double)filter->cy_in; + + if (fabs(old_aspect - new_aspect) <= EPSILON) { + filter->target_valid = false; + return; + } else { + if (new_aspect > old_aspect) { + filter->cx_out = (int)(cy_f * new_aspect); + filter->cy_out = cy; + } else { + filter->cx_out = cx; + filter->cy_out = (int)(cx_f / new_aspect); + } + } + } else { + filter->cx_out = filter->cx_in; + filter->cy_out = filter->cy_in; + } + + vec2_set(&filter->dimension_i, + 1.0f / (float)cx, + 1.0f / (float)cy); + + /* ------------------------- */ + + lower_than_2x = filter->cx_out < cx / 2 || filter->cy_out < cy / 2; + + if (lower_than_2x && filter->sampling != OBS_SCALE_POINT) { + type = OBS_EFFECT_BILINEAR_LOWRES; + } else { + switch (filter->sampling) { + default: + case OBS_SCALE_POINT: + case OBS_SCALE_BILINEAR: type = OBS_EFFECT_DEFAULT; break; + case OBS_SCALE_BICUBIC: type = OBS_EFFECT_BICUBIC; break; + case OBS_SCALE_LANCZOS: type = OBS_EFFECT_LANCZOS; break; + } + } + + filter->effect = obs_get_base_effect(type); + filter->image_param = gs_effect_get_param_by_name(filter->effect, + "image"); + + if (type != OBS_EFFECT_DEFAULT) { + filter->dimension_param = gs_effect_get_param_by_name( + filter->effect, "base_dimension_i"); + } else { + filter->dimension_param = NULL; + } + + UNUSED_PARAMETER(seconds); +} + +static void scale_filter_render(void *data, gs_effect_t *effect) +{ + struct scale_filter_data *filter = data; + + if (!filter->valid || !filter->target_valid) { + obs_source_skip_video_filter(filter->context); + return; + } + + if (!obs_source_process_filter_begin(filter->context, GS_RGBA, + OBS_NO_DIRECT_RENDERING)) + return; + + if (filter->dimension_param) + gs_effect_set_vec2(filter->dimension_param, + &filter->dimension_i); + + if (filter->sampling == OBS_SCALE_POINT) + gs_effect_set_next_sampler(filter->image_param, + filter->point_sampler); + + obs_source_process_filter_end(filter->context, filter->effect, + filter->cx_out, filter->cy_out); + + UNUSED_PARAMETER(effect); +} + +static const double downscale_vals[] = { + 1.0, + 1.25, + (1.0/0.75), + 1.5, + (1.0/0.6), + 1.75, + 2.0, + 2.25, + 2.5, + 2.75, + 3.0 +}; + +#define NUM_DOWNSCALES (sizeof(downscale_vals) / sizeof(double)) + +static const char *aspects[] = { + "16:9", + "16:10", + "4:3", + "1:1" +}; + +#define NUM_ASPECTS (sizeof(aspects) / sizeof(const char *)) + +static obs_properties_t *scale_filter_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + struct obs_video_info ovi; + obs_property_t *p; + uint32_t cx; + uint32_t cy; + + struct { + int cx; + int cy; + } downscales[NUM_DOWNSCALES]; + + /* ----------------- */ + + obs_get_video_info(&ovi); + cx = ovi.base_width; + cy = ovi.base_height; + + for (size_t i = 0; i < NUM_DOWNSCALES; i++) { + downscales[i].cx = (int)((double)cx / downscale_vals[i]); + downscales[i].cy = (int)((double)cy / downscale_vals[i]); + } + + p = obs_properties_add_list(props, S_SAMPLING, T_SAMPLING, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, T_SAMPLING_POINT, S_SAMPLING_POINT); + obs_property_list_add_string(p, T_SAMPLING_BILINEAR, S_SAMPLING_BILINEAR); + obs_property_list_add_string(p, T_SAMPLING_BICUBIC, S_SAMPLING_BICUBIC); + obs_property_list_add_string(p, T_SAMPLING_LANCZOS, S_SAMPLING_LANCZOS); + + /* ----------------- */ + + p = obs_properties_add_list(props, S_RESOLUTION, T_RESOLUTION, + OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); + + obs_property_list_add_string(p, T_NONE, T_NONE); + + for (size_t i = 0; i < NUM_ASPECTS; i++) + obs_property_list_add_string(p, aspects[i], aspects[i]); + + for (size_t i = 0; i < NUM_DOWNSCALES; i++) { + char str[32]; + snprintf(str, 32, "%dx%d", downscales[i].cx, downscales[i].cy); + obs_property_list_add_string(p, str, str); + } + + /* ----------------- */ + + UNUSED_PARAMETER(data); + return props; +} + +static void scale_filter_defaults(obs_data_t *settings) +{ + obs_data_set_default_string(settings, S_SAMPLING, S_SAMPLING_BICUBIC); + obs_data_set_default_string(settings, S_RESOLUTION, T_NONE); +} + +static uint32_t scale_filter_width(void *data) +{ + struct scale_filter_data *filter = data; + return (uint32_t)filter->cx_out; +} + +static uint32_t scale_filter_height(void *data) +{ + struct scale_filter_data *filter = data; + return (uint32_t)filter->cy_out; +} + +struct obs_source_info scale_filter = { + .id = "scale_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_VIDEO, + .get_name = scale_filter_name, + .create = scale_filter_create, + .destroy = scale_filter_destroy, + .video_tick = scale_filter_tick, + .video_render = scale_filter_render, + .update = scale_filter_update, + .get_properties = scale_filter_properties, + .get_defaults = scale_filter_defaults, + .get_width = scale_filter_width, + .get_height = scale_filter_height +}; diff --git a/plugins/obs-filters/scroll-filter.c b/plugins/obs-filters/scroll-filter.c index 1e38a77..2ad8793 100644 --- a/plugins/obs-filters/scroll-filter.c +++ b/plugins/obs-filters/scroll-filter.c @@ -7,8 +7,10 @@ struct scroll_filter_data { gs_effect_t *effect; gs_eparam_t *param_add; gs_eparam_t *param_mul; + gs_eparam_t *param_image; struct vec2 scroll_speed; + gs_samplerstate_t *sampler; bool limit_cx; bool limit_cy; uint32_t cx; @@ -29,10 +31,17 @@ static void *scroll_filter_create(obs_data_t *settings, obs_source_t *context) struct scroll_filter_data *filter = bzalloc(sizeof(*filter)); char *effect_path = obs_module_file("crop_filter.effect"); + struct gs_sampler_info sampler_info = { + .filter = GS_FILTER_LINEAR, + .address_u = GS_ADDRESS_WRAP, + .address_v = GS_ADDRESS_WRAP + }; + filter->context = context; obs_enter_graphics(); filter->effect = gs_effect_create_from_file(effect_path, NULL); + filter->sampler = gs_samplerstate_create(&sampler_info); obs_leave_graphics(); bfree(effect_path); @@ -46,6 +55,8 @@ static void *scroll_filter_create(obs_data_t *settings, obs_source_t *context) "add_val"); filter->param_mul = gs_effect_get_param_by_name(filter->effect, "mul_val"); + filter->param_image = gs_effect_get_param_by_name(filter->effect, + "image"); obs_source_update(context, settings); return filter; @@ -57,6 +68,7 @@ static void scroll_filter_destroy(void *data) obs_enter_graphics(); gs_effect_destroy(filter->effect); + gs_samplerstate_destroy(filter->sampler); obs_leave_graphics(); bfree(filter); @@ -187,6 +199,8 @@ static void scroll_filter_render(void *data, gs_effect_t *effect) gs_effect_set_vec2(filter->param_add, &filter->offset); gs_effect_set_vec2(filter->param_mul, &mul_val); + gs_effect_set_next_sampler(filter->param_image, filter->sampler); + obs_source_process_filter_end(filter->context, filter->effect, cx, cy); UNUSED_PARAMETER(effect); diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index f0e42f0..1051974 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -62,6 +62,7 @@ endif() set(obs-outputs_HEADERS obs-output-ver.h rtmp-helpers.h + net-if.h flv-mux.h flv-output.h librtmp) @@ -69,7 +70,8 @@ set(obs-outputs_SOURCES obs-outputs.c rtmp-stream.c flv-output.c - flv-mux.c) + flv-mux.c + net-if.c) add_library(obs-outputs MODULE ${obs-outputs_SOURCES} diff --git a/plugins/obs-outputs/data/locale/en-US.ini b/plugins/obs-outputs/data/locale/en-US.ini index 1a3a7fe..c28e54e 100644 --- a/plugins/obs-outputs/data/locale/en-US.ini +++ b/plugins/obs-outputs/data/locale/en-US.ini @@ -2,3 +2,4 @@ RTMPStream="RTMP Stream" RTMPStream.DropThreshold="Drop Threshold (milliseconds)" FLVOutput="FLV File Output" FLVOutput.FilePath="File Path" +Default="Default" diff --git a/plugins/obs-outputs/data/locale/fi-FI.ini b/plugins/obs-outputs/data/locale/fi-FI.ini index 05b45be..c84208c 100644 --- a/plugins/obs-outputs/data/locale/fi-FI.ini +++ b/plugins/obs-outputs/data/locale/fi-FI.ini @@ -1,5 +1,5 @@ RTMPStream="RTMP-virtaus" -RTMPStream.DropThreshold="Pudotuskynnys (ms)" +RTMPStream.DropThreshold="Pudotuskynnys (millisekuntia)" FLVOutput="FLV-tiedosto ulostulo" FLVOutput.FilePath="Tiedostopolku" diff --git a/plugins/obs-outputs/data/locale/zh-TW.ini b/plugins/obs-outputs/data/locale/zh-TW.ini index 487f8cd..f002021 100644 --- a/plugins/obs-outputs/data/locale/zh-TW.ini +++ b/plugins/obs-outputs/data/locale/zh-TW.ini @@ -1,5 +1,5 @@ RTMPStream="RTMP 串流" -RTMPStream.DropThreshold="丟失阈值(毫秒)" +RTMPStream.DropThreshold="丟棄閾值 (毫秒)" FLVOutput="FLV 檔案輸出" FLVOutput.FilePath="檔案路徑" diff --git a/plugins/obs-outputs/flv-output.c b/plugins/obs-outputs/flv-output.c index 35c5596..a0e803a 100644 --- a/plugins/obs-outputs/flv-output.c +++ b/plugins/obs-outputs/flv-output.c @@ -46,14 +46,14 @@ static const char *flv_output_getname(void *unused) return obs_module_text("FLVOutput"); } -static void flv_output_stop(void *data); +static void flv_output_stop(void *data, uint64_t ts); static void flv_output_destroy(void *data) { struct flv_output *stream = data; if (stream->active) - flv_output_stop(data); + flv_output_stop(data, 0); dstr_free(&stream->path); bfree(stream); @@ -68,7 +68,7 @@ static void *flv_output_create(obs_data_t *settings, obs_output_t *output) return stream; } -static void flv_output_stop(void *data) +static void flv_output_stop(void *data, uint64_t ts) { struct flv_output *stream = data; @@ -84,6 +84,8 @@ static void flv_output_stop(void *data) info("FLV file output complete"); } + + UNUSED_PARAMETER(ts); } static int write_packet(struct flv_output *stream, diff --git a/plugins/obs-outputs/net-if.c b/plugins/obs-outputs/net-if.c new file mode 100644 index 0000000..28dc087 --- /dev/null +++ b/plugins/obs-outputs/net-if.c @@ -0,0 +1,270 @@ +/****************************************************************************** + Copyright (C) 2016 B. Lee + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "net-if.h" +#include +#include + +#define do_log(level, format, ...) \ + blog(level, "[net if] " format, ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) + +static inline void netif_saddr_data_push_back(struct netif_saddr_data *sd, + const char *ip, const char *adapter) +{ + struct netif_saddr_item item; + struct dstr full_name = {0}; + char *ip_dup = bstrdup(ip); + + if (adapter && *adapter) + dstr_printf(&full_name, "[%s] %s", adapter, ip); + else + dstr_copy(&full_name, ip); + + item.name = full_name.array; + item.addr = ip_dup; + + da_push_back(sd->addrs, &item); +} + +static void netif_convert_to_string(char *dest, + struct sockaddr_storage *byte_address) +{ + int family = byte_address->ss_family; + char temp_char[INET6_ADDRSTRLEN] = {0}; + +#ifndef _WIN32 + if (family == AF_INET) + inet_ntop(family, &(((struct sockaddr_in*)byte_address)->sin_addr), + temp_char, INET6_ADDRSTRLEN); + else if (family == AF_INET6) + inet_ntop(family, &(((struct sockaddr_in*)byte_address)->sin_addr), + temp_char, INET6_ADDRSTRLEN); +#else + if (family == AF_INET) + InetNtopA(family, &(((SOCKADDR_IN *)byte_address)->sin_addr), + temp_char, INET6_ADDRSTRLEN); + else if (family == AF_INET6) + InetNtopA(family, &(((SOCKADDR_IN6 *)byte_address)->sin6_addr), + temp_char, INET6_ADDRSTRLEN); +#endif + strncpy(dest, temp_char, INET6_ADDRSTRLEN); +} + +static void netif_push(struct sockaddr *copy_source, + struct netif_saddr_data *saddr_d, + const char *adapter) +{ + char temp_char[INET6_ADDRSTRLEN] = {0}; + struct sockaddr_storage sa = {0}; + + if (copy_source->sa_family == AF_INET) + memcpy(&sa, copy_source, sizeof(struct sockaddr_in)); + else if (copy_source->sa_family == AF_INET6) + memcpy(&sa, copy_source, sizeof(struct sockaddr_in6)); + + netif_convert_to_string(temp_char, &sa); + netif_saddr_data_push_back(saddr_d, temp_char, adapter); +} + +void netif_log_saddrs(struct netif_saddr_data *sd) +{ + for(size_t i = 0; i < sd->addrs.num; i++) + info("\t\t%s", sd->addrs.array[i].name); +} + +bool netif_str_to_addr(struct sockaddr_storage *out, int *addr_len, + const char *addr) +{ + bool ipv6; + + memset(out, 0, sizeof(*out)); + *addr_len = 0; + + if (!addr) + return false; + + ipv6 = (strchr(addr, ':') != NULL); + out->ss_family = ipv6 ? AF_INET6 : AF_INET; + *addr_len = sizeof(*out); + +#ifdef _WIN32 + int ret = WSAStringToAddressA((LPSTR)addr, out->ss_family, NULL, + (LPSOCKADDR)out, addr_len); + if (ret == SOCKET_ERROR) + warn("Could not parse address, error code: %d", GetLastError()); + return ret != SOCKET_ERROR; +#else + struct sockaddr_in *sin = (struct sockaddr_in *)out; + if (inet_pton(out->ss_family, addr, &sin->sin_addr)) { + *addr_len = ipv6 ? + sizeof(struct sockaddr_in6) : + sizeof(struct sockaddr_in); + return true; + } + + return false; +#endif +} + +#ifndef _WIN32 +static inline bool is_loopback(struct ifaddrs *ifa) +{ + const char *n = ifa->ifa_name; + return n && (strcmp(n, "lo") == 0 || strcmp(n, "lo0") == 0); +} + +static inline void netif_get_addrs_nix(struct netif_saddr_data *ifaddrs) +{ + struct ifaddrs *ifaddr, *ifa; + unsigned int family, s; + char host[NI_MAXHOST]; + + if (getifaddrs(&ifaddr) == -1) { + warn("getifaddrs() failed"); + return; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || is_loopback(ifa)) + continue; + + family = ifa->ifa_addr->sa_family; + + if ((family == AF_INET) || (family == AF_INET6)) { + s = getnameinfo(ifa->ifa_addr, + (family == AF_INET) ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6), + host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (s != 0) { + warn("getnameinfo() failed: %s", + gai_strerror(s)); + continue; + } + + netif_push(ifa->ifa_addr, ifaddrs, ifa->ifa_name); + } + } + + freeifaddrs(ifaddr); +} + +#else + +static inline PIP_ADAPTER_ADDRESSES get_adapters(void) +{ + PIP_ADAPTER_ADDRESSES adapter = NULL; + unsigned long ret = 0; + unsigned long out_buf_len = 4096; + unsigned long flags = + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER; + const int max_tries = 3; + int i = 0; + + do { + adapter = (IP_ADAPTER_ADDRESSES*)bmalloc(out_buf_len); + if (!adapter) + return NULL; + + ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapter, + &out_buf_len); + if (ret == ERROR_BUFFER_OVERFLOW) { + bfree(adapter); + adapter = NULL; + } else { + break; + } + i++; + } while ((ret == ERROR_BUFFER_OVERFLOW) && (i < max_tries)); + + if (ret != NO_ERROR && ret != ERROR_NO_DATA) { + LPSTR msg_buf = NULL; + + bfree(adapter); + adapter = NULL; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ret, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msg_buf, 0, NULL); + if (msg_buf) { + warn("Call to GetAdaptersAddresses failed: %s (%d)", + msg_buf, ret); + LocalFree(msg_buf); + } + } + + return adapter; +} + +static inline void netif_get_addrs_win32(struct netif_saddr_data *ifaddrs) +{ + PIP_ADAPTER_ADDRESSES adapter = get_adapters(); + PIP_ADAPTER_UNICAST_ADDRESS unicast = NULL; + PIP_ADAPTER_ADDRESSES cur_adap = NULL; + SOCKET_ADDRESS socket_addr; + int family; + + if (!adapter) + return; + + for (cur_adap = adapter; !!cur_adap; cur_adap = cur_adap->Next) { + char *adap_name = NULL; + + if (cur_adap->OperStatus != IfOperStatusUp || + cur_adap->IfType == IF_TYPE_SOFTWARE_LOOPBACK) + continue; + + os_wcs_to_utf8_ptr(cur_adap->FriendlyName, 0, &adap_name); + + unicast = cur_adap->FirstUnicastAddress; + + for (; !!unicast; unicast = unicast->Next) { + socket_addr = unicast->Address; + + family = socket_addr.lpSockaddr->sa_family; + if (family == AF_INET || family == AF_INET6) + netif_push(socket_addr.lpSockaddr, ifaddrs, + adap_name); + } + + bfree(adap_name); + } + + bfree(adapter); +} +#endif + +void netif_get_addrs(struct netif_saddr_data *ifaddrs) +{ + da_init(ifaddrs->addrs); + +#ifdef _WIN32 + netif_get_addrs_win32(ifaddrs); +#else + netif_get_addrs_nix(ifaddrs); +#endif +} diff --git a/plugins/obs-outputs/net-if.h b/plugins/obs-outputs/net-if.h new file mode 100644 index 0000000..4ff1c76 --- /dev/null +++ b/plugins/obs-outputs/net-if.h @@ -0,0 +1,76 @@ +/****************************************************************************** + Copyright (C) 2016 B. Lee + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include + +#ifdef _WIN32 +# include +# include +# include +# include +#else + +# ifdef __linux__ +# include +# elif __FreeBSD__ +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# define __NET_IF_GNU_SOURCE__ +# endif //_GNU_SOURCE +# endif //__FreeBSD__ + +# include +# include +# include +# include +# include +# include +# include + +# ifdef __FreeBSD__ +# ifdef ___NET_IF_GNU_SOURCE__ +# undef ___NET_IF_GNU_SOURCE__ +# undef _GNU_SOURCE +# endif +# endif + +#endif + +struct netif_saddr_item { + char *name; + char *addr; +}; + +struct netif_saddr_data { + DARRAY(struct netif_saddr_item) addrs; +}; + +static inline void netif_saddr_data_free(struct netif_saddr_data *data) +{ + for (size_t i = 0; i < data->addrs.num; i++) { + bfree(data->addrs.array[i].name); + bfree(data->addrs.array[i].addr); + } + da_free(data->addrs); +} + +extern bool netif_str_to_addr(struct sockaddr_storage *out, int *addr_len, + const char *addr); +extern void netif_get_addrs(struct netif_saddr_data *ifaddrs); +extern void netif_log_saddrs(struct netif_saddr_data *sd); diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 9fbdc98..822f10e 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -25,6 +25,7 @@ #include "librtmp/rtmp.h" #include "librtmp/log.h" #include "flv-mux.h" +#include "net-if.h" #ifdef _WIN32 #include @@ -42,6 +43,7 @@ #define OPT_DROP_THRESHOLD "drop_threshold_ms" #define OPT_MAX_SHUTDOWN_TIME_SEC "max_shutdown_time_sec" +#define OPT_BIND_IP "bind_ip" //#define TEST_FRAMEDROPS @@ -63,10 +65,12 @@ struct rtmp_stream { os_sem_t *send_sem; os_event_t *stop_event; + uint64_t stop_ts; struct dstr path, key; struct dstr username, password; struct dstr encoder_name; + struct dstr bind_ip; /* frame drop variables */ int64_t drop_threshold_usec; @@ -146,6 +150,7 @@ static void rtmp_stream_destroy(void *data) if (stream->connecting) pthread_join(stream->connect_thread, NULL); + stream->stop_ts = 0; os_event_signal(stream->stop_event); if (active(stream)) { @@ -162,6 +167,7 @@ static void rtmp_stream_destroy(void *data) dstr_free(&stream->username); dstr_free(&stream->password); dstr_free(&stream->encoder_name); + dstr_free(&stream->bind_ip); os_event_destroy(stream->stop_event); os_sem_destroy(stream->send_sem); pthread_mutex_destroy(&stream->packets_mutex); @@ -193,7 +199,7 @@ fail: return NULL; } -static void rtmp_stream_stop(void *data) +static void rtmp_stream_stop(void *data, uint64_t ts) { struct rtmp_stream *stream = data; @@ -203,11 +209,12 @@ static void rtmp_stream_stop(void *data) if (connecting(stream)) pthread_join(stream->connect_thread, NULL); + stream->stop_ts = ts / 1000ULL; os_event_signal(stream->stop_event); if (active(stream)) { - os_sem_post(stream->send_sem); - obs_output_end_data_capture(stream->output); + if (stream->stop_ts == 0) + os_sem_post(stream->send_sem); } } @@ -313,33 +320,6 @@ static int send_packet(struct rtmp_stream *stream, static inline bool send_headers(struct rtmp_stream *stream); -static bool send_remaining_packets(struct rtmp_stream *stream) -{ - struct encoder_packet packet; - uint64_t max_ns = (uint64_t)stream->max_shutdown_time_sec * 1000000000; - uint64_t begin_time_ns = os_gettime_ns(); - - if (!stream->sent_headers) { - if (!send_headers(stream)) - return false; - } - - while (get_next_packet(stream, &packet)) { - if (send_packet(stream, &packet, false, packet.track_idx) < 0) - return false; - - /* Just disconnect if it takes too long to shut down */ - if ((os_gettime_ns() - begin_time_ns) > max_ns) { - info("Took longer than %d second(s) to shut down, " - "automatically stopping connection", - stream->max_shutdown_time_sec); - return false; - } - } - - return true; -} - static void *send_thread(void *data) { struct rtmp_stream *stream = data; @@ -349,11 +329,20 @@ static void *send_thread(void *data) while (os_sem_wait(stream->send_sem) == 0) { struct encoder_packet packet; - if (stopping(stream)) + if (stopping(stream) && stream->stop_ts == 0) { break; + } + if (!get_next_packet(stream, &packet)) continue; + if (stopping(stream)) { + if (packet.sys_dts_usec >= (int64_t)stream->stop_ts) { + obs_free_encoder_packet(&packet); + break; + } + } + if (!stream->sent_headers) { if (!send_headers(stream)) { os_atomic_set_bool(&stream->disconnected, true); @@ -367,12 +356,8 @@ static void *send_thread(void *data) } } - if (!disconnected(stream) && !send_remaining_packets(stream)) - os_atomic_set_bool(&stream->disconnected, true); - if (disconnected(stream)) { info("Disconnected from %s", stream->path.array); - free_packets(stream); } else { info("User stopped the stream"); } @@ -382,8 +367,11 @@ static void *send_thread(void *data) if (!stopping(stream)) { pthread_detach(stream->send_thread); obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED); + } else { + obs_output_end_data_capture(stream->output); } + free_packets(stream); os_event_reset(stream->stop_event); os_atomic_set_bool(&stream->active, false); stream->sent_headers = false; @@ -453,7 +441,6 @@ static inline bool send_headers(struct rtmp_stream *stream) stream->sent_headers = true; size_t i = 0; bool next = true; - bool fail = false; if (!send_audio_header(stream, i++, &next)) return false; @@ -617,6 +604,17 @@ static int try_connect(struct rtmp_stream *stream) set_rtmp_dstr(&stream->rtmp.Link.flashVer, &stream->encoder_name); stream->rtmp.Link.swfUrl = stream->rtmp.Link.tcUrl; + if (dstr_is_empty(&stream->bind_ip) || + dstr_cmp(&stream->bind_ip, "default") == 0) { + memset(&stream->rtmp.m_bindIP, 0, sizeof(stream->rtmp.m_bindIP)); + } else { + bool success = netif_str_to_addr(&stream->rtmp.m_bindIP.addr, + &stream->rtmp.m_bindIP.addrLen, + stream->bind_ip.array); + if (success) + info("Binding to IP"); + } + RTMP_AddStream(&stream->rtmp, stream->key.array); for (size_t idx = 1;; idx++) { @@ -653,6 +651,7 @@ static bool init_connect(struct rtmp_stream *stream) { obs_service_t *service; obs_data_t *settings; + const char *bind_ip; if (stopping(stream)) pthread_join(stream->send_thread, NULL); @@ -680,6 +679,10 @@ static bool init_connect(struct rtmp_stream *stream) (int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD) * 1000; stream->max_shutdown_time_sec = (int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC); + + bind_ip = obs_data_get_string(settings, OPT_BIND_IP); + dstr_copy(&stream->bind_ip, bind_ip); + obs_data_release(settings); return true; } @@ -826,7 +829,7 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet) struct encoder_packet new_packet; bool added_packet = false; - if (disconnected(stream)) + if (disconnected(stream) || !active(stream)) return; if (packet->type == OBS_ENCODER_VIDEO) @@ -854,6 +857,7 @@ static void rtmp_stream_defaults(obs_data_t *defaults) { obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 600); obs_data_set_default_int(defaults, OPT_MAX_SHUTDOWN_TIME_SEC, 5); + obs_data_set_default_string(defaults, OPT_BIND_IP, "default"); } static obs_properties_t *rtmp_stream_properties(void *unused) @@ -861,11 +865,26 @@ static obs_properties_t *rtmp_stream_properties(void *unused) UNUSED_PARAMETER(unused); obs_properties_t *props = obs_properties_create(); + struct netif_saddr_data addrs = {0}; + obs_property_t *p; obs_properties_add_int(props, OPT_DROP_THRESHOLD, obs_module_text("RTMPStream.DropThreshold"), 200, 10000, 100); + p = obs_properties_add_list(props, OPT_BIND_IP, + obs_module_text("RTMPStream.BindIP"), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + + obs_property_list_add_string(p, obs_module_text("Default"), "default"); + + netif_get_addrs(&addrs); + for (size_t i = 0; i < addrs.addrs.num; i++) { + struct netif_saddr_item item = addrs.addrs.array[i]; + obs_property_list_add_string(p, item.name, item.addr); + } + netif_saddr_data_free(&addrs); + return props; } diff --git a/plugins/obs-transitions/CMakeLists.txt b/plugins/obs-transitions/CMakeLists.txt index c6b6f57..3b8838d 100644 --- a/plugins/obs-transitions/CMakeLists.txt +++ b/plugins/obs-transitions/CMakeLists.txt @@ -7,6 +7,7 @@ set(obs-transitions_SOURCES transition-fade.c transition-cut.c transition-fade-to-color.c + transition-luma-wipe.c ) add_library(obs-transitions MODULE diff --git a/plugins/obs-transitions/data/locale/en-US.ini b/plugins/obs-transitions/data/locale/en-US.ini index a207269..3bf1684 100644 --- a/plugins/obs-transitions/data/locale/en-US.ini +++ b/plugins/obs-transitions/data/locale/en-US.ini @@ -11,3 +11,41 @@ Direction.Down="Down" SwipeIn="Swipe In" Color="Color" SwitchPoint="Peak Color Point (percentage)" +LumaWipeTransition="Luma Wipe" +LumaWipe.Image="Image" +LumaWipe.Invert="Invert" +LumaWipe.Softness="Softness" +LumaWipe.Type.BarndoorBottomLeft="Barndoor Bottom Left" +LumaWipe.Type.BarndoorHorizontal="Barndoor Horizontal" +LumaWipe.Type.BarndoorTopLeft="Barndoor Top Left" +LumaWipe.Type.BarndoorVertical="Barndoor Vertical" +LumaWipe.Type.BlindsHorizontal="Blinds Horizontal" +LumaWipe.Type.BoxBottomLeft="Box Bottom Left" +LumaWipe.Type.BoxBottomRight="Box Bottom Right" +LumaWipe.Type.BoxTopLeft="Box Top Left" +LumaWipe.Type.BoxTopRight="Box Top Right" +LumaWipe.Type.Burst="Burst" +LumaWipe.Type.CheckerboardSmall="Checkerboard Small" +LumaWipe.Type.Circles="Circles" +LumaWipe.Type.Clock="Clock" +LumaWipe.Type.Cloud="Cloud" +LumaWipe.Type.Curtain="Curtain" +LumaWipe.Type.Fan="Fan" +LumaWipe.Type.Fractal="Fractal" +LumaWipe.Type.Iris="Iris" +LumaWipe.Type.LinearHorizontal="Linear Horizontal" +LumaWipe.Type.LinearTopLeft="Linear Top Left" +LumaWipe.Type.LinearTopRight="Linear Top Right" +LumaWipe.Type.LinearVertical="Linear Vertical" +LumaWipe.Type.ParallelZigzagHorizontal="Parallel Zigzag Horizontal" +LumaWipe.Type.ParallelZigzagVertical="Parallel Zigzag Vertical" +LumaWipe.Type.Sinus9="Sinus 9" +LumaWipe.Type.Spiral="Spiral" +LumaWipe.Type.Square="Square" +LumaWipe.Type.Squares="Squares" +LumaWipe.Type.Stripes="Stripes" +LumaWipe.Type.StripsHorizontal="Strips Horizontal" +LumaWipe.Type.StripsVertical="Strips Vertical" +LumaWipe.Type.Watercolor="Watercolor" +LumaWipe.Type.ZigzagHorizontal="Zigzag Horizontal" +LumaWipe.Type.ZigzagVertical="Zigzag Vertical" diff --git a/plugins/obs-transitions/data/locale/it-IT.ini b/plugins/obs-transitions/data/locale/it-IT.ini new file mode 100644 index 0000000..7a7c7fb --- /dev/null +++ b/plugins/obs-transitions/data/locale/it-IT.ini @@ -0,0 +1,14 @@ +FadeTransition="Dissolvenza" +CutTransition="Taglio" +SwipeTransition="Scorri" +SlideTransition="Scivola" +FadeToColorTransition="Dissolvenza a colore" +Direction="Direzione" +Direction.Left="Sinistra" +Direction.Right="Destra" +Direction.Up="Sù" +Direction.Down="Giù" +SwipeIn="Scorri verso l'alto" +Color="Colore" +SwitchPoint="Picco Punto Colore (percentuale)" + diff --git a/plugins/obs-transitions/data/locale/nb-NO.ini b/plugins/obs-transitions/data/locale/nb-NO.ini new file mode 100644 index 0000000..56d5b20 --- /dev/null +++ b/plugins/obs-transitions/data/locale/nb-NO.ini @@ -0,0 +1,14 @@ +FadeTransition="Forløpning" +CutTransition="Kutt" +SwipeTransition="Sveip" +SlideTransition="Skyv" +FadeToColorTransition="Forløpning til farge" +Direction="Retning" +Direction.Left="Venstre" +Direction.Right="Høyre" +Direction.Up="Opp" +Direction.Down="Ned" +SwipeIn="Sveip inn" +Color="Farge" +SwitchPoint="Farge ved høydepunkt (prosent)" + diff --git a/plugins/obs-transitions/data/locale/nl-NL.ini b/plugins/obs-transitions/data/locale/nl-NL.ini index 7986bac..df6765f 100644 --- a/plugins/obs-transitions/data/locale/nl-NL.ini +++ b/plugins/obs-transitions/data/locale/nl-NL.ini @@ -1,6 +1,6 @@ FadeTransition="Vervagen" CutTransition="Knippen" -SwipeTransition="Swipe" +SwipeTransition="Vegen" SlideTransition="Slide" FadeToColorTransition="Vervagen naar Kleur" Direction="Richting" diff --git a/plugins/obs-transitions/data/locale/pt-BR.ini b/plugins/obs-transitions/data/locale/pt-BR.ini index e280221..70b8986 100644 --- a/plugins/obs-transitions/data/locale/pt-BR.ini +++ b/plugins/obs-transitions/data/locale/pt-BR.ini @@ -1,5 +1,6 @@ FadeTransition="Esmaecer" CutTransition="Cortar" +SwipeTransition="Deslizar" SlideTransition="Deslizar" FadeToColorTransition="Esmaecer para a Cor" Direction="Direção" diff --git a/plugins/obs-transitions/data/locale/pt-PT.ini b/plugins/obs-transitions/data/locale/pt-PT.ini new file mode 100644 index 0000000..ac3a110 --- /dev/null +++ b/plugins/obs-transitions/data/locale/pt-PT.ini @@ -0,0 +1,14 @@ +FadeTransition="Desvanecer" +CutTransition="Cortar" +SwipeTransition="Deslizar" +SlideTransition="Deslizar" +FadeToColorTransition="Desvanecer para Cor" +Direction="Direção" +Direction.Left="Esquerda" +Direction.Right="Direita" +Direction.Up="Cima" +Direction.Down="Baixo" +SwipeIn="Deslizar para dentro" +Color="Cor" +SwitchPoint="Ponto de pico de Cor (percentagem)" + diff --git a/plugins/obs-transitions/data/locale/zh-TW.ini b/plugins/obs-transitions/data/locale/zh-TW.ini index f9115c3..11246f1 100644 --- a/plugins/obs-transitions/data/locale/zh-TW.ini +++ b/plugins/obs-transitions/data/locale/zh-TW.ini @@ -1,7 +1,14 @@ -Direction="方向:" +FadeTransition="淡入淡出" +CutTransition="直接轉場" +SwipeTransition="滑出" +SlideTransition="推出" +FadeToColorTransition="淡出至指定色彩" +Direction="方向" Direction.Left="左" Direction.Right="右" -Direction.Up="上:" +Direction.Up="上" Direction.Down="下" +SwipeIn="滑入" Color="顏色" +SwitchPoint="顏色峰值點 (百分比)" diff --git a/plugins/obs-transitions/data/luma_wipe_transition.effect b/plugins/obs-transitions/data/luma_wipe_transition.effect new file mode 100644 index 0000000..baab294 --- /dev/null +++ b/plugins/obs-transitions/data/luma_wipe_transition.effect @@ -0,0 +1,61 @@ +// Based rendermix wipe shader +// https://github.com/rectalogic/rendermix-basic-effects/blob/master/assets/com/rendermix/Wipe/Wipe.frag + +uniform float4x4 ViewProj; +uniform texture2d a_tex; +uniform texture2d b_tex; +uniform texture2d l_tex; +uniform float progress; +uniform bool invert; +uniform float softness; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData VSDefault(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv; + return vert_out; +} + +float4 PSLumaWipe(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + float4 a_color = a_tex.Sample(textureSampler, uv); + float4 b_color = b_tex.Sample(textureSampler, uv); + float luma = l_tex.Sample(textureSampler, uv).x; + + if (invert) + luma = 1.0f - luma; + + float time = lerp(0.0f, 1.0f + softness, progress); + + if (luma <= time - softness) + return b_color; + + if (luma >= time) + return a_color; + + float alpha = (time - luma) / softness; + + return lerp(a_color, b_color, alpha); +} + +technique LumaWipe +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSLumaWipe(v_in); + } +} diff --git a/plugins/obs-transitions/data/luma_wipes/barndoor-botleft.png b/plugins/obs-transitions/data/luma_wipes/barndoor-botleft.png new file mode 100644 index 0000000..0e83cfc Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/barndoor-botleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/barndoor-h.png b/plugins/obs-transitions/data/luma_wipes/barndoor-h.png new file mode 100644 index 0000000..c60729e Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/barndoor-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/barndoor-topleft.png b/plugins/obs-transitions/data/luma_wipes/barndoor-topleft.png new file mode 100644 index 0000000..a21fd8f Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/barndoor-topleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/barndoor-v.png b/plugins/obs-transitions/data/luma_wipes/barndoor-v.png new file mode 100644 index 0000000..bb538e5 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/barndoor-v.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/blinds-h.png b/plugins/obs-transitions/data/luma_wipes/blinds-h.png new file mode 100644 index 0000000..8de01de Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/blinds-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/box-botleft.png b/plugins/obs-transitions/data/luma_wipes/box-botleft.png new file mode 100644 index 0000000..0be56f5 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/box-botleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/box-botright.png b/plugins/obs-transitions/data/luma_wipes/box-botright.png new file mode 100644 index 0000000..c2b15e8 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/box-botright.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/box-topleft.png b/plugins/obs-transitions/data/luma_wipes/box-topleft.png new file mode 100644 index 0000000..cbef40e Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/box-topleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/box-topright.png b/plugins/obs-transitions/data/luma_wipes/box-topright.png new file mode 100644 index 0000000..0e55734 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/box-topright.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/burst.png b/plugins/obs-transitions/data/luma_wipes/burst.png new file mode 100644 index 0000000..74e5f4e Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/burst.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/checkerboard-small.png b/plugins/obs-transitions/data/luma_wipes/checkerboard-small.png new file mode 100644 index 0000000..d6fb839 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/checkerboard-small.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/circles.png b/plugins/obs-transitions/data/luma_wipes/circles.png new file mode 100644 index 0000000..2b83a1b Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/circles.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/clock.png b/plugins/obs-transitions/data/luma_wipes/clock.png new file mode 100644 index 0000000..3bd2b87 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/clock.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/cloud.png b/plugins/obs-transitions/data/luma_wipes/cloud.png new file mode 100644 index 0000000..0095f71 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/cloud.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/curtain.png b/plugins/obs-transitions/data/luma_wipes/curtain.png new file mode 100644 index 0000000..552ee17 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/curtain.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/fan.png b/plugins/obs-transitions/data/luma_wipes/fan.png new file mode 100644 index 0000000..4fd4957 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/fan.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/fractal.png b/plugins/obs-transitions/data/luma_wipes/fractal.png new file mode 100644 index 0000000..b875a50 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/fractal.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/iris.png b/plugins/obs-transitions/data/luma_wipes/iris.png new file mode 100644 index 0000000..69e637c Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/iris.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/linear-h.png b/plugins/obs-transitions/data/luma_wipes/linear-h.png new file mode 100644 index 0000000..10ce279 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/linear-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/linear-topleft.png b/plugins/obs-transitions/data/luma_wipes/linear-topleft.png new file mode 100644 index 0000000..c5bf908 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/linear-topleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/linear-topright.png b/plugins/obs-transitions/data/luma_wipes/linear-topright.png new file mode 100644 index 0000000..dfcb76c Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/linear-topright.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/linear-v.png b/plugins/obs-transitions/data/luma_wipes/linear-v.png new file mode 100644 index 0000000..031970a Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/linear-v.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-h.png b/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-h.png new file mode 100644 index 0000000..05e5e57 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-v.png b/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-v.png new file mode 100644 index 0000000..2c9ee23 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-v.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/sinus9.png b/plugins/obs-transitions/data/luma_wipes/sinus9.png new file mode 100644 index 0000000..07dc820 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/sinus9.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/spiral.png b/plugins/obs-transitions/data/luma_wipes/spiral.png new file mode 100644 index 0000000..a07a501 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/spiral.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/square.png b/plugins/obs-transitions/data/luma_wipes/square.png new file mode 100644 index 0000000..f33faf3 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/square.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/squares.png b/plugins/obs-transitions/data/luma_wipes/squares.png new file mode 100644 index 0000000..2cc9ff4 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/squares.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/stripes.png b/plugins/obs-transitions/data/luma_wipes/stripes.png new file mode 100644 index 0000000..84e90e8 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/stripes.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/strips-h.png b/plugins/obs-transitions/data/luma_wipes/strips-h.png new file mode 100644 index 0000000..1b44739 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/strips-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/strips-v.png b/plugins/obs-transitions/data/luma_wipes/strips-v.png new file mode 100644 index 0000000..e4dc434 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/strips-v.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/watercolor.png b/plugins/obs-transitions/data/luma_wipes/watercolor.png new file mode 100644 index 0000000..571b979 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/watercolor.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/wipes.json b/plugins/obs-transitions/data/luma_wipes/wipes.json new file mode 100644 index 0000000..cc297e2 --- /dev/null +++ b/plugins/obs-transitions/data/luma_wipes/wipes.json @@ -0,0 +1,36 @@ +{ + "LumaWipe.Type.BarndoorBottomLeft": "barndoor-botleft.png", + "LumaWipe.Type.BarndoorHorizontal": "barndoor-h.png", + "LumaWipe.Type.BarndoorTopLeft": "barndoor-topleft.png", + "LumaWipe.Type.BarndoorVertical": "barndoor-v.png", + "LumaWipe.Type.BlindsHorizontal": "blinds-h.png", + "LumaWipe.Type.BoxBottomLeft": "box-botleft.png", + "LumaWipe.Type.BoxBottomRight": "box-botright.png", + "LumaWipe.Type.BoxTopLeft": "box-topleft.png", + "LumaWipe.Type.BoxTopRight": "box-topright.png", + "LumaWipe.Type.Burst": "burst.png", + "LumaWipe.Type.CheckerboardSmall": "checkerboard-small.png", + "LumaWipe.Type.Circles": "circles.png", + "LumaWipe.Type.Clock": "clock.png", + "LumaWipe.Type.Cloud": "cloud.png", + "LumaWipe.Type.Curtain": "curtain.png", + "LumaWipe.Type.Fan": "fan.png", + "LumaWipe.Type.Fractal": "fractal.png", + "LumaWipe.Type.Iris": "iris.png", + "LumaWipe.Type.LinearHorizontal": "linear-h.png", + "LumaWipe.Type.LinearTopLeft": "linear-topleft.png", + "LumaWipe.Type.LinearTopRight": "linear-topright.png", + "LumaWipe.Type.LinearVertical": "linear-v.png", + "LumaWipe.Type.ParallelZigzagHorizontal": "parallel-zigzag-h.png", + "LumaWipe.Type.ParallelZigzagVertical": "parallel-zigzag-v.png", + "LumaWipe.Type.Sinus9": "sinus9.png", + "LumaWipe.Type.Spiral": "spiral.png", + "LumaWipe.Type.Square": "square.png", + "LumaWipe.Type.Squares": "squares.png", + "LumaWipe.Type.Stripes": "stripes.png", + "LumaWipe.Type.StripsHorizontal": "strips-h.png", + "LumaWipe.Type.StripsVertical": "strips-v.png", + "LumaWipe.Type.Watercolor": "watercolor.png", + "LumaWipe.Type.ZigzagHorizontal": "zigzag-h.png", + "LumaWipe.Type.ZigzagVertical": "zigzag-v.png" +} diff --git a/plugins/obs-transitions/data/luma_wipes/zigzag-h.png b/plugins/obs-transitions/data/luma_wipes/zigzag-h.png new file mode 100644 index 0000000..c042d49 Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/zigzag-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/zigzag-v.png b/plugins/obs-transitions/data/luma_wipes/zigzag-v.png new file mode 100644 index 0000000..2f4380b Binary files /dev/null and b/plugins/obs-transitions/data/luma_wipes/zigzag-v.png differ diff --git a/plugins/obs-transitions/obs-transitions.c b/plugins/obs-transitions/obs-transitions.c index 351726c..3841f48 100644 --- a/plugins/obs-transitions/obs-transitions.c +++ b/plugins/obs-transitions/obs-transitions.c @@ -9,6 +9,7 @@ extern struct obs_source_info fade_transition; extern struct obs_source_info swipe_transition; extern struct obs_source_info slide_transition; extern struct obs_source_info fade_to_color_transition; +extern struct obs_source_info luma_wipe_transition; bool obs_module_load(void) { @@ -17,5 +18,6 @@ bool obs_module_load(void) obs_register_source(&swipe_transition); obs_register_source(&slide_transition); obs_register_source(&fade_to_color_transition); + obs_register_source(&luma_wipe_transition); return true; } diff --git a/plugins/obs-transitions/transition-luma-wipe.c b/plugins/obs-transitions/transition-luma-wipe.c new file mode 100644 index 0000000..6b8ec2c --- /dev/null +++ b/plugins/obs-transitions/transition-luma-wipe.c @@ -0,0 +1,213 @@ +#include +#include +#include + +#define S_LUMA_IMG "luma_image" +#define S_LUMA_INV "luma_invert" +#define S_LUMA_SOFT "luma_softness" + +#define T_LUMA_IMG obs_module_text("LumaWipe.Image") +#define T_LUMA_INV obs_module_text("LumaWipe.Invert") +#define T_LUMA_SOFT obs_module_text("LumaWipe.Softness") + +struct luma_wipe_info { + obs_source_t *source; + + gs_effect_t *effect; + gs_eparam_t *ep_a_tex; + gs_eparam_t *ep_b_tex; + gs_eparam_t *ep_l_tex; + gs_eparam_t *ep_progress; + gs_eparam_t *ep_invert; + gs_eparam_t *ep_softness; + + gs_image_file_t luma_image; + bool invert_luma; + float softness; + obs_data_t *wipes_list; +}; + +static const char *luma_wipe_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return obs_module_text("LumaWipeTransition"); +} + +static void luma_wipe_update(void *data, obs_data_t *settings) +{ + struct luma_wipe_info *lwipe = data; + + const char *name = obs_data_get_string(settings, S_LUMA_IMG); + lwipe->invert_luma = obs_data_get_bool(settings, S_LUMA_INV); + lwipe->softness = (float)obs_data_get_double(settings, S_LUMA_SOFT); + + struct dstr path = {0}; + + dstr_copy(&path, "luma_wipes/"); + dstr_cat(&path, name); + + char *file = obs_module_file(path.array); + + obs_enter_graphics(); + gs_image_file_free(&lwipe->luma_image); + obs_leave_graphics(); + + gs_image_file_init(&lwipe->luma_image, file); + + obs_enter_graphics(); + gs_image_file_init_texture(&lwipe->luma_image); + obs_leave_graphics(); + + bfree(file); + dstr_free(&path); + + UNUSED_PARAMETER(settings); +} + +static void luma_wipe_get_list(void *data) +{ + struct luma_wipe_info *lwipe = data; + + char *path = obs_module_file("luma_wipes/wipes.json"); + + lwipe->wipes_list = obs_data_create_from_json_file(path); + + bfree(path); +} + +static void *luma_wipe_create(obs_data_t *settings, obs_source_t *source) +{ + struct luma_wipe_info *lwipe; + gs_effect_t *effect; + char *file = obs_module_file("luma_wipe_transition.effect"); + + obs_enter_graphics(); + effect = gs_effect_create_from_file(file, NULL); + obs_leave_graphics(); + + if (!effect) { + blog(LOG_ERROR, "Could not open luma_wipe_transition.effect"); + return NULL; + } + + bfree(file); + + lwipe = bzalloc(sizeof(*lwipe)); + + lwipe->effect = effect; + lwipe->ep_a_tex = gs_effect_get_param_by_name(effect, "a_tex"); + lwipe->ep_b_tex = gs_effect_get_param_by_name(effect, "b_tex"); + lwipe->ep_l_tex = gs_effect_get_param_by_name(effect, "l_tex"); + lwipe->ep_progress = gs_effect_get_param_by_name(effect, "progress"); + lwipe->ep_invert = gs_effect_get_param_by_name(effect, "invert"); + lwipe->ep_softness = gs_effect_get_param_by_name(effect, "softness"); + lwipe->source = source; + + luma_wipe_get_list(lwipe); + + luma_wipe_update(lwipe, settings); + + return lwipe; +} + +static void luma_wipe_destroy(void *data) +{ + struct luma_wipe_info *lwipe = data; + + obs_enter_graphics(); + gs_image_file_free(&lwipe->luma_image); + obs_leave_graphics(); + + obs_data_release(lwipe->wipes_list); + + bfree(lwipe); +} + +static obs_properties_t *luma_wipe_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + struct luma_wipe_info *lwipe = data; + + obs_property_t *p; + + p = obs_properties_add_list(props, S_LUMA_IMG, T_LUMA_IMG, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + + obs_data_item_t *item = obs_data_first(lwipe->wipes_list); + + for (; item != NULL; obs_data_item_next(&item)) { + const char *name = obs_data_item_get_name(item); + const char *path = obs_data_item_get_string(item); + obs_property_list_add_string(p, obs_module_text(name), path); + } + + obs_properties_add_float(props, S_LUMA_SOFT, T_LUMA_SOFT, 0.0, 1.0, 0.05); + obs_properties_add_bool(props, S_LUMA_INV, T_LUMA_INV); + + return props; +} + +static void luma_wipe_defaults(obs_data_t *settings) +{ + obs_data_set_default_string(settings, S_LUMA_IMG, "linear-h.png"); + obs_data_set_default_double(settings, S_LUMA_SOFT, 0.03); + obs_data_set_default_bool(settings, S_LUMA_INV, false); +} + +static void luma_wipe_callback(void *data, gs_texture_t *a, gs_texture_t *b, + float t, uint32_t cx, uint32_t cy) +{ + struct luma_wipe_info *lwipe = data; + + gs_effect_set_texture(lwipe->ep_a_tex, a); + gs_effect_set_texture(lwipe->ep_b_tex, b); + gs_effect_set_texture(lwipe->ep_l_tex, lwipe->luma_image.texture); + gs_effect_set_float(lwipe->ep_progress, t); + + gs_effect_set_bool(lwipe->ep_invert, lwipe->invert_luma); + gs_effect_set_float(lwipe->ep_softness, lwipe->softness); + + while (gs_effect_loop(lwipe->effect, "LumaWipe")) + gs_draw_sprite(NULL, 0, cx, cy); +} + +void luma_wipe_video_render(void *data, gs_effect_t *effect) +{ + struct luma_wipe_info *lwipe = data; + obs_transition_video_render(lwipe->source, luma_wipe_callback); + UNUSED_PARAMETER(effect); +} + +static float mix_a(void *data, float t) +{ + UNUSED_PARAMETER(data); + return 1.0f - t; +} + +static float mix_b(void *data, float t) +{ + UNUSED_PARAMETER(data); + return t; +} + +bool luma_wipe_audio_render(void *data, uint64_t *ts_out, + struct obs_source_audio_mix *audio, uint32_t mixers, + size_t channels, size_t sample_rate) +{ + struct luma_wipe_info *lwipe = data; + return obs_transition_audio_render(lwipe->source, ts_out, audio, mixers, + channels, sample_rate, mix_a, mix_b); +} + +struct obs_source_info luma_wipe_transition = { + .id = "wipe_transition", + .type = OBS_SOURCE_TYPE_TRANSITION, + .get_name = luma_wipe_get_name, + .create = luma_wipe_create, + .destroy = luma_wipe_destroy, + .update = luma_wipe_update, + .video_render = luma_wipe_video_render, + .audio_render = luma_wipe_audio_render, + .get_properties = luma_wipe_properties, + .get_defaults = luma_wipe_defaults +}; diff --git a/plugins/obs-x264/data/locale/et-EE.ini b/plugins/obs-x264/data/locale/et-EE.ini new file mode 100644 index 0000000..4976d32 --- /dev/null +++ b/plugins/obs-x264/data/locale/et-EE.ini @@ -0,0 +1,4 @@ +Bitrate="Bitikiirus" +CustomBufsize="Kasuta kohandatud puhvri suurust" +BufferSize="Puhvri suurus" + diff --git a/plugins/obs-x264/data/locale/fi-FI.ini b/plugins/obs-x264/data/locale/fi-FI.ini index af3aa38..8f59661 100644 --- a/plugins/obs-x264/data/locale/fi-FI.ini +++ b/plugins/obs-x264/data/locale/fi-FI.ini @@ -1,10 +1,10 @@ Bitrate="Bitrate" -CustomBufsize="Käytä valinnaista puskurin kokoa" +CustomBufsize="Käytä mukautettua puskurin kokoa" BufferSize="Puskurin koko" -RateControl="Nopeudensäädin" +RateControl="Rate Control -tila" CRF="CRF" -KeyframeIntervalSec="Keyframe-väli (sec, 0=auto)" -CPUPreset="CPU:n käyttö (korkeampi vaatii enemmän tehoa)" +KeyframeIntervalSec="Keyframe-väli (sekunteina, 0=automaattinen)" +CPUPreset="Enkooderin asetus (hitaampi asetus lisää suorittimen käyttöä)" Profile="Profiili" Tune="Hienosäätö" None="(Ei mitään)" diff --git a/plugins/obs-x264/data/locale/he-IL.ini b/plugins/obs-x264/data/locale/he-IL.ini index 2962f17..2f374bb 100644 --- a/plugins/obs-x264/data/locale/he-IL.ini +++ b/plugins/obs-x264/data/locale/he-IL.ini @@ -1,6 +1,7 @@ Bitrate="קצב ביטים" CustomBufsize="השתמש בגודל אוגר מותאם אישית" BufferSize="גודל אוגר" +RateControl="בקרת קצב" CRF="קבוע פקטור קצב (CRF)" KeyframeIntervalSec="מרווח ערך ה keyframe בשניות (0=אוטומטי)" CPUPreset="ערך שימוש במעבד (גבוה יותר = פחות מעבד)" diff --git a/plugins/obs-x264/data/locale/hr-HR.ini b/plugins/obs-x264/data/locale/hr-HR.ini index df9f4ac..dde409f 100644 --- a/plugins/obs-x264/data/locale/hr-HR.ini +++ b/plugins/obs-x264/data/locale/hr-HR.ini @@ -1,6 +1,7 @@ Bitrate="Protok" CustomBufsize="Koristi specifičnu veličinu bafera" BufferSize="Veličina buffer-a" +RateControl="Kontrola protoka" CRF="CRF" KeyframeIntervalSec="Keyframe interval (sekunde, 0=automatski)" CPUPreset="Koristi CPU šablon (veće = manje CPU-a)" diff --git a/plugins/obs-x264/data/locale/it-IT.ini b/plugins/obs-x264/data/locale/it-IT.ini index 3abe79a..dd6ffc2 100644 --- a/plugins/obs-x264/data/locale/it-IT.ini +++ b/plugins/obs-x264/data/locale/it-IT.ini @@ -1,6 +1,7 @@ Bitrate="Bitrate" CustomBufsize="Usa dimensione personalizzata del buffer" BufferSize="Grandezza buffer" +RateControl="Controllo frequenza" CRF="CRF" KeyframeIntervalSec="Intervallo keyframe (secondi, 0=auto)" CPUPreset="Preset di utilizzo della CPU (superiore = meno CPU)" diff --git a/plugins/obs-x264/data/locale/nb-NO.ini b/plugins/obs-x264/data/locale/nb-NO.ini index 42b36f0..5a94692 100644 --- a/plugins/obs-x264/data/locale/nb-NO.ini +++ b/plugins/obs-x264/data/locale/nb-NO.ini @@ -1,6 +1,7 @@ Bitrate="Bitrate" CustomBufsize="Bruk egendefinert bufferstørrelse" BufferSize="Buffer størrelse" +RateControl="Hastighetskontroll" CRF="CRF" KeyframeIntervalSec="Nøkkelbildeintervall (sekunder, 0 = automatisk)" CPUPreset="Forhåndsinstilling for prosessorbruk (raskere betyr mindre belastning)" diff --git a/plugins/obs-x264/data/locale/pl-PL.ini b/plugins/obs-x264/data/locale/pl-PL.ini index c26d199..78b80c1 100644 --- a/plugins/obs-x264/data/locale/pl-PL.ini +++ b/plugins/obs-x264/data/locale/pl-PL.ini @@ -1,6 +1,7 @@ Bitrate="Przepływność bitowa" CustomBufsize="Użyj własnego rozmiaru bufora" BufferSize="Rozmiar bufora" +RateControl="Typ przepływności" CRF="CRF" KeyframeIntervalSec="Interwał klatek kluczowych (sekundy, 0 - auto)" CPUPreset="Ustawienie obciążenia CPU (wyższe = mniej CPU)" diff --git a/plugins/obs-x264/data/locale/pt-BR.ini b/plugins/obs-x264/data/locale/pt-BR.ini index 910c81d..906328f 100644 --- a/plugins/obs-x264/data/locale/pt-BR.ini +++ b/plugins/obs-x264/data/locale/pt-BR.ini @@ -1,6 +1,7 @@ Bitrate="Taxa de Bits" CustomBufsize="Utilizar tamanho do buffer personalizado" BufferSize="Tamanho do Buffer" +RateControl="Controle de Taxa" CRF="CRF" KeyframeIntervalSec="Intervalo de Keyframe (segundos, 0 = auto)" CPUPreset="Predefinição de utilização do processador (maior = menos processamento)" diff --git a/plugins/obs-x264/data/locale/pt-PT.ini b/plugins/obs-x264/data/locale/pt-PT.ini index d0f9140..124ea79 100644 --- a/plugins/obs-x264/data/locale/pt-PT.ini +++ b/plugins/obs-x264/data/locale/pt-PT.ini @@ -1,6 +1,7 @@ Bitrate="Bitrate" CustomBufsize="Utilizar tamanho do buffer personalizado" BufferSize="Tamanho do buffer" +RateControl="Controle de Taxa" CRF="CRF" KeyframeIntervalSec="Intervalo do keyframe (segundos, 0=automático)" CPUPreset="Predefinição de utilização do processador (maior = menos processamento)" diff --git a/plugins/obs-x264/data/locale/sr-CS.ini b/plugins/obs-x264/data/locale/sr-CS.ini index df9f4ac..dde409f 100644 --- a/plugins/obs-x264/data/locale/sr-CS.ini +++ b/plugins/obs-x264/data/locale/sr-CS.ini @@ -1,6 +1,7 @@ Bitrate="Protok" CustomBufsize="Koristi specifičnu veličinu bafera" BufferSize="Veličina buffer-a" +RateControl="Kontrola protoka" CRF="CRF" KeyframeIntervalSec="Keyframe interval (sekunde, 0=automatski)" CPUPreset="Koristi CPU šablon (veće = manje CPU-a)" diff --git a/plugins/obs-x264/data/locale/sr-SP.ini b/plugins/obs-x264/data/locale/sr-SP.ini index 1ad6370..181c946 100644 --- a/plugins/obs-x264/data/locale/sr-SP.ini +++ b/plugins/obs-x264/data/locale/sr-SP.ini @@ -1,6 +1,7 @@ Bitrate="Проток" CustomBufsize="Користи специфичну величину бафера" BufferSize="Величина buffer-а" +RateControl="Контрола протока" CRF="CRF" KeyframeIntervalSec="Keyframe интервал (секунде, 0=аутоматски)" CPUPreset="Користи CPU шаблон (веће = мање CPU-а)" diff --git a/plugins/obs-x264/data/locale/sv-SE.ini b/plugins/obs-x264/data/locale/sv-SE.ini index 4550f89..cd1237a 100644 --- a/plugins/obs-x264/data/locale/sv-SE.ini +++ b/plugins/obs-x264/data/locale/sv-SE.ini @@ -1,6 +1,7 @@ Bitrate="Bithastighet" CustomBufsize="Använd anpassad buffertstorlek" BufferSize="Buffertstorlek" +RateControl="Hastighetskontroll" CRF="CRF" KeyframeIntervalSec="Keyframe-tidsintervall (i sek, 0=auto)" CPUPreset="CPU-användning förinställning (högre = mindre CPU)" diff --git a/plugins/obs-x264/data/locale/zh-TW.ini b/plugins/obs-x264/data/locale/zh-TW.ini index 00d68cf..3f9ad80 100644 --- a/plugins/obs-x264/data/locale/zh-TW.ini +++ b/plugins/obs-x264/data/locale/zh-TW.ini @@ -1,12 +1,13 @@ -Bitrate="流量" +Bitrate="位元率" CustomBufsize="使用自訂的緩衝區大小" BufferSize="緩衝區大小" +RateControl="位元率控制" CRF="CRF" -KeyframeIntervalSec="關鍵影格間隔 (秒,0 = 自動):" +KeyframeIntervalSec="關鍵訊框間隔 (秒,0 = 自動):" CPUPreset="CPU 使用率設定 (越快 = 越少CPU使用率)" -Profile="演算法" -Tune="最佳化" -None="(空)" -EncoderOptions="x264 選項 (用空格分開)" -VFR="變動式流量 (VFR)" +Profile="配置" +Tune="微調類別" +None="(無)" +EncoderOptions="x264 參數(以空格分隔)" +VFR="可變畫面播放速率 (VFR)" diff --git a/plugins/obs-x264/obs-x264.c b/plugins/obs-x264/obs-x264.c index 193e445..27bbe8a 100644 --- a/plugins/obs-x264/obs-x264.c +++ b/plugins/obs-x264/obs-x264.c @@ -97,7 +97,7 @@ static void obs_x264_defaults(obs_data_t *settings) obs_data_set_default_int (settings, "keyint_sec", 0); obs_data_set_default_int (settings, "crf", 23); obs_data_set_default_bool (settings, "vfr", false); - obs_data_set_default_bool (settings, "rate_control","CBR"); + obs_data_set_default_string(settings, "rate_control","CBR"); obs_data_set_default_string(settings, "preset", "veryfast"); obs_data_set_default_string(settings, "profile", ""); @@ -142,6 +142,7 @@ static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, obs_data_t *settings) { const char *rc = obs_data_get_string(settings, "rate_control"); + bool use_bufsize = obs_data_get_bool(settings, "use_bufsize"); bool abr = astrcmpi(rc, "CBR") == 0 || astrcmpi(rc, "ABR") == 0; bool rc_crf = astrcmpi(rc, "CRF") == 0; @@ -152,8 +153,8 @@ static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, obs_property_set_visible(p, !rc_crf); p = obs_properties_get(ppts, "use_bufsize"); obs_property_set_visible(p, !rc_crf); - p = obs_properties_get(ppts, "buffse_size"); - obs_property_set_visible(p, !rc_crf); + p = obs_properties_get(ppts, "buffer_size"); + obs_property_set_visible(p, !rc_crf && use_bufsize); return true; } @@ -416,11 +417,7 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t *settings, rate_control = "CBR"; } - if (astrcmpi(rate_control, "CBR") == 0) { - rc = RATE_CONTROL_CBR; - crf = 0; - - } else if (astrcmpi(rate_control, "ABR") == 0) { + if (astrcmpi(rate_control, "ABR") == 0) { rc = RATE_CONTROL_ABR; crf = 0; @@ -431,6 +428,10 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t *settings, rc = RATE_CONTROL_CRF; bitrate = 0; buffer_size = 0; + + } else { /* CBR */ + rc = RATE_CONTROL_CBR; + crf = 0; } if (keyint_sec) diff --git a/plugins/rtmp-services/data/locale/zh-TW.ini b/plugins/rtmp-services/data/locale/zh-TW.ini index 0be7e0d..cbd62a7 100644 --- a/plugins/rtmp-services/data/locale/zh-TW.ini +++ b/plugins/rtmp-services/data/locale/zh-TW.ini @@ -6,4 +6,5 @@ StreamKey="串流金鑰" UseAuth="使用身份驗證" Username="使用者名稱" Password="密碼" +ShowAll="顯示所有服務" diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index a8a8b63..d323130 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 21, + "version": 29, "files": [ { "name": "services.json", - "version": 21 + "version": 29 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 50a26d7..1cc56da 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -134,7 +134,7 @@ "recommended": { "keyint": 4, "profile": "main", - "max video bitrate": 9000, + "max video bitrate": 18000, "max audio bitrate": 160 } }, @@ -324,43 +324,47 @@ ] }, { - "name": "JOICASTER", + "name": "Switchboard Live (Joicaster)", "servers": [ { - "name": "U.S. - Washington, DC", - "url": "rtmp://ingest-1.wdc01.joicaster.co/live" + "name": "Default - Performance Global", + "url": "rtmp://ingest-global-a.switchboard.zone/live" }, { "name": "U.S. - Dallas, TX", - "url": "rtmp://ingest-1.dal09.joicaster.co/live" + "url": "rtmp://ingest-us-dal.switchboard.zone/live" }, { "name": "U.S. - Seattle, WA", - "url": "rtmp://ingest-1.sea01.joicaster.co/live" + "url": "rtmp://ingest-us-sea.switchboard.zone/live" + }, + { + "name": "U.S. - Washington, DC", + "url": "rtmp://ingest-us-wdc.switchboard.zone/live" }, { "name": "C.A. - Toronto, Canada", - "url": "rtmp://ingest-1.tor01.joicaster.co/live" + "url": "rtmp://ingest-cn-tor.switchboard.zone/live" }, { "name": "E.U. - London, England", - "url": "rtmp://ingest-1.lon02.joicaster.co/live" + "url": "rtmp://ingest-eu-lon.switchboard.zone/live" }, { "name": "E.U. - Amsterdam, Netherlands", - "url": "rtmp://ingest-1.ams03.joicaster.co/live" + "url": "rtmp://ingest-as-tok.switchboard.zone/live" }, { "name": "A.S. - Tokyo, Japan", - "url": "rtmp://ingest-1.tok02.joicaster.co/live" + "url": "rtmp://ingest-as-tok.switchboard.zone/live" }, { "name": "A.U. - Melbourne, Australia", - "url": "rtmp://ingest-1.mel01.joicaster.co/live" + "url": "rtmp://ingest-au-mel.switchboard.zone/live" }, { "name": "S.A - São Paulo, Brazil", - "url": "rtmp://ingest-1.sao01.joicaster.co/live" + "url": "rtmp://ingest-sa-sao.switchboard.zone/live" } ] }, @@ -421,6 +425,45 @@ } ] }, + { + "name": "Kamcord", + "servers": [ + { + "name": "US East (N. Virginia)", + "url": "rtmp://us-east-1.stream.kamcord.com/live" + }, + { + "name": "Asia Pacific (Seoul)", + "url": "rtmp://ap-northeast-2.stream.kamcord.com/live" + }, + { + "name": "Asia Pacific (Sydney)", + "url": "rtmp://ap-southeast-2.stream.kamcord.com/live" + }, + { + "name": "Asia Pacific (Tokyo)", + "url": "rtmp://ap-northeast-1.stream.kamcord.com/live" + }, + { + "name": "EU (Frankfurt)", + "url": "rtmp://eu-central-1.stream.kamcord.com/live" + }, + { + "name": "South America (Sao Paulo)", + "url": "rtmp://sa-east-1.stream.kamcord.com/live" + }, + { + "name": "US West (N. California)", + "url": "rtmp://us-west-1.stream.kamcord.com/live" + } + ], + "recommended": { + "keyint": 2, + "profile": "main", + "max video bitrate": 4000, + "max audio bitrate": 192 + } + }, { "name": "CyberGame.TV", "servers": [ @@ -472,9 +515,9 @@ } ], "recommended": { - "keyint": 1, + "keyint": 2, "profile": "main", - "max video bitrate": 2000, + "max video bitrate": 2500, "max audio bitrate": 160 } }, @@ -490,31 +533,130 @@ "name": "US-West (Los Angeles, California)", "url": "rtmp://us-la.restream.io/live" }, + { + "name": "US-West (Seattle, Washington)", + "url": "rtmp://us-seattle.restream.io/live" + }, + { + "name": "US-Central (Dallas, Texas)", + "url": "rtmp://us-central.restream.io/live" + }, { "name": "US-East (Washington, D.C.)", "url": "rtmp://us-east.restream.io/live" }, { - "name": "US-Central (Dallas, TX)", - "url": "rtmp://us-central.restream.io/live" + "name": "NA-East (Toronto, Canada)", + "url": "rtmp://na-toronto.restream.io/live" }, { "name": "EU-Central (Frankfurt, Germany)", "url": "rtmp://eu.restream.io/live" }, { - "name": "EU-West (London, GB)", + "name": "EU-West (Amsterdam, Netherlands)", + "url": "rtmp://eu-ams.restream.io/live" + }, + { + "name": "EU-West (London, Great Britain)", "url": "rtmp://eu-london.restream.io/live" }, { "name": "Australia (Sydney)", - "url": "rtmp://au-secondary.restream.io/live" + "url": "rtmp://au.restream.io/live" }, { "name": "South America (Sao Paulo, Brazil)", "url": "rtmp://sa.restream.io/live" } + ], + "recommended": { + "keyint": 2 + } + }, + { + "name": "Nood", + "servers": [ + { + "name": "EU Central: Frankfurt, Germany", + "url": "rtmp://broadcast-frf.nood.tv/20D2AB/live" + }, + { + "name": "EU North: Amsterdam, Netherlands", + "url": "rtmp://broadcast-ams.nood.tv/20D2AB/live" + }, + { + "name": "EU West: Stockholm, Sweden", + "url": "rtmp://broadcast-arn.nood.tv/20D2AB/live" + }, + { + "name": "US East: Washington, DC", + "url": "rtmp://broadcast-dca.nood.tv/20D2AB/live" + }, + { + "name": "US West: Los Angeles, CA", + "url": "rtmp://broadcast-oxr.nood.tv/20D2AB/live" + }, + { + "name": "Australia: Sydney", + "url": "rtmp://broadcast-syd.nood.tv/20D2AB/live" + }, + { + "name": "Asia: Hong Kong, China", + "url": "rtmp://broadcast-hhp.nood.tv/20D2AB/live" + } + ], + "recommended": { + "keyint": 2, + "profile": "main", + "max video bitrate": 3500, + "max audio bitrate": 192 + } + }, + { + "name": "Boomstream", + "servers": [ + { + "name": "Default", + "url": "rtmp://live.boomstream.com/live" + } ] + }, + { + "name": "Interactive LifeStream", + "servers": [ + { + "name": "Default", + "url": "rtmp://lifestreamcdn.com/live/_definst_/doPublish=wls36" + } + ] + }, + { + "name": "Stre.am", + "servers": [ + { + "name": "Default", + "url": "rtmp://media.stre.am:1935/live" + } + ], + "recommended": { + "keyint": 2, + "profile": "main", + "max video bitrate": 2500, + "max audio bitrate": 160 + } + }, + { + "name": "Coderwall", + "servers": [ + { + "name": "Primary", + "url": "rtmp://live.coderwall.com/coderwall" + } + ], + "recommended": { + "max video bitrate": 1500 + } } ] } diff --git a/plugins/text-freetype2/data/locale/zh-TW.ini b/plugins/text-freetype2/data/locale/zh-TW.ini index d0a65fc..cf0815e 100644 --- a/plugins/text-freetype2/data/locale/zh-TW.ini +++ b/plugins/text-freetype2/data/locale/zh-TW.ini @@ -1,8 +1,9 @@ +TextFreetype2="文字 (FreeType 2)" Font="字型" Text="文字" TextFile="文字檔案 (UTF-8 或 UTF-16)" TextFileFilter="文字檔案 (*.txt);;" -ChatLogMode="聊天記錄模式(最多 6 行)" +ChatLogMode="聊天記錄模式 (最後 6 行)" Color1="顏色 1" Color2="顏色 2" Outline="外框" diff --git a/plugins/text-freetype2/text-freetype2.c b/plugins/text-freetype2/text-freetype2.c index c1a22ce..b885620 100644 --- a/plugins/text-freetype2/text-freetype2.c +++ b/plugins/text-freetype2/text-freetype2.c @@ -447,8 +447,6 @@ static void *ft2_source_create(obs_data_t *settings, obs_source_t *source) obs_data_set_default_int(settings, "color1", 0xFFFFFFFF); obs_data_set_default_int(settings, "color2", 0xFFFFFFFF); - obs_data_set_default_string(settings, "text", - "The lazy snake jumps over the happy MASKEN."); ft2_source_update(srcdata, settings); diff --git a/plugins/vlc-video/CMakeLists.txt b/plugins/vlc-video/CMakeLists.txt new file mode 100644 index 0000000..bcc95fd --- /dev/null +++ b/plugins/vlc-video/CMakeLists.txt @@ -0,0 +1,41 @@ +project(vlc-video) + +if(DISABLE_VLC) + message(STATUS "VLC video plugin disabled") + return() +endif() + +find_package(LibVLC QUIET) + +if(NOT LIBVLC_INCLUDES_FOUND AND ENABLE_VLC) + message(FATAL_ERROR "LibVLC includes not found but set as enabled") +elseif(NOT LIBVLC_INCLUDES_FOUND) + message(STATUS "LibVLC includes not found, VLC video plugin disabled") + return() +endif() + +include_directories(${LIBVLC_INCLUDE_DIRS}) +add_definitions(${LIBVLC_DEFINITIONS}) + +if(MSVC) + set(vlc-video_PLATFORM_DEPS + w32-pthreads) +endif() + +set(vlc-video_HEADERS + vlc-video-plugin.h + ) + +set(vlc-video_SOURCES + vlc-video-plugin.c + vlc-video-source.c + ) + +add_library(vlc-video MODULE + ${vlc-video_SOURCES} + ${vlc-video_HEADERS}) +target_link_libraries(vlc-video + libobs + ${vlc-video_PLATFORM_DEPS}) + +install_obs_plugin_with_data(vlc-video data) diff --git a/plugins/vlc-video/data/locale/ca-ES.ini b/plugins/vlc-video/data/locale/ca-ES.ini new file mode 100644 index 0000000..fbdf5fb --- /dev/null +++ b/plugins/vlc-video/data/locale/ca-ES.ini @@ -0,0 +1,8 @@ +VLCSource="Origen de vídeo VLC" +Playlist="Llista de reproducció" +LoopPlaylist="Repetir la llista de reproducció" +PlaybackBehavior="Comportament de la visibilitat" +PlaybackBehavior.StopRestart="Aturar quan no sigui visible, reiniciar quan sigui visible" +PlaybackBehavior.PauseUnpause="Pausa quan no sigui visible, reprendre quan sigui visible" +PlaybackBehavior.AlwaysPlay="Reproduir sempre fins i tot quan no sigui visible" + diff --git a/plugins/vlc-video/data/locale/cs-CZ.ini b/plugins/vlc-video/data/locale/cs-CZ.ini new file mode 100644 index 0000000..97581e7 --- /dev/null +++ b/plugins/vlc-video/data/locale/cs-CZ.ini @@ -0,0 +1,8 @@ +VLCSource="Video zdroj VLC" +Playlist="Seznam skladeb" +LoopPlaylist="Opakovat seznam skladeb" +PlaybackBehavior="Závislost na viditelnosti" +PlaybackBehavior.StopRestart="Zastavit při skrytém, restartovat při obnovení" +PlaybackBehavior.PauseUnpause="Pozastavit při skrytém, pokračovat při obnovení" +PlaybackBehavior.AlwaysPlay="Přehrát vždy (i když není vidět)" + diff --git a/plugins/vlc-video/data/locale/de-DE.ini b/plugins/vlc-video/data/locale/de-DE.ini new file mode 100644 index 0000000..0b31791 --- /dev/null +++ b/plugins/vlc-video/data/locale/de-DE.ini @@ -0,0 +1,8 @@ +VLCSource="VLC Videoquellle" +Playlist="Wiedergabeliste" +LoopPlaylist="Wiedergabeliste wiederholen" +PlaybackBehavior="Sichtbarkeitsverhalten" +PlaybackBehavior.StopRestart="Anhalten wenn nicht sichtbar, neustarten wenn sichtbar" +PlaybackBehavior.PauseUnpause="Pausieren wenn nicht sichtbar, fortsetzen wenn sichtbar" +PlaybackBehavior.AlwaysPlay="Immer abspielen, auch wenn nicht sichtbar" + diff --git a/plugins/vlc-video/data/locale/en-US.ini b/plugins/vlc-video/data/locale/en-US.ini new file mode 100644 index 0000000..cd5a768 --- /dev/null +++ b/plugins/vlc-video/data/locale/en-US.ini @@ -0,0 +1,7 @@ +VLCSource="VLC Video Source" +Playlist="Playlist" +LoopPlaylist="Loop Playlist" +PlaybackBehavior="Visibility behavior" +PlaybackBehavior.StopRestart="Stop when not visible, restart when visible" +PlaybackBehavior.PauseUnpause="Pause when not visible, unpause when visible" +PlaybackBehavior.AlwaysPlay="Always play even when not visible" diff --git a/plugins/vlc-video/data/locale/es-ES.ini b/plugins/vlc-video/data/locale/es-ES.ini new file mode 100644 index 0000000..1ec000d --- /dev/null +++ b/plugins/vlc-video/data/locale/es-ES.ini @@ -0,0 +1,8 @@ +VLCSource="Fuente de vídeo VLC" +Playlist="Lista de reproducción" +LoopPlaylist="Repetir lista de reproducción" +PlaybackBehavior="Comportamiento de la visibilidad" +PlaybackBehavior.StopRestart="Detener cuando no sea visible, reiniciar cuando sea visible" +PlaybackBehavior.PauseUnpause="Pausar cuando no sea visible, reanudar cuando sea visible" +PlaybackBehavior.AlwaysPlay="Reproducir siempre incluso cuando no sea visible" + diff --git a/plugins/vlc-video/data/locale/eu-ES.ini b/plugins/vlc-video/data/locale/eu-ES.ini new file mode 100644 index 0000000..445885b --- /dev/null +++ b/plugins/vlc-video/data/locale/eu-ES.ini @@ -0,0 +1,8 @@ +VLCSource="VLC bideo-iturburua" +Playlist="Erreprodukzio-zerrenda" +LoopPlaylist="Errepikatu erreprodukzio-zerrenda" +PlaybackBehavior="Ikuste-jokabidea" +PlaybackBehavior.StopRestart="Ikusten ez bada gelditu, ikusten denean berrabiarazi" +PlaybackBehavior.PauseUnpause="Ikusten ez bada pausatu, ikusten denean jarraitu" +PlaybackBehavior.AlwaysPlay="Erreproduzitu beti nahiz eta ez ikusi" + diff --git a/plugins/vlc-video/data/locale/fi-FI.ini b/plugins/vlc-video/data/locale/fi-FI.ini new file mode 100644 index 0000000..4760d2d --- /dev/null +++ b/plugins/vlc-video/data/locale/fi-FI.ini @@ -0,0 +1,8 @@ +VLCSource="VLC Video-lähde" +Playlist="Soittolista" +LoopPlaylist="Toista soittolistaa jatkuvasti" +PlaybackBehavior="Näkyvyyden käyttäytyminen" +PlaybackBehavior.StopRestart="Pysäytä toisto kun lähde ei näy. Käynnistä toisto uudelleen, kun se on taas näkyvissä" +PlaybackBehavior.PauseUnpause="Keskeytä toisto kun lähde ei näy. Jatka toistoa, kun se on taas näkyvissä" +PlaybackBehavior.AlwaysPlay="Toista aina, vaikka lähde ei olisi näkyvissä" + diff --git a/plugins/vlc-video/data/locale/fr-FR.ini b/plugins/vlc-video/data/locale/fr-FR.ini new file mode 100644 index 0000000..12b0628 --- /dev/null +++ b/plugins/vlc-video/data/locale/fr-FR.ini @@ -0,0 +1,8 @@ +VLCSource="Source vidéo VLC" +Playlist="Liste de lecture" +LoopPlaylist="Répéter la liste de lecture" +PlaybackBehavior="Comportement de visibilité" +PlaybackBehavior.StopRestart="Arrêter quand elle n'est pas visible, redémarrer lorsqu'elle est visible" +PlaybackBehavior.PauseUnpause="Suspendre lorsqu'elle n'est pas visible, reprendre lorsqu'elle est visible" +PlaybackBehavior.AlwaysPlay="Toujours jouer même lorsqu'elle n'est pas visible" + diff --git a/plugins/vlc-video/data/locale/he-IL.ini b/plugins/vlc-video/data/locale/he-IL.ini new file mode 100644 index 0000000..3636736 --- /dev/null +++ b/plugins/vlc-video/data/locale/he-IL.ini @@ -0,0 +1,8 @@ +VLCSource="מקור וידאו VLC" +Playlist="רשימת השמעה" +LoopPlaylist="לולאת רשימת השמעה" +PlaybackBehavior="התנהגות ניראות" +PlaybackBehavior.StopRestart="עצור כאשר אינו נראה, התחל מחדש כאשר נראה" +PlaybackBehavior.PauseUnpause="השהה כאשר אינו נראה, בטל השהייה כאשר נראה" +PlaybackBehavior.AlwaysPlay="נגן תמיד גם כאשר לא נראה" + diff --git a/plugins/vlc-video/data/locale/hr-HR.ini b/plugins/vlc-video/data/locale/hr-HR.ini new file mode 100644 index 0000000..a136269 --- /dev/null +++ b/plugins/vlc-video/data/locale/hr-HR.ini @@ -0,0 +1,8 @@ +VLCSource="VLC video izvor" +Playlist="Plejlista" +LoopPlaylist="Ponavljanje plejliste" +PlaybackBehavior="Ponašanje pri (ne)vidljivosti" +PlaybackBehavior.StopRestart="Zaustavi kada se ne vidi, ponovi kad se vidi" +PlaybackBehavior.PauseUnpause="Pauziraj kada se ne vidi, odpauziraj kada se vidi" +PlaybackBehavior.AlwaysPlay="Uvek reprodukuj čak i kada se ne vidi" + diff --git a/plugins/vlc-video/data/locale/hu-HU.ini b/plugins/vlc-video/data/locale/hu-HU.ini new file mode 100644 index 0000000..8a4e627 --- /dev/null +++ b/plugins/vlc-video/data/locale/hu-HU.ini @@ -0,0 +1,8 @@ +VLCSource="VLC Videoforrás" +Playlist="Lejátszási lista" +LoopPlaylist="Ismétlődő lejátszási lista" +PlaybackBehavior="Láthatósági opció" +PlaybackBehavior.StopRestart="Leállítás, ha nem látható, újraindul, ha látható" +PlaybackBehavior.PauseUnpause="Szüneteltet, ha nem látható, folytatás, ha látható" +PlaybackBehavior.AlwaysPlay="Mindig lejátsza, akkor is, ha nem látható" + diff --git a/plugins/vlc-video/data/locale/it-IT.ini b/plugins/vlc-video/data/locale/it-IT.ini new file mode 100644 index 0000000..77bfc93 --- /dev/null +++ b/plugins/vlc-video/data/locale/it-IT.ini @@ -0,0 +1,8 @@ +VLCSource="Source Video VLC" +Playlist="Playlist" +LoopPlaylist="Riproduci playlist di continuo" +PlaybackBehavior="Comportamento visibilità" +PlaybackBehavior.StopRestart="Interrompi quando non visibile, riavvia quando visibile" +PlaybackBehavior.PauseUnpause="Pausa quando non visibile, riprendi quando visibile" +PlaybackBehavior.AlwaysPlay="Continua anche quando non visibile" + diff --git a/plugins/vlc-video/data/locale/ja-JP.ini b/plugins/vlc-video/data/locale/ja-JP.ini new file mode 100644 index 0000000..c5677a5 --- /dev/null +++ b/plugins/vlc-video/data/locale/ja-JP.ini @@ -0,0 +1,8 @@ +VLCSource="VLC ビデオソース" +Playlist="プレイリスト" +LoopPlaylist="プレイリストをループ再生" +PlaybackBehavior="表示の動作" +PlaybackBehavior.StopRestart="表示されていないときに停止、表示時に再開" +PlaybackBehavior.PauseUnpause="表示されていないときに一時停止、表示時に一時停止を解除" +PlaybackBehavior.AlwaysPlay="表示されていないときにも常に再生" + diff --git a/plugins/vlc-video/data/locale/ko-KR.ini b/plugins/vlc-video/data/locale/ko-KR.ini new file mode 100644 index 0000000..b0e4df7 --- /dev/null +++ b/plugins/vlc-video/data/locale/ko-KR.ini @@ -0,0 +1,8 @@ +VLCSource="VLC 비디오 소스" +Playlist="재생목록" +LoopPlaylist="반복 재생" +PlaybackBehavior="표시 동작 설정" +PlaybackBehavior.StopRestart="보이지 않을 때 중단, 보이면 재시작" +PlaybackBehavior.PauseUnpause="보이지 않을 때 일시 중지, 보이면 일시 중지 해제" +PlaybackBehavior.AlwaysPlay="보이지 않더라도 항상 재생" + diff --git a/plugins/vlc-video/data/locale/nb-NO.ini b/plugins/vlc-video/data/locale/nb-NO.ini new file mode 100644 index 0000000..587d99c --- /dev/null +++ b/plugins/vlc-video/data/locale/nb-NO.ini @@ -0,0 +1,8 @@ +VLCSource="VLC-videokilde" +Playlist="Spilleliste" +LoopPlaylist="Repeter spilleliste" +PlaybackBehavior="Synlighetsadferd" +PlaybackBehavior.StopRestart="Stans når ikke synlig, spill fra starten når synlig" +PlaybackBehavior.PauseUnpause="Stans når ikke synlig, spill når synlig" +PlaybackBehavior.AlwaysPlay="Spill alltid, selv når ikke synlig" + diff --git a/plugins/vlc-video/data/locale/nl-NL.ini b/plugins/vlc-video/data/locale/nl-NL.ini new file mode 100644 index 0000000..b1669a7 --- /dev/null +++ b/plugins/vlc-video/data/locale/nl-NL.ini @@ -0,0 +1,8 @@ +VLCSource="VLC Videobron" +Playlist="Playlist" +LoopPlaylist="Playlist herhalen" +PlaybackBehavior="Zichtbaarheidsgedrag" +PlaybackBehavior.StopRestart="Stop wanneer niet zichtbaar, herstart wanneer zichtbaar" +PlaybackBehavior.PauseUnpause="Pauzeer wanneer niet zichtbaar, hervat wanneer zichtbaar" +PlaybackBehavior.AlwaysPlay="Altijd spelen, zelfs wanneer niet zichtbaar" + diff --git a/plugins/vlc-video/data/locale/pl-PL.ini b/plugins/vlc-video/data/locale/pl-PL.ini new file mode 100644 index 0000000..cc74c42 --- /dev/null +++ b/plugins/vlc-video/data/locale/pl-PL.ini @@ -0,0 +1,8 @@ +VLCSource="Odtwarzacz VLC" +Playlist="Lista odtwarzania" +LoopPlaylist="Powtarzaj listę odtwarzania" +PlaybackBehavior="Zachowanie" +PlaybackBehavior.StopRestart="Zatrzymaj, gdy niewidoczne. Odtwarzaj od początku, gdy widoczne." +PlaybackBehavior.PauseUnpause="Wstrzymaj, gdy niewidoczne. Wznów, gdy widoczne." +PlaybackBehavior.AlwaysPlay="Odtwarzaj cały czas bez względu na widoczność." + diff --git a/plugins/vlc-video/data/locale/pt-BR.ini b/plugins/vlc-video/data/locale/pt-BR.ini new file mode 100644 index 0000000..d6f2f85 --- /dev/null +++ b/plugins/vlc-video/data/locale/pt-BR.ini @@ -0,0 +1,8 @@ +VLCSource="Fonte de vídeo VLC" +Playlist="Lista de reprodução" +LoopPlaylist="Loop Lista de reprodução" +PlaybackBehavior="Comportamento de visibilidade" +PlaybackBehavior.StopRestart="Parar quando não visível, reiniciar quando visível" +PlaybackBehavior.PauseUnpause="Pausa quando não visível, resumir quando visível" +PlaybackBehavior.AlwaysPlay="Resumir sempre mesmo quando não visível" + diff --git a/plugins/vlc-video/data/locale/pt-PT.ini b/plugins/vlc-video/data/locale/pt-PT.ini new file mode 100644 index 0000000..d6f2f85 --- /dev/null +++ b/plugins/vlc-video/data/locale/pt-PT.ini @@ -0,0 +1,8 @@ +VLCSource="Fonte de vídeo VLC" +Playlist="Lista de reprodução" +LoopPlaylist="Loop Lista de reprodução" +PlaybackBehavior="Comportamento de visibilidade" +PlaybackBehavior.StopRestart="Parar quando não visível, reiniciar quando visível" +PlaybackBehavior.PauseUnpause="Pausa quando não visível, resumir quando visível" +PlaybackBehavior.AlwaysPlay="Resumir sempre mesmo quando não visível" + diff --git a/plugins/vlc-video/data/locale/ru-RU.ini b/plugins/vlc-video/data/locale/ru-RU.ini new file mode 100644 index 0000000..b7085c6 --- /dev/null +++ b/plugins/vlc-video/data/locale/ru-RU.ini @@ -0,0 +1,8 @@ +VLCSource="Источник VLC видео" +Playlist="Плейлист" +LoopPlaylist="Циклическое воспроизведение" +PlaybackBehavior="Поведение видимости" +PlaybackBehavior.StopRestart="Остановить, когда не видно, перезагрузить, когда видно" +PlaybackBehavior.PauseUnpause="Пауза, когда не видно, возобновить, когда видно" +PlaybackBehavior.AlwaysPlay="Всегда играть, даже когда не видно" + diff --git a/plugins/vlc-video/data/locale/sr-CS.ini b/plugins/vlc-video/data/locale/sr-CS.ini new file mode 100644 index 0000000..a136269 --- /dev/null +++ b/plugins/vlc-video/data/locale/sr-CS.ini @@ -0,0 +1,8 @@ +VLCSource="VLC video izvor" +Playlist="Plejlista" +LoopPlaylist="Ponavljanje plejliste" +PlaybackBehavior="Ponašanje pri (ne)vidljivosti" +PlaybackBehavior.StopRestart="Zaustavi kada se ne vidi, ponovi kad se vidi" +PlaybackBehavior.PauseUnpause="Pauziraj kada se ne vidi, odpauziraj kada se vidi" +PlaybackBehavior.AlwaysPlay="Uvek reprodukuj čak i kada se ne vidi" + diff --git a/plugins/vlc-video/data/locale/sr-SP.ini b/plugins/vlc-video/data/locale/sr-SP.ini new file mode 100644 index 0000000..6a670cd --- /dev/null +++ b/plugins/vlc-video/data/locale/sr-SP.ini @@ -0,0 +1,8 @@ +VLCSource="VLC видео извор" +Playlist="Плејлиста" +LoopPlaylist="Понављање плејлисте" +PlaybackBehavior="Понашање при (не)видљивости" +PlaybackBehavior.StopRestart="Заустави када се не види, понови када се види" +PlaybackBehavior.PauseUnpause="Паузирај када се не види, одпаузирај када се види" +PlaybackBehavior.AlwaysPlay="Увек репродукуј чак и када се не види" + diff --git a/plugins/vlc-video/data/locale/sv-SE.ini b/plugins/vlc-video/data/locale/sv-SE.ini new file mode 100644 index 0000000..9581e6b --- /dev/null +++ b/plugins/vlc-video/data/locale/sv-SE.ini @@ -0,0 +1,8 @@ +VLCSource="VLC-videokälla" +Playlist="Spellista" +LoopPlaylist="Slinga spellista" +PlaybackBehavior="Synlighetsbeteende" +PlaybackBehavior.StopRestart="Stoppa när den inte syns, starta om när den syns" +PlaybackBehavior.PauseUnpause="Pausa när den inte syns, återuppta när den syns" +PlaybackBehavior.AlwaysPlay="Spela alltid även när den inte syns" + diff --git a/plugins/vlc-video/data/locale/zh-CN.ini b/plugins/vlc-video/data/locale/zh-CN.ini new file mode 100644 index 0000000..307572c --- /dev/null +++ b/plugins/vlc-video/data/locale/zh-CN.ini @@ -0,0 +1,8 @@ +VLCSource="VLC 视频源" +Playlist="播放列表" +LoopPlaylist="循环播放列表" +PlaybackBehavior="可见性的行为" +PlaybackBehavior.StopRestart="不可见时停止, 可见时重启" +PlaybackBehavior.PauseUnpause="不可见时暂停, 可见时取消暂停" +PlaybackBehavior.AlwaysPlay="即使在不可见时也保持播放" + diff --git a/plugins/vlc-video/data/locale/zh-TW.ini b/plugins/vlc-video/data/locale/zh-TW.ini new file mode 100644 index 0000000..c8c005e --- /dev/null +++ b/plugins/vlc-video/data/locale/zh-TW.ini @@ -0,0 +1,8 @@ +VLCSource="VLC視訊來源" +Playlist="播放清單" +LoopPlaylist="循環播放清單" +PlaybackBehavior="播放行為" +PlaybackBehavior.StopRestart="不可見時停止,可見時重新開始" +PlaybackBehavior.PauseUnpause="不可見時暫停,可見時取消暫停" +PlaybackBehavior.AlwaysPlay="即使不可見一樣播放" + diff --git a/plugins/vlc-video/vlc-video-plugin.c b/plugins/vlc-video/vlc-video-plugin.c new file mode 100644 index 0000000..0cbe7aa --- /dev/null +++ b/plugins/vlc-video/vlc-video-plugin.c @@ -0,0 +1,201 @@ +#ifdef _WIN32 +#include +#endif + +#include +#include "vlc-video-plugin.h" + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("vlc-video", "en-US") + +/* libvlc core */ +LIBVLC_NEW libvlc_new_; +LIBVLC_RELEASE libvlc_release_; +LIBVLC_CLOCK libvlc_clock_; +LIBVLC_EVENT_ATTACH libvlc_event_attach_; + +/* libvlc media */ +LIBVLC_MEDIA_NEW_PATH libvlc_media_new_path_; +LIBVLC_MEDIA_RELEASE libvlc_media_release_; +LIBVLC_MEDIA_RELEASE libvlc_media_retain_; + +/* libvlc media player */ +LIBVLC_MEDIA_PLAYER_NEW libvlc_media_player_new_; +LIBVLC_MEDIA_PLAYER_NEW_FROM_MEDIA libvlc_media_player_new_from_media_; +LIBVLC_MEDIA_PLAYER_RELEASE libvlc_media_player_release_; +LIBVLC_VIDEO_SET_CALLBACKS libvlc_video_set_callbacks_; +LIBVLC_VIDEO_SET_FORMAT_CALLBACKS libvlc_video_set_format_callbacks_; +LIBVLC_AUDIO_SET_CALLBACKS libvlc_audio_set_callbacks_; +LIBVLC_AUDIO_SET_FORMAT_CALLBACKS libvlc_audio_set_format_callbacks_; +LIBVLC_MEDIA_PLAYER_PLAY libvlc_media_player_play_; +LIBVLC_MEDIA_PLAYER_STOP libvlc_media_player_stop_; +LIBVLC_MEDIA_PLAYER_GET_TIME libvlc_media_player_get_time_; +LIBVLC_VIDEO_GET_SIZE libvlc_video_get_size_; +LIBVLC_MEDIA_PLAYER_EVENT_MANAGER libvlc_media_player_event_manager_; + +/* libvlc media list */ +LIBVLC_MEDIA_LIST_NEW libvlc_media_list_new_; +LIBVLC_MEDIA_LIST_RELEASE libvlc_media_list_release_; +LIBVLC_MEDIA_LIST_ADD_MEDIA libvlc_media_list_add_media_; +LIBVLC_MEDIA_LIST_LOCK libvlc_media_list_lock_; +LIBVLC_MEDIA_LIST_UNLOCK libvlc_media_list_unlock_; +LIBVLC_MEDIA_LIST_EVENT_MANAGER libvlc_media_list_event_manager_; + +/* libvlc media list player */ +LIBVLC_MEDIA_LIST_PLAYER_NEW libvlc_media_list_player_new_; +LIBVLC_MEDIA_LIST_PLAYER_RELEASE libvlc_media_list_player_release_; +LIBVLC_MEDIA_LIST_PLAYER_PLAY libvlc_media_list_player_play_; +LIBVLC_MEDIA_LIST_PLAYER_PAUSE libvlc_media_list_player_pause_; +LIBVLC_MEDIA_LIST_PLAYER_STOP libvlc_media_list_player_stop_; +LIBVLC_MEDIA_LIST_PLAYER_SET_MEDIA_PLAYER libvlc_media_list_player_set_media_player_; +LIBVLC_MEDIA_LIST_PLAYER_SET_MEDIA_LIST libvlc_media_list_player_set_media_list_; +LIBVLC_MEDIA_LIST_PLAYER_EVENT_MANAGER libvlc_media_list_player_event_manager_; +LIBVLC_MEDIA_LIST_PLAYER_SET_PLAYBACK_MODE libvlc_media_list_player_set_playback_mode_; + +void *libvlc_module = NULL; +libvlc_instance_t *libvlc = NULL; +uint64_t time_start = 0; + +static bool load_vlc_funcs(void) +{ +#define LOAD_VLC_FUNC(func) \ + do { \ + func ## _ = os_dlsym(libvlc_module, #func); \ + if (!func ## _) { \ + blog(LOG_WARNING, "Could not func VLC function %s, " \ + "VLC loading failed", #func); \ + return false; \ + } \ + } while (false) + + /* libvlc core */ + LOAD_VLC_FUNC(libvlc_new); + LOAD_VLC_FUNC(libvlc_release); + LOAD_VLC_FUNC(libvlc_clock); + LOAD_VLC_FUNC(libvlc_event_attach); + + /* libvlc media */ + LOAD_VLC_FUNC(libvlc_media_new_path); + LOAD_VLC_FUNC(libvlc_media_release); + LOAD_VLC_FUNC(libvlc_media_retain); + + /* libvlc media player */ + LOAD_VLC_FUNC(libvlc_media_player_new); + LOAD_VLC_FUNC(libvlc_media_player_new_from_media); + LOAD_VLC_FUNC(libvlc_media_player_release); + LOAD_VLC_FUNC(libvlc_video_set_callbacks); + LOAD_VLC_FUNC(libvlc_video_set_format_callbacks); + LOAD_VLC_FUNC(libvlc_audio_set_callbacks); + LOAD_VLC_FUNC(libvlc_audio_set_format_callbacks); + LOAD_VLC_FUNC(libvlc_media_player_play); + LOAD_VLC_FUNC(libvlc_media_player_stop); + LOAD_VLC_FUNC(libvlc_media_player_get_time); + LOAD_VLC_FUNC(libvlc_video_get_size); + LOAD_VLC_FUNC(libvlc_media_player_event_manager); + + /* libvlc media list */ + LOAD_VLC_FUNC(libvlc_media_list_new); + LOAD_VLC_FUNC(libvlc_media_list_release); + LOAD_VLC_FUNC(libvlc_media_list_add_media); + LOAD_VLC_FUNC(libvlc_media_list_lock); + LOAD_VLC_FUNC(libvlc_media_list_unlock); + LOAD_VLC_FUNC(libvlc_media_list_event_manager); + + /* libvlc media list player */ + LOAD_VLC_FUNC(libvlc_media_list_player_new); + LOAD_VLC_FUNC(libvlc_media_list_player_release); + LOAD_VLC_FUNC(libvlc_media_list_player_play); + LOAD_VLC_FUNC(libvlc_media_list_player_pause); + LOAD_VLC_FUNC(libvlc_media_list_player_stop); + LOAD_VLC_FUNC(libvlc_media_list_player_set_media_player); + LOAD_VLC_FUNC(libvlc_media_list_player_set_media_list); + LOAD_VLC_FUNC(libvlc_media_list_player_event_manager); + LOAD_VLC_FUNC(libvlc_media_list_player_set_playback_mode); + return true; +} + +static bool load_libvlc_module(void) +{ +#ifdef _WIN32 + char *path_utf8 = NULL; + wchar_t path[1024]; + LSTATUS status; + DWORD size; + HKEY key; + + memset(path, 0, 1024 * sizeof(wchar_t)); + + status = RegOpenKeyW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\VideoLAN\\VLC", + &key); + if (status != ERROR_SUCCESS) + return false; + + size = 1024; + status = RegQueryValueExW(key, L"InstallDir", NULL, NULL, + (LPBYTE)path, &size); + if (status == ERROR_SUCCESS) { + wcscat(path, L"\\libvlc.dll"); + os_wcs_to_utf8_ptr(path, 0, &path_utf8); + libvlc_module = os_dlopen(path_utf8); + bfree(path_utf8); + } + + RegCloseKey(key); +#else + +#ifdef __APPLE__ +#define LIBVLC_DIR "/Applications/VLC.app/Contents/MacOS/" +#define LIBVLC_FILE LIBVLC_DIR "lib/libvlc.5.dylib" + setenv("VLC_PLUGIN_PATH", LIBVLC_DIR "plugins", false); +#else +#define LIBVLC_FILE "libvlc.so.5" +#endif + libvlc_module = os_dlopen(LIBVLC_FILE); + +#endif + + return libvlc_module != NULL; +} + +extern struct obs_source_info vlc_source_info; + +bool load_libvlc(void) +{ + if (libvlc) + return true; + + libvlc = libvlc_new_(0, 0); + if (!libvlc) { + blog(LOG_INFO, "Couldn't create libvlc instance"); + return false; + } + + time_start = (uint64_t)libvlc_clock_() * 1000ULL; + return true; +} + +bool obs_module_load(void) +{ + if (!load_libvlc_module()) { + blog(LOG_INFO, "Couldn't find VLC installation, VLC video " + "source disabled"); + return true; + } + + if (!load_vlc_funcs()) + return true; + + blog(LOG_INFO, "VLC found, VLC video source enabled"); + + obs_register_source(&vlc_source_info); + return true; +} + +void obs_module_unload(void) +{ + if (libvlc) + libvlc_release_(libvlc); + if (libvlc_module) + os_dlclose(libvlc_module); +} diff --git a/plugins/vlc-video/vlc-video-plugin.h b/plugins/vlc-video/vlc-video-plugin.h new file mode 100644 index 0000000..9eefb39 --- /dev/null +++ b/plugins/vlc-video/vlc-video-plugin.h @@ -0,0 +1,229 @@ +#include +#include + +#ifdef _MSC_VER +#include +typedef SSIZE_T ssize_t; +#endif + +#include +#include +#include +#include +#include + +extern libvlc_instance_t *libvlc; +extern uint64_t time_start; + +extern bool load_libvlc(void); + +/* libvlc core */ +typedef libvlc_instance_t *(*LIBVLC_NEW)(int argc, const char *const *argv); +typedef void (*LIBVLC_RELEASE)(libvlc_instance_t *p_instance); +typedef int64_t (*LIBVLC_CLOCK)(void); +typedef int (*LIBVLC_EVENT_ATTACH)(libvlc_event_manager_t *p_event_manager, + libvlc_event_type_t i_event_type, + libvlc_callback_t f_callback, + void *user_data); + +/* libvlc media */ +typedef libvlc_media_t *(*LIBVLC_MEDIA_NEW_PATH)( + libvlc_instance_t *p_instance, const char *path); +typedef void (*LIBVLC_MEDIA_RETAIN)(libvlc_media_t *p_md); +typedef void (*LIBVLC_MEDIA_RELEASE)(libvlc_media_t *p_md); + +/* libvlc media player */ +typedef libvlc_media_player_t *(*LIBVLC_MEDIA_PLAYER_NEW)( + libvlc_instance_t *p_libvlc); +typedef libvlc_media_player_t *(*LIBVLC_MEDIA_PLAYER_NEW_FROM_MEDIA)( + libvlc_media_t *p_md); +typedef void (*LIBVLC_MEDIA_PLAYER_RELEASE)( + libvlc_media_player_t *p_mi); +typedef void (*LIBVLC_VIDEO_SET_CALLBACKS)( + libvlc_media_player_t *mp, + libvlc_video_lock_cb lock, + libvlc_video_unlock_cb unlock, + libvlc_video_display_cb display, + void *opaque); +typedef void (*LIBVLC_VIDEO_SET_FORMAT_CALLBACKS)( + libvlc_media_player_t *mp, + libvlc_video_format_cb setup, + libvlc_video_cleanup_cb cleanup); +typedef void (*LIBVLC_AUDIO_SET_CALLBACKS)( + libvlc_media_player_t *mp, + libvlc_audio_play_cb play, + libvlc_audio_pause_cb pause, + libvlc_audio_resume_cb resume, + libvlc_audio_flush_cb flush, + libvlc_audio_drain_cb drain, + void *opaque); +typedef void (*LIBVLC_AUDIO_SET_FORMAT_CALLBACKS)( + libvlc_media_player_t *mp, + libvlc_audio_setup_cb setup, + libvlc_audio_cleanup_cb cleanup); +typedef int (*LIBVLC_MEDIA_PLAYER_PLAY)( + libvlc_media_player_t *p_mi); +typedef void (*LIBVLC_MEDIA_PLAYER_STOP)( + libvlc_media_player_t *p_mi); +typedef libvlc_time_t (*LIBVLC_MEDIA_PLAYER_GET_TIME)( + libvlc_media_player_t *p_mi); +typedef int (*LIBVLC_VIDEO_GET_SIZE)( + libvlc_media_player_t *p_mi, + unsigned num, + unsigned *px, + unsigned *py); +typedef libvlc_event_manager_t *(*LIBVLC_MEDIA_PLAYER_EVENT_MANAGER)( + libvlc_media_player_t *p_mp); + +/* libvlc media list */ +typedef libvlc_media_list_t *(*LIBVLC_MEDIA_LIST_NEW)( + libvlc_instance_t *p_instance); +typedef void (*LIBVLC_MEDIA_LIST_RELEASE)(libvlc_media_list_t *p_ml); +typedef int (*LIBVLC_MEDIA_LIST_ADD_MEDIA)(libvlc_media_list_t *p_ml, + libvlc_media_t *p_md); +typedef void (*LIBVLC_MEDIA_LIST_LOCK)(libvlc_media_list_t *p_ml); +typedef void (*LIBVLC_MEDIA_LIST_UNLOCK)(libvlc_media_list_t *p_ml); +typedef libvlc_event_manager_t *(*LIBVLC_MEDIA_LIST_EVENT_MANAGER)( + libvlc_media_list_t *p_ml); + +/* libvlc media list player */ +typedef libvlc_media_list_player_t *(*LIBVLC_MEDIA_LIST_PLAYER_NEW)( + libvlc_instance_t * p_instance); +typedef void (*LIBVLC_MEDIA_LIST_PLAYER_RELEASE)( + libvlc_media_list_player_t *p_mlp); +typedef void (*LIBVLC_MEDIA_LIST_PLAYER_PLAY)( + libvlc_media_list_player_t *p_mlp); +typedef void (*LIBVLC_MEDIA_LIST_PLAYER_PAUSE)( + libvlc_media_list_player_t *p_mlp); +typedef void (*LIBVLC_MEDIA_LIST_PLAYER_STOP)( + libvlc_media_list_player_t *p_mlp); +typedef void (*LIBVLC_MEDIA_LIST_PLAYER_SET_MEDIA_PLAYER)( + libvlc_media_list_player_t *p_mlp, + libvlc_media_player_t *p_mp); +typedef void (*LIBVLC_MEDIA_LIST_PLAYER_SET_MEDIA_LIST)( + libvlc_media_list_player_t *p_mlp, + libvlc_media_list_t *p_mlist); +typedef libvlc_event_manager_t *(*LIBVLC_MEDIA_LIST_PLAYER_EVENT_MANAGER)( + libvlc_media_list_player_t *p_mlp); +typedef void (*LIBVLC_MEDIA_LIST_PLAYER_SET_PLAYBACK_MODE)( + libvlc_media_list_player_t *p_mlp, + libvlc_playback_mode_t e_mode); + +/* -------------------------------------------------------------------- */ + +/* libvlc core */ +extern LIBVLC_NEW libvlc_new_; +extern LIBVLC_RELEASE libvlc_release_; +extern LIBVLC_CLOCK libvlc_clock_; +extern LIBVLC_EVENT_ATTACH libvlc_event_attach_; + +/* libvlc media */ +extern LIBVLC_MEDIA_NEW_PATH libvlc_media_new_path_; +extern LIBVLC_MEDIA_RELEASE libvlc_media_release_; +extern LIBVLC_MEDIA_RETAIN libvlc_media_retain_; + +/* libvlc media player */ +extern LIBVLC_MEDIA_PLAYER_NEW libvlc_media_player_new_; +extern LIBVLC_MEDIA_PLAYER_NEW_FROM_MEDIA libvlc_media_player_new_from_media_; +extern LIBVLC_MEDIA_PLAYER_RELEASE libvlc_media_player_release_; +extern LIBVLC_VIDEO_SET_CALLBACKS libvlc_video_set_callbacks_; +extern LIBVLC_VIDEO_SET_FORMAT_CALLBACKS libvlc_video_set_format_callbacks_; +extern LIBVLC_AUDIO_SET_CALLBACKS libvlc_audio_set_callbacks_; +extern LIBVLC_AUDIO_SET_FORMAT_CALLBACKS libvlc_audio_set_format_callbacks_; +extern LIBVLC_MEDIA_PLAYER_PLAY libvlc_media_player_play_; +extern LIBVLC_MEDIA_PLAYER_STOP libvlc_media_player_stop_; +extern LIBVLC_MEDIA_PLAYER_GET_TIME libvlc_media_player_get_time_; +extern LIBVLC_VIDEO_GET_SIZE libvlc_video_get_size_; +extern LIBVLC_MEDIA_PLAYER_EVENT_MANAGER libvlc_media_player_event_manager_; + +/* libvlc media list */ +extern LIBVLC_MEDIA_LIST_NEW libvlc_media_list_new_; +extern LIBVLC_MEDIA_LIST_RELEASE libvlc_media_list_release_; +extern LIBVLC_MEDIA_LIST_ADD_MEDIA libvlc_media_list_add_media_; +extern LIBVLC_MEDIA_LIST_LOCK libvlc_media_list_lock_; +extern LIBVLC_MEDIA_LIST_UNLOCK libvlc_media_list_unlock_; +extern LIBVLC_MEDIA_LIST_EVENT_MANAGER libvlc_media_list_event_manager_; + +/* libvlc media list player */ +extern LIBVLC_MEDIA_LIST_PLAYER_NEW libvlc_media_list_player_new_; +extern LIBVLC_MEDIA_LIST_PLAYER_RELEASE libvlc_media_list_player_release_; +extern LIBVLC_MEDIA_LIST_PLAYER_PLAY libvlc_media_list_player_play_; +extern LIBVLC_MEDIA_LIST_PLAYER_PAUSE libvlc_media_list_player_pause_; +extern LIBVLC_MEDIA_LIST_PLAYER_STOP libvlc_media_list_player_stop_; +extern LIBVLC_MEDIA_LIST_PLAYER_SET_MEDIA_PLAYER libvlc_media_list_player_set_media_player_; +extern LIBVLC_MEDIA_LIST_PLAYER_SET_MEDIA_LIST libvlc_media_list_player_set_media_list_; +extern LIBVLC_MEDIA_LIST_PLAYER_EVENT_MANAGER libvlc_media_list_player_event_manager_; +extern LIBVLC_MEDIA_LIST_PLAYER_SET_PLAYBACK_MODE libvlc_media_list_player_set_playback_mode_; + +#define EXTENSIONS_AUDIO \ + "*.3ga;" \ + "*.669;" \ + "*.a52;" \ + "*.aac;" \ + "*.ac3;" \ + "*.adt;" \ + "*.adts;" \ + "*.aif;"\ + "*.aifc;"\ + "*.aiff;"\ + "*.amb;" \ + "*.amr;" \ + "*.aob;" \ + "*.ape;" \ + "*.au;" \ + "*.awb;" \ + "*.caf;" \ + "*.dts;" \ + "*.flac;"\ + "*.it;" \ + "*.kar;" \ + "*.m4a;" \ + "*.m4b;" \ + "*.m4p;" \ + "*.m5p;" \ + "*.mid;" \ + "*.mka;" \ + "*.mlp;" \ + "*.mod;" \ + "*.mpa;" \ + "*.mp1;" \ + "*.mp2;" \ + "*.mp3;" \ + "*.mpc;" \ + "*.mpga;" \ + "*.mus;" \ + "*.oga;" \ + "*.ogg;" \ + "*.oma;" \ + "*.opus;" \ + "*.qcp;" \ + "*.ra;" \ + "*.rmi;" \ + "*.s3m;" \ + "*.sid;" \ + "*.spx;" \ + "*.tak;" \ + "*.thd;" \ + "*.tta;" \ + "*.voc;" \ + "*.vqf;" \ + "*.w64;" \ + "*.wav;" \ + "*.wma;" \ + "*.wv;" \ + "*.xa;" \ + "*.xm" + +#define EXTENSIONS_VIDEO "*.3g2;*.3gp;*.3gp2;*.3gpp;*.amv;*.asf;*.avi;" \ + "*.bik;*.bin;*.crf;*.divx;*.drc;*.dv;*.evo;*.f4v;*.flv;*.gvi;*.gxf;" \ + "*.iso;*.m1v;*.m2v;*.m2t;*.m2ts;*.m4v;*.mkv;*.mov;*.mp2;*.mp2v;*.mp4;" \ + "*.mp4v;*.mpe;*.mpeg;*.mpeg1;*.mpeg2;*.mpeg4;*.mpg;*.mpv2;*.mts;" \ + "*.mtv;*.mxf;*.mxg;*.nsv;*.nuv;*.ogg;*.ogm;*.ogv;*.ogx;*.ps;*.rec;" \ + "*.rm;*.rmvb;*.rpl;*.thp;*.tod;*.ts;*.tts;*.txd;*.vob;*.vro;*.webm;" \ + "*.wm;*.wmv;*.wtv;*.xesc" + +#define EXTENSIONS_PLAYLIST "*.asx;*.b4s;*.cue;*.ifo;*.m3u;*.m3u8;*.pls;" \ + "*.ram;*.rar;*.sdp;*.vlc;*.xspf;*.wax;*.wvx;*.zip;*.conf" + +#define EXTENSIONS_MEDIA EXTENSIONS_VIDEO ";" EXTENSIONS_AUDIO ";" \ + EXTENSIONS_PLAYLIST diff --git a/plugins/vlc-video/vlc-video-source.c b/plugins/vlc-video/vlc-video-source.c new file mode 100644 index 0000000..824d57c --- /dev/null +++ b/plugins/vlc-video/vlc-video-source.c @@ -0,0 +1,721 @@ +#include "vlc-video-plugin.h" +#include +#include +#include +#include + +#define do_log(level, format, ...) \ + blog(level, "[vlc_source: '%s'] " format, \ + obs_source_get_name(ss->source), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) + +#define S_PLAYLIST "playlist" +#define S_LOOP "loop" +#define S_BEHAVIOR "playback_behavior" +#define S_BEHAVIOR_STOP_RESTART "stop_restart" +#define S_BEHAVIOR_PAUSE_UNPAUSE "pause_unpause" +#define S_BEHAVIOR_ALWAYS_PLAY "always_play" + +#define T_(text) obs_module_text(text) +#define T_PLAYLIST T_("Playlist") +#define T_LOOP T_("LoopPlaylist") +#define T_BEHAVIOR T_("PlaybackBehavior") +#define T_BEHAVIOR_STOP_RESTART T_("PlaybackBehavior.StopRestart") +#define T_BEHAVIOR_PAUSE_UNPAUSE T_("PlaybackBehavior.PauseUnpause") +#define T_BEHAVIOR_ALWAYS_PLAY T_("PlaybackBehavior.AlwaysPlay") + +/* ------------------------------------------------------------------------- */ + +struct media_file_data { + char *path; + libvlc_media_t *media; +}; + +enum behavior { + BEHAVIOR_STOP_RESTART, + BEHAVIOR_PAUSE_UNPAUSE, + BEHAVIOR_ALWAYS_PLAY, +}; + +struct vlc_source { + obs_source_t *source; + + libvlc_media_player_t *media_player; + libvlc_media_list_player_t *media_list_player; + + struct obs_source_frame frame; + struct obs_source_audio audio; + size_t audio_capacity; + + pthread_mutex_t mutex; + DARRAY(struct media_file_data) files; + enum behavior behavior; + bool loop; +}; + +static libvlc_media_t *get_media(struct darray *array, const char *path) +{ + DARRAY(struct media_file_data) files; + libvlc_media_t *media = NULL; + + files.da = *array; + + for (size_t i = 0; i < files.num; i++) { + const char *cur_path = files.array[i].path; + + if (strcmp(path, cur_path) == 0) { + media = files.array[i].media; + libvlc_media_retain_(media); + break; + } + } + + return media; +} + +static inline libvlc_media_t *create_media_from_file(const char *file) +{ + return libvlc_media_new_path_(libvlc, file); +} + +static void free_files(struct darray *array) +{ + DARRAY(struct media_file_data) files; + files.da = *array; + + for (size_t i = 0; i < files.num; i++) { + bfree(files.array[i].path); + libvlc_media_release_(files.array[i].media); + } + + da_free(files); +} + +static inline bool chroma_is(const char *chroma, const char *val) +{ + return *(uint32_t*)chroma == *(uint32_t*)val; +} + +static enum video_format convert_vlc_video_format(char *chroma, bool *full) +{ + *full = false; + +#define CHROMA_TEST(val, ret) \ + if (chroma_is(chroma, val)) return ret +#define CHROMA_CONV(val, new_val, ret) \ + do { \ + if (chroma_is(chroma, val)) { \ + *(uint32_t*)chroma = *(uint32_t*)new_val; \ + return ret; \ + } \ + } while (false) +#define CHROMA_CONV_FULL(val, new_val, ret) \ + do { \ + *full = true; \ + CHROMA_CONV(val, new_val, ret); \ + } while (false) + + CHROMA_TEST("RGBA", VIDEO_FORMAT_RGBA); + CHROMA_TEST("BGRA", VIDEO_FORMAT_BGRA); + + /* 4:2:0 formats */ + CHROMA_TEST("NV12", VIDEO_FORMAT_NV12); + CHROMA_TEST("I420", VIDEO_FORMAT_I420); + CHROMA_TEST("IYUV", VIDEO_FORMAT_I420); + CHROMA_CONV("NV21", "NV12", VIDEO_FORMAT_NV12); + CHROMA_CONV("I422", "NV12", VIDEO_FORMAT_NV12); + CHROMA_CONV("Y42B", "NV12", VIDEO_FORMAT_NV12); + CHROMA_CONV("YV12", "NV12", VIDEO_FORMAT_NV12); + CHROMA_CONV("yv12", "NV12", VIDEO_FORMAT_NV12); + + CHROMA_CONV_FULL("J420", "J420", VIDEO_FORMAT_I420); + + /* 4:2:2 formats */ + CHROMA_TEST("UYVY", VIDEO_FORMAT_UYVY); + CHROMA_TEST("UYNV", VIDEO_FORMAT_UYVY); + CHROMA_TEST("UYNY", VIDEO_FORMAT_UYVY); + CHROMA_TEST("Y422", VIDEO_FORMAT_UYVY); + CHROMA_TEST("HDYC", VIDEO_FORMAT_UYVY); + CHROMA_TEST("AVUI", VIDEO_FORMAT_UYVY); + CHROMA_TEST("uyv1", VIDEO_FORMAT_UYVY); + CHROMA_TEST("2vuy", VIDEO_FORMAT_UYVY); + CHROMA_TEST("2Vuy", VIDEO_FORMAT_UYVY); + CHROMA_TEST("2Vu1", VIDEO_FORMAT_UYVY); + + CHROMA_TEST("YUY2", VIDEO_FORMAT_YUY2); + CHROMA_TEST("YUYV", VIDEO_FORMAT_YUY2); + CHROMA_TEST("YUNV", VIDEO_FORMAT_YUY2); + CHROMA_TEST("V422", VIDEO_FORMAT_YUY2); + + CHROMA_TEST("YVYU", VIDEO_FORMAT_YVYU); + + CHROMA_CONV("v210", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("cyuv", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("CYUV", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("VYUY", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("NV16", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("NV61", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("I410", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("I422", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("Y42B", "UYVY", VIDEO_FORMAT_UYVY); + CHROMA_CONV("J422", "UYVY", VIDEO_FORMAT_UYVY); + + /* 4:4:4 formats */ + CHROMA_TEST("I444", VIDEO_FORMAT_I444); + CHROMA_CONV_FULL("J444", "J444", VIDEO_FORMAT_I444); + CHROMA_CONV("YUVA", "RGBA", VIDEO_FORMAT_RGBA); + + /* 4:4:0 formats */ + CHROMA_CONV("I440", "I444", VIDEO_FORMAT_I444); + CHROMA_CONV("J440", "I444", VIDEO_FORMAT_I444); + + /* 4:1:0 formats */ + CHROMA_CONV("YVU9", "NV12", VIDEO_FORMAT_UYVY); + CHROMA_CONV("I410", "NV12", VIDEO_FORMAT_UYVY); + + /* 4:1:1 formats */ + CHROMA_CONV("I411", "NV12", VIDEO_FORMAT_UYVY); + CHROMA_CONV("Y41B", "NV12", VIDEO_FORMAT_UYVY); + + /* greyscale formats */ + CHROMA_TEST("GREY", VIDEO_FORMAT_Y800); + CHROMA_TEST("Y800", VIDEO_FORMAT_Y800); + CHROMA_TEST("Y8 ", VIDEO_FORMAT_Y800); +#undef CHROMA_CONV_FULL +#undef CHROMA_CONV +#undef CHROMA_TEST + + *(uint32_t*)chroma = *(uint32_t*)"BGRA"; + return VIDEO_FORMAT_BGRA; +} + +static inline unsigned get_format_lines(enum video_format format, + unsigned height, size_t plane) +{ + switch (format) { + case VIDEO_FORMAT_I420: + case VIDEO_FORMAT_NV12: + return (plane == 0) ? height : height / 2; + case VIDEO_FORMAT_YVYU: + case VIDEO_FORMAT_YUY2: + case VIDEO_FORMAT_UYVY: + case VIDEO_FORMAT_I444: + case VIDEO_FORMAT_RGBA: + case VIDEO_FORMAT_BGRA: + case VIDEO_FORMAT_BGRX: + case VIDEO_FORMAT_Y800: + return height; + case VIDEO_FORMAT_NONE:; + } + + return 0; +} + +static enum audio_format convert_vlc_audio_format(char *format) +{ +#define AUDIO_TEST(val, ret) \ + if (chroma_is(format, val)) return ret +#define AUDIO_CONV(val, new_val, ret) \ + do { \ + if (chroma_is(format, val)) { \ + *(uint32_t*)format = *(uint32_t*)new_val; \ + return ret; \ + } \ + } while (false) + + AUDIO_TEST("S16N", AUDIO_FORMAT_16BIT); + AUDIO_TEST("S32N", AUDIO_FORMAT_32BIT); + AUDIO_TEST("FL32", AUDIO_FORMAT_FLOAT); + + AUDIO_CONV("U16N", "S16N", AUDIO_FORMAT_16BIT); + AUDIO_CONV("U32N", "S32N", AUDIO_FORMAT_32BIT); + AUDIO_CONV("S24N", "S32N", AUDIO_FORMAT_32BIT); + AUDIO_CONV("U24N", "S32N", AUDIO_FORMAT_32BIT); + AUDIO_CONV("FL64", "FL32", AUDIO_FORMAT_FLOAT); + + AUDIO_CONV("S16I", "S16N", AUDIO_FORMAT_16BIT); + AUDIO_CONV("U16I", "S16N", AUDIO_FORMAT_16BIT); + AUDIO_CONV("S24I", "S32N", AUDIO_FORMAT_32BIT); + AUDIO_CONV("U24I", "S32N", AUDIO_FORMAT_32BIT); + AUDIO_CONV("S32I", "S32N", AUDIO_FORMAT_32BIT); + AUDIO_CONV("U32I", "S32N", AUDIO_FORMAT_32BIT); +#undef AUDIO_CONV +#undef AUDIO_TEST + + *(uint32_t*)format = *(uint32_t*)"FL32"; + return AUDIO_FORMAT_FLOAT; +} + +/* ------------------------------------------------------------------------- */ + +static const char *vlcs_get_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("VLCSource"); +} + +static void vlcs_destroy(void *data) +{ + struct vlc_source *c = data; + + if (c->media_list_player) { + libvlc_media_list_player_stop_(c->media_list_player); + libvlc_media_list_player_release_(c->media_list_player); + } + if (c->media_player) { + libvlc_media_player_release_(c->media_player); + } + + bfree((void*)c->audio.data[0]); + obs_source_frame_free(&c->frame); + + free_files(&c->files.da); + pthread_mutex_destroy(&c->mutex); + bfree(c); +} + +static void *vlcs_video_lock(void *data, void **planes) +{ + struct vlc_source *c = data; + for (size_t i = 0; i < MAX_AV_PLANES && c->frame.data[i] != NULL; i++) + planes[i] = c->frame.data[i]; + return NULL; +} + +static void vlcs_video_display(void *data, void *picture) +{ + struct vlc_source *c = data; + c->frame.timestamp = (uint64_t)libvlc_clock_() * 1000ULL - time_start; + obs_source_output_video(c->source, &c->frame); + + UNUSED_PARAMETER(picture); +} + +static unsigned vlcs_video_format(void **p_data, char *chroma, unsigned *width, + unsigned *height, unsigned *pitches, unsigned *lines) +{ + struct vlc_source *c = *p_data; + enum video_format new_format; + enum video_range_type range; + bool new_range; + size_t i = 0; + + new_format = convert_vlc_video_format(chroma, &new_range); + + libvlc_video_get_size_(c->media_player, 0, width, height); + + /* don't allocate a new frame if format/width/height hasn't changed */ + if (c->frame.format != new_format || + c->frame.width != *width || + c->frame.height != *height) { + obs_source_frame_free(&c->frame); + obs_source_frame_init(&c->frame, new_format, *width, *height); + + c->frame.format = new_format; + c->frame.full_range = new_range; + range = c->frame.full_range ? + VIDEO_RANGE_FULL : VIDEO_RANGE_PARTIAL; + video_format_get_parameters(VIDEO_CS_DEFAULT, range, + c->frame.color_matrix, + c->frame.color_range_min, + c->frame.color_range_max); + } + + while (c->frame.data[i]) { + pitches[i] = (unsigned)c->frame.linesize[i]; + lines[i] = get_format_lines(c->frame.format, *height, i); + i++; + } + + return 1; +} + +static void vlcs_audio_play(void *data, const void *samples, unsigned count, + int64_t pts) +{ + struct vlc_source *c = data; + size_t size = get_audio_size(c->audio.format, c->audio.speakers, count); + + if (c->audio_capacity < count) { + c->audio.data[0] = brealloc((void*)c->audio.data[0], size); + c->audio_capacity = count; + } + + memcpy((void*)c->audio.data[0], samples, size); + c->audio.timestamp = (uint64_t)pts * 1000ULL - time_start; + c->audio.frames = count; + + obs_source_output_audio(c->source, &c->audio); +} + +static int vlcs_audio_setup(void **p_data, char *format, unsigned *rate, + unsigned *channels) +{ + struct vlc_source *c = *p_data; + enum audio_format new_audio_format; + + new_audio_format = convert_vlc_audio_format(format); + if (*channels > 2) + *channels = 2; + + /* don't free audio data if the data is the same format */ + if (c->audio.format == new_audio_format && + c->audio.samples_per_sec == *rate && + c->audio.speakers == (enum speaker_layout)*channels) + return 0; + + c->audio_capacity = 0; + bfree((void*)c->audio.data[0]); + + memset(&c->audio, 0, sizeof(c->audio)); + c->audio.speakers = (enum speaker_layout)*channels; + c->audio.samples_per_sec = *rate; + c->audio.format = new_audio_format; + return 0; +} + +static void add_file(struct vlc_source *c, struct darray *array, + const char *path) +{ + DARRAY(struct media_file_data) new_files; + struct media_file_data data; + struct dstr new_path = {0}; + libvlc_media_t *new_media; + + new_files.da = *array; + + dstr_copy(&new_path, path); +#ifdef _WIN32 + dstr_replace(&new_path, "/", "\\"); +#endif + path = new_path.array; + + new_media = get_media(&c->files.da, path); + + if (!new_media) + new_media = get_media(&new_files.da, path); + if (!new_media) + new_media = create_media_from_file(path); + + if (new_media) { + data.path = new_path.array; + data.media = new_media; + da_push_back(new_files, &data); + } else { + dstr_free(&new_path); + } + + *array = new_files.da; +} + +static bool valid_extension(const char *ext) +{ + struct dstr test = {0}; + bool valid = false; + const char *b; + const char *e; + + if (!ext || !*ext) + return false; + + b = EXTENSIONS_MEDIA + 1; + e = strchr(b, ';'); + + for (;;) { + if (e) dstr_ncopy(&test, b, e - b); + else dstr_copy(&test, b); + + if (dstr_cmp(&test, ext) == 0) { + valid = true; + break; + } + + if (!e) + break; + + b = e + 2; + e = strchr(b, ';'); + } + + dstr_free(&test); + return valid; +} + +static void vlcs_update(void *data, obs_data_t *settings) +{ + DARRAY(struct media_file_data) new_files; + DARRAY(struct media_file_data) old_files; + libvlc_media_list_t *media_list; + struct vlc_source *c = data; + obs_data_array_t *array; + const char *behavior; + size_t count; + + da_init(new_files); + da_init(old_files); + + array = obs_data_get_array(settings, S_PLAYLIST); + count = obs_data_array_count(array); + + c->loop = obs_data_get_bool(settings, S_LOOP); + + behavior = obs_data_get_string(settings, S_BEHAVIOR); + + if (astrcmpi(behavior, S_BEHAVIOR_PAUSE_UNPAUSE) == 0) { + c->behavior = BEHAVIOR_PAUSE_UNPAUSE; + } else if (astrcmpi(behavior, S_BEHAVIOR_ALWAYS_PLAY) == 0) { + c->behavior = BEHAVIOR_ALWAYS_PLAY; + } else { /* S_BEHAVIOR_STOP_RESTART */ + c->behavior = BEHAVIOR_STOP_RESTART; + } + + /* ------------------------------------- */ + /* create new list of sources */ + + for (size_t i = 0; i < count; i++) { + obs_data_t *item = obs_data_array_item(array, i); + const char *path = obs_data_get_string(item, "value"); + os_dir_t *dir = os_opendir(path); + + if (dir) { + struct dstr dir_path = {0}; + struct os_dirent *ent; + + for (;;) { + const char *ext; + + ent = os_readdir(dir); + if (!ent) + break; + if (ent->directory) + continue; + + ext = os_get_path_extension(ent->d_name); + if (!valid_extension(ext)) + continue; + + dstr_copy(&dir_path, path); + dstr_cat_ch(&dir_path, '/'); + dstr_cat(&dir_path, ent->d_name); + add_file(c, &new_files.da, dir_path.array); + } + + dstr_free(&dir_path); + os_closedir(dir); + } else { + add_file(c, &new_files.da, path); + } + + obs_data_release(item); + } + + /* ------------------------------------- */ + /* update settings data */ + + libvlc_media_list_player_stop_(c->media_list_player); + + pthread_mutex_lock(&c->mutex); + old_files.da = c->files.da; + c->files.da = new_files.da; + pthread_mutex_unlock(&c->mutex); + + /* ------------------------------------- */ + /* clean up and restart playback */ + + free_files(&old_files.da); + + media_list = libvlc_media_list_new_(libvlc); + + libvlc_media_list_lock_(media_list); + for (size_t i = 0; i < c->files.num; i++) + libvlc_media_list_add_media_(media_list, + c->files.array[i].media); + libvlc_media_list_unlock_(media_list); + + libvlc_media_list_player_set_media_list_(c->media_list_player, + media_list); + libvlc_media_list_release_(media_list); + + libvlc_media_list_player_set_playback_mode_(c->media_list_player, + c->loop ? libvlc_playback_mode_loop : + libvlc_playback_mode_default); + + if (c->files.num && + (c->behavior == BEHAVIOR_ALWAYS_PLAY || obs_source_active(c->source))) + libvlc_media_list_player_play_(c->media_list_player); + else + obs_source_output_video(c->source, NULL); + + obs_data_array_release(array); +} + +static void vlcs_stopped(const struct libvlc_event_t *event, void *data) +{ + struct vlc_source *c = data; + if (!c->loop) + obs_source_output_video(c->source, NULL); + + UNUSED_PARAMETER(event); +} + +static void *vlcs_create(obs_data_t *settings, obs_source_t *source) +{ + struct vlc_source *c = bzalloc(sizeof(*c)); + c->source = source; + + pthread_mutex_init_value(&c->mutex); + if (pthread_mutex_init(&c->mutex, NULL) != 0) + goto error; + + if (!load_libvlc()) + goto error; + + c->media_list_player = libvlc_media_list_player_new_(libvlc); + if (!c->media_list_player) + goto error; + + c->media_player = libvlc_media_player_new_(libvlc); + if (!c->media_player) + goto error; + + libvlc_media_list_player_set_media_player_(c->media_list_player, + c->media_player); + + libvlc_video_set_callbacks_(c->media_player, + vlcs_video_lock, NULL, vlcs_video_display, + c); + libvlc_video_set_format_callbacks_(c->media_player, + vlcs_video_format, NULL); + + libvlc_audio_set_callbacks_(c->media_player, + vlcs_audio_play, NULL, NULL, NULL, NULL, c); + libvlc_audio_set_format_callbacks_(c->media_player, + vlcs_audio_setup, NULL); + + libvlc_event_manager_t *event_manager; + event_manager = libvlc_media_player_event_manager_(c->media_player); + libvlc_event_attach_(event_manager, libvlc_MediaPlayerEndReached, + vlcs_stopped, c); + + obs_source_update(source, NULL); + + UNUSED_PARAMETER(settings); + return c; + +error: + vlcs_destroy(c); + return NULL; +} + +static void vlcs_activate(void *data) +{ + struct vlc_source *c = data; + + if (c->behavior == BEHAVIOR_STOP_RESTART) { + libvlc_media_list_player_play_(c->media_list_player); + + } else if (c->behavior == BEHAVIOR_PAUSE_UNPAUSE) { + libvlc_media_list_player_play_(c->media_list_player); + } +} + +static void vlcs_deactivate(void *data) +{ + struct vlc_source *c = data; + + if (c->behavior == BEHAVIOR_STOP_RESTART) { + libvlc_media_list_player_stop_(c->media_list_player); + obs_source_output_video(c->source, NULL); + + } else if (c->behavior == BEHAVIOR_PAUSE_UNPAUSE) { + libvlc_media_list_player_pause_(c->media_list_player); + } +} + +static void vlcs_defaults(obs_data_t *settings) +{ + obs_data_set_default_bool(settings, S_LOOP, true); + obs_data_set_default_string(settings, S_BEHAVIOR, + S_BEHAVIOR_STOP_RESTART); +} + +static obs_properties_t *vlcs_properties(void *data) +{ + obs_properties_t *ppts = obs_properties_create(); + struct vlc_source *c = data; + struct dstr filter = {0}; + struct dstr exts = {0}; + struct dstr path = {0}; + obs_property_t *p; + + obs_properties_add_bool(ppts, S_LOOP, T_LOOP); + + if (c) { + pthread_mutex_lock(&c->mutex); + if (c->files.num) { + struct media_file_data *last = da_end(c->files); + const char *slash; + + dstr_copy(&path, last->path); + dstr_replace(&path, "\\", "/"); + slash = strrchr(path.array, '/'); + if (slash) + dstr_resize(&path, slash - path.array + 1); + } + pthread_mutex_unlock(&c->mutex); + } + + p = obs_properties_add_list(ppts, S_BEHAVIOR, T_BEHAVIOR, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, T_BEHAVIOR_STOP_RESTART, + S_BEHAVIOR_STOP_RESTART); + obs_property_list_add_string(p, T_BEHAVIOR_PAUSE_UNPAUSE, + S_BEHAVIOR_PAUSE_UNPAUSE); + obs_property_list_add_string(p, T_BEHAVIOR_ALWAYS_PLAY, + S_BEHAVIOR_ALWAYS_PLAY); + + dstr_cat(&filter, "Media Files ("); + dstr_copy(&exts, EXTENSIONS_MEDIA); + dstr_replace(&exts, ";", " "); + dstr_cat_dstr(&filter, &exts); + + dstr_cat(&filter, ");;Video Files ("); + dstr_copy(&exts, EXTENSIONS_VIDEO); + dstr_replace(&exts, ";", " "); + dstr_cat_dstr(&filter, &exts); + + dstr_cat(&filter, ");;Audio Files ("); + dstr_copy(&exts, EXTENSIONS_AUDIO); + dstr_replace(&exts, ";", " "); + dstr_cat_dstr(&filter, &exts); + + dstr_cat(&filter, ");;Playlist Files ("); + dstr_copy(&exts, EXTENSIONS_PLAYLIST); + dstr_replace(&exts, ";", " "); + dstr_cat_dstr(&filter, &exts); + dstr_cat(&filter, ")"); + + obs_properties_add_editable_list(ppts, S_PLAYLIST, T_PLAYLIST, + OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS, + filter.array, path.array); + dstr_free(&path); + dstr_free(&filter); + dstr_free(&exts); + + return ppts; +} + +struct obs_source_info vlc_source_info = { + .id = "vlc_source", + .type = OBS_SOURCE_TYPE_INPUT, + .output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO, + .get_name = vlcs_get_name, + .create = vlcs_create, + .destroy = vlcs_destroy, + .update = vlcs_update, + .get_defaults = vlcs_defaults, + .get_properties = vlcs_properties, + .activate = vlcs_activate, + .deactivate = vlcs_deactivate +}; diff --git a/test/test-input/test-filter.c b/test/test-input/test-filter.c index f8509cf..0fbeec9 100644 --- a/test/test-input/test-filter.c +++ b/test/test-input/test-filter.c @@ -5,8 +5,9 @@ struct test_filter { gs_effect_t *whatever; }; -static const char *filter_getname(void) +static const char *filter_getname(void *unused) { + UNUSED_PARAMETER(unused); return "Test"; } diff --git a/test/test-input/test-random.c b/test/test-input/test-random.c index 417f142..34ebd15 100644 --- a/test/test-input/test-random.c +++ b/test/test-input/test-random.c @@ -10,8 +10,9 @@ struct random_tex { bool initialized; }; -static const char *random_getname(void) +static const char *random_getname(void *unused) { + UNUSED_PARAMETER(unused); return "20x20 Random Pixel Texture Source (Test)"; } diff --git a/test/test-input/test-sinewave.c b/test/test-input/test-sinewave.c index 402536d..7a920c8 100644 --- a/test/test-input/test-sinewave.c +++ b/test/test-input/test-sinewave.c @@ -58,8 +58,9 @@ static void *sinewave_thread(void *pdata) /* ------------------------------------------------------------------------- */ -static const char *sinewave_getname(void) +static const char *sinewave_getname(void *unused) { + UNUSED_PARAMETER(unused); return "Sinewave Sound Source (Test)"; }