From afb755f8a09cf0018f30f242f1137f928d9bbe36 Mon Sep 17 00:00:00 2001 From: pkv Date: Thu, 22 Sep 2022 11:59:03 +0200 Subject: [PATCH 01/22] obs-filter: Update model for NVIDIA Audio FX This updates the model when swapping effects with NVIDIA Audio Effects. This fixes a bug where the model was not updated. :( Signed-off-by: pkv --- plugins/obs-filters/noise-suppress-filter.c | 33 ++++++++++++++++----- plugins/obs-filters/nvafx-load.h | 6 ++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/plugins/obs-filters/noise-suppress-filter.c b/plugins/obs-filters/noise-suppress-filter.c index 3d0854663..e37d8eb6a 100644 --- a/plugins/obs-filters/noise-suppress-filter.c +++ b/plugins/obs-filters/noise-suppress-filter.c @@ -119,6 +119,7 @@ struct noise_suppress_data { char *model; bool nvafx_initialized; const char *fx; + char *sdk_path; /* Resampler */ audio_resampler_t *nvafx_resampler; @@ -217,6 +218,7 @@ static void noise_suppress_destroy(void *data) audio_resampler_destroy(ng->nvafx_resampler_back); } bfree(ng->model); + bfree(ng->sdk_path); bfree((void *)ng->fx); if (ng->nvafx_enabled) { if (ng->use_nvafx) @@ -425,7 +427,25 @@ static inline enum speaker_layout convert_speaker_layout(uint8_t channels) return SPEAKERS_UNKNOWN; } } +#ifdef LIBNVAFX_ENABLED +static void set_model(void *data, const char *method) +{ + struct noise_suppress_data *ng = data; + const char *file; + if (strcmp(NVAFX_EFFECT_DEREVERB, method) == 0) + file = NVAFX_EFFECT_DEREVERB_MODEL; + else if (strcmp(NVAFX_EFFECT_DEREVERB_DENOISER, method) == 0) + file = NVAFX_EFFECT_DEREVERB_DENOISER_MODEL; + else + file = NVAFX_EFFECT_DENOISER_MODEL; + size_t size = strlen(ng->sdk_path) + strlen(file) + 1; + char *buffer = (char *)bmalloc(size); + strcpy(buffer, ng->sdk_path); + strcat(buffer, file); + ng->model = buffer; +} +#endif static void noise_suppress_update(void *data, obs_data_t *s) { struct noise_suppress_data *ng = data; @@ -444,6 +464,8 @@ static void noise_suppress_update(void *data, obs_data_t *s) strcmp(method, S_METHOD_NVAFX_DEREVERB) == 0 || strcmp(method, S_METHOD_NVAFX_DEREVERB_DENOISER) == 0; #ifdef LIBNVAFX_ENABLED + if (nvafx_requested) + set_model(ng, method); float intensity = (float)obs_data_get_double(s, S_NVAFX_INTENSITY); if (ng->use_nvafx && ng->nvafx_initialized) { if (intensity != ng->intensity_ratio && @@ -469,6 +491,7 @@ static void noise_suppress_update(void *data, obs_data_t *s) bfree((void *)ng->fx); ng->fx = bstrdup(method); ng->intensity_ratio = intensity; + set_model(ng, method); os_atomic_set_bool(&ng->reinit_done, false); pthread_mutex_lock(&ng->nvafx_mutex); for (int i = 0; i < (int)ng->channels; i++) { @@ -736,13 +759,9 @@ static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter) ng->nvafx_enabled = false; do_log(LOG_ERROR, "NVAFX redist is not installed."); } else { - const char *file = "\\models\\denoiser_48k.trtpkg"; - size_t size = strlen(sdk_path) + strlen(file) + 1; - char *buffer = (char *)bmalloc(size); - - strcpy(buffer, sdk_path); - strcat(buffer, file); - ng->model = buffer; + size_t size = sizeof(sdk_path) + 1; + ng->sdk_path = bmalloc(size); + strcpy(ng->sdk_path, sdk_path); ng->nvafx_enabled = true; ng->nvafx_initialized = false; ng->nvafx_loading = false; diff --git a/plugins/obs-filters/nvafx-load.h b/plugins/obs-filters/nvafx-load.h index 9725d814c..1b128e137 100644 --- a/plugins/obs-filters/nvafx-load.h +++ b/plugins/obs-filters/nvafx-load.h @@ -18,6 +18,12 @@ static HMODULE nv_cuda = NULL; #define NVAFX_EFFECT_AEC "aec" #define NVAFX_EFFECT_SUPERRES "superres" +/** Model paths */ +#define NVAFX_EFFECT_DENOISER_MODEL "\\models\\denoiser_48k.trtpkg" +#define NVAFX_EFFECT_DEREVERB_MODEL "\\models\\dereverb_48k.trtpkg" +#define NVAFX_EFFECT_DEREVERB_DENOISER_MODEL \ + "\\models\\dereverb_denoiser_48k.trtpkg" + #define NVAFX_CHAINED_EFFECT_DENOISER_16k_SUPERRES_16k_TO_48k \ "denoiser16k_superres16kto48k" #define NVAFX_CHAINED_EFFECT_DEREVERB_16k_SUPERRES_16k_TO_48k \ From 087ee39e2d8b9240b2ada1492fc7ad85102084ab Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 22 Sep 2022 09:40:51 -0700 Subject: [PATCH 02/22] libobs: Update version to 28.0.2 --- libobs/obs-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 2a33fdc6a..a61b448ac 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -41,7 +41,7 @@ * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 1 +#define LIBOBS_API_PATCH_VER 2 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) From 19d92a29a251a16f80f4acf9ce979be5f0598b52 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Thu, 22 Sep 2022 05:29:54 -0500 Subject: [PATCH 03/22] UI: Fix dock titlebar icons not loading The icons had the wrong name in the qss files. The docks were missing the icons on at least Linux. I have no idea why the dock icons still worked on Windows though. --- UI/data/themes/Acri.qss | 4 ++-- UI/data/themes/Grey.qss | 4 ++-- UI/data/themes/Light.qss | 4 ++-- UI/data/themes/Rachni.qss | 4 ++-- UI/data/themes/Yami.qss | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss index 50f0ea79a..4e480a4ba 100644 --- a/UI/data/themes/Acri.qss +++ b/UI/data/themes/Acri.qss @@ -300,8 +300,8 @@ QDockWidget { font-size: 10.5pt; font-weight: bold; - titlebar-close-icon: url('./Dark/Close.svg'); - titlebar-normal-icon: url('./Dark/Popout.svg'); + titlebar-close-icon: url('./Dark/close.svg'); + titlebar-normal-icon: url('./Dark/popout.svg'); } QDockWidget::title { diff --git a/UI/data/themes/Grey.qss b/UI/data/themes/Grey.qss index 34a6cdf1e..e5986648e 100644 --- a/UI/data/themes/Grey.qss +++ b/UI/data/themes/Grey.qss @@ -300,8 +300,8 @@ QDockWidget { font-size: 10.5pt; font-weight: bold; - titlebar-close-icon: url('./Dark/Close.svg'); - titlebar-normal-icon: url('./Dark/Popout.svg'); + titlebar-close-icon: url('./Dark/close.svg'); + titlebar-normal-icon: url('./Dark/popout.svg'); } QDockWidget::title { diff --git a/UI/data/themes/Light.qss b/UI/data/themes/Light.qss index 1e6c8eb61..d8e7e968a 100644 --- a/UI/data/themes/Light.qss +++ b/UI/data/themes/Light.qss @@ -300,8 +300,8 @@ QDockWidget { font-size: 10.5pt; font-weight: bold; - titlebar-close-icon: url('./Light/Close.svg'); - titlebar-normal-icon: url('./Light/Popout.svg'); + titlebar-close-icon: url('./Light/close.svg'); + titlebar-normal-icon: url('./Light/popout.svg'); } QDockWidget::title { diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index 9f729a1e8..db7baac4f 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -302,8 +302,8 @@ QDockWidget { font-size: 10.5pt; font-weight: bold; - titlebar-close-icon: url('./Dark/Close.svg'); - titlebar-normal-icon: url('./Dark/Popout.svg'); + titlebar-close-icon: url('./Dark/close.svg'); + titlebar-normal-icon: url('./Dark/popout.svg'); } QDockWidget::title { diff --git a/UI/data/themes/Yami.qss b/UI/data/themes/Yami.qss index 1d1111731..dfadf0eed 100644 --- a/UI/data/themes/Yami.qss +++ b/UI/data/themes/Yami.qss @@ -304,8 +304,8 @@ QDockWidget { font-size: 10.5pt; font-weight: bold; - titlebar-close-icon: url('./Dark/Close.svg'); - titlebar-normal-icon: url('./Dark/Popout.svg'); + titlebar-close-icon: url('./Dark/close.svg'); + titlebar-normal-icon: url('./Dark/popout.svg'); } QDockWidget::title { From ea94eeb04537cdc1310e7cd35dd94eebd0bf9c54 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Thu, 22 Sep 2022 06:39:26 -0500 Subject: [PATCH 04/22] UI: Fix color of popout icon In the light theme, the dock popout icon was the wrong color, basically making it invisible. --- UI/data/themes/Light/popout.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/data/themes/Light/popout.svg b/UI/data/themes/Light/popout.svg index afba03a27..fbf285d6d 100644 --- a/UI/data/themes/Light/popout.svg +++ b/UI/data/themes/Light/popout.svg @@ -1 +1 @@ - + From d314eafdc651d946cb80a9f473be8777d6e929ae Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 23 Sep 2022 16:04:02 -0400 Subject: [PATCH 05/22] obs-ffmpeg: Cap AMF encoder at 100 Mbps The AMF docs specify that the maximum value for TARGET_BITRATE and PEAK_BITRATE is 100,000,000 bit/s. Trying to set values above this maximum value results in AMF choosing from its defaults instead of capping at the maximum value. Let's cap at 100,000 Kbps in the UI to prevent users from running in to this. Fixes GitHub Issue obsproject/obs-studio#7423. --- plugins/obs-ffmpeg/texture-amf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/texture-amf.cpp b/plugins/obs-ffmpeg/texture-amf.cpp index a0e7a8a16..6a1796726 100644 --- a/plugins/obs-ffmpeg/texture-amf.cpp +++ b/plugins/obs-ffmpeg/texture-amf.cpp @@ -964,7 +964,7 @@ static obs_properties_t *amf_properties_internal(bool hevc) obs_property_set_modified_callback(p, rate_control_modified); p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), - 50, 300000, 50); + 50, 100000, 50); obs_property_int_set_suffix(p, " Kbps"); obs_properties_add_int(props, "cqp", obs_module_text("NVENC.CQLevel"), From 8e9d9fba245dcbf5c08de8d53ecfda48881154e7 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Fri, 23 Sep 2022 15:39:52 -0700 Subject: [PATCH 06/22] linux-capture: Fixup window name/class checking There were a couple mistakes here that caused the fallback checks other than window ID to fail to ever return valid results. This restores this functionality that was broken since the c++/c transition. fixes #7404 --- plugins/linux-capture/xcomposite-input.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/linux-capture/xcomposite-input.c b/plugins/linux-capture/xcomposite-input.c index b63bda614..fc804e0b0 100644 --- a/plugins/linux-capture/xcomposite-input.c +++ b/plugins/linux-capture/xcomposite-input.c @@ -310,11 +310,10 @@ xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp, char *wcls = bzalloc(strEnd - thirdStr + 1); memcpy(wname, secondStr, secondMark - secondStr); memcpy(wcls, thirdStr, strEnd - thirdStr); - xcb_window_t wid = (xcb_window_t)strtol(str, NULL, 10); + ret = (xcb_window_t)strtol(str, NULL, 10); // first try to find a match by the window-id - if (da_find(tlw, &wid, 0) != DARRAY_INVALID) { - ret = wid; + if (da_find(tlw, &ret, 0) != DARRAY_INVALID) { goto cleanup2; } @@ -325,8 +324,8 @@ xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp, struct dstr cwname = xcomp_window_name(conn, disp, cwin); struct dstr cwcls = xcomp_window_class(conn, cwin); - bool found = (strcmp(wname, cwname.array) == 0 && - strcmp(wcls, cwcls.array)); + bool found = strcmp(wname, cwname.array) == 0 && + strcmp(wcls, cwcls.array) == 0; dstr_free(&cwname); dstr_free(&cwcls); @@ -345,7 +344,7 @@ cleanup2: bfree(wcls); cleanup1: da_free(tlw); - return wid; + return ret; } void xcomp_cleanup_pixmap(Display *disp, struct xcompcap *s) From b27c8997613c9f02bee0ab59a785c455da996e4c Mon Sep 17 00:00:00 2001 From: jpark37 Date: Fri, 23 Sep 2022 23:02:43 -0700 Subject: [PATCH 07/22] libobs/media-io: Avoid scaler for range diff Green tint appears when using converting full NV12 to limited NV12 with swscale, so just avoid creating scaler by color range for now. --- libobs/media-io/video-io.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libobs/media-io/video-io.c b/libobs/media-io/video-io.c index 32d8d73b9..1a80c4b1b 100644 --- a/libobs/media-io/video-io.c +++ b/libobs/media-io/video-io.c @@ -297,7 +297,11 @@ static size_t video_get_input_idx(const video_t *video, static bool match_range(enum video_range_type a, enum video_range_type b) { - return (a == VIDEO_RANGE_FULL) == (b == VIDEO_RANGE_FULL); + //return (a == VIDEO_RANGE_FULL) == (b == VIDEO_RANGE_FULL); + /* TODO: Restore test when full NV12 to limited NV12 works */ + UNUSED_PARAMETER(a); + UNUSED_PARAMETER(b); + return true; } static enum video_colorspace collapse_space(enum video_colorspace cs) From 972628346fde680df94e12a8cbadab8cbf7716f7 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Sun, 25 Sep 2022 10:26:30 +1000 Subject: [PATCH 08/22] CI: Downgrade Sphinx to fix docs build error This should be reverted once sphinx_rtd_theme is updated. See https://github.com/readthedocs/sphinx_rtd_theme/issues/1343 --- .github/workflows/docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 91c0d2ca2..d4d9d1050 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -42,6 +42,7 @@ jobs: build_only: True target_branch: 'master' target_path: '../home/_build' + pre_build_commands: 'pip install -Iv sphinx==5.1.1' - uses: actions/upload-artifact@v3 with: name: OBS Studio Documentation (HTML) From fb972b25207d02afb15938484a1ccb5902b3bd43 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Sun, 25 Sep 2022 11:47:36 -0700 Subject: [PATCH 09/22] libobs/media-io: Restore color range conversion FFmpeg needs color range during context init to handle conversions. sws_setColorspaceDetails after the fact is too late. --- libobs/media-io/video-io.c | 6 +----- libobs/media-io/video-scaler-ffmpeg.c | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/libobs/media-io/video-io.c b/libobs/media-io/video-io.c index 1a80c4b1b..32d8d73b9 100644 --- a/libobs/media-io/video-io.c +++ b/libobs/media-io/video-io.c @@ -297,11 +297,7 @@ static size_t video_get_input_idx(const video_t *video, static bool match_range(enum video_range_type a, enum video_range_type b) { - //return (a == VIDEO_RANGE_FULL) == (b == VIDEO_RANGE_FULL); - /* TODO: Restore test when full NV12 to limited NV12 works */ - UNUSED_PARAMETER(a); - UNUSED_PARAMETER(b); - return true; + return (a == VIDEO_RANGE_FULL) == (b == VIDEO_RANGE_FULL); } static enum video_colorspace collapse_space(enum video_colorspace cs) diff --git a/libobs/media-io/video-scaler-ffmpeg.c b/libobs/media-io/video-scaler-ffmpeg.c index 2bc653ab2..c83fced2e 100644 --- a/libobs/media-io/video-scaler-ffmpeg.c +++ b/libobs/media-io/video-scaler-ffmpeg.c @@ -19,6 +19,7 @@ #include "video-scaler.h" #include +#include #include struct video_scaler { @@ -188,16 +189,27 @@ int video_scaler_create(video_scaler_t **scaler_out, goto fail; } - scaler->swscale = sws_getCachedContext(NULL, src->width, src->height, - format_src, dst->width, - dst->height, format_dst, - scale_type, NULL, NULL, NULL); + scaler->swscale = sws_alloc_context(); if (!scaler->swscale) { blog(LOG_ERROR, "video_scaler_create: Could not create " "swscale"); goto fail; } + av_opt_set_int(scaler->swscale, "sws_flags", scale_type, 0); + av_opt_set_int(scaler->swscale, "srcw", src->width, 0); + av_opt_set_int(scaler->swscale, "srch", src->height, 0); + av_opt_set_int(scaler->swscale, "dstw", dst->width, 0); + av_opt_set_int(scaler->swscale, "dsth", dst->height, 0); + av_opt_set_int(scaler->swscale, "src_format", format_src, 0); + av_opt_set_int(scaler->swscale, "dst_format", format_dst, 0); + av_opt_set_int(scaler->swscale, "src_range", range_src, 0); + av_opt_set_int(scaler->swscale, "dst_range", range_dst, 0); + if (sws_init_context(scaler->swscale, NULL, NULL) < 0) { + blog(LOG_ERROR, "video_scaler_create: sws_init_context failed"); + goto fail; + } + ret = sws_setColorspaceDetails(scaler->swscale, coeff_src, range_src, coeff_dst, range_dst, 0, FIXED_1_0, FIXED_1_0); From e52ad554aaace4ccf5bbc9d5df81a02363e5340d Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Mon, 26 Sep 2022 16:53:58 +0200 Subject: [PATCH 10/22] linux-pipewire: Close sessions as we are done with them This way the stream can be cleaned up. Otherwise, we closed them all when OBS closes which works but is not entirely correct. --- plugins/linux-pipewire/screencast-portal.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/linux-pipewire/screencast-portal.c b/plugins/linux-pipewire/screencast-portal.c index f020a77e8..08ed2f442 100644 --- a/plugins/linux-pipewire/screencast-portal.c +++ b/plugins/linux-pipewire/screencast-portal.c @@ -627,6 +627,20 @@ static bool reload_session_cb(obs_properties_t *properties, g_clear_pointer(&capture->restore_token, bfree); g_clear_pointer(&capture->obs_pw, obs_pipewire_destroy); + if (capture->session_handle) { + blog(LOG_DEBUG, "[pipewire] Cleaning previous session %s", + capture->session_handle); + g_dbus_connection_call(portal_get_dbus_connection(), + "org.freedesktop.portal.Desktop", + capture->session_handle, + "org.freedesktop.portal.Session", + "Close", NULL, NULL, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, + NULL); + + g_clear_pointer(&capture->session_handle, g_free); + } + init_screencast_capture(capture); return false; From dbab97684306cd4e24d12666fa4eab3ff7f20ce0 Mon Sep 17 00:00:00 2001 From: shinji3 Date: Thu, 22 Sep 2022 17:28:33 +0900 Subject: [PATCH 11/22] obs-ffmpeg: Fix m3u8 recording in AMF --- plugins/obs-ffmpeg/obs-ffmpeg-mux.c | 38 +++++++++++++++++++++++------ plugins/obs-ffmpeg/texture-amf.cpp | 5 ++++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index b214105b6..67c1a2e2d 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -375,18 +375,33 @@ inline static void ts_offset_update(struct ffmpeg_muxer *stream, stream->found_audio[packet->track_idx] = true; } -static bool ffmpeg_mux_start(void *data) +static inline void update_encoder_settings(struct ffmpeg_muxer *stream, + const char *path) { - struct ffmpeg_muxer *stream = data; - obs_data_t *settings; - const char *path; + obs_encoder_t *vencoder = obs_output_get_video_encoder(stream->output); + const char *ext = strrchr(path, '.'); + + /* if using m3u8, repeat headers */ + if (ext && strcmp(ext, ".m3u8") == 0) { + obs_data_t *settings = obs_encoder_get_settings(vencoder); + obs_data_set_bool(settings, "repeat_headers", true); + obs_encoder_update(vencoder, settings); + obs_data_release(settings); + } +} + +static inline bool ffmpeg_mux_start_internal(struct ffmpeg_muxer *stream, + obs_data_t *settings) +{ + const char *path = obs_data_get_string(settings, "path"); + + update_encoder_settings(stream, path); if (!obs_output_can_begin_data_capture(stream->output, 0)) return false; if (!obs_output_initialize_encoders(stream->output, 0)) return false; - settings = obs_output_get_settings(stream->output); if (stream->is_network) { obs_service_t *service; service = obs_output_get_service(stream->output); @@ -395,7 +410,6 @@ static bool ffmpeg_mux_start(void *data) path = obs_service_get_url(service); stream->split_file = false; } else { - path = obs_data_get_string(settings, "path"); stream->max_time = obs_data_get_int(settings, "max_time_sec") * 1000000LL; @@ -427,7 +441,6 @@ static bool ffmpeg_mux_start(void *data) } start_pipe(stream, path); - obs_data_release(settings); if (!stream->pipe) { obs_output_set_last_error( @@ -446,6 +459,17 @@ static bool ffmpeg_mux_start(void *data) return true; } +static bool ffmpeg_mux_start(void *data) +{ + struct ffmpeg_muxer *stream = data; + + obs_data_t *settings = obs_output_get_settings(stream->output); + bool success = ffmpeg_mux_start_internal(stream, settings); + obs_data_release(settings); + + return success; +} + int deactivate(struct ffmpeg_muxer *stream, int code) { int ret = -1; diff --git a/plugins/obs-ffmpeg/texture-amf.cpp b/plugins/obs-ffmpeg/texture-amf.cpp index 6a1796726..f5487e5b1 100644 --- a/plugins/obs-ffmpeg/texture-amf.cpp +++ b/plugins/obs-ffmpeg/texture-amf.cpp @@ -1158,6 +1158,11 @@ static bool amf_avc_init(void *data, obs_data_t *settings) : 250; set_avc_property(enc, IDR_PERIOD, gop_size); + + bool repeat_headers = obs_data_get_bool(settings, "repeat_headers"); + if (repeat_headers) + set_avc_property(enc, HEADER_INSERTION_SPACING, gop_size); + set_avc_property(enc, DE_BLOCKING_FILTER, true); const char *ffmpeg_opts = obs_data_get_string(settings, "ffmpeg_opts"); From becbb5d88535b652d5733cfc319a49c885893ede Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 30 Sep 2022 01:57:24 +0200 Subject: [PATCH 12/22] UI: Remove executable bit from public key file --- UI/data/OBSPublicRSAKey.pem | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 UI/data/OBSPublicRSAKey.pem diff --git a/UI/data/OBSPublicRSAKey.pem b/UI/data/OBSPublicRSAKey.pem old mode 100755 new mode 100644 From c3c4adedd582773af267ed4890ac2f2ae9416ddb Mon Sep 17 00:00:00 2001 From: gxalpha Date: Fri, 30 Sep 2022 14:05:06 +0200 Subject: [PATCH 13/22] UI: Don't reselect SceneTree items if tree is clearing In the scene tree, we currently prevent deselecting items by automatically reselecting a new one to workaround a regression in Qt 6.2 that would let users unselect items even in SingleSelection QItemViews. When clearing however, we explicitly want to unselect the currently selected item, so we should avoid reselecting it in the SceneTree. --- UI/item-widget-helpers.cpp | 4 ++++ UI/scene-tree.cpp | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/UI/item-widget-helpers.cpp b/UI/item-widget-helpers.cpp index 18a3f9ac8..b1444f933 100644 --- a/UI/item-widget-helpers.cpp +++ b/UI/item-widget-helpers.cpp @@ -37,10 +37,14 @@ void DeleteListItem(QListWidget *widget, QListWidgetItem *item) void ClearListItems(QListWidget *widget) { + // Workaround for the SceneTree workaround for QTBUG-105870 + widget->setProperty("clearing", true); + widget->setCurrentItem(nullptr, QItemSelectionModel::Clear); for (int i = 0; i < widget->count(); i++) delete widget->itemWidget(widget->item(i)); widget->clear(); + widget->setProperty("clearing", false); } diff --git a/UI/scene-tree.cpp b/UI/scene-tree.cpp index f458b8e0b..544405173 100644 --- a/UI/scene-tree.cpp +++ b/UI/scene-tree.cpp @@ -252,6 +252,7 @@ void SceneTree::rowsInserted(const QModelIndex &parent, int start, int end) void SceneTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { - if (selected.count() == 0 && deselected.count() > 0) + if (selected.count() == 0 && deselected.count() > 0 && + !property("clearing").toBool()) setCurrentRow(deselected.indexes().front().row()); } From 4b39ea9df31134ea6f62e2b77cd7afe8ef73920b Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 30 Sep 2022 13:07:19 -0700 Subject: [PATCH 14/22] libobs: Add function to get encoder pause offset --- libobs/obs-encoder.c | 5 +++++ libobs/obs.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 38371180a..8b9bae3f5 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -1534,3 +1534,8 @@ void obs_encoder_set_last_error(obs_encoder_t *encoder, const char *message) else encoder->last_error_message = NULL; } + +uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder) +{ + return encoder ? encoder->pause.ts_offset : 0; +} diff --git a/libobs/obs.h b/libobs/obs.h index 4fcce92e3..46c006ce7 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -2413,6 +2413,8 @@ EXPORT const char *obs_encoder_get_last_error(obs_encoder_t *encoder); EXPORT void obs_encoder_set_last_error(obs_encoder_t *encoder, const char *message); +EXPORT uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder); + /* ------------------------------------------------------------------------- */ /* Stream Services */ From 30463ba91cbbb5fec9cc9bf38483e5943b31f15e Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 30 Sep 2022 13:08:06 -0700 Subject: [PATCH 15/22] obs-ffmpeg: Fix unpause causing certain encoders to fail Due to the timeout detection, we need to factor in the current pause offset to prevent pause from affecting the timeout. Closes obsproject/obs-studio#7313 --- plugins/obs-ffmpeg/obs-ffmpeg-video-encoders.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-video-encoders.c b/plugins/obs-ffmpeg/obs-ffmpeg-video-encoders.c index 64bf832b1..d0de9c4d4 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-video-encoders.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-video-encoders.c @@ -221,6 +221,8 @@ bool ffmpeg_video_encode(struct ffmpeg_video_encoder *enc, AVPacket av_pkt = {0}; bool timeout = false; const int64_t cur_ts = (int64_t)os_gettime_ns(); + const int64_t pause_offset = + (int64_t)obs_encoder_get_pause_offset(enc->encoder); int got_packet; int ret; @@ -270,7 +272,7 @@ bool ffmpeg_video_encode(struct ffmpeg_video_encoder *enc, debug("cur: %lld, packet: %lld, diff: %lld", cur_ts, recv_ts_nsec, cur_ts - recv_ts_nsec); #endif - if ((cur_ts - recv_ts_nsec) > TIMEOUT_MAX_NSEC) { + if ((cur_ts - recv_ts_nsec - pause_offset) > TIMEOUT_MAX_NSEC) { char timeout_str[16]; snprintf(timeout_str, sizeof(timeout_str), "%d", TIMEOUT_MAX_SEC); From 41d56fd0a7f79812165479c8d7ef55c73d388d15 Mon Sep 17 00:00:00 2001 From: Exeldro Date: Sat, 24 Sep 2022 08:52:21 +0200 Subject: [PATCH 16/22] obs-scripting: Fix compile when python is not found --- deps/obs-scripting/obs-scripting.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/deps/obs-scripting/obs-scripting.c b/deps/obs-scripting/obs-scripting.c index 74398b858..2c1d353ec 100644 --- a/deps/obs-scripting/obs-scripting.c +++ b/deps/obs-scripting/obs-scripting.c @@ -458,4 +458,9 @@ bool obs_scripting_python_runtime_linked(void) { return (bool)true; } + +void obs_scripting_python_version(char *version, size_t version_length) +{ + version[0] = 0; +} #endif From 539318bd58fc44973654e766af28af0c612d2c80 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 1 Oct 2022 06:07:25 -0700 Subject: [PATCH 17/22] libobs: Update version to 28.0.3 --- libobs/obs-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index a61b448ac..7d64d9c70 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -41,7 +41,7 @@ * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 2 +#define LIBOBS_API_PATCH_VER 3 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) From 8efb109144ac14349605296bcd5e8b4c81f1fba1 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Fri, 30 Sep 2022 21:50:33 -0700 Subject: [PATCH 18/22] win-wasapi: Don't reconnect when inactive Unnecessary reconnect attempts may cause noticeable hitches. --- plugins/win-wasapi/win-wasapi.cpp | 74 ++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 08aeca496..2dcc858f7 100644 --- a/plugins/win-wasapi/win-wasapi.cpp +++ b/plugins/win-wasapi/win-wasapi.cpp @@ -182,7 +182,7 @@ class WASAPISource { std::atomic isDefaultDevice = false; bool previouslyFailed = false; - WinHandle reconnectThread; + WinHandle reconnectThread = NULL; class CallbackStartCapture : public ARtwqAsyncCallback { public: @@ -238,6 +238,7 @@ class WASAPISource { WinHandle stopSignal; WinHandle receiveSignal; WinHandle restartSignal; + WinHandle reconnectExitSignal; WinHandle exitSignal; WinHandle initSignal; DWORD reconnectDuration = 0; @@ -298,6 +299,9 @@ public: void Update(obs_data_t *settings); void OnWindowChanged(obs_data_t *settings); + void Activate(); + void Deactivate(); + void SetDefaultDevice(EDataFlow flow, ERole role, LPCWSTR id); void OnStartCapture(); @@ -390,6 +394,10 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_, if (!restartSignal.Valid()) throw "Could not create restart signal"; + reconnectExitSignal = CreateEvent(nullptr, true, false, nullptr); + if (!reconnectExitSignal.Valid()) + throw "Could not create reconnect exit signal"; + exitSignal = CreateEvent(nullptr, true, false, nullptr); if (!exitSignal.Valid()) throw "Could not create exit signal"; @@ -402,11 +410,6 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_, if (!reconnectSignal.Valid()) throw "Could not create reconnect signal"; - reconnectThread = CreateThread( - nullptr, 0, WASAPISource::ReconnectThread, this, 0, nullptr); - if (!reconnectThread.Valid()) - throw "Failed to create reconnect thread"; - notify = new WASAPINotify(this); if (!notify) throw "Could not create WASAPINotify"; @@ -525,6 +528,9 @@ void WASAPISource::Start() void WASAPISource::Stop() { + if (!reconnectThread.Valid()) + WaitForSingleObject(reconnectSignal, INFINITE); + SetEvent(stopSignal); blog(LOG_INFO, "WASAPI: Device '%s' Terminated", device_name.c_str()); @@ -532,11 +538,15 @@ void WASAPISource::Stop() if (rtwq_supported) SetEvent(receiveSignal); - WaitForSingleObject(idleSignal, INFINITE); + if (reconnectThread.Valid()) + WaitForSingleObject(idleSignal, INFINITE); SetEvent(exitSignal); - WaitForSingleObject(reconnectThread, INFINITE); + if (reconnectThread.Valid()) { + SetEvent(reconnectExitSignal); + WaitForSingleObject(reconnectThread, INFINITE); + } if (rtwq_supported) rtwq_unlock_work_queue(sampleReady.GetQueueId()); @@ -657,6 +667,25 @@ void WASAPISource::OnWindowChanged(obs_data_t *settings) SetEvent(restartSignal); } +void WASAPISource::Activate() +{ + if (!reconnectThread.Valid()) { + ResetEvent(reconnectExitSignal); + reconnectThread = CreateThread(nullptr, 0, + WASAPISource::ReconnectThread, + this, 0, nullptr); + } +} + +void WASAPISource::Deactivate() +{ + if (reconnectThread.Valid()) { + SetEvent(reconnectExitSignal); + WaitForSingleObject(reconnectThread, INFINITE); + reconnectThread = NULL; + } +} + ComPtr WASAPISource::InitDevice(IMMDeviceEnumerator *enumerator, bool isDefaultDevice, SourceType type, @@ -1005,10 +1034,15 @@ DWORD WINAPI WASAPISource::ReconnectThread(LPVOID param) WASAPISource *source = (WASAPISource *)param; const HANDLE sigs[] = { - source->exitSignal, + source->reconnectExitSignal, source->reconnectSignal, }; + const HANDLE reconnect_sigs[] = { + source->reconnectExitSignal, + source->stopSignal, + }; + bool exit = false; while (!exit) { const DWORD ret = WaitForMultipleObjects(_countof(sigs), sigs, @@ -1020,8 +1054,10 @@ DWORD WINAPI WASAPISource::ReconnectThread(LPVOID param) default: assert(ret == (WAIT_OBJECT_0 + 1)); if (source->reconnectDuration > 0) { - WaitForSingleObject(source->stopSignal, - source->reconnectDuration); + WaitForMultipleObjects( + _countof(reconnect_sigs), + reconnect_sigs, false, + source->reconnectDuration); } source->Start(); } @@ -1426,6 +1462,16 @@ static void UpdateWASAPISource(void *obj, obs_data_t *settings) static_cast(obj)->Update(settings); } +static void ActivateWASAPISource(void *obj) +{ + static_cast(obj)->Activate(); +} + +static void DeactivateWASAPISource(void *obj) +{ + static_cast(obj)->Deactivate(); +} + static bool UpdateWASAPIMethod(obs_properties_t *props, obs_property_t *, obs_data_t *settings) { @@ -1542,6 +1588,8 @@ void RegisterWASAPIInput() info.create = CreateWASAPIInput; info.destroy = DestroyWASAPISource; info.update = UpdateWASAPISource; + info.activate = ActivateWASAPISource; + info.deactivate = DeactivateWASAPISource; info.get_defaults = GetWASAPIDefaultsInput; info.get_properties = GetWASAPIPropertiesInput; info.icon_type = OBS_ICON_TYPE_AUDIO_INPUT; @@ -1559,6 +1607,8 @@ void RegisterWASAPIDeviceOutput() info.create = CreateWASAPIDeviceOutput; info.destroy = DestroyWASAPISource; info.update = UpdateWASAPISource; + info.activate = ActivateWASAPISource; + info.deactivate = DeactivateWASAPISource; info.get_defaults = GetWASAPIDefaultsDeviceOutput; info.get_properties = GetWASAPIPropertiesDeviceOutput; info.icon_type = OBS_ICON_TYPE_AUDIO_OUTPUT; @@ -1576,6 +1626,8 @@ void RegisterWASAPIProcessOutput() info.create = CreateWASAPIProcessOutput; info.destroy = DestroyWASAPISource; info.update = UpdateWASAPISource; + info.activate = ActivateWASAPISource; + info.deactivate = DeactivateWASAPISource; info.get_defaults = GetWASAPIDefaultsProcessOutput; info.get_properties = GetWASAPIPropertiesProcessOutput; info.icon_type = OBS_ICON_TYPE_PROCESS_AUDIO_OUTPUT; From cee2e7db3f2d73ec1903fbf2c3ff2d0beecfae15 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 27 Sep 2022 19:39:28 +0200 Subject: [PATCH 19/22] UI: Fix AutoRemux not working when FFmpeg output configured When the Advanced Output configuration is set to use custom FFmpeg output, automatic remuxing is disabled. Unfortunately this check will also take place even if Simple Output is used (as the value is set in the configuration data, but is not "active"). This check ensures that the check for custom FFmpeg output is only applied when Advanced Output is enabled. --- UI/window-basic-main.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index ae4acfe41..cf80c57d6 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -7122,17 +7122,31 @@ void OBSBasic::StreamingStop(int code, QString last_error) void OBSBasic::AutoRemux(QString input, bool no_show) { - bool autoRemux = config_get_bool(Config(), "Video", "AutoRemux"); + auto config = Config(); + + bool autoRemux = config_get_bool(config, "Video", "AutoRemux"); if (!autoRemux) return; - const char *recType = config_get_string(Config(), "AdvOut", "RecType"); + bool isSimpleMode = false; - bool ffmpegOutput = astrcmpi(recType, "FFmpeg") == 0; + const char *mode = config_get_string(config, "Output", "Mode"); + if (!mode) { + isSimpleMode = true; + } else { + isSimpleMode = strcmp(mode, "Simple") == 0; + } - if (ffmpegOutput) - return; + if (!isSimpleMode) { + const char *recType = + config_get_string(config, "AdvOut", "RecType"); + + bool ffmpegOutput = astrcmpi(recType, "FFmpeg") == 0; + + if (ffmpegOutput) + return; + } if (input.isEmpty()) return; From 89d9493468a679b45e81e445aae5786ccfd3c3f4 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Sat, 6 Aug 2022 13:52:39 +1000 Subject: [PATCH 20/22] frontend-tools: Display Python version in UI --- .../frontend-tools/data/locale/en-US.ini | 2 ++ .../frontend-tools/forms/scripts.ui | 7 +++++++ UI/frontend-plugins/frontend-tools/scripts.cpp | 17 +++++++++++++++++ UI/frontend-plugins/frontend-tools/scripts.hpp | 2 ++ deps/obs-scripting/obs-scripting-python.c | 11 +++++++++++ deps/obs-scripting/obs-scripting.h | 1 + 6 files changed, 40 insertions(+) diff --git a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini index b43843591..46d8056d9 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini @@ -39,6 +39,8 @@ PythonSettings="Python Settings" PythonSettings.PythonInstallPath32bit="Python Install Path (32bit)" PythonSettings.PythonInstallPath64bit="Python Install Path (64bit)" PythonSettings.BrowsePythonPath="Browse Python Path" +PythonSettings.PythonVersion="Loaded Python Version: %1" +PythonSettings.PythonNotLoaded="Python not currently loaded" ScriptLogWindow="Script Log" Description="Description" ScriptDescriptionLink.Text="Open this link in your default web browser?" diff --git a/UI/frontend-plugins/frontend-tools/forms/scripts.ui b/UI/frontend-plugins/frontend-tools/forms/scripts.ui index ce8987c50..e07844cfa 100644 --- a/UI/frontend-plugins/frontend-tools/forms/scripts.ui +++ b/UI/frontend-plugins/frontend-tools/forms/scripts.ui @@ -268,6 +268,13 @@ + + + + + + + diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp index 4c561de7d..66fbd4ef6 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.cpp +++ b/UI/frontend-plugins/frontend-tools/scripts.cpp @@ -194,6 +194,7 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool) config_get_string(config, "Python", "Path" ARCH_NAME); ui->pythonPath->setText(path); ui->pythonPathLabel->setText(obs_module_text(PYTHONPATH_LABEL_TEXT)); + updatePythonVersionLabel(); #else delete ui->pythonSettingsTab; ui->pythonSettingsTab = nullptr; @@ -219,6 +220,20 @@ ScriptsTool::~ScriptsTool() ui->scripts->currentRow()); } +void ScriptsTool::updatePythonVersionLabel() +{ + QString label; + if (obs_scripting_python_loaded()) { + char version[8]; + obs_scripting_python_version(version, sizeof(version)); + label = QString(obs_module_text("PythonSettings.PythonVersion")) + .arg(version); + } else { + label = obs_module_text("PythonSettings.PythonNotLoaded"); + } + ui->pythonVersionLabel->setText(label); +} + void ScriptsTool::RemoveScript(const char *path) { for (size_t i = 0; i < scriptData->scripts.size(); i++) { @@ -460,6 +475,8 @@ void ScriptsTool::on_pythonPathBrowse_clicked() if (!obs_scripting_load_python(path)) return; + updatePythonVersionLabel(); + for (OBSScript &script : scriptData->scripts) { enum obs_script_lang lang = obs_script_get_lang(script); if (lang == OBS_SCRIPT_LANG_PYTHON) { diff --git a/UI/frontend-plugins/frontend-tools/scripts.hpp b/UI/frontend-plugins/frontend-tools/scripts.hpp index 3ce5385ad..052145352 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.hpp +++ b/UI/frontend-plugins/frontend-tools/scripts.hpp @@ -31,6 +31,8 @@ class ScriptsTool : public QDialog { std::unique_ptr ui; QWidget *propertiesView = nullptr; + void updatePythonVersionLabel(); + public: ScriptsTool(); ~ScriptsTool(); diff --git a/deps/obs-scripting/obs-scripting-python.c b/deps/obs-scripting/obs-scripting-python.c index 35f549938..124164ebd 100644 --- a/deps/obs-scripting/obs-scripting-python.c +++ b/deps/obs-scripting/obs-scripting-python.c @@ -1590,6 +1590,17 @@ bool obs_scripting_python_runtime_linked(void) return (bool)RUNTIME_LINK; } +void obs_scripting_python_version(char *version, size_t version_length) +{ +#if RUNTIME_LINK + snprintf(version, version_length, "%d.%d", python_version.major, + python_version.minor); +#else + snprintf(version, version_length, "%d.%d", PY_MAJOR_VERSION, + PY_MINOR_VERSION); +#endif +} + bool obs_scripting_python_loaded(void) { return python_loaded; diff --git a/deps/obs-scripting/obs-scripting.h b/deps/obs-scripting/obs-scripting.h index 415c313e3..420c4b19a 100644 --- a/deps/obs-scripting/obs-scripting.h +++ b/deps/obs-scripting/obs-scripting.h @@ -46,6 +46,7 @@ EXPORT void obs_scripting_set_log_callback(scripting_log_handler_t handler, void *param); EXPORT bool obs_scripting_python_runtime_linked(void); +EXPORT void obs_scripting_python_version(char *version, size_t version_length); EXPORT bool obs_scripting_python_loaded(void); EXPORT bool obs_scripting_load_python(const char *python_path); From 05b63462503db22d83fee994a20b37e3675e0a26 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Sat, 6 Aug 2022 15:32:31 +1000 Subject: [PATCH 21/22] frontend-tools: Display dialog when changing Python version Runtime switching of Python version is not supported. --- .../frontend-tools/data/locale/en-US.ini | 2 ++ UI/frontend-plugins/frontend-tools/scripts.cpp | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini index 46d8056d9..80f2a4118 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini @@ -41,6 +41,8 @@ PythonSettings.PythonInstallPath64bit="Python Install Path (64bit)" PythonSettings.BrowsePythonPath="Browse Python Path" PythonSettings.PythonVersion="Loaded Python Version: %1" PythonSettings.PythonNotLoaded="Python not currently loaded" +PythonSettings.AlreadyLoaded.Title="Python Already Loaded" +PythonSettings.AlreadyLoaded.Message="A copy of Python %1 is already loaded. To load the newly selected Python version, please restart OBS." ScriptLogWindow="Script Log" Description="Description" ScriptDescriptionLink.Text="Open this link in your default web browser?" diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp index 66fbd4ef6..c4174da4f 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.cpp +++ b/UI/frontend-plugins/frontend-tools/scripts.cpp @@ -470,8 +470,24 @@ void ScriptsTool::on_pythonPathBrowse_clicked() ui->pythonPath->setText(newPath); - if (obs_scripting_python_loaded()) + bool loaded = obs_scripting_python_loaded(); + + if (loaded && !newPath.isEmpty() && curPath.compare(newPath) != 0) { + char version[8]; + obs_scripting_python_version(version, sizeof(version)); + QString message = + QString(obs_module_text( + "PythonSettings.AlreadyLoaded.Message")) + .arg(version); + OBSMessageBox::information( + this, + obs_module_text("PythonSettings.AlreadyLoaded.Title"), + message); return; + } else if (loaded) { + return; + } + if (!obs_scripting_load_python(path)) return; From d21891b3ca1a8607de479687cd2e12a4455525d7 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Sat, 1 Oct 2022 09:41:53 -0700 Subject: [PATCH 22/22] win-wasapi: Fix Stop hang Fix hang when audio capture is active in the background and stopped by either closing OBS, or switching scene collections. --- plugins/win-wasapi/win-wasapi.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 2dcc858f7..85c252e66 100644 --- a/plugins/win-wasapi/win-wasapi.cpp +++ b/plugins/win-wasapi/win-wasapi.cpp @@ -528,9 +528,6 @@ void WASAPISource::Start() void WASAPISource::Stop() { - if (!reconnectThread.Valid()) - WaitForSingleObject(reconnectSignal, INFINITE); - SetEvent(stopSignal); blog(LOG_INFO, "WASAPI: Device '%s' Terminated", device_name.c_str()); @@ -538,8 +535,12 @@ void WASAPISource::Stop() if (rtwq_supported) SetEvent(receiveSignal); - if (reconnectThread.Valid()) + if (reconnectThread.Valid()) { WaitForSingleObject(idleSignal, INFINITE); + } else { + const HANDLE sigs[] = {reconnectSignal, idleSignal}; + WaitForMultipleObjects(_countof(sigs), sigs, false, INFINITE); + } SetEvent(exitSignal);