From f2628de7f28f36640453c514c56be26e6bea95a9 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 15 Feb 2022 23:12:20 -0800 Subject: [PATCH 01/66] obs-browser: Acquire, copy, and release immediately Instead of holding onto an acquire over the duration of an entire frame, acquire, copy to a texture, and immediately release the lock. (Note from Jim: When it comes to synchronization, the idea of acquiring a lock, and then releasing all the way in the next frame just feels a bit worrisome. Especially when it's a GPU object. But at this point, I'm just making shots in the dark in an attempt to eliminate any possible things that might even have a remote chance to trigger latent bugs in a GPU driver or the kernel. You know, that whole system freeze thing. This probably doesn't have anything to do with it, at this point I have nothing but wild geese to chase. Whatever.) --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index b0d687937..b22c82179 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit b0d687937af876b52b69c46e276b4ab601b07f0e +Subproject commit b22c82179953a90d30e7956b23a0dac9a0131f29 From e43f75fc92935ffbb6119daa101bc0118c468371 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 15 Feb 2022 23:19:17 -0800 Subject: [PATCH 02/66] obs-browser: Update version to 2.17.10 --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index b22c82179..6bd63ef1a 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit b22c82179953a90d30e7956b23a0dac9a0131f29 +Subproject commit 6bd63ef1aebc7d544d5510eaa51f06a610a2f02e From 5643e9cbbf9b7abcfabf7ea6da730649f8745b95 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 15 Feb 2022 23:51:07 -0800 Subject: [PATCH 03/66] obs-browser: Fix sRGB rendering --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index 6bd63ef1a..344626c31 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit 6bd63ef1aebc7d544d5510eaa51f06a610a2f02e +Subproject commit 344626c31df1c90b4605733f8706f033bc8a648b From 59a675738f39c3384db605b75eee3957a38eb855 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Fri, 18 Feb 2022 19:46:02 +1100 Subject: [PATCH 04/66] UI: Don't collapse preview in Filters splitter view Fixes #5959 --- UI/forms/OBSBasicFilters.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/forms/OBSBasicFilters.ui b/UI/forms/OBSBasicFilters.ui index 4ee72dd38..aead3fdc2 100644 --- a/UI/forms/OBSBasicFilters.ui +++ b/UI/forms/OBSBasicFilters.ui @@ -474,6 +474,9 @@ Qt::Vertical + + false + From b6366757c0bdeee3af6635998a7ffc2584c205fd Mon Sep 17 00:00:00 2001 From: Florian Zwoch Date: Sun, 13 Feb 2022 12:23:35 +0100 Subject: [PATCH 05/66] linux-capture: Fix for pipewire capture leaking texture handles Enter the graphics context before calling gs_texture_destroy() or else the call will fail causing the texture handles to leak. --- plugins/linux-capture/pipewire.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/linux-capture/pipewire.c b/plugins/linux-capture/pipewire.c index 76f68c13a..07b94d439 100644 --- a/plugins/linux-capture/pipewire.c +++ b/plugins/linux-capture/pipewire.c @@ -298,8 +298,10 @@ static void destroy_session(obs_pipewire_data *obs_pw) } g_clear_pointer(&obs_pw->sender_name, bfree); + obs_enter_graphics(); g_clear_pointer(&obs_pw->cursor.texture, gs_texture_destroy); g_clear_pointer(&obs_pw->texture, gs_texture_destroy); + obs_leave_graphics(); g_cancellable_cancel(obs_pw->cancellable); g_clear_object(&obs_pw->cancellable); } From 92bf2ebe937560d38966c5e499e15955ac93d385 Mon Sep 17 00:00:00 2001 From: pkv Date: Tue, 15 Feb 2022 11:23:37 +0100 Subject: [PATCH 06/66] obs-ffmpeg: Force mpegts format & disable restart on activate for srt & rist This fixes crashes in media Source when : - the user forgets to set the input format for srt or rist URLs; - disabling / enabling / disabling etc Media source with a rist URL. Signed-off-by: pkv --- plugins/obs-ffmpeg/obs-ffmpeg-source.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-source.c b/plugins/obs-ffmpeg/obs-ffmpeg-source.c index 0cf73f0c3..73b7bf62e 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-source.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-source.c @@ -386,6 +386,15 @@ static void ffmpeg_source_tick(void *data, float seconds) } } +#define SRT_PROTO "srt" +#define RIST_PROTO "rist" + +static bool requires_mpegts(char *path) +{ + return !astrcmpi_n(path, SRT_PROTO, sizeof(SRT_PROTO) - 1) || + !astrcmpi_n(path, RIST_PROTO, sizeof(RIST_PROTO) - 1); +} + static void ffmpeg_source_update(void *data, obs_data_t *settings) { struct ffmpeg_source *s = data; @@ -406,6 +415,10 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings) input = (char *)obs_data_get_string(settings, "input"); input_format = (char *)obs_data_get_string(settings, "input_format"); + if (requires_mpegts(input)) { + input_format = "mpegts"; + obs_data_set_string(settings, "input_format", "mpegts"); + } s->reconnect_delay_sec = (int)obs_data_get_int(settings, "reconnect_delay_sec"); s->reconnect_delay_sec = s->reconnect_delay_sec == 0 @@ -429,7 +442,9 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings) s->is_clear_on_media_end = obs_data_get_bool(settings, "clear_on_media_end"); s->restart_on_activate = - obs_data_get_bool(settings, "restart_on_activate"); + !astrcmpi_n(input, RIST_PROTO, sizeof(RIST_PROTO) - 1) + ? false + : obs_data_get_bool(settings, "restart_on_activate"); s->range = (enum video_range_type)obs_data_get_int(settings, "color_range"); s->is_linear_alpha = obs_data_get_bool(settings, "linear_alpha"); From 9c42e9f75206c25455bce9c4502807a5d401f821 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Sat, 19 Feb 2022 14:07:54 +1100 Subject: [PATCH 07/66] win-wasapi: Only enable work queue on Windows 10+ Fixes #5967 --- plugins/win-wasapi/win-wasapi.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 6dfa9996f..07aa27821 100644 --- a/plugins/win-wasapi/win-wasapi.cpp +++ b/plugins/win-wasapi/win-wasapi.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -324,7 +325,12 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_, /* OBS will already load DLL on startup if it exists */ const HMODULE rtwq_module = GetModuleHandle(L"RTWorkQ.dll"); - rtwq_supported = rtwq_module != NULL; + + // while RTWQ was introduced in Win 8.1, it silently fails + // to capture Desktop Audio for some reason. Disable for now. + if (get_win_ver_int() >= _WIN32_WINNT_WIN10) + rtwq_supported = rtwq_module != NULL; + if (rtwq_supported) { rtwq_unlock_work_queue = (PFN_RtwqUnlockWorkQueue)GetProcAddress( From c4455d4de04482d18b61f584dd8d01bfc53a75df Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Sat, 19 Feb 2022 16:23:01 +0100 Subject: [PATCH 08/66] CI: Update main workflow file to use fixed obs-deps Prior version was linked against libxcb, because it was present on Github Actions macOS runners. Consequently builds on CI will succeed as the library is always present, will fail on user's machines though. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bf41afb1a..d2a20ac09 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,7 @@ jobs: runs-on: [macos-latest] env: MIN_MACOS_VERSION: '10.13' - MACOS_DEPS_VERSION: '2022-01-31' + MACOS_DEPS_VERSION: '2022-02-13' VLC_VERSION: '3.0.8' SPARKLE_VERSION: '1.23.0' QT_VERSION: '5.15.2' From a75687abec36bc3e8178cccf86ecc5c507f0fba2 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Sat, 19 Feb 2022 16:24:40 +0100 Subject: [PATCH 09/66] CI: Ensure SOVERSION symlinks exist in created App Bundle Bundles libraries are available via their specific versioned filename as well as SOVERSION and versionless symlinks. Dylibbundler does _not_ include these symlinks when copying linked libraries into the bundle. This change ensures that all libraries and their SOVERSION symlinks exist in the created App Bundle. --- CI/full-build-macos.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CI/full-build-macos.sh b/CI/full-build-macos.sh index 96cb9b1fb..a0c0e6973 100755 --- a/CI/full-build-macos.sh +++ b/CI/full-build-macos.sh @@ -422,6 +422,8 @@ prepare_macos_bundle() { /bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/MacOS/ /bin/rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/ fi + # dylibbundler will only copy actually linked files into bundle, but not symlinks + /bin/cp -cpR /tmp/obsdeps/lib/*.dylib ./OBS.app/Contents/Frameworks bundle_dylibs install_frameworks From 5f7c4329b235d583ce7672be59ca04988fde031e Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 19 Feb 2022 15:39:00 +0100 Subject: [PATCH 10/66] UI: Refresh edit menu on item select/deselect --- UI/source-tree.cpp | 2 ++ UI/window-basic-main.cpp | 8 +------- UI/window-basic-main.hpp | 2 -- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index 4ae014d1d..8a1175bc4 100644 --- a/UI/source-tree.cpp +++ b/UI/source-tree.cpp @@ -623,12 +623,14 @@ void SourceTreeItem::Select() { tree->SelectItem(sceneitem, true); OBSBasic::Get()->UpdateContextBarDeferred(); + OBSBasic::Get()->UpdateEditMenu(); } void SourceTreeItem::Deselect() { tree->SelectItem(sceneitem, false); OBSBasic::Get()->UpdateContextBarDeferred(); + OBSBasic::Get()->UpdateEditMenu(); } /* ========================================================================= */ diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index fa172ce5e..1cadc9052 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1842,6 +1842,7 @@ void OBSBasic::OBSInit() ui->contextContainer->setVisible(contextVisible); if (contextVisible) UpdateContextBar(true); + UpdateEditMenu(); { ProfileScope("OBSBasic::Load"); @@ -5369,8 +5370,6 @@ ColorSelect::ColorSelect(QWidget *parent) void OBSBasic::CreateSourcePopupMenu(int idx, bool preview) { - UpdateEditMenu(); - QMenu popup(this); delete previewProjectorSource; delete sourceProjector; @@ -7825,11 +7824,6 @@ void OBSBasic::UpdateEditMenu() ui->actionHorizontalCenter->setEnabled(canTransform); } -void OBSBasic::on_menuBasic_MainMenu_Edit_aboutToShow() -{ - UpdateEditMenu(); -} - void OBSBasic::on_actionEditTransform_triggered() { if (transformWindow) diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 565314bcb..9e54ef891 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -966,8 +966,6 @@ private slots: void on_actionShowCrashLogs_triggered(); void on_actionUploadLastCrashLog_triggered(); - void on_menuBasic_MainMenu_Edit_aboutToShow(); - void on_actionEditTransform_triggered(); void on_actionCopyTransform_triggered(); void on_actionPasteTransform_triggered(); From 94016b459f4208bcf800ee39932c524888c2e496 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 19 Feb 2022 12:55:01 +0100 Subject: [PATCH 11/66] libobs: Adjust path for legacy browser source block The current path would prevent the browser source from loading if OBS itself is in the "Application Support" folder, where it might end up when being installed via certain distribution platforms. This adjusts the existing hack to specifically check for the obs-studio subfolder where the old browser source library would reside. --- libobs/obs-module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-module.c b/libobs/obs-module.c index cc2f5e186..3f3cb98a0 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -110,7 +110,7 @@ int obs_open_module(obs_module_t **module, const char *path, /* HACK: Do not load obsolete obs-browser build on macOS; the * obs-browser plugin used to live in the Application Support * directory. */ - if (astrstri(path, "Library/Application Support") != NULL && + if (astrstri(path, "Library/Application Support/obs-studio") != NULL && astrstri(path, "obs-browser") != NULL) { blog(LOG_WARNING, "Ignoring old obs-browser.so version"); return MODULE_ERROR; From d1f8caeb8dacdb4d85d642cb27d642c381baa9a3 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Sun, 20 Feb 2022 13:38:17 +1100 Subject: [PATCH 12/66] UI: Log 'Hide OBS from capture' on startup & settings change --- UI/obs-app.cpp | 6 ++++++ UI/window-basic-settings.cpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 010fa12eb..35466fb8b 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1455,6 +1455,12 @@ bool OBSApp::OBSInit() blog(LOG_INFO, "Browser Hardware Acceleration: %s", browserHWAccel ? "true" : "false"); #endif +#ifdef _WIN32 + bool hideFromCapture = config_get_bool(globalConfig, "BasicWindow", + "HideOBSWindowsFromCapture"); + blog(LOG_INFO, "Hide OBS windows from screen capture: %s", + hideFromCapture ? "true" : "false"); +#endif blog(LOG_INFO, "Qt Version: %s (runtime), %s (compiled)", qVersion(), QT_VERSION_STR); diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 02eb3ca07..66be37901 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -3008,6 +3008,9 @@ void OBSBasicSettings::SaveGeneralSettings() main->SetDisplayAffinity(window); } } + + blog(LOG_INFO, "Hide OBS windows from screen capture: %s", + hide_window ? "true" : "false"); } #endif if (WidgetChanged(ui->openStatsOnStartup)) From 8ad7d1b08d609dc899d8337aa6e5b934a1056f6e Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Mon, 14 Feb 2022 17:14:02 -0800 Subject: [PATCH 13/66] linux-v4l2: Use decoded MJPEG pixel format Previously we assumed mjpeg was always decoded to 422 but it seems some cameras provide frames that decode to different pixel formats such as 420. This change delays setting the obs frame pixel format until after we have decoded the v4l2 frame. fixes #5821 --- plugins/linux-v4l2/v4l2-helpers.h | 2 -- plugins/linux-v4l2/v4l2-input.c | 13 ++++--------- plugins/linux-v4l2/v4l2-mjpeg.c | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-helpers.h b/plugins/linux-v4l2/v4l2-helpers.h index 506dfa29d..b14d0199f 100644 --- a/plugins/linux-v4l2/v4l2-helpers.h +++ b/plugins/linux-v4l2/v4l2-helpers.h @@ -79,8 +79,6 @@ static inline enum video_format v4l2_to_obs_video_format(uint_fast32_t format) #endif case V4L2_PIX_FMT_BGR24: return VIDEO_FORMAT_BGR3; - case V4L2_PIX_FMT_MJPEG: - return VIDEO_FORMAT_I422; default: return VIDEO_FORMAT_NONE; } diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index f6d0a6f0c..adcbb9bae 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -148,13 +148,6 @@ static void v4l2_prep_obs_frame(struct v4l2_data *data, plane_offsets[1] = data->linesize * data->height; plane_offsets[2] = data->linesize * data->height * 5 / 4; break; - case V4L2_PIX_FMT_MJPEG: - frame->linesize[0] = 0; - frame->linesize[1] = 0; - frame->linesize[2] = 0; - plane_offsets[1] = 0; - plane_offsets[2] = 0; - break; default: frame->linesize[0] = data->linesize; break; @@ -479,7 +472,8 @@ static void v4l2_format_list(int dev, obs_property_t *prop) dstr_cat(&buffer, " (Emulated)"); if (v4l2_to_obs_video_format(fmt.pixelformat) != - VIDEO_FORMAT_NONE) { + VIDEO_FORMAT_NONE || + fmt.pixelformat == V4L2_PIX_FMT_MJPEG) { obs_property_list_add_int(prop, buffer.array, fmt.pixelformat); blog(LOG_INFO, "Pixelformat: %s (available)", @@ -1003,7 +997,8 @@ static void v4l2_init(struct v4l2_data *data) blog(LOG_ERROR, "Unable to set format"); goto fail; } - if (v4l2_to_obs_video_format(data->pixfmt) == VIDEO_FORMAT_NONE) { + if (v4l2_to_obs_video_format(data->pixfmt) == VIDEO_FORMAT_NONE && + data->pixfmt != V4L2_PIX_FMT_MJPEG) { blog(LOG_ERROR, "Selected video format not supported"); goto fail; } diff --git a/plugins/linux-v4l2/v4l2-mjpeg.c b/plugins/linux-v4l2/v4l2-mjpeg.c index 45b2b9193..a04293265 100644 --- a/plugins/linux-v4l2/v4l2-mjpeg.c +++ b/plugins/linux-v4l2/v4l2-mjpeg.c @@ -45,7 +45,6 @@ int v4l2_init_mjpeg(struct v4l2_mjpeg_decoder *decoder) } decoder->context->flags2 |= AV_CODEC_FLAG2_FAST; - decoder->context->pix_fmt = AV_PIX_FMT_YUVJ422P; if (avcodec_open2(decoder->context, decoder->codec, NULL) < 0) { blog(LOG_ERROR, "failed to open codec"); @@ -94,5 +93,20 @@ int v4l2_decode_mjpeg(struct obs_source_frame *out, uint8_t *data, out->linesize[i] = decoder->frame->linesize[i]; } + switch (decoder->context->pix_fmt) { + case AV_PIX_FMT_YUVJ422P: + case AV_PIX_FMT_YUV422P: + out->format = VIDEO_FORMAT_I422; + break; + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUV420P: + out->format = VIDEO_FORMAT_I420; + break; + case AV_PIX_FMT_YUVJ444P: + case AV_PIX_FMT_YUV444P: + out->format = VIDEO_FORMAT_I444; + break; + } + return 0; } From a0c37e7aa95c43d00a8a3c5b3d719b2943e022eb Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Sun, 20 Feb 2022 17:33:02 +1100 Subject: [PATCH 14/66] obs-browser: Fix issues with rendering on Linux/macOS --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index 344626c31..cb0f2db98 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit 344626c31df1c90b4605733f8706f033bc8a648b +Subproject commit cb0f2db98b6a26444af95d0322a85fac6f2146b6 From 3eec65d127744036ca5f278ef7f6a6c25b77cac7 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 19 Feb 2022 22:52:19 -0800 Subject: [PATCH 15/66] obs-browser: Fix texture recreating every frame --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index cb0f2db98..cf4f9f769 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit cb0f2db98b6a26444af95d0322a85fac6f2146b6 +Subproject commit cf4f9f769807478ef5c9b6fd03f050adc0f787d4 From 0f97427ef26f758265d3cf28fcaa6802d9c4b261 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 20 Feb 2022 11:41:10 -0800 Subject: [PATCH 16/66] obs-browser: Add support for custom OBS CEF Our version fixes the system freeze issue, reduces resource usage, and improves performance back to 3770 levels. See obsproject/cef@36fdac16a711 for detailed information. --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index cf4f9f769..915761778 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit cf4f9f769807478ef5c9b6fd03f050adc0f787d4 +Subproject commit 915761778ec1eae99e740ad4bf63b40db3142ee2 From c9dfad73404a77bb461cb337e07acb66bbf1eec0 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Mon, 21 Feb 2022 09:13:11 +1100 Subject: [PATCH 17/66] CI: Bump Windows CEF cache for new OnAcceleratedPaint2 See https://github.com/obsproject/cef/commit/36fdac16a7 for more info. This only bumps the cache on Windows as the changes are currently Windows-specific. --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d2a20ac09..3c7e2397c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -559,7 +559,7 @@ jobs: CACHE_NAME: 'windows-cef-64-cache' with: path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64 - key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-1 + key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-2 - name: 'Install prerequisite: QT' if: steps.qt-cache.outputs.cache-hit != 'true' run: | @@ -668,7 +668,7 @@ jobs: CACHE_NAME: 'cef-32-cache' with: path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86 - key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-1 + key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-2 - name: 'Install prerequisite: QT' if: steps.qt-cache.outputs.cache-hit != 'true' run: | From 2f48733f3c7ebabdf317000d57033fa74fa8af25 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Mon, 21 Feb 2022 10:32:17 +1100 Subject: [PATCH 18/66] CI: Bump Windows CEF cache to fix reported version While the previous cache had the correct changes, the version was reported incorrectly. This will avoid confusion. --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3c7e2397c..f7ba56b33 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -559,7 +559,7 @@ jobs: CACHE_NAME: 'windows-cef-64-cache' with: path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64 - key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-2 + key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-3 - name: 'Install prerequisite: QT' if: steps.qt-cache.outputs.cache-hit != 'true' run: | @@ -668,7 +668,7 @@ jobs: CACHE_NAME: 'cef-32-cache' with: path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86 - key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-2 + key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-3 - name: 'Install prerequisite: QT' if: steps.qt-cache.outputs.cache-hit != 'true' run: | From bfcc066099bd20f7ecf1542d9f2ece96a03bce77 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Mon, 21 Feb 2022 00:32:06 +0100 Subject: [PATCH 19/66] obs-outputs: Set a fixed size socket buffer on Windows 7 Auto tuning apparently doesn't work very well on this version and af6844f5c24d77ca7b4b1bcc000504d8e693a563 caused throughput regressions. --- plugins/obs-outputs/rtmp-stream.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index e2223a869..bbd8bc6fb 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -16,6 +16,9 @@ ******************************************************************************/ #include "rtmp-stream.h" +#ifdef _WIN32 +#include +#endif #ifndef SEC_TO_NSEC #define SEC_TO_NSEC 1000000000ULL @@ -605,6 +608,24 @@ static void *send_thread(void *data) os_set_thread_name("rtmp-stream: send_thread"); #if defined(_WIN32) + // Despite MSDN claiming otherwise, send buffer auto tuning on + // Windows 7 doesn't seem to work very well. + if (get_win_ver_int() == 0x601) { + DWORD cur_sendbuf_size; + DWORD desired_sendbuf_size = 524288; + socklen_t int_size = sizeof(int); + + if (!getsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET, + SO_SNDBUF, (char *)&cur_sendbuf_size, + &int_size) && + cur_sendbuf_size < desired_sendbuf_size) { + + setsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET, + SO_SNDBUF, (char *)&desired_sendbuf_size, + sizeof(desired_sendbuf_size)); + } + } + log_sndbuf_size(stream); #endif From acad9dbaf7bcf8f567c3e5c613411ca04ba92fa9 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 20 Feb 2022 16:35:15 -0800 Subject: [PATCH 20/66] libobs: Update version to 27.2.1 --- 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 779dfecfb..1fb6765c0 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 0 +#define LIBOBS_API_PATCH_VER 1 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) From 68a8264197cb1b332d9886d7e4542e7840b4adc8 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 16 Feb 2022 15:58:21 +0100 Subject: [PATCH 21/66] libobs: Map wayland keymap with MAP_PRIVATE Wayland clients are required to use MAP_PRIVATE starting with version 7 of the wl_seat protocol. Signed-off-by: Julian Orth --- libobs/obs-nix-wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-nix-wayland.c b/libobs/obs-nix-wayland.c index b3ffddc48..867703871 100644 --- a/libobs/obs-nix-wayland.c +++ b/libobs/obs-nix-wayland.c @@ -84,7 +84,7 @@ static void platform_keyboard_keymap(void *data, struct wl_keyboard *keyboard, UNUSED_PARAMETER(format); obs_hotkeys_platform_t *plat = (obs_hotkeys_platform_t *)data; - char *keymap_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + char *keymap_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); if (keymap_shm == MAP_FAILED) { close(fd); return; From cc1395857af1eeddf9cabc90d3a78657dcfb16c4 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 22 Feb 2022 13:03:53 -0500 Subject: [PATCH 22/66] CI: Update workflow to copy SOVERSION symlinks Commit 7a5bffc0a66980113c32dbed123b3928d38abe8b applied a fix to the macOS build script. This applies the same fix to the GitHub Actions workflow that is actually currently used on CI. --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f7ba56b33..c164b7ea4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -215,6 +215,8 @@ jobs: rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/ fi + /bin/cp -cpR /tmp/obsdeps/lib/*.dylib ./OBS.app/Contents/Frameworks + BUNDLE_PLUGINS=( ./OBS.app/Contents/PlugIns/coreaudio-encoder.so ./OBS.app/Contents/PlugIns/decklink-ouput-ui.so From bad7edd05134d50e18c1b84ca5937675489526d4 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Thu, 24 Feb 2022 23:25:29 +0100 Subject: [PATCH 23/66] win-wasapi: Fall back to old code if RTWQ fails Fixes a crash if RTWQ is unavailable, e.g. if the mmcss service is not running. --- plugins/win-wasapi/win-wasapi.cpp | 80 +++++++++++++++++-------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 07aa27821..718a52c97 100644 --- a/plugins/win-wasapi/win-wasapi.cpp +++ b/plugins/win-wasapi/win-wasapi.cpp @@ -347,46 +347,54 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_, (PFN_RtwqPutWaitingWorkItem)GetProcAddress( rtwq_module, "RtwqPutWaitingWorkItem"); - hr = rtwq_create_async_result(nullptr, &startCapture, nullptr, - &startCaptureAsyncResult); - if (FAILED(hr)) { - enumerator->UnregisterEndpointNotificationCallback( - notify); - throw HRError( - "Could not create startCaptureAsyncResult", hr); - } + try { + hr = rtwq_create_async_result(nullptr, &startCapture, + nullptr, + &startCaptureAsyncResult); + if (FAILED(hr)) { + throw HRError( + "Could not create startCaptureAsyncResult", + hr); + } - hr = rtwq_create_async_result(nullptr, &sampleReady, nullptr, - &sampleReadyAsyncResult); - if (FAILED(hr)) { - enumerator->UnregisterEndpointNotificationCallback( - notify); - throw HRError("Could not create sampleReadyAsyncResult", - hr); - } + hr = rtwq_create_async_result(nullptr, &sampleReady, + nullptr, + &sampleReadyAsyncResult); + if (FAILED(hr)) { + throw HRError( + "Could not create sampleReadyAsyncResult", + hr); + } - hr = rtwq_create_async_result(nullptr, &restart, nullptr, - &restartAsyncResult); - if (FAILED(hr)) { - enumerator->UnregisterEndpointNotificationCallback( - notify); - throw HRError("Could not create restartAsyncResult", - hr); - } + hr = rtwq_create_async_result(nullptr, &restart, + nullptr, + &restartAsyncResult); + if (FAILED(hr)) { + throw HRError( + "Could not create restartAsyncResult", + hr); + } - DWORD taskId = 0; - DWORD id = 0; - hr = rtwq_lock_shared_work_queue(L"Capture", 0, &taskId, &id); - if (FAILED(hr)) { - enumerator->UnregisterEndpointNotificationCallback( - notify); - throw HRError("RtwqLockSharedWorkQueue failed", hr); - } + DWORD taskId = 0; + DWORD id = 0; + hr = rtwq_lock_shared_work_queue(L"Capture", 0, &taskId, + &id); + if (FAILED(hr)) { + throw HRError("RtwqLockSharedWorkQueue failed", + hr); + } - startCapture.SetQueueId(id); - sampleReady.SetQueueId(id); - restart.SetQueueId(id); - } else { + startCapture.SetQueueId(id); + sampleReady.SetQueueId(id); + restart.SetQueueId(id); + } catch (HRError &err) { + blog(LOG_ERROR, "RTWQ setup failed: %s (0x%08X)", + err.str, err.hr); + rtwq_supported = false; + } + } + + if (!rtwq_supported) { captureThread = CreateThread(nullptr, 0, WASAPISource::CaptureThread, this, 0, nullptr); From eff0148dd7f626935d0d395aca0069af2747f173 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Mon, 21 Feb 2022 14:41:07 -0800 Subject: [PATCH 24/66] linux-v4l2: Fix warnings in mjpeg Previously the switch did not catch all cases issuing a quite large warning. Also there was a const-ness warning for codecs on ffmpeg 5.0 that this addresses. --- plugins/linux-v4l2/v4l2-mjpeg.c | 3 ++- plugins/linux-v4l2/v4l2-mjpeg.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-mjpeg.c b/plugins/linux-v4l2/v4l2-mjpeg.c index a04293265..f8deea3ab 100644 --- a/plugins/linux-v4l2/v4l2-mjpeg.c +++ b/plugins/linux-v4l2/v4l2-mjpeg.c @@ -75,7 +75,6 @@ void v4l2_destroy_mjpeg(struct v4l2_mjpeg_decoder *decoder) int v4l2_decode_mjpeg(struct obs_source_frame *out, uint8_t *data, size_t length, struct v4l2_mjpeg_decoder *decoder) { - decoder->packet->data = data; decoder->packet->size = length; if (avcodec_send_packet(decoder->context, decoder->packet) < 0) { @@ -106,6 +105,8 @@ int v4l2_decode_mjpeg(struct obs_source_frame *out, uint8_t *data, case AV_PIX_FMT_YUV444P: out->format = VIDEO_FORMAT_I444; break; + default: + break; } return 0; diff --git a/plugins/linux-v4l2/v4l2-mjpeg.h b/plugins/linux-v4l2/v4l2-mjpeg.h index 04ff75c39..aa19ec4d9 100644 --- a/plugins/linux-v4l2/v4l2-mjpeg.h +++ b/plugins/linux-v4l2/v4l2-mjpeg.h @@ -29,7 +29,7 @@ extern "C" { * Data structure for mjpeg decoding */ struct v4l2_mjpeg_decoder { - AVCodec *codec; + const AVCodec *codec; AVCodecContext *context; AVPacket *packet; AVFrame *frame; From 46f9a5a8b9f76274e3dffad739dff65841f7bdb3 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Thu, 24 Feb 2022 20:50:37 +0100 Subject: [PATCH 25/66] UI: Refresh edit menu on item locked signal --- UI/source-tree.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index 8a1175bc4..cb2ed3f4e 100644 --- a/UI/source-tree.cpp +++ b/UI/source-tree.cpp @@ -520,6 +520,7 @@ void SourceTreeItem::VisibilityChanged(bool visible) void SourceTreeItem::LockedChanged(bool locked) { lock->setChecked(locked); + OBSBasic::Get()->UpdateEditMenu(); } void SourceTreeItem::Renamed(const QString &name) From cedd8b9d15b759a202ce9a60b603f269b994ca3a Mon Sep 17 00:00:00 2001 From: obiwac Date: Thu, 24 Feb 2022 19:26:03 +0100 Subject: [PATCH 26/66] libobs/graphics: gs_query_dmabuf_* on FreeBSD too --- libobs/graphics/graphics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 8b55534c9..6c21a8c26 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -936,7 +936,7 @@ EXPORT gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, EXPORT void gs_register_loss_callbacks(const struct gs_device_loss *callbacks); EXPORT void gs_unregister_loss_callbacks(void *data); -#elif __linux__ +#elif defined(__linux__) || defined(__FreeBSD__) EXPORT gs_texture_t *gs_texture_create_from_dmabuf( unsigned int width, unsigned int height, uint32_t drm_format, From c09429aa8cac5e90d465e529e71bad78e3e9a900 Mon Sep 17 00:00:00 2001 From: obiwac Date: Sat, 26 Feb 2022 13:50:21 +0100 Subject: [PATCH 27/66] linux-v4l2: scandir with alphasort on non-Linux Sort video device entries with `alphasort` on non-Linux platforms, as opposed to `versionsort` on Linux. (`versionsort` is a GNU extension, unavailable on e.g. FreeBSD.) UI: Fix call to `to_string` on FreeBSD --- UI/platform-x11.cpp | 2 +- plugins/linux-v4l2/v4l2-output.c | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/UI/platform-x11.cpp b/UI/platform-x11.cpp index 536b1ea86..d4fc81674 100644 --- a/UI/platform-x11.cpp +++ b/UI/platform-x11.cpp @@ -136,7 +136,7 @@ const char *RunOnce::thr_name = "OBS runonce"; void PIDFileCheck(bool &already_running) { std::string tmpfile_name = - "/tmp/obs-studio.lock." + to_string(geteuid()); + "/tmp/obs-studio.lock." + std::to_string(geteuid()); int fd = open(tmpfile_name.c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0600); if (fd == -1) { already_running = true; diff --git a/plugins/linux-v4l2/v4l2-output.c b/plugins/linux-v4l2/v4l2-output.c index 2c39544ed..5ab423fbb 100644 --- a/plugins/linux-v4l2/v4l2-output.c +++ b/plugins/linux-v4l2/v4l2-output.c @@ -185,7 +185,14 @@ static bool virtualcam_start(void *data) return false; } - n = scandir("/dev", &list, scanfilter, versionsort); + n = scandir("/dev", &list, scanfilter, +#if defined(__linux__) + versionsort +#else + alphasort +#endif + ); + if (n == -1) return false; From db3cee0868206af76d9df3ffb29e85e2d46a598d Mon Sep 17 00:00:00 2001 From: Kevin Degeling Date: Tue, 15 Feb 2022 16:50:31 +0100 Subject: [PATCH 28/66] UI: Additional product details Based on the product description from the main website --- .../com.obsproject.Studio.appdata.xml.in | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/UI/xdg-data/com.obsproject.Studio.appdata.xml.in b/UI/xdg-data/com.obsproject.Studio.appdata.xml.in index 8ee13d932..b11b1afa0 100644 --- a/UI/xdg-data/com.obsproject.Studio.appdata.xml.in +++ b/UI/xdg-data/com.obsproject.Studio.appdata.xml.in @@ -9,6 +9,22 @@ Live streaming and video recording software

Free and open source software for video capturing, recording, and live streaming.

+

Features:

+
    +
  • High performance real time video/audio capturing and mixing. Create scenes made up of multiple sources including window captures, images, text, browser windows, webcams, capture cards and more.
  • +
  • Set up an unlimited number of scenes you can switch between seamlessly via custom transitions.
  • +
  • Intuitive audio mixer with per-source filters such as noise gate, noise suppression, and gain. Take full control with VST plugin support.
  • +
  • Powerful and easy to use configuration options. Add new Sources, duplicate existing ones, and adjust their properties effortlessly.
  • +
  • Streamlined Settings panel gives you access to a wide array of configuration options to tweak every aspect of your broadcast or recording.
  • +
  • Modular 'Dock' UI allows you to rearrange the layout exactly as you like. You can even pop out each individual Dock to its own window.
  • +
+

Create Professional Productions:

+
    +
  • Choose from a number of different and customizable transitions for when you switch between your scenes or add your own stinger video files.
  • +
  • Set hotkeys for nearly every sort of action, such as switching between scenes, starting/stopping streams or recordings, muting audio sources, push to talk, and more.
  • +
  • Studio Mode lets you preview your scenes and sources before pushing them live. Adjust your scenes and sources or create new ones and ensure they're perfect before your viewers ever see them.
  • +
  • Get a high level view of your production using the Multiview. Monitor 8 different scenes and easily cue or transition to any of them with merely a single or double click.
  • +
https://obsproject.com https://github.com/obsproject/obs-studio/issues @@ -16,7 +32,7 @@ https://crowdin.com/project/obs-studio - https://obsproject.com/assets/images/OBSDemoApp.jpg + https://obsproject.com/assets/images/OBSDemoApp2610.png From 2d7cda18e985e2c303f1db8a6d00da0e4d7619e1 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 28 Feb 2022 01:49:41 -0800 Subject: [PATCH 29/66] libobs/util: Fix rounding error with os_sleepto_ns() os_sleepto_ns() can occasionally return false on times that the processor may not have reached yet. The reason is because the count_target, which converts time_target into a QPC counter, is subject to a rounding error. Using numbers I generated from an actual clock cycle on my own CPU, I can show an example of this occurring: if the clock frequency value is 10000000.0, and you call os_sleepto_ns(42164590320600), it will convert that number first to a double floating point of its QPC value: 421645903205.99994. Then, because it converts that to a LONGLONG integer, it of course strips off the decimal point. If you convert 421645903205 *back* to a time value, the new value will be 42164590320500, which is lower than the original value by approximately 100 nanoseconds. While this may seem insignificant, it was apparently enough to cause the os_sleepto_ns() call in video_sleep() to sometimes return false despite the current time being lower than the target time, which would cause it to incorrectly calculate how many frames were duplicated by subtracting the frame time from the current system time, divide that by the current frame interval, set the vframe_info.count value to 0, and thus cause an infinite loop in the encode_gpu() function because queue_frame now starts returning negative numbers in perpetuity. This change fixes some rare reports of users having their video lock up and disconnect, forcing the user to have to forcibly shut down the program. Thanks to Twitch user SNLabat for having the patience to kindly provide us with a dump file from the freeze, and to Matt for coordinating with that user to obtain it from them. --- libobs/util/platform-windows.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 34c459d31..828d92872 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "base.h" #include "platform.h" @@ -333,7 +334,7 @@ bool os_sleepto_ns(uint64_t time_target) { const double freq = (double)get_clockfreq(); const LONGLONG count_target = - (LONGLONG)((double)time_target * freq / 1000000000.0); + (LONGLONG)(round((double)time_target * freq / 1000000000.0)); LARGE_INTEGER count; QueryPerformanceCounter(&count); From 2e1a6c9c643e6cc1b065c6261aa8c421b546c3f6 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Mon, 28 Feb 2022 09:15:31 -0800 Subject: [PATCH 30/66] libobs: Clamp video timing for safety os_gettime_ns and os_sleepto_ns may not match up exactly. --- libobs/obs-video.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libobs/obs-video.c b/libobs/obs-video.c index 1eceeac88..e98422261 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -723,7 +723,12 @@ static inline void video_sleep(struct obs_core_video *video, bool raw_active, *p_time = t; count = 1; } else { - count = (int)((os_gettime_ns() - cur_time) / interval_ns); + const uint64_t udiff = os_gettime_ns() - cur_time; + int64_t diff; + memcpy(&diff, &udiff, sizeof(diff)); + const uint64_t clamped_diff = + (diff > (int64_t)interval_ns) ? diff : interval_ns; + count = (int)(clamped_diff / interval_ns); *p_time = cur_time + interval_ns * count; } From fea9281a1ff779b5f013aec61f1eea51f0f420c6 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Mon, 28 Feb 2022 09:19:11 -0800 Subject: [PATCH 31/66] libobs/util: Use integer math for Windows timing Cleaner and faster than double math. --- libobs/util/platform-windows.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 828d92872..c5f7c7457 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -26,6 +26,7 @@ #include "platform.h" #include "darray.h" #include "dstr.h" +#include "util_uint64.h" #include "windows/win-registry.h" #include "windows/win-version.h" @@ -332,9 +333,9 @@ void os_cpu_usage_info_destroy(os_cpu_usage_info_t *info) bool os_sleepto_ns(uint64_t time_target) { - const double freq = (double)get_clockfreq(); + const uint64_t freq = get_clockfreq(); const LONGLONG count_target = - (LONGLONG)(round((double)time_target * freq / 1000000000.0)); + util_mul_div64(time_target, freq, 1000000000); LARGE_INTEGER count; QueryPerformanceCounter(&count); @@ -371,14 +372,9 @@ void os_sleep_ms(uint32_t duration) uint64_t os_gettime_ns(void) { LARGE_INTEGER current_time; - double time_val; - QueryPerformanceCounter(¤t_time); - time_val = (double)current_time.QuadPart; - time_val *= 1000000000.0; - time_val /= (double)get_clockfreq(); - - return (uint64_t)time_val; + return util_mul_div64(current_time.QuadPart, 1000000000, + get_clockfreq()); } /* returns [folder]\[name] on windows */ From 1acc6c69daf1ad54531dce5840e060d21f41cadc Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 1 Mar 2022 03:04:37 -0800 Subject: [PATCH 32/66] obs-scripting: Make callback "removed" variable atomic Makes the callback variable "removed" atomic, and on script unload, first sets all callbacks to removed before actually unloading the script out of a safety precaution. (See note at the bottom for further details) This minimizes the possibility of a race condition where the script callback could be called while those callbacks were being removed. Big note for this change, this change should eventually be replaced with a reference counting ownership method where script callbacks can hold a reference and share ownership of the script if it's still alive while the script callback is being called. That way the script callbacks can safely execute. May require a fair amount of reworking of the script object. --- deps/obs-scripting/obs-scripting-callback.h | 9 +++- .../obs-scripting-lua-frontend.c | 4 +- deps/obs-scripting/obs-scripting-lua.c | 42 +++++++++++++------ .../obs-scripting-python-frontend.c | 4 +- deps/obs-scripting/obs-scripting-python.c | 40 +++++++++++++----- deps/obs-scripting/obs-scripting-python.h | 1 + 6 files changed, 71 insertions(+), 29 deletions(-) diff --git a/deps/obs-scripting/obs-scripting-callback.h b/deps/obs-scripting/obs-scripting-callback.h index 81c5fb72f..f83119eea 100644 --- a/deps/obs-scripting/obs-scripting-callback.h +++ b/deps/obs-scripting/obs-scripting-callback.h @@ -33,9 +33,14 @@ struct script_callback { obs_script_t *script; calldata_t extra; - bool removed; + volatile bool removed; }; +static inline bool script_callback_removed(struct script_callback *cb) +{ + return os_atomic_load_bool(&cb->removed); +} + static inline void *add_script_callback(struct script_callback **first, obs_script_t *script, size_t extra_size) { @@ -54,7 +59,7 @@ static inline void *add_script_callback(struct script_callback **first, static inline void remove_script_callback(struct script_callback *cb) { - cb->removed = true; + os_atomic_set_bool(&cb->removed, true); struct script_callback *next = cb->next; if (next) diff --git a/deps/obs-scripting/obs-scripting-lua-frontend.c b/deps/obs-scripting/obs-scripting-lua-frontend.c index 421183bbd..1668260b0 100644 --- a/deps/obs-scripting/obs-scripting-lua-frontend.c +++ b/deps/obs-scripting/obs-scripting-lua-frontend.c @@ -206,7 +206,7 @@ static void frontend_event_callback(enum obs_frontend_event event, void *priv) struct lua_obs_callback *cb = priv; lua_State *script = cb->script; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { obs_frontend_remove_event_callback(frontend_event_callback, cb); return; } @@ -254,7 +254,7 @@ static void frontend_save_callback(obs_data_t *save_data, bool saving, struct lua_obs_callback *cb = priv; lua_State *script = cb->script; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { obs_frontend_remove_save_callback(frontend_save_callback, cb); return; } diff --git a/deps/obs-scripting/obs-scripting-lua.c b/deps/obs-scripting/obs-scripting-lua.c index 85bc3815a..acfdc2637 100644 --- a/deps/obs-scripting/obs-scripting-lua.c +++ b/deps/obs-scripting/obs-scripting-lua.c @@ -288,7 +288,7 @@ static void timer_call(struct script_callback *p_cb) { struct lua_obs_callback *cb = (struct lua_obs_callback *)p_cb; - if (p_cb->removed) + if (script_callback_removed(p_cb)) return; lock_callback(); @@ -329,7 +329,7 @@ static void obs_lua_main_render_callback(void *priv, uint32_t cx, uint32_t cy) struct lua_obs_callback *cb = priv; lua_State *script = cb->script; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { obs_remove_main_render_callback(obs_lua_main_render_callback, cb); return; @@ -377,7 +377,7 @@ static void obs_lua_tick_callback(void *priv, float seconds) struct lua_obs_callback *cb = priv; lua_State *script = cb->script; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { obs_remove_tick_callback(obs_lua_tick_callback, cb); return; } @@ -423,7 +423,7 @@ static void calldata_signal_callback(void *priv, calldata_t *cd) struct lua_obs_callback *cb = priv; lua_State *script = cb->script; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { signal_handler_remove_current(); return; } @@ -505,7 +505,7 @@ static void calldata_signal_callback_global(void *priv, const char *signal, struct lua_obs_callback *cb = priv; lua_State *script = cb->script; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { signal_handler_remove_current(); return; } @@ -663,7 +663,7 @@ static void hotkey_pressed(void *p_cb, bool pressed) struct lua_obs_callback *cb = p_cb; lua_State *script = cb->script; - if (cb->base.removed) + if (script_callback_removed(&cb->base)) return; lock_callback(); @@ -689,7 +689,7 @@ static void hotkey_callback(void *p_cb, obs_hotkey_id id, obs_hotkey_t *hotkey, { struct lua_obs_callback *cb = p_cb; - if (cb->base.removed) + if (script_callback_removed(&cb->base)) return; if (pressed) @@ -745,7 +745,7 @@ static bool button_prop_clicked(obs_properties_t *props, obs_property_t *p, lua_State *script = cb->script; bool ret = false; - if (cb->base.removed) + if (script_callback_removed(&cb->base)) return false; lock_callback(); @@ -801,7 +801,7 @@ static bool modified_callback(void *p_cb, obs_properties_t *props, lua_State *script = cb->script; bool ret = false; - if (cb->base.removed) + if (script_callback_removed(&cb->base)) return false; lock_callback(); @@ -1078,7 +1078,7 @@ static void lua_tick(void *param, float seconds) struct lua_obs_timer *next = timer->next; struct lua_obs_callback *cb = lua_obs_timer_cb(timer); - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { lua_obs_timer_remove(timer); } else { uint64_t elapsed = ts - timer->last_ts; @@ -1156,6 +1156,25 @@ void obs_lua_script_unload(obs_script_t *s) lua_State *script = data->script; + /* ---------------------------- */ + /* mark callbacks as removed */ + + pthread_mutex_lock(&data->mutex); + + /* XXX: scripts can potentially make callbacks when this happens, so + * this probably still isn't ideal as we can't predict how the + * processor or operating system is going to schedule things. a more + * ideal method would be to reference count the script objects and + * atomically share ownership with callbacks when they're called. */ + struct lua_obs_callback *cb = + (struct lua_obs_callback *)data->first_callback; + while (cb) { + os_atomic_set_bool(&cb->base.removed, true); + cb = (struct lua_obs_callback *)cb->base.next; + } + + pthread_mutex_unlock(&data->mutex); + /* ---------------------------- */ /* undefine source types */ @@ -1189,8 +1208,7 @@ void obs_lua_script_unload(obs_script_t *s) /* ---------------------------- */ /* remove all callbacks */ - struct lua_obs_callback *cb = - (struct lua_obs_callback *)data->first_callback; + cb = (struct lua_obs_callback *)data->first_callback; while (cb) { struct lua_obs_callback *next = (struct lua_obs_callback *)cb->base.next; diff --git a/deps/obs-scripting/obs-scripting-python-frontend.c b/deps/obs-scripting/obs-scripting-python-frontend.c index 52279b562..94787ffb4 100644 --- a/deps/obs-scripting/obs-scripting-python-frontend.c +++ b/deps/obs-scripting/obs-scripting-python-frontend.c @@ -280,7 +280,7 @@ static void frontend_save_callback(obs_data_t *save_data, bool saving, { struct python_obs_callback *cb = priv; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { obs_frontend_remove_save_callback(frontend_save_callback, cb); return; } @@ -355,7 +355,7 @@ static void frontend_event_callback(enum obs_frontend_event event, void *priv) { struct python_obs_callback *cb = priv; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { obs_frontend_remove_event_callback(frontend_event_callback, cb); return; } diff --git a/deps/obs-scripting/obs-scripting-python.c b/deps/obs-scripting/obs-scripting-python.c index 5f3b76eac..06e9fe9fc 100644 --- a/deps/obs-scripting/obs-scripting-python.c +++ b/deps/obs-scripting/obs-scripting-python.c @@ -431,7 +431,7 @@ static void timer_call(struct script_callback *p_cb) { struct python_obs_callback *cb = (struct python_obs_callback *)p_cb; - if (p_cb->removed) + if (script_callback_removed(p_cb)) return; lock_callback(cb); @@ -476,7 +476,7 @@ static void obs_python_tick_callback(void *priv, float seconds) { struct python_obs_callback *cb = priv; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { obs_remove_tick_callback(obs_python_tick_callback, cb); return; } @@ -546,7 +546,7 @@ static void calldata_signal_callback(void *priv, calldata_t *cd) { struct python_obs_callback *cb = priv; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { signal_handler_remove_current(); return; } @@ -652,7 +652,7 @@ static void calldata_signal_callback_global(void *priv, const char *signal, { struct python_obs_callback *cb = priv; - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { signal_handler_remove_current(); return; } @@ -767,7 +767,7 @@ static void hotkey_pressed(void *p_cb, bool pressed) { struct python_obs_callback *cb = p_cb; - if (cb->base.removed) + if (script_callback_removed(&cb->base)) return; lock_callback(cb); @@ -803,7 +803,7 @@ static void hotkey_callback(void *p_cb, obs_hotkey_id id, obs_hotkey_t *hotkey, { struct python_obs_callback *cb = p_cb; - if (cb->base.removed) + if (script_callback_removed(&cb->base)) return; if (pressed) @@ -873,7 +873,7 @@ static bool button_prop_clicked(obs_properties_t *props, obs_property_t *p, struct python_obs_callback *cb = p_cb; bool ret = false; - if (cb->base.removed) + if (script_callback_removed(&cb->base)) return false; lock_callback(cb); @@ -937,7 +937,7 @@ static bool modified_callback(void *p_cb, obs_properties_t *props, struct python_obs_callback *cb = p_cb; bool ret = false; - if (cb->base.removed) + if (script_callback_removed(&cb->base)) return false; lock_callback(cb); @@ -1333,6 +1333,24 @@ void obs_python_script_unload(obs_script_t *s) if (!s->loaded || !python_loaded) return; + /* ---------------------------- */ + /* mark callbacks as removed */ + + lock_python(); + + /* XXX: scripts can potentially make callbacks when this happens, so + * this probably still isn't ideal as we can't predict how the + * processor or operating system is going to schedule things. a more + * ideal method would be to reference count the script objects and + * atomically share ownership with callbacks when they're called. */ + struct script_callback *cb = data->first_callback; + while (cb) { + os_atomic_set_bool(&cb->removed, true); + cb = cb->next; + } + + unlock_python(); + /* ---------------------------- */ /* unhook tick function */ @@ -1350,7 +1368,7 @@ void obs_python_script_unload(obs_script_t *s) data->next_tick = NULL; } - lock_python(); + relock_python(); Py_XDECREF(data->tick); Py_XDECREF(data->save); @@ -1364,7 +1382,7 @@ void obs_python_script_unload(obs_script_t *s) /* ---------------------------- */ /* remove all callbacks */ - struct script_callback *cb = data->first_callback; + cb = data->first_callback; while (cb) { struct script_callback *next = cb->next; remove_script_callback(cb); @@ -1532,7 +1550,7 @@ static void python_tick(void *param, float seconds) struct python_obs_timer *next = timer->next; struct python_obs_callback *cb = python_obs_timer_cb(timer); - if (cb->base.removed) { + if (script_callback_removed(&cb->base)) { python_obs_timer_remove(timer); } else { uint64_t elapsed = ts - timer->last_ts; diff --git a/deps/obs-scripting/obs-scripting-python.h b/deps/obs-scripting/obs-scripting-python.h index aaa3fa966..c573817da 100644 --- a/deps/obs-scripting/obs-scripting-python.h +++ b/deps/obs-scripting/obs-scripting-python.h @@ -195,6 +195,7 @@ static inline bool py_error_(const char *func, int line) #define py_error() py_error_(__FUNCTION__, __LINE__) #define lock_python() PyGILState_STATE gstate = PyGILState_Ensure() +#define relock_python() gstate = PyGILState_Ensure() #define unlock_python() PyGILState_Release(gstate) struct py_source; From d23cb5279691eba498ecc0771223d7a119b8ac40 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 23 Feb 2022 03:53:04 -0800 Subject: [PATCH 33/66] win-dshow: Ensure thread is joinable before joining This may have been the issue behind the reported firefox crashes, but strangely, the thread should always be joinable here because it's explicitly always created in the constructor and joined in the destructor. --- plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp index ddd943538..b8c25a5d6 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp @@ -112,7 +112,8 @@ VCamFilter::VCamFilter() VCamFilter::~VCamFilter() { SetEvent(thread_stop); - th.join(); + if (th.joinable()) + th.join(); video_queue_close(vq); if (placeholder.scaled_data) From 91759c2a2e27b049a3efdff8e3b90773ad8407a5 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 27 Feb 2022 22:19:30 -0800 Subject: [PATCH 34/66] virtualcam-module: Stop thread on Stop call This causes the thread to only start when the IMediaFilter::Run/Pause calls have been made, and stop whenever either the IMediaFilter::Stop call has been made, or on destruction, whichever comes first. This potentially will work around a suspected race condition that appears to be in the WebRTC library where the filter's library will be released while the filter is in the process of being destroyed, which can take longer than usual if the join takes too long. Basically, fixes a reported crash (that doesn't appear to technically be our fault) when the filter is used with browsers when the virtualcam is deactivating in web browsers. --- .../virtualcam-module/virtualcam-filter.cpp | 62 ++++++++++++++++--- .../virtualcam-module/virtualcam-filter.hpp | 5 ++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp index b8c25a5d6..de2bcd6c5 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp @@ -45,6 +45,17 @@ VCamFilter::VCamFilter() in_obs = !!wcsstr(file, obs_process); + /* ---------------------------------------- */ + + AddRef(); +} + +inline void VCamFilter::ActuallyStart() +{ + if (th.joinable()) { + return; + } + /* ---------------------------------------- */ /* add last/current obs res/interval */ @@ -103,21 +114,32 @@ VCamFilter::VCamFilter() } /* ---------------------------------------- */ + /* Actually start */ + ResetEvent(thread_stop); th = std::thread([this] { Thread(); }); + SetEvent(thread_start); +} - AddRef(); +inline void VCamFilter::ActuallyStop() +{ + if (!th.joinable()) { + return; + } + + SetEvent(thread_stop); + th.join(); + video_queue_close(vq); + + if (placeholder.scaled_data) { + free(placeholder.scaled_data); + placeholder.scaled_data = nullptr; + } } VCamFilter::~VCamFilter() { - SetEvent(thread_stop); - if (th.joinable()) - th.join(); - video_queue_close(vq); - - if (placeholder.scaled_data) - free(placeholder.scaled_data); + ActuallyStop(); } const wchar_t *VCamFilter::FilterName() const @@ -134,7 +156,29 @@ STDMETHODIMP VCamFilter::Pause() return hr; } - SetEvent(thread_start); + ActuallyStart(); + return S_OK; +} + +STDMETHODIMP VCamFilter::Run(REFERENCE_TIME tStart) +{ + HRESULT hr = OutputFilter::Run(tStart); + if (FAILED(hr)) { + return hr; + } + + ActuallyStart(); + return S_OK; +} + +STDMETHODIMP VCamFilter::Stop() +{ + HRESULT hr = OutputFilter::Stop(); + if (FAILED(hr)) { + return hr; + } + + ActuallyStop(); return S_OK; } diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp index fd5cb1ddc..939b35d72 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp @@ -52,6 +52,9 @@ class VCamFilter : public DShow::OutputFilter { void UpdatePlaceholder(void); const int GetOutputBufferSize(void); + inline void ActuallyStart(); + inline void ActuallyStop(); + protected: const wchar_t *FilterName() const override; @@ -60,4 +63,6 @@ public: ~VCamFilter() override; STDMETHODIMP Pause() override; + STDMETHODIMP Stop() override; + STDMETHODIMP Run(REFERENCE_TIME tStart) override; }; From c1ff655dfede2195ac890068ac403a44eebd633a Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 27 Feb 2022 22:26:10 -0800 Subject: [PATCH 35/66] virtualcam-module: Remove unnecessarily inlines --- plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp | 4 ++-- plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp index de2bcd6c5..4820c7e1d 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp @@ -50,7 +50,7 @@ VCamFilter::VCamFilter() AddRef(); } -inline void VCamFilter::ActuallyStart() +void VCamFilter::ActuallyStart() { if (th.joinable()) { return; @@ -121,7 +121,7 @@ inline void VCamFilter::ActuallyStart() SetEvent(thread_start); } -inline void VCamFilter::ActuallyStop() +void VCamFilter::ActuallyStop() { if (!th.joinable()) { return; diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp index 939b35d72..bf05c8948 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp @@ -52,8 +52,8 @@ class VCamFilter : public DShow::OutputFilter { void UpdatePlaceholder(void); const int GetOutputBufferSize(void); - inline void ActuallyStart(); - inline void ActuallyStop(); + void ActuallyStart(); + void ActuallyStop(); protected: const wchar_t *FilterName() const override; From 34b55180ab748bc85022b473cddd5cb26bf90bf2 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 1 Mar 2022 11:40:16 -0800 Subject: [PATCH 36/66] libobs: Update version to 27.2.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 1fb6765c0..58650ed75 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 b5f85bc7884c20447d8d9632308504322d9188b2 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Tue, 22 Feb 2022 19:06:31 -0800 Subject: [PATCH 37/66] UI: Restore LC_NUMERIC to C locale on Mac/Linux Recommended by QCoreApplication doc for Unix. Fixes CUBE LUT load, and FFmpeg output args for foreign locales. --- UI/obs-app.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 35466fb8b..4ba4e10f0 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1156,6 +1156,12 @@ bool OBSApp::InitTheme() OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store) : QApplication(argc, argv), profilerNameStore(store) { + /* fix float handling */ +#if defined(Q_OS_UNIX) + if (!setlocale(LC_NUMERIC, "C")) + blog(LOG_WARNING, "Failed to set LC_NUMERIC to C locale"); +#endif + sleepInhibitor = os_inhibit_sleep_create("OBS Video/audio"); #ifdef __APPLE__ From 5bd791dea6dcfe66595d0613e2b2b3b902da39d0 Mon Sep 17 00:00:00 2001 From: jpark37 Date: Sun, 27 Feb 2022 11:37:49 -0800 Subject: [PATCH 38/66] UI: Remove conflicting setlocale call Qt already sets LC_ALL to "" for non-Windows. We set LC_NUMERIC to "C" afterward for sanity, and we don't want that setting to get smashed. --- UI/platform-x11.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/UI/platform-x11.cpp b/UI/platform-x11.cpp index d4fc81674..6162b6497 100644 --- a/UI/platform-x11.cpp +++ b/UI/platform-x11.cpp @@ -213,7 +213,6 @@ string GetDefaultVideoSavePath() vector GetPreferredLocales() { - setlocale(LC_ALL, ""); vector matched; string messages = setlocale(LC_MESSAGES, NULL); if (!messages.size() || messages == "C" || messages == "POSIX") From fc20e7a71b66072b0b3164fb3d26b7e6f8180de7 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 2 Mar 2022 04:19:02 -0800 Subject: [PATCH 39/66] virtualcam-module: Fix incorrect correct res/fps Apparently the testing that was done was not sufficient enough or did not check all test cases, so resolution/fps does not get set properly on the virtualcam. --- .../virtualcam-module/virtualcam-filter.cpp | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp index 4820c7e1d..618d260fc 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp @@ -45,17 +45,6 @@ VCamFilter::VCamFilter() in_obs = !!wcsstr(file, obs_process); - /* ---------------------------------------- */ - - AddRef(); -} - -void VCamFilter::ActuallyStart() -{ - if (th.joinable()) { - return; - } - /* ---------------------------------------- */ /* add last/current obs res/interval */ @@ -114,7 +103,15 @@ void VCamFilter::ActuallyStart() } /* ---------------------------------------- */ - /* Actually start */ + + AddRef(); +} + +void VCamFilter::ActuallyStart() +{ + if (th.joinable()) { + return; + } ResetEvent(thread_stop); th = std::thread([this] { Thread(); }); @@ -129,6 +126,12 @@ void VCamFilter::ActuallyStop() SetEvent(thread_stop); th.join(); +} + +VCamFilter::~VCamFilter() +{ + ActuallyStop(); + video_queue_close(vq); if (placeholder.scaled_data) { @@ -137,11 +140,6 @@ void VCamFilter::ActuallyStop() } } -VCamFilter::~VCamFilter() -{ - ActuallyStop(); -} - const wchar_t *VCamFilter::FilterName() const { return L"VCamFilter"; From 201e9df04174c263fb76d70aadd1577810180c78 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 2 Mar 2022 04:25:58 -0800 Subject: [PATCH 40/66] libobs: Update version to 27.2.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 58650ed75..46c81517d 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 9f0650658d8bfdca2ce1de7f88fa086d7c199c1b Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 2 Mar 2022 05:04:47 -0800 Subject: [PATCH 41/66] virtualcam-module: Only initialize placeholder once --- plugins/win-dshow/virtualcam-module/placeholder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/win-dshow/virtualcam-module/placeholder.cpp b/plugins/win-dshow/virtualcam-module/placeholder.cpp index 8b222d1ee..65066adb6 100644 --- a/plugins/win-dshow/virtualcam-module/placeholder.cpp +++ b/plugins/win-dshow/virtualcam-module/placeholder.cpp @@ -123,6 +123,9 @@ static bool load_placeholder_internal() bool initialize_placeholder() { + if (initialized) + return true; + GdiplusStartupInput si; ULONG_PTR token; GdiplusStartup(&token, &si, nullptr); From 399636847d3a8506c551eac330f3276208e18d83 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 2 Mar 2022 05:05:13 -0800 Subject: [PATCH 42/66] virtualcam-module: Prevent placeholder memory leak --- plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp index 618d260fc..96c6b51cd 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp @@ -210,6 +210,11 @@ void VCamFilter::Thread() /* ---------------------------------------- */ /* load placeholder image */ + if (placeholder.scaled_data) { + free(placeholder.scaled_data); + placeholder.scaled_data = nullptr; + } + if (initialize_placeholder()) { placeholder.source_data = get_placeholder_ptr(); get_placeholder_size(&placeholder.cx, &placeholder.cy); From b65ee7e7fbca05e6c4bfd55b7ff721150b3c0735 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 2 Mar 2022 17:28:23 -0800 Subject: [PATCH 43/66] virtualcam-module: Revert changes since 27.1.3 (for now) This reverts commit 4b0767741ab7f3c10d95c96c17a9ac0c0f726ca3, 3f3f9ed7e60a8dbe8c904b234a8b715841df880f, 0e7c17bd6e2f17a6002556ef8229f0b7e2124e91, and 865eecb7396841ccf4b4154b79253f5c8f1a1b76. --- .../virtualcam-module/virtualcam-filter.cpp | 59 ++----------------- .../virtualcam-module/virtualcam-filter.hpp | 5 -- 2 files changed, 6 insertions(+), 58 deletions(-) diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp index 96c6b51cd..b8c25a5d6 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp @@ -104,40 +104,20 @@ VCamFilter::VCamFilter() /* ---------------------------------------- */ - AddRef(); -} - -void VCamFilter::ActuallyStart() -{ - if (th.joinable()) { - return; - } - - ResetEvent(thread_stop); th = std::thread([this] { Thread(); }); - SetEvent(thread_start); -} -void VCamFilter::ActuallyStop() -{ - if (!th.joinable()) { - return; - } - - SetEvent(thread_stop); - th.join(); + AddRef(); } VCamFilter::~VCamFilter() { - ActuallyStop(); - + SetEvent(thread_stop); + if (th.joinable()) + th.join(); video_queue_close(vq); - if (placeholder.scaled_data) { + if (placeholder.scaled_data) free(placeholder.scaled_data); - placeholder.scaled_data = nullptr; - } } const wchar_t *VCamFilter::FilterName() const @@ -154,29 +134,7 @@ STDMETHODIMP VCamFilter::Pause() return hr; } - ActuallyStart(); - return S_OK; -} - -STDMETHODIMP VCamFilter::Run(REFERENCE_TIME tStart) -{ - HRESULT hr = OutputFilter::Run(tStart); - if (FAILED(hr)) { - return hr; - } - - ActuallyStart(); - return S_OK; -} - -STDMETHODIMP VCamFilter::Stop() -{ - HRESULT hr = OutputFilter::Stop(); - if (FAILED(hr)) { - return hr; - } - - ActuallyStop(); + SetEvent(thread_start); return S_OK; } @@ -210,11 +168,6 @@ void VCamFilter::Thread() /* ---------------------------------------- */ /* load placeholder image */ - if (placeholder.scaled_data) { - free(placeholder.scaled_data); - placeholder.scaled_data = nullptr; - } - if (initialize_placeholder()) { placeholder.source_data = get_placeholder_ptr(); get_placeholder_size(&placeholder.cx, &placeholder.cy); diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp index bf05c8948..fd5cb1ddc 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp @@ -52,9 +52,6 @@ class VCamFilter : public DShow::OutputFilter { void UpdatePlaceholder(void); const int GetOutputBufferSize(void); - void ActuallyStart(); - void ActuallyStop(); - protected: const wchar_t *FilterName() const override; @@ -63,6 +60,4 @@ public: ~VCamFilter() override; STDMETHODIMP Pause() override; - STDMETHODIMP Stop() override; - STDMETHODIMP Run(REFERENCE_TIME tStart) override; }; From afa85277239832b8d93f5a0e4ccef2fc4866d0b6 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 23 Mar 2022 05:45:03 +0100 Subject: [PATCH 44/66] obs-scripting: Load obspython.py from Resources on macOS --- deps/obs-scripting/obs-scripting-python.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/deps/obs-scripting/obs-scripting-python.c b/deps/obs-scripting/obs-scripting-python.c index 06e9fe9fc..4c65f64ba 100644 --- a/deps/obs-scripting/obs-scripting-python.c +++ b/deps/obs-scripting/obs-scripting-python.c @@ -1676,10 +1676,24 @@ bool obs_scripting_load_python(const char *python_path) bfree(absolute_script_path); #if __APPLE__ - char *exec_path = os_get_executable_path_ptr(""); - if (exec_path) - add_to_python_path(exec_path); - bfree(exec_path); + char *absolute_exec_path = os_get_executable_path_ptr(""); + + if (absolute_exec_path != NULL) { + add_to_python_path(absolute_exec_path); + + struct dstr resources_path; + dstr_init_move_array(&resources_path, absolute_exec_path); + dstr_cat(&resources_path, "../Resources"); + + char *absolute_resources_path = os_get_abs_path_ptr(resources_path.array); + if (absolute_resources_path != NULL) { + add_to_python_path(absolute_resources_path); + bfree(absolute_resources_path); + } + + dstr_free(&resources_path); + bfree(absolute_exec_path); + } #endif py_obspython = PyImport_ImportModule("obspython"); From 20332b01fd7d69a03fc746f63525a04a5bc5f9ad Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 23 Mar 2022 05:45:18 +0100 Subject: [PATCH 45/66] CI: Move obspython.py to Resources --- .github/workflows/main.yml | 2 +- CI/full-build-macos.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c164b7ea4..3dac17e7d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -211,7 +211,7 @@ jobs: if [ -d ./OBS.app/Contents/Resources/data/obs-scripting ]; then mv ./OBS.app/Contents/Resources/data/obs-scripting/obslua.so ./OBS.app/Contents/MacOS/ mv ./OBS.app/Contents/Resources/data/obs-scripting/_obspython.so ./OBS.app/Contents/MacOS/ - mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/MacOS/ + mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/Resources/ rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/ fi diff --git a/CI/full-build-macos.sh b/CI/full-build-macos.sh index a0c0e6973..8b2b2cb14 100755 --- a/CI/full-build-macos.sh +++ b/CI/full-build-macos.sh @@ -419,7 +419,7 @@ prepare_macos_bundle() { if [ -d ./OBS.app/Contents/Resources/data/obs-scripting ]; then /bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obslua.so ./OBS.app/Contents/MacOS/ /bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/_obspython.so ./OBS.app/Contents/MacOS/ - /bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/MacOS/ + /bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/Resources/ /bin/rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/ fi # dylibbundler will only copy actually linked files into bundle, but not symlinks From 4b6170f258156abcbc5d3b9f8a06eaead7b5e55c Mon Sep 17 00:00:00 2001 From: jpark37 Date: Sun, 27 Feb 2022 12:48:28 -0800 Subject: [PATCH 46/66] win-dshow: Fix hwdevice_ctx leak --- plugins/win-dshow/ffmpeg-decode.c | 4 ++++ plugins/win-dshow/ffmpeg-decode.h | 1 + 2 files changed, 5 insertions(+) diff --git a/plugins/win-dshow/ffmpeg-decode.c b/plugins/win-dshow/ffmpeg-decode.c index 0e0990e76..2f7cba516 100644 --- a/plugins/win-dshow/ffmpeg-decode.c +++ b/plugins/win-dshow/ffmpeg-decode.c @@ -61,6 +61,7 @@ static void init_hw_decoder(struct ffmpeg_decode *d) } if (hw_ctx) { + d->hw_device_ctx = hw_ctx; d->decoder->hw_device_ctx = av_buffer_ref(hw_ctx); d->hw = true; } @@ -117,6 +118,9 @@ void ffmpeg_decode_free(struct ffmpeg_decode *decode) if (decode->frame) av_frame_free(&decode->frame); + if (decode->hw_device_ctx) + av_buffer_unref(&decode->hw_device_ctx); + if (decode->packet_buffer) bfree(decode->packet_buffer); diff --git a/plugins/win-dshow/ffmpeg-decode.h b/plugins/win-dshow/ffmpeg-decode.h index f06c01b09..a087208b9 100644 --- a/plugins/win-dshow/ffmpeg-decode.h +++ b/plugins/win-dshow/ffmpeg-decode.h @@ -37,6 +37,7 @@ extern "C" { #endif struct ffmpeg_decode { + AVBufferRef *hw_device_ctx; AVCodecContext *decoder; AVCodec *codec; From 23bc7c754496bdb55243386c1c7232c5bb72ce99 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 8 Mar 2022 01:02:44 -0800 Subject: [PATCH 47/66] UI: Fix duplicated source names in audio settings When you add an audio capture source and use the "use existing" feature in the source selection dialog, and then recreate the same audio source again in audio settings, it will result in two sources using identical names, which although relatively harmless can cause issues when doing things such as trying to find sources by their name. Fixes obsproject/obs-studio#5621 Closes obsproject/obs-studio#5947 --- UI/window-basic-main.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 1cadc9052..f4ea0ae9f 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -4412,6 +4412,25 @@ bool OBSBasic::ResetAudio() return obs_reset_audio(&ai); } +extern std::string strprintf(const char *format, ...); + +static std::string GetDupName(const char *name) +{ + std::string newName = name; + int inc = 1; + + for (;;) { + OBSSourceAutoRelease existing_source = + obs_get_source_by_name(newName.c_str()); + if (!existing_source) + break; + + newName = strprintf("%s (%d)", name, ++inc); + } + + return newName; +} + void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId, const char *deviceDesc, int channel) { @@ -4435,9 +4454,11 @@ void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId, } } else if (!disable) { + std::string name = GetDupName(deviceDesc); + settings = obs_data_create(); obs_data_set_string(settings, "device_id", deviceId); - source = obs_source_create(sourceId, deviceDesc, settings, + source = obs_source_create(sourceId, name.c_str(), settings, nullptr); obs_set_output_source(channel, source); From 09df945fb8ce39eeb802353ee6279516623488f6 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 8 Mar 2022 01:27:20 -0800 Subject: [PATCH 48/66] UI: Use get_new_source_name instead of strprintf Remove a bit of duplicated code, and fix non-Windows compilation. This is a bit of a workaround to not have to modify cmake, but might as well reuse a function that already exists anyway rather than duplicate the code. --- UI/window-basic-main.cpp | 24 +++--------------------- UI/window-basic-source-select.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index f4ea0ae9f..e0a277575 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -4412,24 +4412,7 @@ bool OBSBasic::ResetAudio() return obs_reset_audio(&ai); } -extern std::string strprintf(const char *format, ...); - -static std::string GetDupName(const char *name) -{ - std::string newName = name; - int inc = 1; - - for (;;) { - OBSSourceAutoRelease existing_source = - obs_get_source_by_name(newName.c_str()); - if (!existing_source) - break; - - newName = strprintf("%s (%d)", name, ++inc); - } - - return newName; -} +extern char *get_new_source_name(const char *name, const char *format); void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId, const char *deviceDesc, int channel) @@ -4454,12 +4437,11 @@ void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId, } } else if (!disable) { - std::string name = GetDupName(deviceDesc); + BPtr name = get_new_source_name(deviceDesc, "%s (%d)"); settings = obs_data_create(); obs_data_set_string(settings, "device_id", deviceId); - source = obs_source_create(sourceId, name.c_str(), settings, - nullptr); + source = obs_source_create(sourceId, name, settings, nullptr); obs_set_output_source(channel, source); } diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp index f71e8bbdc..256b9992f 100644 --- a/UI/window-basic-source-select.cpp +++ b/UI/window-basic-source-select.cpp @@ -130,7 +130,7 @@ static void AddSource(void *_data, obs_scene_t *scene) obs_sceneitem_set_visible(sceneitem, data->visible); } -static char *get_new_source_name(const char *name) +char *get_new_source_name(const char *name, const char *format) { struct dstr new_name = {0}; int inc = 0; @@ -143,7 +143,7 @@ static char *get_new_source_name(const char *name) if (!existing_source) break; - dstr_printf(&new_name, "%s %d", name, ++inc + 1); + dstr_printf(&new_name, format, name, ++inc + 1); } return new_name.array; @@ -160,8 +160,8 @@ static void AddExisting(OBSSource source, bool visible, bool duplicate, if (duplicate) { OBSSource from = source; - char *new_name = - get_new_source_name(obs_source_get_name(source)); + char *new_name = get_new_source_name( + obs_source_get_name(source), "%s %d"); source = obs_source_duplicate(from, new_name, false); obs_source_release(source); bfree(new_name); From 4845e88219339607814323963f6e136648e37605 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Mon, 21 Feb 2022 07:03:11 -0800 Subject: [PATCH 49/66] UI: Avoid calling obs_source_update multiple times When we added undo/redo in 86eb7aeb a distinction between Updates and Visual updates was made, presumably for filters which need special handling of the update case. These functions were also added to regular source properties, but didnt use any special code instead calling obs_source_update an additional time after a short time. Instead lets remove the 2nd delayed call and only call obs_source_update once per settings change. --- UI/window-basic-properties.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/UI/window-basic-properties.cpp b/UI/window-basic-properties.cpp index 945c8d408..448129171 100644 --- a/UI/window-basic-properties.cpp +++ b/UI/window-basic-properties.cpp @@ -81,20 +81,10 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) OBSDataAutoRelease nd_settings = obs_source_get_settings(source); obs_data_apply(oldSettings, nd_settings); - auto handle_memory = [](void *vp, obs_data_t *old_settings, - obs_data_t *new_settings) { - obs_source_t *source = reinterpret_cast(vp); - - obs_source_update(source, new_settings); - - UNUSED_PARAMETER(old_settings); - UNUSED_PARAMETER(vp); - }; - view = new OBSPropertiesView( nd_settings.Get(), source, (PropertiesReloadCallback)obs_source_properties, - (PropertiesUpdateCallback)handle_memory, + (PropertiesUpdateCallback) nullptr, // No special handling required for undo/redo (PropertiesVisualUpdateCb)obs_source_update); view->setMinimumHeight(150); From 27bb60813044c1ffd78b377bfa140cd36997d9c2 Mon Sep 17 00:00:00 2001 From: Exeldro Date: Thu, 10 Feb 2022 19:13:43 +0100 Subject: [PATCH 50/66] libobs: Fix overflow subtracting unsigned numbers When offset_size is greater than size, size would overflow --- libobs/obs-encoder.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 76fb8b2e8..2ae02685e 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -1086,13 +1086,15 @@ static inline void push_back_audio(struct obs_encoder *encoder, struct audio_data *data, size_t size, size_t offset_size) { + if (offset_size >= size) + return; + size -= offset_size; /* push in to the circular buffer */ - if (size) - for (size_t i = 0; i < encoder->planes; i++) - circlebuf_push_back(&encoder->audio_input_buffer[i], - data->data[i] + offset_size, size); + for (size_t i = 0; i < encoder->planes; i++) + circlebuf_push_back(&encoder->audio_input_buffer[i], + data->data[i] + offset_size, size); } static inline size_t calc_offset_size(struct obs_encoder *encoder, From 63f535c86e184a88efa8055f3e6aa4044ce03c55 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Tue, 1 Mar 2022 01:57:15 -0600 Subject: [PATCH 51/66] UI: Fix mixer hide toggle in studio mode If the user would toggle hide in mixer in studio mode, the source would show up in audio mixer, even if it wasn't active. --- UI/window-basic-main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index e0a277575..a1ffc621f 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3531,6 +3531,8 @@ void OBSBasic::ActivateAudioSource(OBSSource source) { if (SourceMixerHidden(source)) return; + if (!obs_source_active(source)) + return; if (!obs_source_audio_active(source)) return; From 1cfd1bb90a9ae1b8e5894c88c227c9cf8338c10e Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 9 Mar 2022 09:14:32 -0800 Subject: [PATCH 52/66] UI: Rename visual update callback variable Having both a "callback" and "cb" doesn't make sense. One is specifically for visual updates only, the other is for actual updates, so use an appropriate name for both --- UI/properties-view.cpp | 14 ++++++++------ UI/properties-view.hpp | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 87a438f6a..3030f9e14 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -180,14 +180,15 @@ void OBSPropertiesView::GetScrollPos(int &h, int &v) OBSPropertiesView::OBSPropertiesView(OBSData settings_, obs_object_t *obj, PropertiesReloadCallback reloadCallback, PropertiesUpdateCallback callback_, - PropertiesVisualUpdateCb cb_, int minSize_) + PropertiesVisualUpdateCb visUpdateCb_, + int minSize_) : VScrollArea(nullptr), properties(nullptr, obs_properties_destroy), settings(settings_), weakObj(obs_object_get_weak_object(obj)), reloadCallback(reloadCallback), callback(callback_), - cb(cb_), + visUpdateCb(visUpdateCb_), minSize(minSize_) { setFrameShape(QFrame::NoFrame); @@ -198,14 +199,15 @@ OBSPropertiesView::OBSPropertiesView(OBSData settings_, obs_object_t *obj, OBSPropertiesView::OBSPropertiesView(OBSData settings_, void *obj, PropertiesReloadCallback reloadCallback, PropertiesUpdateCallback callback_, - PropertiesVisualUpdateCb cb_, int minSize_) + PropertiesVisualUpdateCb visUpdateCb_, + int minSize_) : VScrollArea(nullptr), properties(nullptr, obs_properties_destroy), settings(settings_), rawObj(obj), reloadCallback(reloadCallback), callback(callback_), - cb(cb_), + visUpdateCb(visUpdateCb_), minSize(minSize_) { setFrameShape(QFrame::NoFrame); @@ -2028,11 +2030,11 @@ void WidgetInfo::ControlChanged() blog(LOG_DEBUG, "No update timer or no callback!"); } - if (view->cb && !view->deferUpdate) { + if (view->visUpdateCb && !view->deferUpdate) { OBSObject strongObj = view->GetObject(); void *obj = strongObj ? strongObj.Get() : view->rawObj; if (obj) - view->cb(obj, view->settings); + view->visUpdateCb(obj, view->settings); } view->SignalChanged(); diff --git a/UI/properties-view.hpp b/UI/properties-view.hpp index 2ee80ded9..ab2eee675 100644 --- a/UI/properties-view.hpp +++ b/UI/properties-view.hpp @@ -102,7 +102,7 @@ private: std::string type; PropertiesReloadCallback reloadCallback; PropertiesUpdateCallback callback = nullptr; - PropertiesVisualUpdateCb cb = nullptr; + PropertiesVisualUpdateCb visUpdateCb = nullptr; int minSize; std::vector> children; std::string lastFocused; From fb96d637e20136a6c8c04c1cdc536801dbaa96a8 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 9 Mar 2022 13:10:24 -0800 Subject: [PATCH 53/66] UI: Fix deferred source properties not updating --- UI/properties-view.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/UI/properties-view.hpp b/UI/properties-view.hpp index ab2eee675..17a93bc8f 100644 --- a/UI/properties-view.hpp +++ b/UI/properties-view.hpp @@ -188,7 +188,10 @@ public: inline void UpdateSettings() { - callback(OBSGetStrongRef(weakObj), nullptr, settings); + if (callback) + callback(OBSGetStrongRef(weakObj), nullptr, settings); + else if (visUpdateCb) + visUpdateCb(OBSGetStrongRef(weakObj), settings); } inline bool DeferUpdate() const { return deferUpdate; } From 2056a41ca591ffcb866c800c8f776aa20ecf176f Mon Sep 17 00:00:00 2001 From: cg2121 Date: Thu, 10 Mar 2022 02:22:42 -0600 Subject: [PATCH 54/66] UI: Fix handling of remove signal with projectors Since the remove signal is coming from another thread, QMetaObject::invokeMethod has to be used, or corruption could possibly occur. --- UI/window-projector.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index 38e3ac42d..b6111caa2 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -626,11 +626,7 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) void OBSProjector::OBSSourceRemoved(void *data, calldata_t *params) { OBSProjector *window = reinterpret_cast(data); - - OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); - main->DeleteProjector(window); - allProjectors.removeAll(window); - + QMetaObject::invokeMethod(window, "EscapeTriggered"); UNUSED_PARAMETER(params); } From 0d68aacfb6102c64c3d789ee60adae4d373b191c Mon Sep 17 00:00:00 2001 From: eightball567 Date: Wed, 9 Mar 2022 09:48:23 +0100 Subject: [PATCH 55/66] mac-capture: Add vbcable to whitelist for loopback devices --- plugins/mac-capture/audio-device-enum.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mac-capture/audio-device-enum.c b/plugins/mac-capture/audio-device-enum.c index 4b0feb9b8..052979e34 100644 --- a/plugins/mac-capture/audio-device-enum.c +++ b/plugins/mac-capture/audio-device-enum.c @@ -15,7 +15,8 @@ static inline bool device_is_input(char *device) astrstri(device, "ishowu") == NULL && astrstri(device, "blackhole") == NULL && astrstri(device, "loopback") == NULL && - astrstri(device, "groundcontrol") == NULL; + astrstri(device, "groundcontrol") == NULL && + astrstri(device, "vbcable") == NULL; } static inline bool enum_success(OSStatus stat, const char *msg) From b718aa41f47ae893dea89e569f6533190e4409bb Mon Sep 17 00:00:00 2001 From: jpark37 Date: Sat, 12 Mar 2022 20:46:13 -0800 Subject: [PATCH 56/66] win-waspai: Tighten version check for RTWQ Getting reports that it isn't working for 1607 and below. If they continue, we should probably just pull the RTWQ path. --- plugins/win-wasapi/win-wasapi.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 718a52c97..46b7d1939 100644 --- a/plugins/win-wasapi/win-wasapi.cpp +++ b/plugins/win-wasapi/win-wasapi.cpp @@ -328,7 +328,14 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_, // while RTWQ was introduced in Win 8.1, it silently fails // to capture Desktop Audio for some reason. Disable for now. - if (get_win_ver_int() >= _WIN32_WINNT_WIN10) + struct win_version_info win1703 = {}; + win1703.major = 10; + win1703.minor = 0; + win1703.build = 15063; + win1703.revis = 0; + struct win_version_info ver; + get_win_ver(&ver); + if (win_version_compare(&ver, &win1703) >= 0) rtwq_supported = rtwq_module != NULL; if (rtwq_supported) { From 928597ec9d441a45ba56085ef0f73cd92c0b2be4 Mon Sep 17 00:00:00 2001 From: pkv Date: Thu, 17 Mar 2022 20:31:34 +0100 Subject: [PATCH 57/66] obs-libfdk: Set bitstream to ADTS for mpegts output This sets the aac bitstream to ADTS for the mpegts output. This fixes a bug with mpegts ouput where the avformat muxer issues an error with fdk-aac encoder. Signed-off-by: pkv --- plugins/obs-libfdk/obs-libfdk.c | 6 +++++- plugins/rtmp-services/rtmp-custom.c | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/obs-libfdk/obs-libfdk.c b/plugins/obs-libfdk/obs-libfdk.c index 234ca0ea6..b6e93cdb7 100644 --- a/plugins/obs-libfdk/obs-libfdk.c +++ b/plugins/obs-libfdk/obs-libfdk.c @@ -96,6 +96,8 @@ static void *libfdk_create(obs_data_t *settings, obs_encoder_t *encoder) int bitrate = (int)obs_data_get_int(settings, "bitrate") * 1000; int afterburner = obs_data_get_bool(settings, "afterburner") ? 1 : 0; audio_t *audio = obs_encoder_audio(encoder); + bool set_to_ADTS = obs_data_get_bool(settings, "set_to_ADTS"); + int transmux = set_to_ADTS ? 2 : 0; int mode = 0; AACENC_ERROR err; @@ -159,7 +161,9 @@ static void *libfdk_create(obs_data_t *settings, obs_encoder_t *encoder) aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATEMODE, 0)); CHECK_LIBFDK( aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATE, bitrate)); - CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, 0)); + + CHECK_LIBFDK( + aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, transmux)); CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AFTERBURNER, afterburner)); diff --git a/plugins/rtmp-services/rtmp-custom.c b/plugins/rtmp-services/rtmp-custom.c index 3b7ca2240..2649a76b9 100644 --- a/plugins/rtmp-services/rtmp-custom.c +++ b/plugins/rtmp-services/rtmp-custom.c @@ -122,6 +122,7 @@ static void rtmp_custom_apply_settings(void *data, obs_data_t *video_settings, strncmp(service->server, RTMP_PROTOCOL, strlen(RTMP_PROTOCOL)) != 0) { obs_data_set_bool(video_settings, "repeat_headers", true); + obs_data_set_bool(audio_settings, "set_to_ADTS", true); } } From ef53407c75053a07582fb6b51847244b3f5550f8 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 17 Mar 2022 12:43:21 -0400 Subject: [PATCH 58/66] CI: Fix Qt XML Validator workflow libxml2-utils updated recently but the manifest is out-of-date. Try to fix it with the suggestion of running apt-get update first. --- .github/workflows/qt-xml.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/qt-xml.yml b/.github/workflows/qt-xml.yml index fbee08fea..4379ce18e 100644 --- a/.github/workflows/qt-xml.yml +++ b/.github/workflows/qt-xml.yml @@ -19,6 +19,7 @@ jobs: - name: Install xmllint run: | + sudo apt-get -qq update sudo apt-get install --no-install-recommends -y libxml2-utils - name: Register Annotations From a1ba04536f4cd4b83dc6f694bfd8f72d272fa1c6 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 24 Mar 2022 11:17:03 +0100 Subject: [PATCH 59/66] CI: Fix CEF zip extraction path on Windows --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3dac17e7d..d352ac8f9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -581,7 +581,7 @@ jobs: if: steps.cef-cache.outputs.cache-hit != 'true' run: | curl -kL https://cdn-fastly.obsproject.com/downloads/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64.zip -f --retry 5 -o cef.zip - 7z x cef.zip -o"${{ github.workspace }}/cmbuild" + 7z x cef.zip -o"${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64" - name: 'Configure' run: | mkdir ./build @@ -690,7 +690,7 @@ jobs: if: steps.cef-cache.outputs.cache-hit != 'true' run: | curl -kL https://cdn-fastly.obsproject.com/downloads/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86.zip -f --retry 5 -o cef.zip - 7z x cef.zip -o"${{ github.workspace }}/cmbuild" + 7z x cef.zip -o"${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86" - name: 'Configure' run: | mkdir ./build From c2632669750bd21458498d40621b49476d7d2100 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 24 Mar 2022 11:21:46 +0100 Subject: [PATCH 60/66] CI: Enable legacy CI for PRs to release branches --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d352ac8f9..f1aa2b1d9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,6 +14,7 @@ on: - '**.md' branches: - master + - 'release/**' env: MACOS_CEF_BUILD_VERSION: '4638' From 48f41c67dc088cdf7215af1f75ef8efb7d3b6793 Mon Sep 17 00:00:00 2001 From: Monsteer <55640387+COOLIGUAY@users.noreply.github.com> Date: Thu, 24 Mar 2022 12:51:49 -0400 Subject: [PATCH 61/66] UI: Add missing previousIcon in Rachni theme The previous icon for the media icons set in the Rachni theme is missing in the theme's .qss file. --- UI/data/themes/Rachni.qss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index 1f5aa4591..f7c3fa060 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -1464,6 +1464,10 @@ QPushButton#sourceFiltersButton { qproperty-icon: url(./Dark/media/media_next.svg); } +* [themeID="previousIcon"] { + qproperty-icon: url(./Dark/media/media_previous.svg); +} + /* YouTube Integration */ OBSYoutubeActions { qproperty-thumbPlaceholder: url(./Dark/sources/image.svg); From aab14ba29a759037e42610f127ba844f17dbd416 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 27 Mar 2022 16:28:40 -0700 Subject: [PATCH 62/66] libobs: Update version to 27.2.4 --- 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 46c81517d..1dddea7d1 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 3 +#define LIBOBS_API_PATCH_VER 4 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) From d9865bdffb5b8cb6ce616b20087b75d4611d13c6 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Sun, 10 Apr 2022 14:28:58 -0700 Subject: [PATCH 63/66] libobs-opengl: Use gl helpers in create_dmabuf_image This replaces direct OpenGL calls to error handling helpers. Previously this would cause errors to be misattributed to the next OpenGL functions called. Fixes DMA-BUF importing returning a texture on failure on KDE+NVIDIA. --- libobs-opengl/gl-egl-common.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/libobs-opengl/gl-egl-common.c b/libobs-opengl/gl-egl-common.c index d26e6e1a3..4bc1d24e9 100644 --- a/libobs-opengl/gl-egl-common.c +++ b/libobs-opengl/gl-egl-common.c @@ -199,17 +199,23 @@ gl_egl_create_dmabuf_image(EGLDisplay egl_display, unsigned int width, return NULL; } - texture = gs_texture_create(width, height, color_format, 1, NULL, - GS_DYNAMIC); + if ((texture = gs_texture_create(width, height, color_format, 1, NULL, + GS_DYNAMIC)) == NULL) { + return NULL; + } const GLuint gltex = *(GLuint *)gs_texture_get_obj(texture); - glBindTexture(GL_TEXTURE_2D, gltex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_bind_texture(GL_TEXTURE_2D, gltex); + gl_tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image); + if (!gl_success("glEGLImageTargetTexture2DOES")) { + gs_texture_destroy(texture); + texture = NULL; + } - glBindTexture(GL_TEXTURE_2D, 0); + gl_bind_texture(GL_TEXTURE_2D, 0); eglDestroyImage(egl_display, egl_image); return texture; From ce78cd446e26ad65db4a8619e3ea95d4cdb206b1 Mon Sep 17 00:00:00 2001 From: Dossy Shiobara Date: Tue, 3 May 2022 17:24:46 +0200 Subject: [PATCH 64/66] mac-capture: Improve window capture performance Replaces the current implementation of Window Capture, which uses Core Graphics to render a bitmap raster, with one that accesses the window's CGImage bitmap directly, blits it, and hands that off as a frame to OBS instead. Co-Authored-By: Sebastian Beckmann --- plugins/mac-capture/mac-window-capture.m | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/mac-capture/mac-window-capture.m b/plugins/mac-capture/mac-window-capture.m index 17c0c283e..594090ed7 100644 --- a/plugins/mac-capture/mac-window-capture.m +++ b/plugins/mac-capture/mac-window-capture.m @@ -50,28 +50,28 @@ static inline void capture_frame(struct window_capture *wc) size_t width = CGImageGetWidth(img); size_t height = CGImageGetHeight(img); - CGRect rect = {{0, 0}, {width, height}}; - da_resize(wc->buffer, width * height * 4); - uint8_t *data = wc->buffer.array; + if (!width || !height || CGImageGetBitsPerPixel(img) != 32 || + CGImageGetBitsPerComponent(img) != 8) { + CGImageRelease(img); + return; + } - CGContextRef cg_context = CGBitmapContextCreate( - data, width, height, 8, width * 4, wc->color_space, - kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); - CGContextSetBlendMode(cg_context, kCGBlendModeCopy); - CGContextDrawImage(cg_context, rect, img); - CGContextRelease(cg_context); - CGImageRelease(img); + CGDataProviderRef provider = CGImageGetDataProvider(img); + CFDataRef data = CGDataProviderCopyData(provider); struct obs_source_frame frame = { .format = VIDEO_FORMAT_BGRA, .width = width, .height = height, - .data[0] = data, - .linesize[0] = width * 4, + .data[0] = (uint8_t *)CFDataGetBytePtr(data), + .linesize[0] = CGImageGetBytesPerRow(img), .timestamp = ts, }; obs_source_output_video(wc->source, &frame); + + CGImageRelease(img); + CFRelease(data); } static void *capture_thread(void *data) From 2b60f04974896c326656cab713d2294ed7b98d80 Mon Sep 17 00:00:00 2001 From: Theodore Dubois Date: Mon, 11 Apr 2022 09:55:36 +0200 Subject: [PATCH 65/66] libobs: Rewrite macOS hotkeys implementation Co-authored-by: Sebastian Beckmann --- libobs/obs-cocoa.m | 1245 +++++++------------------------------------- 1 file changed, 196 insertions(+), 1049 deletions(-) diff --git a/libobs/obs-cocoa.m b/libobs/obs-cocoa.m index 1831c60a9..baf34adc5 100644 --- a/libobs/obs-cocoa.m +++ b/libobs/obs-cocoa.m @@ -151,17 +151,12 @@ static void log_available_memory(void) memory_available / 1024 / 1024); } -static bool using_10_15_or_above = true; - static void log_os(void) { NSProcessInfo *pi = [NSProcessInfo processInfo]; blog(LOG_INFO, "OS Name: Mac OS X"); blog(LOG_INFO, "OS Version: %s", [[pi operatingSystemVersionString] UTF8String]); - - NSOperatingSystemVersion catalina = {10, 15, 0}; - using_10_15_or_above = [pi isOperatingSystemAtLeastVersion:catalina]; } static void log_kernel_version(void) @@ -202,12 +197,11 @@ static bool dstr_from_cfstring(struct dstr *str, CFStringRef ref) struct obs_hotkeys_platform { volatile long refs; - bool secure_input_activated; + CFTypeRef monitor; + bool is_key_down[OBS_KEY_LAST_VALUE]; TISInputSourceRef tis; CFDataRef layout_data; UCKeyboardLayout *layout; - IOHIDManagerRef manager; - DARRAY(IOHIDElementRef) keys[OBS_KEY_LAST_VALUE]; }; static void hotkeys_retain(struct obs_hotkeys_platform *plat) @@ -224,242 +218,150 @@ static void hotkeys_release(struct obs_hotkeys_platform *plat) #define INVALID_KEY 0xff +#pragma GCC diagnostic ignored "-Winitializer-overrides" +static const int virtual_keys[] = { + [0 ... OBS_KEY_LAST_VALUE] = INVALID_KEY, + + [OBS_KEY_A] = kVK_ANSI_A, + [OBS_KEY_B] = kVK_ANSI_B, + [OBS_KEY_C] = kVK_ANSI_C, + [OBS_KEY_D] = kVK_ANSI_D, + [OBS_KEY_E] = kVK_ANSI_E, + [OBS_KEY_F] = kVK_ANSI_F, + [OBS_KEY_G] = kVK_ANSI_G, + [OBS_KEY_H] = kVK_ANSI_H, + [OBS_KEY_I] = kVK_ANSI_I, + [OBS_KEY_J] = kVK_ANSI_J, + [OBS_KEY_K] = kVK_ANSI_K, + [OBS_KEY_L] = kVK_ANSI_L, + [OBS_KEY_M] = kVK_ANSI_M, + [OBS_KEY_N] = kVK_ANSI_N, + [OBS_KEY_O] = kVK_ANSI_O, + [OBS_KEY_P] = kVK_ANSI_P, + [OBS_KEY_Q] = kVK_ANSI_Q, + [OBS_KEY_R] = kVK_ANSI_R, + [OBS_KEY_S] = kVK_ANSI_S, + [OBS_KEY_T] = kVK_ANSI_T, + [OBS_KEY_U] = kVK_ANSI_U, + [OBS_KEY_V] = kVK_ANSI_V, + [OBS_KEY_W] = kVK_ANSI_W, + [OBS_KEY_X] = kVK_ANSI_X, + [OBS_KEY_Y] = kVK_ANSI_Y, + [OBS_KEY_Z] = kVK_ANSI_Z, + + [OBS_KEY_1] = kVK_ANSI_1, + [OBS_KEY_2] = kVK_ANSI_2, + [OBS_KEY_3] = kVK_ANSI_3, + [OBS_KEY_4] = kVK_ANSI_4, + [OBS_KEY_5] = kVK_ANSI_5, + [OBS_KEY_6] = kVK_ANSI_6, + [OBS_KEY_7] = kVK_ANSI_7, + [OBS_KEY_8] = kVK_ANSI_8, + [OBS_KEY_9] = kVK_ANSI_9, + [OBS_KEY_0] = kVK_ANSI_0, + + [OBS_KEY_RETURN] = kVK_Return, + [OBS_KEY_ESCAPE] = kVK_Escape, + [OBS_KEY_BACKSPACE] = kVK_Delete, + [OBS_KEY_TAB] = kVK_Tab, + [OBS_KEY_SPACE] = kVK_Space, + [OBS_KEY_MINUS] = kVK_ANSI_Minus, + [OBS_KEY_EQUAL] = kVK_ANSI_Equal, + [OBS_KEY_BRACKETLEFT] = kVK_ANSI_LeftBracket, + [OBS_KEY_BRACKETRIGHT] = kVK_ANSI_RightBracket, + [OBS_KEY_BACKSLASH] = kVK_ANSI_Backslash, + [OBS_KEY_SEMICOLON] = kVK_ANSI_Semicolon, + [OBS_KEY_QUOTE] = kVK_ANSI_Quote, + [OBS_KEY_DEAD_GRAVE] = kVK_ANSI_Grave, + [OBS_KEY_COMMA] = kVK_ANSI_Comma, + [OBS_KEY_PERIOD] = kVK_ANSI_Period, + [OBS_KEY_SLASH] = kVK_ANSI_Slash, + [OBS_KEY_CAPSLOCK] = kVK_CapsLock, + [OBS_KEY_SECTION] = kVK_ISO_Section, + + [OBS_KEY_F1] = kVK_F1, + [OBS_KEY_F2] = kVK_F2, + [OBS_KEY_F3] = kVK_F3, + [OBS_KEY_F4] = kVK_F4, + [OBS_KEY_F5] = kVK_F5, + [OBS_KEY_F6] = kVK_F6, + [OBS_KEY_F7] = kVK_F7, + [OBS_KEY_F8] = kVK_F8, + [OBS_KEY_F9] = kVK_F9, + [OBS_KEY_F10] = kVK_F10, + [OBS_KEY_F11] = kVK_F11, + [OBS_KEY_F12] = kVK_F12, + + [OBS_KEY_HELP] = kVK_Help, + [OBS_KEY_HOME] = kVK_Home, + [OBS_KEY_PAGEUP] = kVK_PageUp, + [OBS_KEY_DELETE] = kVK_ForwardDelete, + [OBS_KEY_END] = kVK_End, + [OBS_KEY_PAGEDOWN] = kVK_PageDown, + + [OBS_KEY_RIGHT] = kVK_RightArrow, + [OBS_KEY_LEFT] = kVK_LeftArrow, + [OBS_KEY_DOWN] = kVK_DownArrow, + [OBS_KEY_UP] = kVK_UpArrow, + + [OBS_KEY_CLEAR] = kVK_ANSI_KeypadClear, + [OBS_KEY_NUMSLASH] = kVK_ANSI_KeypadDivide, + [OBS_KEY_NUMASTERISK] = kVK_ANSI_KeypadMultiply, + [OBS_KEY_NUMMINUS] = kVK_ANSI_KeypadMinus, + [OBS_KEY_NUMPLUS] = kVK_ANSI_KeypadPlus, + [OBS_KEY_ENTER] = kVK_ANSI_KeypadEnter, + + [OBS_KEY_NUM1] = kVK_ANSI_Keypad1, + [OBS_KEY_NUM2] = kVK_ANSI_Keypad2, + [OBS_KEY_NUM3] = kVK_ANSI_Keypad3, + [OBS_KEY_NUM4] = kVK_ANSI_Keypad4, + [OBS_KEY_NUM5] = kVK_ANSI_Keypad5, + [OBS_KEY_NUM6] = kVK_ANSI_Keypad6, + [OBS_KEY_NUM7] = kVK_ANSI_Keypad7, + [OBS_KEY_NUM8] = kVK_ANSI_Keypad8, + [OBS_KEY_NUM9] = kVK_ANSI_Keypad9, + [OBS_KEY_NUM0] = kVK_ANSI_Keypad0, + + [OBS_KEY_NUMPERIOD] = kVK_ANSI_KeypadDecimal, + [OBS_KEY_NUMEQUAL] = kVK_ANSI_KeypadEquals, + + [OBS_KEY_F13] = kVK_F13, + [OBS_KEY_F14] = kVK_F14, + [OBS_KEY_F15] = kVK_F15, + [OBS_KEY_F16] = kVK_F16, + [OBS_KEY_F17] = kVK_F17, + [OBS_KEY_F18] = kVK_F18, + [OBS_KEY_F19] = kVK_F19, + [OBS_KEY_F20] = kVK_F20, + + [OBS_KEY_CONTROL] = kVK_Control, + [OBS_KEY_SHIFT] = kVK_Shift, + [OBS_KEY_ALT] = kVK_Option, + [OBS_KEY_META] = kVK_Command, + [OBS_KEY_CONTROL] = kVK_RightControl, +}; + int obs_key_to_virtual_key(obs_key_t code) { - switch (code) { - case OBS_KEY_A: - return kVK_ANSI_A; - case OBS_KEY_B: - return kVK_ANSI_B; - case OBS_KEY_C: - return kVK_ANSI_C; - case OBS_KEY_D: - return kVK_ANSI_D; - case OBS_KEY_E: - return kVK_ANSI_E; - case OBS_KEY_F: - return kVK_ANSI_F; - case OBS_KEY_G: - return kVK_ANSI_G; - case OBS_KEY_H: - return kVK_ANSI_H; - case OBS_KEY_I: - return kVK_ANSI_I; - case OBS_KEY_J: - return kVK_ANSI_J; - case OBS_KEY_K: - return kVK_ANSI_K; - case OBS_KEY_L: - return kVK_ANSI_L; - case OBS_KEY_M: - return kVK_ANSI_M; - case OBS_KEY_N: - return kVK_ANSI_N; - case OBS_KEY_O: - return kVK_ANSI_O; - case OBS_KEY_P: - return kVK_ANSI_P; - case OBS_KEY_Q: - return kVK_ANSI_Q; - case OBS_KEY_R: - return kVK_ANSI_R; - case OBS_KEY_S: - return kVK_ANSI_S; - case OBS_KEY_T: - return kVK_ANSI_T; - case OBS_KEY_U: - return kVK_ANSI_U; - case OBS_KEY_V: - return kVK_ANSI_V; - case OBS_KEY_W: - return kVK_ANSI_W; - case OBS_KEY_X: - return kVK_ANSI_X; - case OBS_KEY_Y: - return kVK_ANSI_Y; - case OBS_KEY_Z: - return kVK_ANSI_Z; + return virtual_keys[code]; +} - case OBS_KEY_1: - return kVK_ANSI_1; - case OBS_KEY_2: - return kVK_ANSI_2; - case OBS_KEY_3: - return kVK_ANSI_3; - case OBS_KEY_4: - return kVK_ANSI_4; - case OBS_KEY_5: - return kVK_ANSI_5; - case OBS_KEY_6: - return kVK_ANSI_6; - case OBS_KEY_7: - return kVK_ANSI_7; - case OBS_KEY_8: - return kVK_ANSI_8; - case OBS_KEY_9: - return kVK_ANSI_9; - case OBS_KEY_0: - return kVK_ANSI_0; - - case OBS_KEY_RETURN: - return kVK_Return; - case OBS_KEY_ESCAPE: - return kVK_Escape; - case OBS_KEY_BACKSPACE: - return kVK_Delete; - case OBS_KEY_TAB: - return kVK_Tab; - case OBS_KEY_SPACE: - return kVK_Space; - case OBS_KEY_MINUS: - return kVK_ANSI_Minus; - case OBS_KEY_EQUAL: - return kVK_ANSI_Equal; - case OBS_KEY_BRACKETLEFT: - return kVK_ANSI_LeftBracket; - case OBS_KEY_BRACKETRIGHT: - return kVK_ANSI_RightBracket; - case OBS_KEY_BACKSLASH: - return kVK_ANSI_Backslash; - case OBS_KEY_SEMICOLON: - return kVK_ANSI_Semicolon; - case OBS_KEY_QUOTE: - return kVK_ANSI_Quote; - case OBS_KEY_DEAD_GRAVE: - return kVK_ANSI_Grave; - case OBS_KEY_COMMA: - return kVK_ANSI_Comma; - case OBS_KEY_PERIOD: - return kVK_ANSI_Period; - case OBS_KEY_SLASH: - return kVK_ANSI_Slash; - case OBS_KEY_CAPSLOCK: - return kVK_CapsLock; - case OBS_KEY_SECTION: - return kVK_ISO_Section; - - case OBS_KEY_F1: - return kVK_F1; - case OBS_KEY_F2: - return kVK_F2; - case OBS_KEY_F3: - return kVK_F3; - case OBS_KEY_F4: - return kVK_F4; - case OBS_KEY_F5: - return kVK_F5; - case OBS_KEY_F6: - return kVK_F6; - case OBS_KEY_F7: - return kVK_F7; - case OBS_KEY_F8: - return kVK_F8; - case OBS_KEY_F9: - return kVK_F9; - case OBS_KEY_F10: - return kVK_F10; - case OBS_KEY_F11: - return kVK_F11; - case OBS_KEY_F12: - return kVK_F12; - - case OBS_KEY_HELP: - return kVK_Help; - case OBS_KEY_HOME: - return kVK_Home; - case OBS_KEY_PAGEUP: - return kVK_PageUp; - case OBS_KEY_DELETE: - return kVK_ForwardDelete; - case OBS_KEY_END: - return kVK_End; - case OBS_KEY_PAGEDOWN: - return kVK_PageDown; - - case OBS_KEY_RIGHT: - return kVK_RightArrow; - case OBS_KEY_LEFT: - return kVK_LeftArrow; - case OBS_KEY_DOWN: - return kVK_DownArrow; - case OBS_KEY_UP: - return kVK_UpArrow; - - case OBS_KEY_CLEAR: - return kVK_ANSI_KeypadClear; - case OBS_KEY_NUMSLASH: - return kVK_ANSI_KeypadDivide; - case OBS_KEY_NUMASTERISK: - return kVK_ANSI_KeypadMultiply; - case OBS_KEY_NUMMINUS: - return kVK_ANSI_KeypadMinus; - case OBS_KEY_NUMPLUS: - return kVK_ANSI_KeypadPlus; - case OBS_KEY_ENTER: - return kVK_ANSI_KeypadEnter; - - case OBS_KEY_NUM1: - return kVK_ANSI_Keypad1; - case OBS_KEY_NUM2: - return kVK_ANSI_Keypad2; - case OBS_KEY_NUM3: - return kVK_ANSI_Keypad3; - case OBS_KEY_NUM4: - return kVK_ANSI_Keypad4; - case OBS_KEY_NUM5: - return kVK_ANSI_Keypad5; - case OBS_KEY_NUM6: - return kVK_ANSI_Keypad6; - case OBS_KEY_NUM7: - return kVK_ANSI_Keypad7; - case OBS_KEY_NUM8: - return kVK_ANSI_Keypad8; - case OBS_KEY_NUM9: - return kVK_ANSI_Keypad9; - case OBS_KEY_NUM0: - return kVK_ANSI_Keypad0; - - case OBS_KEY_NUMPERIOD: - return kVK_ANSI_KeypadDecimal; - case OBS_KEY_NUMEQUAL: - return kVK_ANSI_KeypadEquals; - - case OBS_KEY_F13: - return kVK_F13; - case OBS_KEY_F14: - return kVK_F14; - case OBS_KEY_F15: - return kVK_F15; - case OBS_KEY_F16: - return kVK_F16; - case OBS_KEY_F17: - return kVK_F17; - case OBS_KEY_F18: - return kVK_F18; - case OBS_KEY_F19: - return kVK_F19; - case OBS_KEY_F20: - return kVK_F20; - - case OBS_KEY_CONTROL: - return kVK_Control; - case OBS_KEY_SHIFT: - return kVK_Shift; - case OBS_KEY_ALT: - return kVK_Option; - case OBS_KEY_META: - return kVK_Command; - //case OBS_KEY_CONTROL: return kVK_RightControl; - //case OBS_KEY_SHIFT: return kVK_RightShift; - //case OBS_KEY_ALT: return kVK_RightOption; - //case OBS_KEY_META: return 0x36; - - case OBS_KEY_NONE: - case OBS_KEY_LAST_VALUE: - default: - break; +obs_key_t obs_key_from_virtual_key(int code) +{ + if (code == kVK_RightShift) + return OBS_KEY_SHIFT; + if (code == kVK_RightOption) + return OBS_KEY_ALT; + if (code == kVK_RightCommand) + return OBS_KEY_META; + if (code == kVK_RightControl) + return OBS_KEY_META; + for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) { + if (virtual_keys[i] == code) { + return i; + } } - return INVALID_KEY; + return OBS_KEY_NONE; } static bool localized_key_to_str(obs_key_t key, struct dstr *str) @@ -760,762 +662,6 @@ void obs_key_combination_to_str(obs_key_combination_t key, struct dstr *str) dstr_free(&key_str); } -static inline CFDictionaryRef copy_device_mask(UInt32 page, UInt32 usage) -{ - CFMutableDictionaryRef dict = CFDictionaryCreateMutable( - kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - CFNumberRef value; - // Add the page value. - value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page); - CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), value); - CFRelease(value); - - // Add the usage value (which is only valid if page value exists). - value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); - CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), value); - CFRelease(value); - - return dict; -} - -static CFSetRef copy_devices(obs_hotkeys_platform_t *plat, UInt32 page, - UInt32 usage) -{ - CFDictionaryRef mask = copy_device_mask(page, usage); - IOHIDManagerSetDeviceMatching(plat->manager, mask); - CFRelease(mask); - - CFSetRef devices = IOHIDManagerCopyDevices(plat->manager); - if (!devices) - return NULL; - - if (CFSetGetCount(devices) < 1) { - CFRelease(devices); - return NULL; - } - - return devices; -} - -static UInt16 usage_to_carbon(UInt32 usage) -{ - switch (usage) { - case kHIDUsage_KeyboardErrorRollOver: - return INVALID_KEY; - case kHIDUsage_KeyboardPOSTFail: - return INVALID_KEY; - case kHIDUsage_KeyboardErrorUndefined: - return INVALID_KEY; - - case kHIDUsage_KeyboardA: - return kVK_ANSI_A; - case kHIDUsage_KeyboardB: - return kVK_ANSI_B; - case kHIDUsage_KeyboardC: - return kVK_ANSI_C; - case kHIDUsage_KeyboardD: - return kVK_ANSI_D; - case kHIDUsage_KeyboardE: - return kVK_ANSI_E; - case kHIDUsage_KeyboardF: - return kVK_ANSI_F; - case kHIDUsage_KeyboardG: - return kVK_ANSI_G; - case kHIDUsage_KeyboardH: - return kVK_ANSI_H; - case kHIDUsage_KeyboardI: - return kVK_ANSI_I; - case kHIDUsage_KeyboardJ: - return kVK_ANSI_J; - case kHIDUsage_KeyboardK: - return kVK_ANSI_K; - case kHIDUsage_KeyboardL: - return kVK_ANSI_L; - case kHIDUsage_KeyboardM: - return kVK_ANSI_M; - case kHIDUsage_KeyboardN: - return kVK_ANSI_N; - case kHIDUsage_KeyboardO: - return kVK_ANSI_O; - case kHIDUsage_KeyboardP: - return kVK_ANSI_P; - case kHIDUsage_KeyboardQ: - return kVK_ANSI_Q; - case kHIDUsage_KeyboardR: - return kVK_ANSI_R; - case kHIDUsage_KeyboardS: - return kVK_ANSI_S; - case kHIDUsage_KeyboardT: - return kVK_ANSI_T; - case kHIDUsage_KeyboardU: - return kVK_ANSI_U; - case kHIDUsage_KeyboardV: - return kVK_ANSI_V; - case kHIDUsage_KeyboardW: - return kVK_ANSI_W; - case kHIDUsage_KeyboardX: - return kVK_ANSI_X; - case kHIDUsage_KeyboardY: - return kVK_ANSI_Y; - case kHIDUsage_KeyboardZ: - return kVK_ANSI_Z; - - case kHIDUsage_Keyboard1: - return kVK_ANSI_1; - case kHIDUsage_Keyboard2: - return kVK_ANSI_2; - case kHIDUsage_Keyboard3: - return kVK_ANSI_3; - case kHIDUsage_Keyboard4: - return kVK_ANSI_4; - case kHIDUsage_Keyboard5: - return kVK_ANSI_5; - case kHIDUsage_Keyboard6: - return kVK_ANSI_6; - case kHIDUsage_Keyboard7: - return kVK_ANSI_7; - case kHIDUsage_Keyboard8: - return kVK_ANSI_8; - case kHIDUsage_Keyboard9: - return kVK_ANSI_9; - case kHIDUsage_Keyboard0: - return kVK_ANSI_0; - - case kHIDUsage_KeyboardReturnOrEnter: - return kVK_Return; - case kHIDUsage_KeyboardEscape: - return kVK_Escape; - case kHIDUsage_KeyboardDeleteOrBackspace: - return kVK_Delete; - case kHIDUsage_KeyboardTab: - return kVK_Tab; - case kHIDUsage_KeyboardSpacebar: - return kVK_Space; - case kHIDUsage_KeyboardHyphen: - return kVK_ANSI_Minus; - case kHIDUsage_KeyboardEqualSign: - return kVK_ANSI_Equal; - case kHIDUsage_KeyboardOpenBracket: - return kVK_ANSI_LeftBracket; - case kHIDUsage_KeyboardCloseBracket: - return kVK_ANSI_RightBracket; - case kHIDUsage_KeyboardBackslash: - return kVK_ANSI_Backslash; - case kHIDUsage_KeyboardNonUSPound: - return INVALID_KEY; - case kHIDUsage_KeyboardSemicolon: - return kVK_ANSI_Semicolon; - case kHIDUsage_KeyboardQuote: - return kVK_ANSI_Quote; - case kHIDUsage_KeyboardGraveAccentAndTilde: - return kVK_ANSI_Grave; - case kHIDUsage_KeyboardComma: - return kVK_ANSI_Comma; - case kHIDUsage_KeyboardPeriod: - return kVK_ANSI_Period; - case kHIDUsage_KeyboardSlash: - return kVK_ANSI_Slash; - case kHIDUsage_KeyboardCapsLock: - return kVK_CapsLock; - - case kHIDUsage_KeyboardF1: - return kVK_F1; - case kHIDUsage_KeyboardF2: - return kVK_F2; - case kHIDUsage_KeyboardF3: - return kVK_F3; - case kHIDUsage_KeyboardF4: - return kVK_F4; - case kHIDUsage_KeyboardF5: - return kVK_F5; - case kHIDUsage_KeyboardF6: - return kVK_F6; - case kHIDUsage_KeyboardF7: - return kVK_F7; - case kHIDUsage_KeyboardF8: - return kVK_F8; - case kHIDUsage_KeyboardF9: - return kVK_F9; - case kHIDUsage_KeyboardF10: - return kVK_F10; - case kHIDUsage_KeyboardF11: - return kVK_F11; - case kHIDUsage_KeyboardF12: - return kVK_F12; - - case kHIDUsage_KeyboardPrintScreen: - return INVALID_KEY; - case kHIDUsage_KeyboardScrollLock: - return INVALID_KEY; - case kHIDUsage_KeyboardPause: - return INVALID_KEY; - case kHIDUsage_KeyboardInsert: - return kVK_Help; - case kHIDUsage_KeyboardHome: - return kVK_Home; - case kHIDUsage_KeyboardPageUp: - return kVK_PageUp; - case kHIDUsage_KeyboardDeleteForward: - return kVK_ForwardDelete; - case kHIDUsage_KeyboardEnd: - return kVK_End; - case kHIDUsage_KeyboardPageDown: - return kVK_PageDown; - - case kHIDUsage_KeyboardRightArrow: - return kVK_RightArrow; - case kHIDUsage_KeyboardLeftArrow: - return kVK_LeftArrow; - case kHIDUsage_KeyboardDownArrow: - return kVK_DownArrow; - case kHIDUsage_KeyboardUpArrow: - return kVK_UpArrow; - - case kHIDUsage_KeypadNumLock: - return kVK_ANSI_KeypadClear; - case kHIDUsage_KeypadSlash: - return kVK_ANSI_KeypadDivide; - case kHIDUsage_KeypadAsterisk: - return kVK_ANSI_KeypadMultiply; - case kHIDUsage_KeypadHyphen: - return kVK_ANSI_KeypadMinus; - case kHIDUsage_KeypadPlus: - return kVK_ANSI_KeypadPlus; - case kHIDUsage_KeypadEnter: - return kVK_ANSI_KeypadEnter; - - case kHIDUsage_Keypad1: - return kVK_ANSI_Keypad1; - case kHIDUsage_Keypad2: - return kVK_ANSI_Keypad2; - case kHIDUsage_Keypad3: - return kVK_ANSI_Keypad3; - case kHIDUsage_Keypad4: - return kVK_ANSI_Keypad4; - case kHIDUsage_Keypad5: - return kVK_ANSI_Keypad5; - case kHIDUsage_Keypad6: - return kVK_ANSI_Keypad6; - case kHIDUsage_Keypad7: - return kVK_ANSI_Keypad7; - case kHIDUsage_Keypad8: - return kVK_ANSI_Keypad8; - case kHIDUsage_Keypad9: - return kVK_ANSI_Keypad9; - case kHIDUsage_Keypad0: - return kVK_ANSI_Keypad0; - - case kHIDUsage_KeypadPeriod: - return kVK_ANSI_KeypadDecimal; - case kHIDUsage_KeyboardNonUSBackslash: - return INVALID_KEY; - case kHIDUsage_KeyboardApplication: - return kVK_F13; - case kHIDUsage_KeyboardPower: - return INVALID_KEY; - case kHIDUsage_KeypadEqualSign: - return kVK_ANSI_KeypadEquals; - - case kHIDUsage_KeyboardF13: - return kVK_F13; - case kHIDUsage_KeyboardF14: - return kVK_F14; - case kHIDUsage_KeyboardF15: - return kVK_F15; - case kHIDUsage_KeyboardF16: - return kVK_F16; - case kHIDUsage_KeyboardF17: - return kVK_F17; - case kHIDUsage_KeyboardF18: - return kVK_F18; - case kHIDUsage_KeyboardF19: - return kVK_F19; - case kHIDUsage_KeyboardF20: - return kVK_F20; - case kHIDUsage_KeyboardF21: - return INVALID_KEY; - case kHIDUsage_KeyboardF22: - return INVALID_KEY; - case kHIDUsage_KeyboardF23: - return INVALID_KEY; - case kHIDUsage_KeyboardF24: - return INVALID_KEY; - - case kHIDUsage_KeyboardExecute: - return INVALID_KEY; - case kHIDUsage_KeyboardHelp: - return INVALID_KEY; - case kHIDUsage_KeyboardMenu: - return 0x7F; - case kHIDUsage_KeyboardSelect: - return kVK_ANSI_KeypadEnter; - case kHIDUsage_KeyboardStop: - return INVALID_KEY; - case kHIDUsage_KeyboardAgain: - return INVALID_KEY; - case kHIDUsage_KeyboardUndo: - return INVALID_KEY; - case kHIDUsage_KeyboardCut: - return INVALID_KEY; - case kHIDUsage_KeyboardCopy: - return INVALID_KEY; - case kHIDUsage_KeyboardPaste: - return INVALID_KEY; - case kHIDUsage_KeyboardFind: - return INVALID_KEY; - - case kHIDUsage_KeyboardMute: - return kVK_Mute; - case kHIDUsage_KeyboardVolumeUp: - return kVK_VolumeUp; - case kHIDUsage_KeyboardVolumeDown: - return kVK_VolumeDown; - - case kHIDUsage_KeyboardLockingCapsLock: - return INVALID_KEY; - case kHIDUsage_KeyboardLockingNumLock: - return INVALID_KEY; - case kHIDUsage_KeyboardLockingScrollLock: - return INVALID_KEY; - - case kHIDUsage_KeypadComma: - return INVALID_KEY; - case kHIDUsage_KeypadEqualSignAS400: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational1: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational2: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational3: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational4: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational5: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational6: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational7: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational8: - return INVALID_KEY; - case kHIDUsage_KeyboardInternational9: - return INVALID_KEY; - - case kHIDUsage_KeyboardLANG1: - return INVALID_KEY; - case kHIDUsage_KeyboardLANG2: - return INVALID_KEY; - case kHIDUsage_KeyboardLANG3: - return INVALID_KEY; - case kHIDUsage_KeyboardLANG4: - return INVALID_KEY; - case kHIDUsage_KeyboardLANG5: - return INVALID_KEY; - case kHIDUsage_KeyboardLANG6: - return INVALID_KEY; - case kHIDUsage_KeyboardLANG7: - return INVALID_KEY; - case kHIDUsage_KeyboardLANG8: - return INVALID_KEY; - case kHIDUsage_KeyboardLANG9: - return INVALID_KEY; - - case kHIDUsage_KeyboardAlternateErase: - return INVALID_KEY; - case kHIDUsage_KeyboardSysReqOrAttention: - return INVALID_KEY; - case kHIDUsage_KeyboardCancel: - return INVALID_KEY; - case kHIDUsage_KeyboardClear: - return INVALID_KEY; - case kHIDUsage_KeyboardPrior: - return INVALID_KEY; - case kHIDUsage_KeyboardReturn: - return INVALID_KEY; - case kHIDUsage_KeyboardSeparator: - return INVALID_KEY; - case kHIDUsage_KeyboardOut: - return INVALID_KEY; - case kHIDUsage_KeyboardOper: - return INVALID_KEY; - case kHIDUsage_KeyboardClearOrAgain: - return INVALID_KEY; - case kHIDUsage_KeyboardCrSelOrProps: - return INVALID_KEY; - case kHIDUsage_KeyboardExSel: - return INVALID_KEY; - - /* 0xa5-0xdf Reserved */ - - case kHIDUsage_KeyboardLeftControl: - return kVK_Control; - case kHIDUsage_KeyboardLeftShift: - return kVK_Shift; - case kHIDUsage_KeyboardLeftAlt: - return kVK_Option; - case kHIDUsage_KeyboardLeftGUI: - return kVK_Command; - case kHIDUsage_KeyboardRightControl: - return kVK_RightControl; - case kHIDUsage_KeyboardRightShift: - return kVK_RightShift; - case kHIDUsage_KeyboardRightAlt: - return kVK_RightOption; - case kHIDUsage_KeyboardRightGUI: - return 0x36; //?? - - /* 0xe8-0xffff Reserved */ - - case kHIDUsage_Keyboard_Reserved: - return INVALID_KEY; - default: - return INVALID_KEY; - } - return INVALID_KEY; -} - -obs_key_t obs_key_from_virtual_key(int code) -{ - switch (code) { - case kVK_ANSI_A: - return OBS_KEY_A; - case kVK_ANSI_B: - return OBS_KEY_B; - case kVK_ANSI_C: - return OBS_KEY_C; - case kVK_ANSI_D: - return OBS_KEY_D; - case kVK_ANSI_E: - return OBS_KEY_E; - case kVK_ANSI_F: - return OBS_KEY_F; - case kVK_ANSI_G: - return OBS_KEY_G; - case kVK_ANSI_H: - return OBS_KEY_H; - case kVK_ANSI_I: - return OBS_KEY_I; - case kVK_ANSI_J: - return OBS_KEY_J; - case kVK_ANSI_K: - return OBS_KEY_K; - case kVK_ANSI_L: - return OBS_KEY_L; - case kVK_ANSI_M: - return OBS_KEY_M; - case kVK_ANSI_N: - return OBS_KEY_N; - case kVK_ANSI_O: - return OBS_KEY_O; - case kVK_ANSI_P: - return OBS_KEY_P; - case kVK_ANSI_Q: - return OBS_KEY_Q; - case kVK_ANSI_R: - return OBS_KEY_R; - case kVK_ANSI_S: - return OBS_KEY_S; - case kVK_ANSI_T: - return OBS_KEY_T; - case kVK_ANSI_U: - return OBS_KEY_U; - case kVK_ANSI_V: - return OBS_KEY_V; - case kVK_ANSI_W: - return OBS_KEY_W; - case kVK_ANSI_X: - return OBS_KEY_X; - case kVK_ANSI_Y: - return OBS_KEY_Y; - case kVK_ANSI_Z: - return OBS_KEY_Z; - - case kVK_ANSI_1: - return OBS_KEY_1; - case kVK_ANSI_2: - return OBS_KEY_2; - case kVK_ANSI_3: - return OBS_KEY_3; - case kVK_ANSI_4: - return OBS_KEY_4; - case kVK_ANSI_5: - return OBS_KEY_5; - case kVK_ANSI_6: - return OBS_KEY_6; - case kVK_ANSI_7: - return OBS_KEY_7; - case kVK_ANSI_8: - return OBS_KEY_8; - case kVK_ANSI_9: - return OBS_KEY_9; - case kVK_ANSI_0: - return OBS_KEY_0; - - case kVK_Return: - return OBS_KEY_RETURN; - case kVK_Escape: - return OBS_KEY_ESCAPE; - case kVK_Delete: - return OBS_KEY_BACKSPACE; - case kVK_Tab: - return OBS_KEY_TAB; - case kVK_Space: - return OBS_KEY_SPACE; - case kVK_ANSI_Minus: - return OBS_KEY_MINUS; - case kVK_ANSI_Equal: - return OBS_KEY_EQUAL; - case kVK_ANSI_LeftBracket: - return OBS_KEY_BRACKETLEFT; - case kVK_ANSI_RightBracket: - return OBS_KEY_BRACKETRIGHT; - case kVK_ANSI_Backslash: - return OBS_KEY_BACKSLASH; - case kVK_ANSI_Semicolon: - return OBS_KEY_SEMICOLON; - case kVK_ANSI_Quote: - return OBS_KEY_QUOTE; - case kVK_ANSI_Grave: - return OBS_KEY_DEAD_GRAVE; - case kVK_ANSI_Comma: - return OBS_KEY_COMMA; - case kVK_ANSI_Period: - return OBS_KEY_PERIOD; - case kVK_ANSI_Slash: - return OBS_KEY_SLASH; - case kVK_CapsLock: - return OBS_KEY_CAPSLOCK; - case kVK_ISO_Section: - return OBS_KEY_SECTION; - - case kVK_F1: - return OBS_KEY_F1; - case kVK_F2: - return OBS_KEY_F2; - case kVK_F3: - return OBS_KEY_F3; - case kVK_F4: - return OBS_KEY_F4; - case kVK_F5: - return OBS_KEY_F5; - case kVK_F6: - return OBS_KEY_F6; - case kVK_F7: - return OBS_KEY_F7; - case kVK_F8: - return OBS_KEY_F8; - case kVK_F9: - return OBS_KEY_F9; - case kVK_F10: - return OBS_KEY_F10; - case kVK_F11: - return OBS_KEY_F11; - case kVK_F12: - return OBS_KEY_F12; - - case kVK_Help: - return OBS_KEY_HELP; - case kVK_Home: - return OBS_KEY_HOME; - case kVK_PageUp: - return OBS_KEY_PAGEUP; - case kVK_ForwardDelete: - return OBS_KEY_DELETE; - case kVK_End: - return OBS_KEY_END; - case kVK_PageDown: - return OBS_KEY_PAGEDOWN; - - case kVK_RightArrow: - return OBS_KEY_RIGHT; - case kVK_LeftArrow: - return OBS_KEY_LEFT; - case kVK_DownArrow: - return OBS_KEY_DOWN; - case kVK_UpArrow: - return OBS_KEY_UP; - - case kVK_ANSI_KeypadClear: - return OBS_KEY_CLEAR; - case kVK_ANSI_KeypadDivide: - return OBS_KEY_NUMSLASH; - case kVK_ANSI_KeypadMultiply: - return OBS_KEY_NUMASTERISK; - case kVK_ANSI_KeypadMinus: - return OBS_KEY_NUMMINUS; - case kVK_ANSI_KeypadPlus: - return OBS_KEY_NUMPLUS; - case kVK_ANSI_KeypadEnter: - return OBS_KEY_ENTER; - - case kVK_ANSI_Keypad1: - return OBS_KEY_NUM1; - case kVK_ANSI_Keypad2: - return OBS_KEY_NUM2; - case kVK_ANSI_Keypad3: - return OBS_KEY_NUM3; - case kVK_ANSI_Keypad4: - return OBS_KEY_NUM4; - case kVK_ANSI_Keypad5: - return OBS_KEY_NUM5; - case kVK_ANSI_Keypad6: - return OBS_KEY_NUM6; - case kVK_ANSI_Keypad7: - return OBS_KEY_NUM7; - case kVK_ANSI_Keypad8: - return OBS_KEY_NUM8; - case kVK_ANSI_Keypad9: - return OBS_KEY_NUM9; - case kVK_ANSI_Keypad0: - return OBS_KEY_NUM0; - - case kVK_ANSI_KeypadDecimal: - return OBS_KEY_NUMPERIOD; - case kVK_ANSI_KeypadEquals: - return OBS_KEY_NUMEQUAL; - - case kVK_F13: - return OBS_KEY_F13; - case kVK_F14: - return OBS_KEY_F14; - case kVK_F15: - return OBS_KEY_F15; - case kVK_F16: - return OBS_KEY_F16; - case kVK_F17: - return OBS_KEY_F17; - case kVK_F18: - return OBS_KEY_F18; - case kVK_F19: - return OBS_KEY_F19; - case kVK_F20: - return OBS_KEY_F20; - - case kVK_Control: - return OBS_KEY_CONTROL; - case kVK_Shift: - return OBS_KEY_SHIFT; - case kVK_Option: - return OBS_KEY_ALT; - case kVK_Command: - return OBS_KEY_META; - case kVK_RightControl: - return OBS_KEY_CONTROL; - case kVK_RightShift: - return OBS_KEY_SHIFT; - case kVK_RightOption: - return OBS_KEY_ALT; - case 0x36: - return OBS_KEY_META; - - case kVK_Function: - case kVK_Mute: - case kVK_VolumeDown: - case kVK_VolumeUp: - break; - } - return OBS_KEY_NONE; -} - -static inline void load_key(obs_hotkeys_platform_t *plat, IOHIDElementRef key) -{ - UInt32 usage_code = IOHIDElementGetUsage(key); - UInt16 carbon_code = usage_to_carbon(usage_code); - - if (carbon_code == INVALID_KEY) - return; - - obs_key_t obs_key = obs_key_from_virtual_key(carbon_code); - if (obs_key == OBS_KEY_NONE) - return; - - da_push_back(plat->keys[obs_key], &key); - CFRetain(*(IOHIDElementRef *)da_end(plat->keys[obs_key])); -} - -static inline void load_keyboard(obs_hotkeys_platform_t *plat, - IOHIDDeviceRef keyboard) -{ - CFArrayRef keys = IOHIDDeviceCopyMatchingElements( - keyboard, NULL, kIOHIDOptionsTypeNone); - - if (!keys) { - blog(LOG_ERROR, "hotkeys-cocoa: Getting keyboard keys failed"); - return; - } - - CFIndex count = CFArrayGetCount(keys); - if (!count) { - blog(LOG_ERROR, "hotkeys-cocoa: Keyboard has no keys"); - CFRelease(keys); - return; - } - - for (CFIndex i = 0; i < count; i++) { - IOHIDElementRef key = - (IOHIDElementRef)CFArrayGetValueAtIndex(keys, i); - - // Skip non-matching keys elements - if (IOHIDElementGetUsagePage(key) != kHIDPage_KeyboardOrKeypad) - continue; - - load_key(plat, key); - } - - CFRelease(keys); -} - -static bool init_keyboard(obs_hotkeys_platform_t *plat) -{ - CFSetRef keyboards = copy_devices(plat, kHIDPage_GenericDesktop, - kHIDUsage_GD_Keyboard); - if (!keyboards) - return false; - - CFIndex count = CFSetGetCount(keyboards); - - CFTypeRef devices[count]; - CFSetGetValues(keyboards, devices); - - for (CFIndex i = 0; i < count; i++) - load_keyboard(plat, (IOHIDDeviceRef)devices[i]); - - CFRelease(keyboards); - return true; -} - -static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat) -{ - if (!plat) - return; - - if (plat->tis) { - CFRelease(plat->tis); - plat->tis = NULL; - } - - if (plat->layout_data) { - CFRelease(plat->layout_data); - plat->layout_data = NULL; - } - - if (plat->manager) { - CFRelease(plat->manager); - plat->manager = NULL; - } - - for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) { - for (size_t j = 0; j < plat->keys[i].num; j++) - CFRelease(plat->keys[i].array[j]); - - da_free(plat->keys[i]); - } - - bfree(plat); -} - static bool log_layout_name(TISInputSourceRef tis) { struct dstr layout_name = {0}; @@ -1542,6 +688,27 @@ fail: return false; } +static void handle_monitor_event(obs_hotkeys_platform_t *plat, NSEvent *event) +{ + if (event.type == NSEventTypeFlagsChanged) { + NSEventModifierFlags flags = event.modifierFlags; + plat->is_key_down[OBS_KEY_CAPSLOCK] = + !!(flags & NSEventModifierFlagCapsLock); + plat->is_key_down[OBS_KEY_SHIFT] = + !!(flags & NSEventModifierFlagShift); + plat->is_key_down[OBS_KEY_ALT] = + !!(flags & NSEventModifierFlagOption); + plat->is_key_down[OBS_KEY_META] = + !!(flags & NSEventModifierFlagCommand); + plat->is_key_down[OBS_KEY_CONTROL] = + !!(flags & NSEventModifierFlagControl); + } else if (event.type == NSEventTypeKeyDown || + event.type == NSEventTypeKeyUp) { + plat->is_key_down[obs_key_from_virtual_key(event.keyCode)] = + (event.type == NSEventTypeKeyDown); + } +} + static bool init_hotkeys_platform(obs_hotkeys_platform_t **plat_) { if (!plat_) @@ -1554,6 +721,15 @@ static bool init_hotkeys_platform(obs_hotkeys_platform_t **plat_) return false; } + void (^handler)(NSEvent *) = ^(NSEvent *event) { + handle_monitor_event(plat, event); + }; + plat->monitor = (__bridge CFTypeRef)[NSEvent + addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown | + NSEventMaskKeyUp | + NSEventMaskFlagsChanged + handler:handler]; + plat->tis = TISCopyCurrentKeyboardLayoutInputSource(); plat->layout_data = (CFDataRef)TISGetInputSourceProperty( plat->tis, kTISPropertyUnicodeKeyLayoutData); @@ -1566,18 +742,6 @@ static bool init_hotkeys_platform(obs_hotkeys_platform_t **plat_) CFRetain(plat->layout_data); plat->layout = (UCKeyboardLayout *)CFDataGetBytePtr(plat->layout_data); - plat->manager = - IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - - IOReturn openStatus = - IOHIDManagerOpen(plat->manager, kIOHIDOptionsTypeNone); - if (openStatus != kIOReturnSuccess) { - blog(LOG_ERROR, "hotkeys-cocoa: Failed opening HIDManager"); - goto fail; - } - - init_keyboard(plat); - return true; fail: @@ -1586,6 +750,29 @@ fail: return false; } +static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat) +{ + if (!plat) + return; + + if (plat->monitor) { + CFRelease(plat->monitor); + plat->monitor = NULL; + } + + if (plat->tis) { + CFRelease(plat->tis); + plat->tis = NULL; + } + + if (plat->layout_data) { + CFRelease(plat->layout_data); + plat->layout_data = NULL; + } + + bfree(plat); +} + static void input_method_changed(CFNotificationCenterRef nc, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) @@ -1707,47 +894,7 @@ bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *plat, if (key >= OBS_KEY_LAST_VALUE) return false; - /* if secure input is activated, kill hotkeys. - * - * TODO: rewrite all mac hotkey code, suspect there's a bug in 10.15 - * causing the crash internally. */ - if (plat->secure_input_activated) { - return false; - } - - for (size_t i = 0; i < plat->keys[key].num;) { - IOHIDElementRef element = plat->keys[key].array[i]; - IOHIDValueRef value = 0; - IOHIDDeviceRef device = IOHIDElementGetDevice(element); - - if (device == NULL) { - continue; - } - - if (using_10_15_or_above && IsSecureEventInputEnabled()) { - plat->secure_input_activated = true; - return false; - } - - if (IOHIDDeviceGetValue(device, element, &value) != - kIOReturnSuccess) { - i += 1; - continue; - } - - if (!value) { - CFRelease(element); - da_erase(plat->keys[key], i); - continue; - } - - if (IOHIDValueGetIntegerValue(value) == 1) - return true; - - i += 1; - } - - return false; + return plat->is_key_down[key]; } void *obs_graphics_thread_autorelease(void *param) From fa50f64c3cedf07f66ce78e0bc605656eb2789f4 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Sat, 23 Jul 2022 17:17:30 +0200 Subject: [PATCH 66/66] libobs: Trigger bindings injected by Qt directly Instead of lettings the hotkey thread handle bindings triggered by Qt, call the callback directly. By this point, Qt has resolved the modifier state (so no need for the hotkey thread to do the same), the pressed state is set as well, the branch takes care of matching the pressed keys. This also fixes the issue with the hotkey changes introduced by #3914, which only inject key events when OBS is _not_ in focus (and as such modifier states are never updated). --- libobs/obs-hotkey.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libobs/obs-hotkey.c b/libobs/obs-hotkey.c index d78fd3698..5e0271128 100644 --- a/libobs/obs-hotkey.c +++ b/libobs/obs-hotkey.c @@ -1289,8 +1289,14 @@ static inline bool inject_hotkey(void *data, size_t idx, event->strict_modifiers)) { bool pressed = binding->key.key == event->hotkey.key && event->pressed; - handle_binding(binding, event->hotkey.modifiers, false, - event->strict_modifiers, &pressed); + if (binding->key.key == OBS_KEY_NONE) + pressed = true; + + if (pressed) { + binding->modifiers_match = true; + if (!binding->pressed) + press_released_binding(binding); + } } return true;