diff --git a/.gitignore b/.gitignore index f78f47adb..c88721bba 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ ipch/ GeneratedFiles/ .moc/ /UI/obs.rc +.vscode/ /other/ diff --git a/CI/before-deploy-osx.sh b/CI/before-deploy-osx.sh index 4bb61885d..236c0d0d5 100755 --- a/CI/before-deploy-osx.sh +++ b/CI/before-deploy-osx.sh @@ -8,9 +8,12 @@ hr() { set -e # Generate file name variables +export GIT_TAG=$(git describe --abbrev=0) export GIT_HASH=$(git rev-parse --short HEAD) export FILE_DATE=$(date +%Y-%m-%d.%H-%M-%S) -export FILENAME=$FILE_DATE-$GIT_HASH-$TRAVIS_BRANCH-osx.pkg +export FILENAME=$FILE_DATE-$GIT_HASH-$TRAVIS_BRANCH-osx.dmg + +echo "git tag: $GIT_TAG" cd ./build @@ -19,9 +22,9 @@ hr "Moving OBS LUA" mv ./rundir/RelWithDebInfo/data/obs-scripting/obslua.so ./rundir/RelWithDebInfo/bin/ # Move obspython -# hr "Moving OBS Python" -# mv ./rundir/RelWithDebInfo/data/obs-scripting/_obspython.so ./rundir/RelWithDebInfo/bin/ -# mv ./rundir/RelWithDebInfo/data/obs-scripting/obspython.py ./rundir/RelWithDebInfo/bin/ +hr "Moving OBS Python" +mv ./rundir/RelWithDebInfo/data/obs-scripting/_obspython.so ./rundir/RelWithDebInfo/bin/ +mv ./rundir/RelWithDebInfo/data/obs-scripting/obspython.py ./rundir/RelWithDebInfo/bin/ # Package everything into a nice .app hr "Packaging .app" @@ -30,32 +33,48 @@ if [ -n "${TRAVIS_TAG}" ]; then STABLE=true fi -sudo python ../CI/install/osx/build_app.py --public-key ../CI/install/osx/OBSPublicDSAKey.pem --sparkle-framework ../../sparkle/Sparkle.framework --stable=$STABLE +#sudo python ../CI/install/osx/build_app.py --public-key ../CI/install/osx/OBSPublicDSAKey.pem --sparkle-framework ../../sparkle/Sparkle.framework --stable=$STABLE + +../CI/install/osx/packageApp.sh + +# fix obs outputs +cp /usr/local/opt/mbedtls/lib/libmbedtls.12.dylib ./OBS.app/Contents/Frameworks/ +cp /usr/local/opt/mbedtls/lib/libmbedcrypto.3.dylib ./OBS.app/Contents/Frameworks/ +cp /usr/local/opt/mbedtls/lib/libmbedx509.0.dylib ./OBS.app/Contents/Frameworks/ +chmod +w ./OBS.app/Contents/Frameworks/*.dylib +install_name_tool -id @executable_path/../Frameworks/libmbedtls.12.dylib ./OBS.app/Contents/Frameworks/libmbedtls.12.dylib +install_name_tool -id @executable_path/../Frameworks/libmbedcrypto.3.dylib ./OBS.app/Contents/Frameworks/libmbedcrypto.3.dylib +install_name_tool -id @executable_path/../Frameworks/libmbedx509.0.dylib ./OBS.app/Contents/Frameworks/libmbedx509.0.dylib +install_name_tool -change /usr/local/opt/mbedtls/lib/libmbedtls.12.dylib @executable_path/../Frameworks/libmbedtls.12.dylib ./OBS.app/Contents/Plugins/obs-outputs.so +install_name_tool -change /usr/local/opt/mbedtls/lib/libmbedcrypto.3.dylib @executable_path/../Frameworks/libmbedcrypto.3.dylib ./OBS.app/Contents/Plugins/obs-outputs.so +install_name_tool -change /usr/local/opt/mbedtls/lib/libmbedx509.0.dylib @executable_path/../Frameworks/libmbedx509.0.dylib ./OBS.app/Contents/Plugins/obs-outputs.so +install_name_tool -change /usr/local/opt/curl/lib/libcurl.4.dylib @executable_path/../Frameworks/libcurl.4.dylib ./OBS.app/Contents/Plugins/obs-outputs.so +install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib ./OBS.app/Contents/Plugins/obs-outputs.so + +# copy sparkle into the app +hr "Copying Sparkle.framework" +cp -R ../../sparkle/Sparkle.framework ./OBS.app/Contents/Frameworks/ +install_name_tool -change @rpath/Sparkle.framework/Versions/A/Sparkle @executable_path/../Frameworks/Sparkle.framework/Versions/A/Sparkle ./OBS.app/Contents/MacOS/obs # Copy Chromium embedded framework to app Frameworks directory hr "Copying Chromium Embedded Framework.framework" sudo mkdir -p OBS.app/Contents/Frameworks -sudo cp -r ../../cef_binary_${CEF_BUILD_VERSION}_macosx64/Release/Chromium\ Embedded\ Framework.framework OBS.app/Contents/Frameworks/ -sudo install_name_tool -change \ - @rpath/Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \ - ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \ - OBS.app/Contents/Resources/obs-plugins/obs-browser.so -sudo install_name_tool -change \ - @executable_path/../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \ - ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \ - OBS.app/Contents/Resources/obs-plugins/obs-browser.so -sudo install_name_tool -change \ - @rpath/Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \ - ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \ - OBS.app/Contents/Resources/obs-plugins/obs-browser-page -sudo install_name_tool -change \ - @executable_path/../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \ - ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \ - OBS.app/Contents/Resources/obs-plugins/obs-browser-page +sudo cp -R ../../cef_binary_${CEF_BUILD_VERSION}_macosx64/Release/Chromium\ Embedded\ Framework.framework OBS.app/Contents/Frameworks/ -# Package app -hr "Generating .pkg" -packagesbuild ../CI/install/osx/CMakeLists.pkgproj +install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./OBS.app/Contents/Plugins/obs-browser.so +install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Plugins/obs-browser.so +install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./OBS.app/Contents/Plugins/obs-browser.so + +cp ../CI/install/osx/OBSPublicDSAKey.pem OBS.app/Contents/Resources + +# edit plist +plutil -insert CFBundleVersion -string $GIT_TAG ./OBS.app/Contents/Info.plist +plutil -insert CFBundleShortVersionString -string $GIT_TAG ./OBS.app/Contents/Info.plist +plutil -insert OBSFeedsURL -string https://obsproject.com/osx_update/feeds.xml ./OBS.app/Contents/Info.plist +plutil -insert SUFeedURL -string https://obsproject.com/osx_update/stable/updates.xml ./OBS.app/Contents/Info.plist +plutil -insert SUPublicDSAKeyFile -string OBSPublicDSAKey.pem ./OBS.app/Contents/Info.plist + +dmgbuild -s ../CI/install/osx/settings.json "OBS" obs.dmg if [ -v "$TRAVIS" ]; then # Signing stuff @@ -70,12 +89,10 @@ if [ -v "$TRAVIS" ]; then security import ./Certificates.p12 -k build.keychain -T /usr/bin/productsign -P "" # macOS 10.12+ security set-key-partition-list -S apple-tool:,apple: -s -k mysecretpassword build.keychain - hr "Signing Package" - productsign --sign 2MMRE5MTB8 ./OBS.pkg ./$FILENAME -else - cp ./OBS.pkg ./$FILENAME fi +cp ./OBS.dmg ./$FILENAME + # Move to the folder that travis uses to upload artifacts from hr "Moving package to nightly folder for distribution" mkdir ../nightly diff --git a/CI/install-dependencies-linux.sh b/CI/install-dependencies-linux.sh index 391c1818c..ca8419b84 100755 --- a/CI/install-dependencies-linux.sh +++ b/CI/install-dependencies-linux.sh @@ -1,7 +1,6 @@ #!/bin/sh set -ex -sudo add-apt-repository ppa:jonathonf/ffmpeg-3 -y curl -L https://packagecloud.io/github/git-lfs/gpgkey | sudo apt-key add - # gets us newer clang diff --git a/CI/install-dependencies-osx.sh b/CI/install-dependencies-osx.sh index c37d58843..ad398e48c 100755 --- a/CI/install-dependencies-osx.sh +++ b/CI/install-dependencies-osx.sh @@ -16,6 +16,8 @@ else /bin/bash -c "sudo xcode-select -s /Applications/Xcode_9.4.1.app/Contents/Developer" fi +git fetch origin --tags + # Leave obs-studio folder cd ../ @@ -28,10 +30,12 @@ sudo installer -pkg ./Packages.pkg -target / brew update #Base OBS Deps and ccache -brew install jack speexdsp ccache mbedtls clang-format +brew install jack speexdsp ccache mbedtls clang-format freetype fdk-aac brew install https://gist.githubusercontent.com/DDRBoxman/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb brew install https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb +pip install dmgbuild + export PATH=/usr/local/opt/ccache/libexec:$PATH ccache -s || echo "CCache is not available." diff --git a/CI/install/osx/Info.plist b/CI/install/osx/Info.plist new file mode 100644 index 000000000..074e14aed --- /dev/null +++ b/CI/install/osx/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleIconFile + obs.icns + CFBundleName + OBS + CFBundleGetInfoString + OBS - Free and Open Source Streaming/Recording Software + CFBundleExecutable + obs + CFBundleIdentifier + com.obsproject.obs-studio + CFBundlePackageType + APPL + LSMinimumSystemVersion + 10.8.5 + NSHighResolutionCapable + + LSAppNapIsDisabled + + NSCameraUsageDescription + OBS needs to access the camera to enable camera sources to work. + NSMicrophoneUsageDescription + OBS needs to access the microphone to enable audio input. + + diff --git a/CI/install/osx/background.png b/CI/install/osx/background.png new file mode 100644 index 000000000..02c4d5501 Binary files /dev/null and b/CI/install/osx/background.png differ diff --git a/CI/install/osx/background.pxd/QuickLook/Icon.tiff b/CI/install/osx/background.pxd/QuickLook/Icon.tiff new file mode 100644 index 000000000..4ef96a409 Binary files /dev/null and b/CI/install/osx/background.pxd/QuickLook/Icon.tiff differ diff --git a/CI/install/osx/background.pxd/QuickLook/Preview.tiff b/CI/install/osx/background.pxd/QuickLook/Preview.tiff new file mode 100644 index 000000000..b28ab1ec4 Binary files /dev/null and b/CI/install/osx/background.pxd/QuickLook/Preview.tiff differ diff --git a/CI/install/osx/background.pxd/QuickLook/Thumbnail.tiff b/CI/install/osx/background.pxd/QuickLook/Thumbnail.tiff new file mode 100644 index 000000000..1bd46a7ea Binary files /dev/null and b/CI/install/osx/background.pxd/QuickLook/Thumbnail.tiff differ diff --git a/CI/install/osx/background.pxd/data/556CF265-5721-4F18-BE83-8CF39483B4C2 b/CI/install/osx/background.pxd/data/556CF265-5721-4F18-BE83-8CF39483B4C2 new file mode 100644 index 000000000..3036891ae Binary files /dev/null and b/CI/install/osx/background.pxd/data/556CF265-5721-4F18-BE83-8CF39483B4C2 differ diff --git a/CI/install/osx/background.pxd/data/8CA689C3-ED2A-459E-952C-E08026CFCD07 b/CI/install/osx/background.pxd/data/8CA689C3-ED2A-459E-952C-E08026CFCD07 new file mode 100644 index 000000000..52d94b97c Binary files /dev/null and b/CI/install/osx/background.pxd/data/8CA689C3-ED2A-459E-952C-E08026CFCD07 differ diff --git a/CI/install/osx/background.pxd/metadata.info b/CI/install/osx/background.pxd/metadata.info new file mode 100644 index 000000000..6054a580b Binary files /dev/null and b/CI/install/osx/background.pxd/metadata.info differ diff --git a/CI/install/osx/background.tiff b/CI/install/osx/background.tiff new file mode 100644 index 000000000..454874005 Binary files /dev/null and b/CI/install/osx/background.tiff differ diff --git a/CI/install/osx/background@2x.png b/CI/install/osx/background@2x.png new file mode 100644 index 000000000..a001edf36 Binary files /dev/null and b/CI/install/osx/background@2x.png differ diff --git a/CI/install/osx/buildDMG b/CI/install/osx/buildDMG new file mode 100755 index 000000000..af2c798e4 --- /dev/null +++ b/CI/install/osx/buildDMG @@ -0,0 +1 @@ +dmgbuild -s ./settings.json "OBS" obs.dmg diff --git a/CI/install/osx/dylibBundler b/CI/install/osx/dylibBundler new file mode 100755 index 000000000..a28f20985 Binary files /dev/null and b/CI/install/osx/dylibBundler differ diff --git a/CI/install/osx/makeRetinaBG b/CI/install/osx/makeRetinaBG new file mode 100755 index 000000000..5d7f309b5 --- /dev/null +++ b/CI/install/osx/makeRetinaBG @@ -0,0 +1 @@ +tiffutil -cathidpicheck background.png background@2x.png -out background.tiff diff --git a/CI/install/osx/obs.icns b/CI/install/osx/obs.icns new file mode 100644 index 000000000..6f878d6a3 Binary files /dev/null and b/CI/install/osx/obs.icns differ diff --git a/CI/install/osx/packageApp.sh b/CI/install/osx/packageApp.sh new file mode 100755 index 000000000..d0a3b89ad --- /dev/null +++ b/CI/install/osx/packageApp.sh @@ -0,0 +1,73 @@ +rm -rf ./OBS.app + +mkdir OBS.app +mkdir OBS.app/Contents +mkdir OBS.app/Contents/MacOS +mkdir OBS.app/Contents/Plugins +mkdir OBS.app/Contents/Resources + +cp -R rundir/RelWithDebInfo/bin/ ./OBS.app/Contents/MacOS +cp -R rundir/RelWithDebInfo/data ./OBS.app/Contents/Resources +cp ../CI/install/osx/obs.icns ./OBS.app/Contents/Resources +cp -R rundir/RelWithDebInfo/obs-plugins/ ./OBS.app/Contents/Plugins +cp ../CI/install/osx/Info.plist ./OBS.app/Contents + +../CI/install/osx/dylibBundler -b -cd -d ./OBS.app/Contents/Frameworks -p @executable_path/../Frameworks/ \ +-s ./OBS.app/Contents/MacOS \ +-s /usr/local/opt/mbedtls/lib/ \ +-x ./OBS.app/Contents/Plugins/coreaudio-encoder.so \ +-x ./OBS.app/Contents/Plugins/decklink-ouput-ui.so \ +-x ./OBS.app/Contents/Plugins/frontend-tools.so \ +-x ./OBS.app/Contents/Plugins/image-source.so \ +-x ./OBS.app/Contents/Plugins/linux-jack.so \ +-x ./OBS.app/Contents/Plugins/mac-avcapture.so \ +-x ./OBS.app/Contents/Plugins/mac-capture.so \ +-x ./OBS.app/Contents/Plugins/mac-decklink.so \ +-x ./OBS.app/Contents/Plugins/mac-syphon.so \ +-x ./OBS.app/Contents/Plugins/mac-vth264.so \ +-x ./OBS.app/Contents/Plugins/obs-browser.so \ +-x ./OBS.app/Contents/Plugins/obs-browser-page \ +-x ./OBS.app/Contents/Plugins/obs-ffmpeg.so \ +-x ./OBS.app/Contents/Plugins/obs-filters.so \ +-x ./OBS.app/Contents/Plugins/obs-transitions.so \ +-x ./OBS.app/Contents/Plugins/obs-vst.so \ +-x ./OBS.app/Contents/Plugins/rtmp-services.so \ +-x ./OBS.app/Contents/MacOS/obs \ +-x ./OBS.app/Contents/MacOS/obs-ffmpeg-mux \ +-x ./OBS.app/Contents/MacOS/obslua.so \ +-x ./OBS.app/Contents/MacOS/_obspython.so \ +-x ./OBS.app/Contents/Plugins/obs-x264.so \ +-x ./OBS.app/Contents/Plugins/text-freetype2.so \ +-x ./OBS.app/Contents/Plugins/obs-libfdk.so +# -x ./OBS.app/Contents/Plugins/obs-outputs.so \ + +/usr/local/Cellar/qt/5.10.1/bin/macdeployqt ./OBS.app + +mv ./OBS.app/Contents/MacOS/libobs-opengl.so ./OBS.app/Contents/Frameworks + +# put qt network in here becasuse streamdeck uses it +cp -R /usr/local/opt/qt/lib/QtNetwork.framework ./OBS.app/Contents/Frameworks +chmod -R +w ./OBS.app/Contents/Frameworks/QtNetwork.framework +rm -r ./OBS.app/Contents/Frameworks/QtNetwork.framework/Headers +rm -r ./OBS.app/Contents/Frameworks/QtNetwork.framework/QtNetwork.prl +rm -r ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/Headers/ +chmod 644 ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/Resources/Info.plist +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/QtNetwork +install_name_tool -change /usr/local/Cellar/qt/5.10.1/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/QtNetwork + + +# decklink ui qt +install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./OBS.app/Contents/Plugins/decklink-ouput-ui.so +install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Plugins/decklink-ouput-ui.so +install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./OBS.app/Contents/Plugins/decklink-ouput-ui.so + +# frontend tools qt +install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./OBS.app/Contents/Plugins/frontend-tools.so +install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Plugins/frontend-tools.so +install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./OBS.app/Contents/Plugins/frontend-tools.so + +# vst qt +install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./OBS.app/Contents/Plugins/obs-vst.so +install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Plugins/obs-vst.so +install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./OBS.app/Contents/Plugins/obs-vst.so +install_name_tool -change /usr/local/opt/qt/lib/QtMacExtras.framework/Versions/5/QtMacExtras @executable_path/../Frameworks/QtMacExtras.framework/Versions/5/QtMacExtras ./OBS.app/Contents/Plugins/obs-vst.so diff --git a/CI/install/osx/settings.json b/CI/install/osx/settings.json new file mode 100644 index 000000000..34fbc2ff4 --- /dev/null +++ b/CI/install/osx/settings.json @@ -0,0 +1,13 @@ +{ + "title": "OBS", + "background": "../CI/install/osx/background.tiff", + "format": "UDZO", + "compression-level": 9, + "window": { "position": { "x": 100, "y": 100 }, + "size": { "width": 540, "height": 380 } }, + "contents": [ + { "x": 120, "y": 180, "type": "file", + "path": "./OBS.app" }, + { "x": 420, "y": 180, "type": "link", "path": "/Applications" } + ] +} diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index a33f9e16d..51b771e56 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -373,14 +373,6 @@ if (APPLE) target_link_libraries(obs Qt5::MacExtras) set_target_properties(obs PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000") - set_property( - TARGET obs - APPEND - PROPERTY INSTALL_RPATH - "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/" - "/Library/Frameworks/Python.framework/Versions/3.6/lib/" - "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/" - ) endif() define_graphic_modules(obs) diff --git a/UI/auth-twitch.cpp b/UI/auth-twitch.cpp index 3cd8b79a6..ce46c60d4 100644 --- a/UI/auth-twitch.cpp +++ b/UI/auth-twitch.cpp @@ -216,7 +216,8 @@ void TwitchAuth::LoadUI() chat->SetWidget(browser); cef->add_force_popup_url(moderation_tools_url, chat.data()); - script = bttv_script; + script = "localStorage.setItem('twilight.theme', 1);"; + script += bttv_script; script += ffz_script; browser->setStartupScript(script); diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index a10e48277..37d929761 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -3685,19 +3685,19 @@ - 44.1khz + 44.1 kHz 0 - 44.1khz + 44.1 kHz - 48khz + 48 kHz diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp index 57d507e7e..fd3c3079a 100644 --- a/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp @@ -126,10 +126,17 @@ static std::string GetWindowTitle(size_t i) if (status >= Success && name != nullptr) { std::string str(name); windowTitle = str; + XFree(name); + } else { + XTextProperty xtp_new_name; + if (XGetWMName(disp(), w, &xtp_new_name) != 0 && + xtp_new_name.value != nullptr) { + std::string str((const char *)xtp_new_name.value); + windowTitle = str; + XFree(xtp_new_name.value); + } } - XFree(name); - return windowTitle; } diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 1e2e7a49f..2eddc5cd7 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1059,21 +1059,27 @@ bool OBSApp::InitTheme() defaultPalette = palette(); const char *themeName = - config_get_string(globalConfig, "General", "CurrentTheme"); + config_get_string(globalConfig, "General", "CurrentTheme2"); - if (!themeName) { + if (!themeName) + /* Use deprecated "CurrentTheme" value if available */ + themeName = config_get_string(globalConfig, "General", + "CurrentTheme"); + if (!themeName) /* Use deprecated "Theme" value if available */ themeName = config_get_string(globalConfig, "General", "Theme"); - if (!themeName) - themeName = DEFAULT_THEME; - if (!themeName) - themeName = "Dark"; - } + if (!themeName) + themeName = DEFAULT_THEME; + if (!themeName) + themeName = "Dark"; if (strcmp(themeName, "Default") == 0) themeName = "System"; - return SetTheme(themeName); + if (strcmp(themeName, "System") != 0 && SetTheme(themeName)) + return true; + + return SetTheme("System"); } OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store) diff --git a/UI/platform-osx.mm b/UI/platform-osx.mm index bc400f5d2..3d0c5334f 100644 --- a/UI/platform-osx.mm +++ b/UI/platform-osx.mm @@ -28,11 +28,29 @@ using namespace std; +bool isInBundle() +{ + NSRunningApplication *app = [NSRunningApplication currentApplication]; + return [app bundleIdentifier] != nil; +} + bool GetDataFilePath(const char *data, string &output) { - stringstream str; - str << OBS_DATA_PATH "/obs-studio/" << data; - output = str.str(); + if (isInBundle()) { + NSRunningApplication *app = + [NSRunningApplication currentApplication]; + NSURL *bundleURL = [app bundleURL]; + NSString *path = [NSString + stringWithFormat:@"Contents/Resources/data/obs-studio/%@", + [NSString stringWithUTF8String:data]]; + NSURL *dataURL = [bundleURL URLByAppendingPathComponent:path]; + output = [[dataURL path] UTF8String]; + } else { + stringstream str; + str << OBS_DATA_PATH "/obs-studio/" << data; + output = str.str(); + } + return !access(output.c_str(), R_OK); } diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 313a2e1be..7c2287204 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -1820,7 +1820,7 @@ void WidgetInfo::ControlChanged() break; case OBS_PROPERTY_GROUP: GroupChanged(setting); - return; + break; } if (view->callback && !view->deferUpdate) diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index a72768c43..02eff198e 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -417,6 +417,8 @@ void AutoConfigStreamPage::on_connectAccount_clicked() #ifdef BROWSER_AVAILABLE std::string service = QT_TO_UTF8(ui->service->currentText()); + OAuth::DeleteCookies(service); + auth = OAuthStreamKey::Login(this, service); if (!!auth) OnAuthConnected(); diff --git a/UI/window-basic-interaction.cpp b/UI/window-basic-interaction.cpp index 8b37b4d37..147aafa0a 100644 --- a/UI/window-basic-interaction.cpp +++ b/UI/window-basic-interaction.cpp @@ -59,6 +59,10 @@ OBSBasicInteraction::OBSBasicInteraction(QWidget *parent, OBSSource source_) const char *name = obs_source_get_name(source); setWindowTitle(QTStr("Basic.InteractionWindow").arg(QT_UTF8(name))); + Qt::WindowFlags flags = windowFlags(); + Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint; + setWindowFlags(flags & (~helpFlag)); + auto addDrawCallback = [this]() { obs_display_add_draw_callback(ui->preview->GetDisplay(), OBSBasicInteraction::DrawPreview, diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 6ee726be6..ede9733a7 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -643,6 +643,7 @@ void SimpleOutput::UpdateRecordingSettings() } else if (videoEncoder == SIMPLE_ENCODER_NVENC) { UpdateRecordingSettings_nvenc(crf); } + UpdateRecordingAudioSettings(); } inline void SimpleOutput::SetupOutputs() diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index cceef021a..e0ea3d125 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -88,6 +88,38 @@ static bool SceneCollectionExists(const char *findName) return found; } +static bool GetUnusedSceneCollectionFile(std::string &name, std::string &file) +{ + char path[512]; + size_t len; + int ret; + + if (!GetFileSafeName(name.c_str(), file)) { + blog(LOG_WARNING, "Failed to create safe file name for '%s'", + name.c_str()); + return false; + } + + ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes/"); + if (ret <= 0) { + blog(LOG_WARNING, "Failed to get scene collection config path"); + return false; + } + + len = file.size(); + file.insert(0, path); + + if (!GetClosestUnusedFileName(file, "json")) { + blog(LOG_WARNING, "Failed to get closest file name for %s", + file.c_str()); + return false; + } + + file.erase(file.size() - 5, 5); + file.erase(0, file.size() - len); + return true; +} + static bool GetSceneCollectionName(QWidget *parent, std::string &name, std::string &file, const char *oldName = nullptr) @@ -95,9 +127,6 @@ static bool GetSceneCollectionName(QWidget *parent, std::string &name, bool rename = oldName != nullptr; const char *title; const char *text; - char path[512]; - size_t len; - int ret; if (rename) { title = Str("Basic.Main.RenameSceneCollection.Title"); @@ -128,29 +157,10 @@ static bool GetSceneCollectionName(QWidget *parent, std::string &name, break; } - if (!GetFileSafeName(name.c_str(), file)) { - blog(LOG_WARNING, "Failed to create safe file name for '%s'", - name.c_str()); + if (!GetUnusedSceneCollectionFile(name, file)) { return false; } - ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get scene collection config path"); - return false; - } - - len = file.size(); - file.insert(0, path); - - if (!GetClosestUnusedFileName(file, "json")) { - blog(LOG_WARNING, "Failed to get closest file name for %s", - file.c_str()); - return false; - } - - file.erase(file.size() - 5, 5); - file.erase(0, file.size() - len); return true; } @@ -166,6 +176,10 @@ bool OBSBasic::AddSceneCollection(bool create_new, const QString &qname) name = QT_TO_UTF8(qname); if (SceneCollectionExists(name.c_str())) return false; + + if (!GetUnusedSceneCollectionFile(name, file)) { + return false; + } } SaveProjectNow(); diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index f09508462..ae47eb288 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -466,6 +466,8 @@ void OBSBasicSettings::on_connectAccount_clicked() #ifdef BROWSER_AVAILABLE std::string service = QT_TO_UTF8(ui->service->currentText()); + OAuth::DeleteCookies(service); + auth = OAuthStreamKey::Login(this, service); if (!!auth) OnAuthConnected(); diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 4d10e72d2..25d720523 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -2189,9 +2189,9 @@ void OBSBasicSettings::LoadAudioSettings() const char *str; if (sampleRate == 48000) - str = "48khz"; + str = "48 kHz"; else - str = "44.1khz"; + str = "44.1 kHz"; int sampleRateIdx = ui->sampleRate->findText(str); if (sampleRateIdx != -1) @@ -2726,7 +2726,7 @@ void OBSBasicSettings::SaveGeneralSettings() themeData = DEFAULT_THEME; if (WidgetChanged(ui->theme)) { - config_set_string(GetGlobalConfig(), "General", "CurrentTheme", + config_set_string(GetGlobalConfig(), "General", "CurrentTheme2", QT_TO_UTF8(themeData)); App()->SetTheme(themeData.toUtf8().constData()); @@ -3253,7 +3253,7 @@ void OBSBasicSettings::SaveAudioSettings() } int sampleRate = 44100; - if (sampleRateStr == "48khz") + if (sampleRateStr == "48 kHz") sampleRate = 48000; if (WidgetChanged(ui->sampleRate)) diff --git a/UI/window-extra-browsers.cpp b/UI/window-extra-browsers.cpp index f6ff2d583..d754d4561 100644 --- a/UI/window-extra-browsers.cpp +++ b/UI/window-extra-browsers.cpp @@ -533,6 +533,24 @@ void OBSBasic::AddExtraBrowserDock(const QString &title, const QString &url, dock->SetWidget(browser); + /* Add support for Twitch Dashboard panels */ + if (url.contains("twitch.tv/popout") && + url.contains("dashboard/live")) { + QRegularExpression re("twitch.tv\\/popout\\/([^/]+)\\/"); + QRegularExpressionMatch match = re.match(url); + QString username = match.captured(1); + if (username.length() > 0) { + std::string script; + script = + "Object.defineProperty(document, 'referrer', { get: () => '"; + script += "https://twitch.tv/"; + script += QT_TO_UTF8(username); + script += "/dashboard/live"; + script += "'});"; + browser->setStartupScript(script); + } + } + addDockWidget(Qt::RightDockWidgetArea, dock); if (firstCreate) { diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index 6dd74ab0d..afdb3733e 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -653,6 +653,9 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) source = curSource; window->source = source; } + } else if (window->type == ProjectorType::Preview && + !main->IsPreviewProgramMode()) { + window->source = nullptr; } if (source) diff --git a/cmake/Modules/ObsHelpers.cmake b/cmake/Modules/ObsHelpers.cmake index 627aadf5b..cb63ba611 100644 --- a/cmake/Modules/ObsHelpers.cmake +++ b/cmake/Modules/ObsHelpers.cmake @@ -61,7 +61,7 @@ if(NOT UNIX_STRUCTURE) set(OBS_INSTALL_PREFIX "") set(OBS_RELATIVE_PREFIX "../") - set(OBS_SCRIPT_PLUGIN_DESTINATION "${OBS_DATA_DESTINATION}/obs-scripting/${_lib_suffix}bit") + set(OBS_SCRIPT_PLUGIN_DESTINATION "${OBS_DATA_DESTINATION}/obs-scripting") else() set(OBS_EXECUTABLE_DESTINATION "bin/${_lib_suffix}bit") set(OBS_EXECUTABLE32_DESTINATION "bin/32bit") diff --git a/deps/media-playback/media-playback/media.c b/deps/media-playback/media-playback/media.c index 35554eb35..e819840f3 100644 --- a/deps/media-playback/media-playback/media.c +++ b/deps/media-playback/media-playback/media.c @@ -139,7 +139,7 @@ static int mp_media_next_packet(mp_media_t *media) int ret = av_read_frame(media->fmt, &pkt); if (ret < 0) { - if (ret != AVERROR_EOF) + if (ret != AVERROR_EOF && ret != AVERROR_EXIT) blog(LOG_WARNING, "MP: av_read_frame failed: %s (%d)", av_err2str(ret), ret); return ret; @@ -230,7 +230,7 @@ static bool mp_media_prepare_frames(mp_media_t *m) while (!mp_media_ready_to_start(m)) { if (!m->eof) { int ret = mp_media_next_packet(m); - if (ret == AVERROR_EOF) + if (ret == AVERROR_EOF || ret == AVERROR_EXIT) m->eof = true; else if (ret < 0) return false; @@ -577,8 +577,10 @@ static bool init_avformat(mp_media_t *m) av_dict_set_int(&opts, "buffer_size", m->buffering, 0); m->fmt = avformat_alloc_context(); - m->fmt->interrupt_callback.callback = interrupt_callback; - m->fmt->interrupt_callback.opaque = m; + if (!m->is_local_file) { + m->fmt->interrupt_callback.callback = interrupt_callback; + m->fmt->interrupt_callback.opaque = m; + } int ret = avformat_open_input(&m->fmt, m->path, format, opts ? &opts : NULL); diff --git a/deps/obs-scripting/CMakeLists.txt b/deps/obs-scripting/CMakeLists.txt index 92fb1b9e1..340eb30ae 100644 --- a/deps/obs-scripting/CMakeLists.txt +++ b/deps/obs-scripting/CMakeLists.txt @@ -12,6 +12,11 @@ if(MSVC) w32-pthreads) endif() +if(APPLE) + set(obs-scripting_PLATFORM_DEPS + objc) +endif() + option(DISABLE_LUA "Disable Lua scripting support" OFF) option(DISABLE_PYTHON "Disable Python scripting support" OFF) diff --git a/deps/obs-scripting/obs-scripting-lua.c b/deps/obs-scripting/obs-scripting-lua.c index a626555da..ebf6a1f79 100644 --- a/deps/obs-scripting/obs-scripting-lua.c +++ b/deps/obs-scripting/obs-scripting-lua.c @@ -43,7 +43,8 @@ static const char *startup_script_template = "\ for val in pairs(package.preload) do\n\ package.preload[val] = nil\n\ end\n\ -package.cpath = package.cpath .. \";\" .. \"%s\" .. \"/?." SO_EXT "\"\n\ +package.cpath = package.cpath .. \";\" .. \"%s/Contents/MacOS/?.so\" .. \";\" .. \"%s\" .. \"/?." SO_EXT + "\"\n\ require \"obslua\"\n"; static const char *get_script_path_func = "\ @@ -1310,7 +1311,31 @@ void obs_lua_load(void) /* ---------------------------------------------- */ /* Initialize Lua startup script */ - dstr_printf(&tmp, startup_script_template, SCRIPT_DIR); + char *bundlePath = "./"; + +#ifdef __APPLE__ + Class nsRunningApplication = objc_lookUpClass("NSRunningApplication"); + SEL currentAppSel = sel_getUid("currentApplication"); + + typedef id (*running_app_func)(Class, SEL); + running_app_func operatingSystemName = (running_app_func)objc_msgSend; + id app = operatingSystemName(nsRunningApplication, currentAppSel); + + typedef id (*bundle_url_func)(id, SEL); + bundle_url_func bundleURL = (bundle_url_func)objc_msgSend; + id url = bundleURL(app, sel_getUid("bundleURL")); + + typedef id (*url_path_func)(id, SEL); + url_path_func urlPath = (url_path_func)objc_msgSend; + + id path = urlPath(url, sel_getUid("path")); + + typedef id (*string_func)(id, SEL); + string_func utf8String = (string_func)objc_msgSend; + bundlePath = (char *)utf8String(path, sel_registerName("UTF8String")); +#endif + + dstr_printf(&tmp, startup_script_template, bundlePath, SCRIPT_DIR); startup_script = tmp.array; dstr_free(&dep_paths); diff --git a/deps/obs-scripting/obs-scripting-python-import.c b/deps/obs-scripting/obs-scripting-python-import.c index 5a6068179..c8f74efb5 100644 --- a/deps/obs-scripting/obs-scripting-python-import.c +++ b/deps/obs-scripting/obs-scripting-python-import.c @@ -32,6 +32,12 @@ #define SO_EXT ".dylib" #endif +#ifdef __APPLE__ +#define PYTHON_LIB_SUBDIR "lib/" +#else +#define PYTHON_LIB_SUBDIR "" +#endif + bool import_python(const char *python_path) { struct dstr lib_path; @@ -44,7 +50,7 @@ bool import_python(const char *python_path) dstr_init_copy(&lib_path, python_path); dstr_replace(&lib_path, "\\", "/"); if (!dstr_is_empty(&lib_path)) { - dstr_cat(&lib_path, "/"); + dstr_cat(&lib_path, "/" PYTHON_LIB_SUBDIR); } dstr_cat(&lib_path, PYTHON_LIB SO_EXT); diff --git a/deps/obs-scripting/obs-scripting-python.c b/deps/obs-scripting/obs-scripting-python.c index d02ac8087..8899df816 100644 --- a/deps/obs-scripting/obs-scripting-python.c +++ b/deps/obs-scripting/obs-scripting-python.c @@ -1651,6 +1651,13 @@ bool obs_scripting_load_python(const char *python_path) add_to_python_path(SCRIPT_DIR); +#if __APPLE__ + char *exec_path = os_get_executable_path_ptr(""); + if (exec_path) + add_to_python_path(exec_path); + bfree(exec_path); +#endif + py_obspython = PyImport_ImportModule("obspython"); bool success = !py_error(); if (!success) { diff --git a/deps/obs-scripting/obspython/CMakeLists.txt b/deps/obs-scripting/obspython/CMakeLists.txt index 57b65bdbc..097327329 100644 --- a/deps/obs-scripting/obspython/CMakeLists.txt +++ b/deps/obs-scripting/obspython/CMakeLists.txt @@ -42,7 +42,12 @@ if(CMAKE_VERSION VERSION_GREATER 3.7.2) else() SWIG_ADD_MODULE(obspython python obspython.i ../cstrcache.cpp ../cstrcache.h) endif() -SWIG_LINK_LIBRARIES(obspython obs-scripting libobs ${PYTHON_LIBRARIES}) + +IF(APPLE) + SWIG_LINK_LIBRARIES(obspython obs-scripting libobs) +ELSE() + SWIG_LINK_LIBRARIES(obspython obs-scripting libobs ${PYTHON_LIBRARIES}) +ENDIF() function(install_plugin_bin_swig target additional_target) if(APPLE) @@ -57,14 +62,7 @@ function(install_plugin_bin_swig target additional_target) PREFIX "") if (APPLE) - set_property( - TARGET ${additional_target} - APPEND - PROPERTY INSTALL_RPATH - "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/" - "/Library/Frameworks/Python.framework/Versions/3.6/lib/" - "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/" - ) + SET_TARGET_PROPERTIES(${additional_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") endif() install(FILES "${CMAKE_CURRENT_BINARY_DIR}/obspython.py" diff --git a/formatcode.sh b/formatcode.sh index 58c63d085..6b8d70346 100755 --- a/formatcode.sh +++ b/formatcode.sh @@ -27,5 +27,14 @@ else CLANG_FORMAT=clang-format fi -find . -type d \( -path ./deps -o -path ./cmake -o -path ./plugins/decklink/win -o -path ./plugins/decklink/mac -o -path ./plugins/decklink/linux -o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \ +find . -type d \( -path ./deps \ +-o -path ./cmake \ +-o -path ./plugins/decklink/win \ +-o -path ./plugins/decklink/mac \ +-o -path ./plugins/decklink/linux \ +-o -path ./plugins/enc-amf \ +-o -path ./plugins/mac-syphon/syphon-framework \ +-o -path ./plugins/obs-outputs/ftl-sdk \ +-o -path ./plugins/obs-vst \ +-o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \ | xargs -I{} -P ${NPROC} ${CLANG_FORMAT} -i -style=file -fallback-style=none {} diff --git a/libobs-d3d11/d3d11-rebuild.cpp b/libobs-d3d11/d3d11-rebuild.cpp index db7dc3dc4..58cb6d9dc 100644 --- a/libobs-d3d11/d3d11-rebuild.cpp +++ b/libobs-d3d11/d3d11-rebuild.cpp @@ -178,10 +178,13 @@ void gs_vertex_shader::Rebuild(ID3D11Device *dev) if (FAILED(hr)) throw HRError("Failed to create vertex shader", hr); - hr = dev->CreateInputLayout(layoutData.data(), (UINT)layoutData.size(), - data.data(), data.size(), &layout); - if (FAILED(hr)) - throw HRError("Failed to create input layout", hr); + const UINT layoutSize = (UINT)layoutData.size(); + if (layoutSize > 0) { + hr = dev->CreateInputLayout(layoutData.data(), layoutSize, + data.data(), data.size(), &layout); + if (FAILED(hr)) + throw HRError("Failed to create input layout", hr); + } if (constantSize) { hr = dev->CreateBuffer(&bd, NULL, &constants); diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 3e4d77f79..43771285f 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -796,6 +796,53 @@ bool device_enum_adapters(bool (*callback)(void *param, const char *name, } } +static bool GetMonitorTarget(const MONITORINFOEX &info, + DISPLAYCONFIG_TARGET_DEVICE_NAME &target) +{ + bool found = false; + + UINT32 numPath, numMode; + if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPath, + &numMode) == ERROR_SUCCESS) { + std::vector paths(numPath); + std::vector modes(numMode); + if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPath, + paths.data(), &numMode, modes.data(), + nullptr) == ERROR_SUCCESS) { + paths.resize(numPath); + for (size_t i = 0; i < numPath; ++i) { + const DISPLAYCONFIG_PATH_INFO &path = paths[i]; + + DISPLAYCONFIG_SOURCE_DEVICE_NAME + source; + source.header.type = + DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; + source.header.size = sizeof(source); + source.header.adapterId = + path.sourceInfo.adapterId; + source.header.id = path.sourceInfo.id; + if (DisplayConfigGetDeviceInfo( + &source.header) == ERROR_SUCCESS && + wcscmp(info.szDevice, + source.viewGdiDeviceName) == 0) { + target.header.type = + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; + target.header.size = sizeof(target); + target.header.adapterId = + path.sourceInfo.adapterId; + target.header.id = path.targetInfo.id; + found = DisplayConfigGetDeviceInfo( + &target.header) == + ERROR_SUCCESS; + break; + } + } + } + } + + return found; +} + static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter) { UINT i; @@ -806,15 +853,41 @@ static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter) if (FAILED(output->GetDesc(&desc))) continue; - RECT rect = desc.DesktopCoordinates; + unsigned refresh = 0; + + bool target_found = false; + DISPLAYCONFIG_TARGET_DEVICE_NAME target; + + MONITORINFOEX info; + info.cbSize = sizeof(info); + if (GetMonitorInfo(desc.Monitor, &info)) { + target_found = GetMonitorTarget(info, target); + + DEVMODE mode; + mode.dmSize = sizeof(mode); + mode.dmDriverExtra = 0; + if (EnumDisplaySettings(info.szDevice, + ENUM_CURRENT_SETTINGS, &mode)) { + refresh = mode.dmDisplayFrequency; + } + } + + if (!target_found) { + target.monitorFriendlyDeviceName[0] = 0; + } + + const RECT &rect = desc.DesktopCoordinates; blog(LOG_INFO, "\t output %u: " "pos={%d, %d}, " "size={%d, %d}, " - "attached=%s", + "attached=%s, " + "refresh=%u, " + "name=%ls", i, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, - desc.AttachedToDesktop ? "true" : "false"); + desc.AttachedToDesktop ? "true" : "false", refresh, + target.monitorFriendlyDeviceName); } } @@ -853,6 +926,25 @@ static inline void LogD3DAdapters() blog(LOG_INFO, "\t Shared VRAM: %u", desc.SharedSystemMemory); + /* driver version */ + LARGE_INTEGER umd; + hr = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), + &umd); + if (SUCCEEDED(hr)) { + const uint64_t version = umd.QuadPart; + const uint16_t aa = (version >> 48) & 0xffff; + const uint16_t bb = (version >> 32) & 0xffff; + const uint16_t ccccc = (version >> 16) & 0xffff; + const uint16_t ddddd = version & 0xffff; + blog(LOG_INFO, + "\t Driver Version: %" PRIu16 ".%" PRIu16 + ".%" PRIu16 ".%" PRIu16, + aa, bb, ccccc, ddddd); + } else { + blog(LOG_INFO, "\t Driver Version: Unknown (0x%X)", + (unsigned)hr); + } + LogAdapterMonitors(adapter); } } diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 868114816..0eabf608c 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -110,7 +110,7 @@ if(WIN32) endif() elseif(APPLE) set(libobs_PLATFORM_SOURCES - obs-cocoa.c + obs-cocoa.m util/threading-posix.c util/pipe-posix.c util/platform-nix.c diff --git a/libobs/obs-cocoa.c b/libobs/obs-cocoa.m similarity index 93% rename from libobs/obs-cocoa.c rename to libobs/obs-cocoa.m index 30d096492..4f0d30d99 100644 --- a/libobs/obs-cocoa.c +++ b/libobs/obs-cocoa.m @@ -29,6 +29,14 @@ #include #include +#import + +bool is_in_bundle() +{ + NSRunningApplication *app = [NSRunningApplication currentApplication]; + return [app bundleIdentifier] != nil; +} + const char *get_module_extension(void) { return ".so"; @@ -51,12 +59,45 @@ void add_default_module_paths(void) { for (int i = 0; i < module_patterns_size; i++) obs_add_module_path(module_bin[i], module_data[i]); + + if (is_in_bundle()) { + NSRunningApplication *app = + [NSRunningApplication currentApplication]; + NSURL *bundleURL = [app bundleURL]; + NSURL *pluginsURL = [bundleURL + URLByAppendingPathComponent:@"Contents/Plugins"]; + NSURL *dataURL = [bundleURL + URLByAppendingPathComponent: + @"Contents/Resources/data/obs-plugins/%module%"]; + + const char *binPath = [[pluginsURL path] + cStringUsingEncoding:NSUTF8StringEncoding]; + const char *dataPath = [[dataURL path] + cStringUsingEncoding:NSUTF8StringEncoding]; + + obs_add_module_path(binPath, dataPath); + } } char *find_libobs_data_file(const char *file) { struct dstr path; - dstr_init_copy(&path, OBS_INSTALL_DATA_PATH "/libobs/"); + + if (is_in_bundle()) { + NSRunningApplication *app = + [NSRunningApplication currentApplication]; + NSURL *bundleURL = [app bundleURL]; + NSURL *libobsDataURL = + [bundleURL URLByAppendingPathComponent: + @"Contents/Resources/data/libobs/"]; + const char *libobsDataPath = [[libobsDataURL path] + cStringUsingEncoding:NSUTF8StringEncoding]; + dstr_init_copy(&path, libobsDataPath); + dstr_cat(&path, "/"); + } else { + dstr_init_copy(&path, OBS_INSTALL_DATA_PATH "/libobs/"); + } + dstr_cat(&path, file); return path.array; } @@ -111,13 +152,20 @@ static void log_available_memory(void) memory_available / 1024 / 1024); } -static void log_os_name(id pi, SEL UTF8String) +static void log_os_name(id pi, SEL UTF8StringSel) { - unsigned long os_id = (unsigned long)objc_msgSend( + typedef int (*os_func)(id, SEL); + os_func operatingSystem = (os_func)objc_msgSend; + unsigned long os_id = (unsigned long)operatingSystem( pi, sel_registerName("operatingSystem")); - id os = objc_msgSend(pi, sel_registerName("operatingSystemName")); - const char *name = (const char *)objc_msgSend(os, UTF8String); + typedef id (*os_name_func)(id, SEL); + os_name_func operatingSystemName = (os_name_func)objc_msgSend; + id os = operatingSystemName(pi, + sel_registerName("operatingSystemName")); + typedef const char *(*utf8_func)(id, SEL); + utf8_func UTF8String = (utf8_func)objc_msgSend; + const char *name = UTF8String(os, UTF8StringSel); if (os_id == 5 /*NSMACHOperatingSystem*/) { blog(LOG_INFO, "OS Name: Mac OS X (%s)", name); @@ -127,11 +175,15 @@ static void log_os_name(id pi, SEL UTF8String) blog(LOG_INFO, "OS Name: %s", name ? name : "Unknown"); } -static void log_os_version(id pi, SEL UTF8String) +static void log_os_version(id pi, SEL UTF8StringSel) { - id vs = objc_msgSend(pi, - sel_registerName("operatingSystemVersionString")); - const char *version = (const char *)objc_msgSend(vs, UTF8String); + typedef id (*version_func)(id, SEL); + version_func operatingSystemVersionString = (version_func)objc_msgSend; + id vs = operatingSystemVersionString( + pi, sel_registerName("operatingSystemVersionString")); + typedef const char *(*utf8_func)(id, SEL); + utf8_func UTF8String = (utf8_func)objc_msgSend; + const char *version = UTF8String(vs, UTF8StringSel); blog(LOG_INFO, "OS Version: %s", version ? version : "Unknown"); } @@ -139,8 +191,9 @@ static void log_os_version(id pi, SEL UTF8String) static void log_os(void) { Class NSProcessInfo = objc_getClass("NSProcessInfo"); - id pi = objc_msgSend((id)NSProcessInfo, - sel_registerName("processInfo")); + typedef id (*func)(id, SEL); + func processInfo = (func)objc_msgSend; + id pi = processInfo((id)NSProcessInfo, sel_registerName("processInfo")); SEL UTF8String = sel_registerName("UTF8String"); @@ -1673,9 +1726,11 @@ static bool mouse_button_pressed(obs_key_t key, bool *pressed) } Class NSEvent = objc_getClass("NSEvent"); - SEL pressedMouseButtons = sel_registerName("pressedMouseButtons"); - NSUInteger buttons = - (NSUInteger)objc_msgSend((id)NSEvent, pressedMouseButtons); + SEL pressedMouseButtonsSel = sel_registerName("pressedMouseButtons"); + typedef int (*func)(id, SEL); + func pressedMouseButtons = (func)objc_msgSend; + NSUInteger buttons = (NSUInteger)pressedMouseButtons( + (id)NSEvent, pressedMouseButtonsSel); *pressed = (buttons & (1 << button)) != 0; return true; @@ -1699,6 +1754,10 @@ bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *plat, IOHIDValueRef value = 0; IOHIDDeviceRef device = IOHIDElementGetDevice(element); + if (device == NULL) { + continue; + } + if (IOHIDDeviceGetValue(device, element, &value) != kIOReturnSuccess) { i += 1; diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 644c2b63b..127004d83 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 6 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) diff --git a/libobs/obs-video-gpu-encode.c b/libobs/obs-video-gpu-encode.c index 808b55c3c..bec537d2c 100644 --- a/libobs/obs-video-gpu-encode.c +++ b/libobs/obs-video-gpu-encode.c @@ -58,9 +58,10 @@ static void *gpu_encode_thread(void *unused) video_output_inc_texture_frames(video->video); for (size_t i = 0; i < video->gpu_encoders.num; i++) { - obs_encoder_t *encoder = video->gpu_encoders.array[i]; - da_push_back(encoders, &encoder); - obs_encoder_addref(encoder); + obs_encoder_t *encoder = obs_encoder_get_ref( + video->gpu_encoders.array[i]); + if (encoder) + da_push_back(encoders, &encoder); } pthread_mutex_unlock(&video->gpu_encoder_mutex); diff --git a/plugins/linux-capture/xcompcap-main.cpp b/plugins/linux-capture/xcompcap-main.cpp index a705c5672..9d3b15333 100644 --- a/plugins/linux-capture/xcompcap-main.cpp +++ b/plugins/linux-capture/xcompcap-main.cpp @@ -664,7 +664,8 @@ void XCompcapMain::render(gs_effect_t *effect) effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); while (gs_effect_loop(effect, "Draw")) { - xcursor_render(p->cursor); + xcursor_render(p->cursor, -p->cur_cut_left, + -p->cur_cut_top); } } } diff --git a/plugins/linux-capture/xcursor.c b/plugins/linux-capture/xcursor.c index 85bb73a4e..021fd4244 100644 --- a/plugins/linux-capture/xcursor.c +++ b/plugins/linux-capture/xcursor.c @@ -103,7 +103,7 @@ void xcursor_tick(xcursor_t *data) XFree(xc); } -void xcursor_render(xcursor_t *data) +void xcursor_render(xcursor_t *data, int x_offset, int y_offset) { if (!data->tex) return; @@ -117,7 +117,8 @@ void xcursor_render(xcursor_t *data) gs_enable_color(true, true, true, false); gs_matrix_push(); - gs_matrix_translate3f(data->render_x, data->render_y, 0.0f); + gs_matrix_translate3f(data->render_x + x_offset, + data->render_y + y_offset, 0.0f); gs_draw_sprite(data->tex, 0, 0, 0); gs_matrix_pop(); diff --git a/plugins/linux-capture/xcursor.h b/plugins/linux-capture/xcursor.h index 663156146..3c03a7b5d 100644 --- a/plugins/linux-capture/xcursor.h +++ b/plugins/linux-capture/xcursor.h @@ -61,7 +61,7 @@ void xcursor_tick(xcursor_t *data); * * This needs to be executed within a valid render context */ -void xcursor_render(xcursor_t *data); +void xcursor_render(xcursor_t *data, int x_offset, int y_offset); /** * Specify offset for the cursor diff --git a/plugins/linux-capture/xhelpers.c b/plugins/linux-capture/xhelpers.c index d73d7da94..a6e88842a 100644 --- a/plugins/linux-capture/xhelpers.c +++ b/plugins/linux-capture/xhelpers.c @@ -108,7 +108,6 @@ int randr_screen_count(xcb_connection_t *xcb) { if (!xcb) return 0; - xcb_screen_t *screen; screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data; diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index c7db74d27..1ff3ebba6 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -324,7 +324,9 @@ static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p, ")", i, w, h, x, y); - obs_property_list_add_int(screens, screen_info.array, i); + if (h > 0 && w > 0) + obs_property_list_add_int(screens, screen_info.array, + i); } /* handle missing screen */ diff --git a/plugins/obs-browser b/plugins/obs-browser index 2ab0e1415..7e04f7f29 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit 2ab0e141512f0235f71347c67ccf80acffa9e69f +Subproject commit 7e04f7f29a26a3695f154d5c1a0f60cc1e7f1d8f diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 834090f68..ab288c022 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -183,7 +183,8 @@ static bool parse_params(AVCodecContext *context, char **opts) *assign = 0; value = assign + 1; - if (av_opt_set(context->priv_data, name, value, 0)) { + if (av_opt_set(context, name, value, + AV_OPT_SEARCH_CHILDREN)) { blog(LOG_WARNING, "Failed to set %s=%s", name, value); ret = false; diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 810ae7f72..d528758fa 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -1342,7 +1342,7 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes) return; } - if (buffer_duration_usec >= DBR_TRIGGER_USEC) { + if ((uint64_t)buffer_duration_usec >= DBR_TRIGGER_USEC) { pthread_mutex_lock(&stream->dbr_mutex); bitrate_changed = dbr_bitrate_lowered(stream); pthread_mutex_unlock(&stream->dbr_mutex); diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index 07e725076..a799f00ce 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -1,15 +1,21 @@ project(rtmp-services) +find_package(Libcurl REQUIRED) + +include_directories(${LIBCURL_INCLUDE_DIRS}) + include_directories(${OBS_JANSSON_INCLUDE_DIRS}) set(rtmp-services_SOURCES twitch.c + younow.c rtmp-common.c rtmp-custom.c rtmp-services-main.c) set(rtmp-services_HEADERS twitch.h + younow.h rtmp-format-ver.h) set(RTMP_SERVICES_URL @@ -28,10 +34,12 @@ add_library(rtmp-services MODULE ${rtmp-services_SOURCES} ${rtmp-services_HEADERS} ${rtmp-services_config_HEADERS}) + target_link_libraries(rtmp-services libobs file-updater - ${OBS_JANSSON_IMPORT}) + ${OBS_JANSSON_IMPORT} + ${LIBCURL_LIBRARIES}) target_include_directories(rtmp-services PUBLIC diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json old mode 100644 new mode 100755 index 56470f7a4..efdf49690 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 112, + "version": 116, "files": [ { "name": "services.json", - "version": 112 + "version": 116 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json old mode 100644 new mode 100755 index 17587f9d0..f3bbee312 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -573,7 +573,7 @@ "recommended": { "keyint": 2, "profile": "main", - "max video bitrate": 4000, + "max video bitrate": 6000, "max audio bitrate": 128 } }, @@ -1465,6 +1465,20 @@ "max audio bitrate": 192 } }, + { + "name": "ChathostessModels", + "servers": [ + { + "name": "ChathostessModels - Default", + "url": "rtmp://wowza01.foobarweb.com/cmschatsys_video" + } + ], + "recommended": { + "keyint": 2, + "max video bitrate": 3000, + "max audio bitrate": 128 + } + }, { "name": "Camplace", "servers": [ @@ -1500,6 +1514,24 @@ "x264opts": "tune=zerolatency" } }, + { + "name": "YouNow", + "common": false, + "servers": [ + { + "name": "younow.com", + "url": "https://signaling-api.younow-prod.video.propsproject.com/api/v1/ingest/server/" + } + ], + "recommended": { + "keyint": 2, + "output": "ftl_output", + "max audio bitrate": 160, + "max video bitrate": 7000, + "profile": "main", + "bframes": 0 + } + }, { "name": "Steam", "common": false, @@ -1559,6 +1591,23 @@ "max video bitrate": 7000, "max audio bitrate": 128 } + }, + { + "name": "Stars.AVN.com", + "servers": [ + { + "name": "Default", + "url": "rtmp://alpha.gateway.stars.avn.com/live" + } + ], + "recommended": { + "keyint": 2, + "profile": "main", + "max video bitrate": 2500, + "max audio bitrate": 192, + "bframes": 0, + "x264opts": "tune=zerolatency" + } } ] } diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index bf0b17891..5734716d3 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -5,6 +5,7 @@ #include "rtmp-format-ver.h" #include "twitch.h" +#include "younow.h" struct rtmp_common { char *service; @@ -485,7 +486,9 @@ static void apply_video_encoder_settings(obs_data_t *settings, obs_data_set_string(settings, "rate_control", "CBR"); item = json_object_get(recommended, "profile"); - if (json_is_string(item)) { + obs_data_item_t *enc_item = obs_data_item_byname(settings, "profile"); + if (json_is_string(item) && + obs_data_item_gettype(enc_item) == OBS_DATA_STRING) { const char *profile = json_string_value(item); obs_data_set_string(settings, "profile", profile); } @@ -594,6 +597,12 @@ static const char *rtmp_common_url(void *data) } } + if (service->service && strcmp(service->service, "YouNow") == 0) { + if (service->server && service->key) { + return younow_get_ingest(service->server, service->key); + } + } + return service->server; } diff --git a/plugins/rtmp-services/younow.c b/plugins/rtmp-services/younow.c new file mode 100644 index 000000000..253660f07 --- /dev/null +++ b/plugins/rtmp-services/younow.c @@ -0,0 +1,113 @@ +#include +#include +#include + +#include +#include "util/base.h" +#include "younow.h" + +struct younow_mem_struct { + char *memory; + size_t size; +}; + +static char *current_ingest = NULL; + +static size_t younow_write_cb(void *contents, size_t size, size_t nmemb, + void *userp) +{ + size_t realsize = size * nmemb; + struct younow_mem_struct *mem = (struct younow_mem_struct *)userp; + + mem->memory = realloc(mem->memory, mem->size + realsize + 1); + if (mem->memory == NULL) { + blog(LOG_WARNING, "yyounow_write_cb: realloc returned NULL"); + return 0; + } + + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +const char *younow_get_ingest(const char *server, const char *key) +{ + CURL *curl_handle; + CURLcode res; + struct younow_mem_struct chunk; + struct dstr uri; + long response_code; + + // find the delimiter in stream key + const char *delim = strchr(key, '_'); + if (delim == NULL) { + blog(LOG_WARNING, + "younow_get_ingest: delimiter not found in stream key"); + return server; + } + + curl_handle = curl_easy_init(); + + chunk.memory = malloc(1); /* will be grown as needed by realloc */ + chunk.size = 0; /* no data at this point */ + + dstr_init(&uri); + dstr_copy(&uri, server); + dstr_ncat(&uri, key, delim - key); + + curl_easy_setopt(curl_handle, CURLOPT_URL, uri.array); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, true); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L); + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 3L); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, younow_write_cb); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + +#if LIBCURL_VERSION_NUM >= 0x072400 + // A lot of servers don't yet support ALPN + curl_easy_setopt(curl_handle, CURLOPT_SSL_ENABLE_ALPN, 0); +#endif + + res = curl_easy_perform(curl_handle); + dstr_free(&uri); + + if (res != CURLE_OK) { + blog(LOG_WARNING, + "younow_get_ingest: curl_easy_perform() failed: %s", + curl_easy_strerror(res)); + curl_easy_cleanup(curl_handle); + free(chunk.memory); + return server; + } + + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code); + if (response_code != 200) { + blog(LOG_WARNING, + "younow_get_ingest: curl_easy_perform() returned code: %ld", + response_code); + curl_easy_cleanup(curl_handle); + free(chunk.memory); + return server; + } + + curl_easy_cleanup(curl_handle); + + if (chunk.size == 0) { + blog(LOG_WARNING, + "younow_get_ingest: curl_easy_perform() returned empty response"); + free(chunk.memory); + return server; + } + + if (current_ingest) { + free(current_ingest); + current_ingest = NULL; + } + + current_ingest = strdup(chunk.memory); + free(chunk.memory); + blog(LOG_INFO, "younow_get_ingest: returning ingest: %s", + current_ingest); + return current_ingest; +} diff --git a/plugins/rtmp-services/younow.h b/plugins/rtmp-services/younow.h new file mode 100644 index 000000000..427ebfe30 --- /dev/null +++ b/plugins/rtmp-services/younow.h @@ -0,0 +1,3 @@ +#pragma once + +extern const char *younow_get_ingest(const char *server, const char *key); \ No newline at end of file diff --git a/plugins/win-capture/window-helpers.c b/plugins/win-capture/window-helpers.c index 0044226f1..8f19ee48b 100644 --- a/plugins/win-capture/window-helpers.c +++ b/plugins/win-capture/window-helpers.c @@ -128,12 +128,23 @@ void get_window_class(struct dstr *class, HWND hwnd) /* not capturable or internal windows */ static const char *internal_microsoft_exes[] = { + "startmenuexperiencehost", "applicationframehost", + "peopleexperiencehost", "shellexperiencehost", + "microsoft.notes", "windowsinternal", + "systemsettings", + "textinputhost", "winstore.app", + "searchapp", + "video.ui", "searchui", "lockapp", + "cortana", + "gamebar", + "tabtip", + "time", NULL, }; diff --git a/plugins/win-dshow/win-dshow.cpp b/plugins/win-dshow/win-dshow.cpp index 625093bd0..76418fedb 100644 --- a/plugins/win-dshow/win-dshow.cpp +++ b/plugins/win-dshow/win-dshow.cpp @@ -468,6 +468,12 @@ static inline enum speaker_layout convert_speaker_layout(uint8_t channels) void DShowInput::OnEncodedVideoData(enum AVCodecID id, unsigned char *data, size_t size, long long ts) { + /* If format changes, free and allow it to recreate the decoder */ + if (ffmpeg_decode_valid(video_decoder) && + video_decoder->codec->id != id) { + ffmpeg_decode_free(video_decoder); + } + if (!ffmpeg_decode_valid(video_decoder)) { /* Only use MJPEG hardware decoding on resolutions higher * than 1920x1080. The reason why is because we want to strike diff --git a/plugins/win-wasapi/enum-wasapi.hpp b/plugins/win-wasapi/enum-wasapi.hpp index cbaa14ff5..6f42be2f7 100644 --- a/plugins/win-wasapi/enum-wasapi.hpp +++ b/plugins/win-wasapi/enum-wasapi.hpp @@ -2,6 +2,7 @@ #define WIN32_MEAN_AND_LEAN #include +#include #include #include #include diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 215946e96..68432b08c 100644 --- a/plugins/win-wasapi/win-wasapi.cpp +++ b/plugins/win-wasapi/win-wasapi.cpp @@ -28,6 +28,7 @@ class WASAPISource { obs_source_t *source; string device_id; string device_name; + string device_sample = "-"; bool isInputDevice; bool useDeviceTiming = false; bool isDefaultDevice = false; @@ -289,7 +290,8 @@ void WASAPISource::InitCapture() client->Start(); active = true; - blog(LOG_INFO, "WASAPI: Device '%s' initialized", device_name.c_str()); + blog(LOG_INFO, "WASAPI: Device '%s' [%s Hz] initialized", + device_name.c_str(), device_sample.c_str()); } void WASAPISource::Initialize() @@ -308,6 +310,22 @@ void WASAPISource::Initialize() device_name = GetDeviceName(device); + HRESULT resSample; + IPropertyStore *store = nullptr; + PWAVEFORMATEX deviceFormatProperties; + PROPVARIANT prop; + resSample = device->OpenPropertyStore(STGM_READ, &store); + if (!FAILED(resSample)) { + resSample = + store->GetValue(PKEY_AudioEngine_DeviceFormat, &prop); + if (!FAILED(resSample)) { + deviceFormatProperties = + (PWAVEFORMATEX)prop.blob.pBlobData; + device_sample = std::to_string( + deviceFormatProperties->nSamplesPerSec); + } + } + InitClient(); if (!isInputDevice) InitRender();