diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bf41afb1a..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' @@ -34,7 +35,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' @@ -211,10 +212,12 @@ 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 + /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 @@ -559,7 +562,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 }}-3 - name: 'Install prerequisite: QT' if: steps.qt-cache.outputs.cache-hit != 'true' run: | @@ -579,7 +582,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 @@ -668,7 +671,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 }}-3 - name: 'Install prerequisite: QT' if: steps.qt-cache.outputs.cache-hit != 'true' run: | @@ -688,7 +691,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 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 diff --git a/CI/full-build-macos.sh b/CI/full-build-macos.sh index 96cb9b1fb..8b2b2cb14 100755 --- a/CI/full-build-macos.sh +++ b/CI/full-build-macos.sh @@ -419,9 +419,11 @@ 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 + /bin/cp -cpR /tmp/obsdeps/lib/*.dylib ./OBS.app/Contents/Frameworks bundle_dylibs install_frameworks 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); 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 + diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 010fa12eb..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__ @@ -1455,6 +1461,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/platform-x11.cpp b/UI/platform-x11.cpp index 536b1ea86..6162b6497 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; @@ -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") 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..17a93bc8f 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; @@ -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; } diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index 4ae014d1d..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) @@ -623,12 +624,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..a1ffc621f 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"); @@ -3530,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; @@ -4411,6 +4414,8 @@ bool OBSBasic::ResetAudio() return obs_reset_audio(&ai); } +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) { @@ -4434,10 +4439,11 @@ void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId, } } else if (!disable) { + 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, deviceDesc, settings, - nullptr); + source = obs_source_create(sourceId, name, settings, nullptr); obs_set_output_source(channel, source); } @@ -5369,8 +5375,6 @@ ColorSelect::ColorSelect(QWidget *parent) void OBSBasic::CreateSourcePopupMenu(int idx, bool preview) { - UpdateEditMenu(); - QMenu popup(this); delete previewProjectorSource; delete sourceProjector; @@ -7825,11 +7829,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(); 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); 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)) 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); 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); } 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 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..4c65f64ba 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; @@ -1658,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"); 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; 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; 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, 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) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 779dfecfb..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 0 +#define LIBOBS_API_PATCH_VER 4 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) 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, 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; 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; 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; 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; } diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 34c459d31..c5f7c7457 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -20,11 +20,13 @@ #include #include #include +#include #include "base.h" #include "platform.h" #include "darray.h" #include "dstr.h" +#include "util_uint64.h" #include "windows/win-registry.h" #include "windows/win-version.h" @@ -331,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)((double)time_target * freq / 1000000000.0); + util_mul_div64(time_target, freq, 1000000000); LARGE_INTEGER count; QueryPerformanceCounter(&count); @@ -370,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 */ 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); } 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..f8deea3ab 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"); @@ -76,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) { @@ -94,5 +92,22 @@ 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; + 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; 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; 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) 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) diff --git a/plugins/obs-browser b/plugins/obs-browser index b0d687937..915761778 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit b0d687937af876b52b69c46e276b4ab601b07f0e +Subproject commit 915761778ec1eae99e740ad4bf63b40db3142ee2 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"); 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/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 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); } } 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; 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); 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) diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 6dfa9996f..46b7d1939 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,19 @@ 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. + 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) { rtwq_unlock_work_queue = (PFN_RtwqUnlockWorkQueue)GetProcAddress( @@ -341,46 +354,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);