diff --git a/.gitignore b/.gitignore
index f78f47a..c88721b 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 4bb6188..7c092c4 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,44 @@ 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/
+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
-# 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 +85,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 391c181..ca8419b 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 c37d588..ad398e4 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 0000000..7eed962
--- /dev/null
+++ b/CI/install/osx/Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ CFBundleIconFile
+ obs.icns
+ CFBundleName
+ OBS
+ CFBundleGetInfoString
+ OBS - Free and Open Source Streaming/Recording Software
+ CFBundleExecutable
+ OBS
+ CFBundleIdentifier
+ com.obsproject.obs-studio
+ CFBundlePackageType
+ APPL
+ CFBundleSignature
+ ????
+ 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 0000000..02c4d55
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 0000000..4ef96a4
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 0000000..b28ab1e
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 0000000..1bd46a7
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 0000000..3036891
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 0000000..52d94b9
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 0000000..6054a58
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 0000000..4548740
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 0000000..a001edf
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 0000000..af2c798
--- /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 0000000..a28f209
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 0000000..5d7f309
--- /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 0000000..6f878d6
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 0000000..e3175b9
--- /dev/null
+++ b/CI/install/osx/packageApp.sh
@@ -0,0 +1,67 @@
+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 +w ./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 0000000..34fbc2f
--- /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 a33f9e1..51b771e 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 3cd8b79..ce46c60 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 a10e482..37d9297 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 57d507e..fd3c307 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/platform-osx.mm b/UI/platform-osx.mm
index bc400f5..3d0c533 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 313a2e1..7c22872 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 a72768c..02eff19 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 8b37b4d..147aafa 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 6ee726b..ede9733 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 cceef02..e0ea3d1 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 f095084..ae47eb2 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 4d10e72..d62f312 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)
@@ -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 f6ff2d5..e90d172 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 6dd74ab..afdb373 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 627aadf..cb63ba6 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 35554eb..e819840 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 92fb1b9..340eb30 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 a626555..ebf6a1f 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 5a60681..c8f74ef 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 d02ac80..8899df8 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 57b65bd..0973273 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 58c63d0..6b8d703 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 db7dc3d..58cb6d9 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 3e4d77f..4377128 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 8681148..0eabf60 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 94%
rename from libobs/obs-cocoa.c
rename to libobs/obs-cocoa.m
index 30d0964..7f261d0 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;
diff --git a/libobs/obs-config.h b/libobs/obs-config.h
index 644c2b6..acc5d73 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 5
#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 808b55c..bec537d 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 a705c56..9d3b153 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 85bb73a..021fd42 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 6631561..3c03a7b 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 d73d7da..a6e8884 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 c7db74d..1ff3ebb 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-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c
index 834090f..ab288c0 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 810ae7f..d528758 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 07e7250..a799f00 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 56470f7..a297b2d
--- 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": 114,
"files": [
{
"name": "services.json",
- "version": 112
+ "version": 114
}
]
}
diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json
old mode 100644
new mode 100755
index 17587f9..724e6e2
--- a/plugins/rtmp-services/data/services.json
+++ b/plugins/rtmp-services/data/services.json
@@ -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 bf0b178..5734716 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 0000000..253660f
--- /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 0000000..427ebfe
--- /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