Compare commits

...
Sign in to create a new pull request.

47 commits

Author SHA1 Message Date
jp9000
6594d0fb15 libobs: Update version to 24.0.6 2019-12-17 12:16:31 -08:00
SCG82
b96cc828dc CI: Fix QtNetwork in Mac packaging script 2019-12-16 11:58:57 -08:00
VodBox
f782acb66d UI: Ensure OBS launches when theme is missing
This change makes it so OBS will still launch even if a theme is
missing. This change also deprecates the "CurrentTheme" config key, and
is replaced by "CurrentTheme2".

This is because a previous change to make sure OBS fell back to the
System theme in case of a missing theme had been accidentally removed.
Changing the key prevents a new version of OBS setting a theme that
doesn't exist in an older version, which would prevent that version from
launching.
2019-12-16 11:55:43 -08:00
SCG82
fbf494ad67 CI: Fix mbedtls id in mac deploy script 2019-12-16 11:55:42 -08:00
SCG82
eaa6a67dbc CI: Fix error deleting QT network framework on OSX 2019-12-16 11:55:42 -08:00
Colin Edwards
fbacd74cd1 libobs: Null check hotkey device on macos 2019-12-16 11:55:42 -08:00
JohannMG
a19fc7227c rtmp-services: Increase FB max bitrate to 6Mbps
Facebook Live now recommends 6000kbps for most streamers and the auto
capping is clamping users to 4000kpbs. This fixes the new suggested
setting.
2019-12-16 11:55:37 -08:00
Colin Edwards
4ac1ffcc9b CI: Fix up QT network framework on OSX 2019-12-16 11:55:05 -08:00
Colin Edwards
15585748d5 CI: Fix osx executable path in plist 2019-12-16 11:55:05 -08:00
Colin Edwards
a32f889397 CI: Fix framework symlinks when building osx app 2019-12-16 11:55:05 -08:00
jp9000
226f54cf06 obs-browser: Disable system flash 2019-12-13 07:35:17 -08:00
jp9000
34af51d095 UI: Fix invalid escape sequences in regex string 2019-12-13 07:32:38 -08:00
jp9000
99638ba697 libobs: Update version to 24.0.5 2019-12-10 00:06:04 -08:00
Colin Edwards
e4bfd03944 CI: Don't run clang format on some submodule plugins 2019-12-10 00:05:08 -08:00
Jim
22bddd0270 Merge pull request #2010 from wanhongqing123/master
win-dshow: fix H264 to mjpeg issue
2019-12-06 03:02:02 -08:00
Jim
3ff3330f65 Merge pull request #2042 from WizardCM/custom-twitch-docks
UI: Add support for Custom (Legacy) Twitch Dashboard Docks
2019-12-06 03:02:02 -08:00
Jim
e99b542b72 Merge pull request #2183 from ratwithacompiler/macos-python-fix-2
obs-scripting: Fix Python in new MacOS .app bundles
2019-12-06 03:02:02 -08:00
Jim
93411e8fc5 Merge pull request #2085 from ratwithacompiler/macos-python-fix
obs-scripting: Fix Python on MacOS
2019-12-06 03:02:01 -08:00
Jim
73cad40a8e Merge pull request #2090 from jpark37/dxgi-refresh-rate
Log display refresh rates, and monitor names on D3D11
2019-12-06 03:02:01 -08:00
Jim
dcf17808c6 Merge pull request #2089 from jpark37/dxgi-driver-version
libobs-d3d11: Add GPU driver version to log
2019-12-06 03:02:01 -08:00
jp9000
8614324cbc UI: Delete cookies before connecting account
Fixes an issue where when trying to connect a Twitch account, and the
Twitch two-factor authentication check fails, the user will still
technically be logged in because the cookies will still be present, thus
when they try to click the "Connect" button again, it will skip the
login part (because they're already technically logged in), and just
give them the two-factor failure message without letting them enter in
their login information again.
2019-12-06 03:01:47 -08:00
jp9000
ece731accc UI: Fix bug in untested/unused function code path
In the current user interface code, OBSBasic::AddSceneCollection has a
qname parameter to allow explicitly specifying a name, but that code
path is unused in the UI code itself, and qname is typically empty.  If
qname is not empty, it does not properly generate a file name associated
with that specified scene collection name.  This fixes that issue.
2019-12-06 03:01:47 -08:00
jp9000
772f80d860 deps/media-playback: Don't use interrupt cb for local files
This fixes an issue where local files were using the avformat interrupt
callback, which is only supposed to be use to prevent blocking on
network calls.
2019-12-06 03:01:47 -08:00
jp9000
d7b00c095f deps/media-playback: Don't exit thread on AVERROR_EXIT
The interrupt callback is designed to prevent the media source from
blocking; FFmpeg will internally call it periodically to prevent FFmpeg
function calls from blocking too long, and allow the caller to determine
whether blocking should stop.

The problem with this however is that AVERROR_EXIT causes the thread to
completely exit.  This fixes it so that it treats it as an EOF rather
than as an abnormal error.
2019-12-06 03:01:47 -08:00
jp9000
576b7981ac deps/obs-scripting: Fix formatting 2019-12-06 03:01:47 -08:00
Jim
cc3c70ba00 Merge pull request #2152 from Rosuav/fix-cursor-position
libobs: Fix cursor draw position bug when cropping a window capture.
2019-12-06 03:01:36 -08:00
jp9000
b7fe32e021 UI: Fix Twitch panels not using dark first time
When the panels start up for the very first time, the dark theme is not
used due to the local storage variable not being set by the first panel.
When the program is restarted, it'll be dark from then on out because
it's set by the secondary panels rather than the primary panel.  This
modifies it so that the locale storage variable is properly set by the
primary panel.
2019-12-06 03:01:29 -08:00
Jim
743ac0f4cb Merge pull request #1914 from YouNow/master
rtmp-services: Add YouNow service and implement ingest lookup
2019-12-06 03:01:22 -08:00
Jim
e89af8ff0a Merge pull request #2140 from DevWolk/avn-obs
rtmp-services: Add Stars.AVN.com streaming service
2019-12-06 03:01:22 -08:00
Jim
6dc81d9439 Merge pull request #2045 from wolf247/master
rtmp-services: Add ChathostessModels
2019-12-06 03:01:22 -08:00
Jim
d920bfcdbb Merge pull request #2179 from WizardCM/remove-help-interact
UI: Remove help icon from Interact titlebars
2019-12-06 03:01:22 -08:00
Jim
3136f69ef1 Merge pull request #2125 from DDRBoxman/appbundle
Fixes to make OBS work on Catalina
2019-12-06 03:01:21 -08:00
Jim
19d140b50a Merge pull request #2168 from kkartaltepe/vaapi-profile-fix
rtmp-services: Type check apply_encoder_settings
2019-12-06 03:01:21 -08:00
Jim
01782d3365 Merge pull request #2148 from eulertour/master
UI: Fall back to XGetWMName if XFetchName fails
2019-12-06 03:01:21 -08:00
Jim
796c6ef3bc Merge pull request #2146 from Fenrirthviti/recording-bitrate-fix
UI: Fix audio bitrate not being set in simple mode
2019-12-06 03:01:21 -08:00
jp9000
f0fa1e1967 libobs: Fix race condition
It is possible for a GPU encoder to be in a destruction state while
still in the GPU encoder list, although very rare.
2019-12-06 03:01:13 -08:00
Jim
e334d1daed Merge pull request #2147 from JohannMG/vscode-ignore
git: Add VSCode working dir to .gitignore
2019-12-06 03:01:05 -08:00
Jim
49f9d05af9 Merge pull request #2134 from WizardCM/wasapi-samplerate
Log Sample Rate for WASAPI devices
2019-12-06 03:01:05 -08:00
Jim
6b9302db8f Merge pull request #2129 from Fenrirthviti/win-blacklist-update
win-capture: Add additional exe to window capture blacklist
2019-12-06 03:01:05 -08:00
Jim
8c5d22e12c Merge pull request #2131 from jpark37/input-layout-error
libobs-d3d11: Fix null input layout rebuild
2019-12-06 03:01:05 -08:00
Jim
50fc8acbf5 Merge pull request #2128 from Xaymar/return-to-break
UI: Fix checkable property groups
2019-12-06 03:01:05 -08:00
Jim
596019e682 Merge pull request #2121 from cg2121/fix-warning
rtmp-stream: Fix comparison between signed and unsigned ints
2019-12-06 03:01:05 -08:00
Jim
4182bd5bd7 Merge pull request #2110 from derrod/ffmpeg-output-fix
obs-ffmpeg: Use av_opt_set on context instead of priv_data
2019-12-06 03:01:05 -08:00
Jim
63c45cb18c Merge pull request #2106 from cg2121/fix-preview-bug
UI: Fix issue with preview projector
2019-12-06 03:01:05 -08:00
Joel Bethke
b2f35ca1fe Merge pull request #2126 from Fenrirthviti/linux-ci-fix
CI: Remove dead PPA to fix Linux build failure
2019-12-06 03:01:04 -08:00
Jim
26312486cf Merge pull request #2091 from Programatic/xshm_wrong_windows
linux-capture: Fix bug where multiple 0x0 windows would appear
2019-12-06 03:01:04 -08:00
Colin Edwards
f4fd376e09 Merge pull request #2120 from jpark37/objc-msgsend
libobs: Strict objc_msgSend support
2019-12-06 03:01:04 -08:00
67 changed files with 777 additions and 143 deletions

1
.gitignore vendored
View file

@ -37,6 +37,7 @@ ipch/
GeneratedFiles/ GeneratedFiles/
.moc/ .moc/
/UI/obs.rc /UI/obs.rc
.vscode/
/other/ /other/

View file

@ -8,9 +8,12 @@ hr() {
set -e set -e
# Generate file name variables # Generate file name variables
export GIT_TAG=$(git describe --abbrev=0)
export GIT_HASH=$(git rev-parse --short HEAD) export GIT_HASH=$(git rev-parse --short HEAD)
export FILE_DATE=$(date +%Y-%m-%d.%H-%M-%S) 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 cd ./build
@ -19,9 +22,9 @@ hr "Moving OBS LUA"
mv ./rundir/RelWithDebInfo/data/obs-scripting/obslua.so ./rundir/RelWithDebInfo/bin/ mv ./rundir/RelWithDebInfo/data/obs-scripting/obslua.so ./rundir/RelWithDebInfo/bin/
# Move obspython # Move obspython
# hr "Moving OBS Python" hr "Moving OBS Python"
# mv ./rundir/RelWithDebInfo/data/obs-scripting/_obspython.so ./rundir/RelWithDebInfo/bin/ mv ./rundir/RelWithDebInfo/data/obs-scripting/_obspython.so ./rundir/RelWithDebInfo/bin/
# mv ./rundir/RelWithDebInfo/data/obs-scripting/obspython.py ./rundir/RelWithDebInfo/bin/ mv ./rundir/RelWithDebInfo/data/obs-scripting/obspython.py ./rundir/RelWithDebInfo/bin/
# Package everything into a nice .app # Package everything into a nice .app
hr "Packaging .app" hr "Packaging .app"
@ -30,32 +33,48 @@ if [ -n "${TRAVIS_TAG}" ]; then
STABLE=true STABLE=true
fi 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 # Copy Chromium embedded framework to app Frameworks directory
hr "Copying Chromium Embedded Framework.framework" hr "Copying Chromium Embedded Framework.framework"
sudo mkdir -p OBS.app/Contents/Frameworks 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 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 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
hr "Generating .pkg" 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
packagesbuild ../CI/install/osx/CMakeLists.pkgproj 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 if [ -v "$TRAVIS" ]; then
# Signing stuff # Signing stuff
@ -70,12 +89,10 @@ if [ -v "$TRAVIS" ]; then
security import ./Certificates.p12 -k build.keychain -T /usr/bin/productsign -P "" security import ./Certificates.p12 -k build.keychain -T /usr/bin/productsign -P ""
# macOS 10.12+ # macOS 10.12+
security set-key-partition-list -S apple-tool:,apple: -s -k mysecretpassword build.keychain 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 fi
cp ./OBS.dmg ./$FILENAME
# Move to the folder that travis uses to upload artifacts from # Move to the folder that travis uses to upload artifacts from
hr "Moving package to nightly folder for distribution" hr "Moving package to nightly folder for distribution"
mkdir ../nightly mkdir ../nightly

View file

@ -1,7 +1,6 @@
#!/bin/sh #!/bin/sh
set -ex set -ex
sudo add-apt-repository ppa:jonathonf/ffmpeg-3 -y
curl -L https://packagecloud.io/github/git-lfs/gpgkey | sudo apt-key add - curl -L https://packagecloud.io/github/git-lfs/gpgkey | sudo apt-key add -
# gets us newer clang # gets us newer clang

View file

@ -16,6 +16,8 @@ else
/bin/bash -c "sudo xcode-select -s /Applications/Xcode_9.4.1.app/Contents/Developer" /bin/bash -c "sudo xcode-select -s /Applications/Xcode_9.4.1.app/Contents/Developer"
fi fi
git fetch origin --tags
# Leave obs-studio folder # Leave obs-studio folder
cd ../ cd ../
@ -28,10 +30,12 @@ sudo installer -pkg ./Packages.pkg -target /
brew update brew update
#Base OBS Deps and ccache #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/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb
brew install https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb brew install https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb
pip install dmgbuild
export PATH=/usr/local/opt/ccache/libexec:$PATH export PATH=/usr/local/opt/ccache/libexec:$PATH
ccache -s || echo "CCache is not available." ccache -s || echo "CCache is not available."

28
CI/install/osx/Info.plist Normal file
View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIconFile</key>
<string>obs.icns</string>
<key>CFBundleName</key>
<string>OBS</string>
<key>CFBundleGetInfoString</key>
<string>OBS - Free and Open Source Streaming/Recording Software</string>
<key>CFBundleExecutable</key>
<string>obs</string>
<key>CFBundleIdentifier</key>
<string>com.obsproject.obs-studio</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSMinimumSystemVersion</key>
<string>10.8.5</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSAppNapIsDisabled</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>OBS needs to access the camera to enable camera sources to work.</string>
<key>NSMicrophoneUsageDescription</key>
<string>OBS needs to access the microphone to enable audio input.</string>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

1
CI/install/osx/buildDMG Executable file
View file

@ -0,0 +1 @@
dmgbuild -s ./settings.json "OBS" obs.dmg

BIN
CI/install/osx/dylibBundler Executable file

Binary file not shown.

1
CI/install/osx/makeRetinaBG Executable file
View file

@ -0,0 +1 @@
tiffutil -cathidpicheck background.png background@2x.png -out background.tiff

BIN
CI/install/osx/obs.icns Normal file

Binary file not shown.

73
CI/install/osx/packageApp.sh Executable file
View file

@ -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

View file

@ -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" }
]
}

View file

@ -373,14 +373,6 @@ if (APPLE)
target_link_libraries(obs target_link_libraries(obs
Qt5::MacExtras) Qt5::MacExtras)
set_target_properties(obs PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000") 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() endif()
define_graphic_modules(obs) define_graphic_modules(obs)

View file

@ -216,7 +216,8 @@ void TwitchAuth::LoadUI()
chat->SetWidget(browser); chat->SetWidget(browser);
cef->add_force_popup_url(moderation_tools_url, chat.data()); 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; script += ffz_script;
browser->setStartupScript(script); browser->setStartupScript(script);

View file

@ -3685,19 +3685,19 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="sampleRate"> <widget class="QComboBox" name="sampleRate">
<property name="currentText"> <property name="currentText">
<string notr="true">44.1khz</string> <string notr="true">44.1 kHz</string>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<property name="text"> <property name="text">
<string>44.1khz</string> <string>44.1 kHz</string>
</property> </property>
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>48khz</string> <string>48 kHz</string>
</property> </property>
</item> </item>
</widget> </widget>

View file

@ -126,10 +126,17 @@ static std::string GetWindowTitle(size_t i)
if (status >= Success && name != nullptr) { if (status >= Success && name != nullptr) {
std::string str(name); std::string str(name);
windowTitle = str; 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; return windowTitle;
} }

View file

@ -1059,21 +1059,27 @@ bool OBSApp::InitTheme()
defaultPalette = palette(); defaultPalette = palette();
const char *themeName = 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 */ /* Use deprecated "Theme" value if available */
themeName = config_get_string(globalConfig, "General", "Theme"); themeName = config_get_string(globalConfig, "General", "Theme");
if (!themeName) if (!themeName)
themeName = DEFAULT_THEME; themeName = DEFAULT_THEME;
if (!themeName) if (!themeName)
themeName = "Dark"; themeName = "Dark";
}
if (strcmp(themeName, "Default") == 0) if (strcmp(themeName, "Default") == 0)
themeName = "System"; 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) OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)

View file

@ -28,11 +28,29 @@
using namespace std; using namespace std;
bool isInBundle()
{
NSRunningApplication *app = [NSRunningApplication currentApplication];
return [app bundleIdentifier] != nil;
}
bool GetDataFilePath(const char *data, string &output) bool GetDataFilePath(const char *data, string &output)
{ {
stringstream str; if (isInBundle()) {
str << OBS_DATA_PATH "/obs-studio/" << data; NSRunningApplication *app =
output = str.str(); [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); return !access(output.c_str(), R_OK);
} }

View file

@ -1820,7 +1820,7 @@ void WidgetInfo::ControlChanged()
break; break;
case OBS_PROPERTY_GROUP: case OBS_PROPERTY_GROUP:
GroupChanged(setting); GroupChanged(setting);
return; break;
} }
if (view->callback && !view->deferUpdate) if (view->callback && !view->deferUpdate)

View file

@ -417,6 +417,8 @@ void AutoConfigStreamPage::on_connectAccount_clicked()
#ifdef BROWSER_AVAILABLE #ifdef BROWSER_AVAILABLE
std::string service = QT_TO_UTF8(ui->service->currentText()); std::string service = QT_TO_UTF8(ui->service->currentText());
OAuth::DeleteCookies(service);
auth = OAuthStreamKey::Login(this, service); auth = OAuthStreamKey::Login(this, service);
if (!!auth) if (!!auth)
OnAuthConnected(); OnAuthConnected();

View file

@ -59,6 +59,10 @@ OBSBasicInteraction::OBSBasicInteraction(QWidget *parent, OBSSource source_)
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
setWindowTitle(QTStr("Basic.InteractionWindow").arg(QT_UTF8(name))); setWindowTitle(QTStr("Basic.InteractionWindow").arg(QT_UTF8(name)));
Qt::WindowFlags flags = windowFlags();
Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint;
setWindowFlags(flags & (~helpFlag));
auto addDrawCallback = [this]() { auto addDrawCallback = [this]() {
obs_display_add_draw_callback(ui->preview->GetDisplay(), obs_display_add_draw_callback(ui->preview->GetDisplay(),
OBSBasicInteraction::DrawPreview, OBSBasicInteraction::DrawPreview,

View file

@ -643,6 +643,7 @@ void SimpleOutput::UpdateRecordingSettings()
} else if (videoEncoder == SIMPLE_ENCODER_NVENC) { } else if (videoEncoder == SIMPLE_ENCODER_NVENC) {
UpdateRecordingSettings_nvenc(crf); UpdateRecordingSettings_nvenc(crf);
} }
UpdateRecordingAudioSettings();
} }
inline void SimpleOutput::SetupOutputs() inline void SimpleOutput::SetupOutputs()

View file

@ -88,6 +88,38 @@ static bool SceneCollectionExists(const char *findName)
return found; 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, static bool GetSceneCollectionName(QWidget *parent, std::string &name,
std::string &file, std::string &file,
const char *oldName = nullptr) const char *oldName = nullptr)
@ -95,9 +127,6 @@ static bool GetSceneCollectionName(QWidget *parent, std::string &name,
bool rename = oldName != nullptr; bool rename = oldName != nullptr;
const char *title; const char *title;
const char *text; const char *text;
char path[512];
size_t len;
int ret;
if (rename) { if (rename) {
title = Str("Basic.Main.RenameSceneCollection.Title"); title = Str("Basic.Main.RenameSceneCollection.Title");
@ -128,29 +157,10 @@ static bool GetSceneCollectionName(QWidget *parent, std::string &name,
break; break;
} }
if (!GetFileSafeName(name.c_str(), file)) { if (!GetUnusedSceneCollectionFile(name, file)) {
blog(LOG_WARNING, "Failed to create safe file name for '%s'",
name.c_str());
return false; 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; return true;
} }
@ -166,6 +176,10 @@ bool OBSBasic::AddSceneCollection(bool create_new, const QString &qname)
name = QT_TO_UTF8(qname); name = QT_TO_UTF8(qname);
if (SceneCollectionExists(name.c_str())) if (SceneCollectionExists(name.c_str()))
return false; return false;
if (!GetUnusedSceneCollectionFile(name, file)) {
return false;
}
} }
SaveProjectNow(); SaveProjectNow();

View file

@ -466,6 +466,8 @@ void OBSBasicSettings::on_connectAccount_clicked()
#ifdef BROWSER_AVAILABLE #ifdef BROWSER_AVAILABLE
std::string service = QT_TO_UTF8(ui->service->currentText()); std::string service = QT_TO_UTF8(ui->service->currentText());
OAuth::DeleteCookies(service);
auth = OAuthStreamKey::Login(this, service); auth = OAuthStreamKey::Login(this, service);
if (!!auth) if (!!auth)
OnAuthConnected(); OnAuthConnected();

View file

@ -2189,9 +2189,9 @@ void OBSBasicSettings::LoadAudioSettings()
const char *str; const char *str;
if (sampleRate == 48000) if (sampleRate == 48000)
str = "48khz"; str = "48 kHz";
else else
str = "44.1khz"; str = "44.1 kHz";
int sampleRateIdx = ui->sampleRate->findText(str); int sampleRateIdx = ui->sampleRate->findText(str);
if (sampleRateIdx != -1) if (sampleRateIdx != -1)
@ -2726,7 +2726,7 @@ void OBSBasicSettings::SaveGeneralSettings()
themeData = DEFAULT_THEME; themeData = DEFAULT_THEME;
if (WidgetChanged(ui->theme)) { if (WidgetChanged(ui->theme)) {
config_set_string(GetGlobalConfig(), "General", "CurrentTheme", config_set_string(GetGlobalConfig(), "General", "CurrentTheme2",
QT_TO_UTF8(themeData)); QT_TO_UTF8(themeData));
App()->SetTheme(themeData.toUtf8().constData()); App()->SetTheme(themeData.toUtf8().constData());
@ -3253,7 +3253,7 @@ void OBSBasicSettings::SaveAudioSettings()
} }
int sampleRate = 44100; int sampleRate = 44100;
if (sampleRateStr == "48khz") if (sampleRateStr == "48 kHz")
sampleRate = 48000; sampleRate = 48000;
if (WidgetChanged(ui->sampleRate)) if (WidgetChanged(ui->sampleRate))

View file

@ -533,6 +533,24 @@ void OBSBasic::AddExtraBrowserDock(const QString &title, const QString &url,
dock->SetWidget(browser); 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); addDockWidget(Qt::RightDockWidgetArea, dock);
if (firstCreate) { if (firstCreate) {

View file

@ -653,6 +653,9 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
source = curSource; source = curSource;
window->source = source; window->source = source;
} }
} else if (window->type == ProjectorType::Preview &&
!main->IsPreviewProgramMode()) {
window->source = nullptr;
} }
if (source) if (source)

View file

@ -61,7 +61,7 @@ if(NOT UNIX_STRUCTURE)
set(OBS_INSTALL_PREFIX "") set(OBS_INSTALL_PREFIX "")
set(OBS_RELATIVE_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() else()
set(OBS_EXECUTABLE_DESTINATION "bin/${_lib_suffix}bit") set(OBS_EXECUTABLE_DESTINATION "bin/${_lib_suffix}bit")
set(OBS_EXECUTABLE32_DESTINATION "bin/32bit") set(OBS_EXECUTABLE32_DESTINATION "bin/32bit")

View file

@ -139,7 +139,7 @@ static int mp_media_next_packet(mp_media_t *media)
int ret = av_read_frame(media->fmt, &pkt); int ret = av_read_frame(media->fmt, &pkt);
if (ret < 0) { if (ret < 0) {
if (ret != AVERROR_EOF) if (ret != AVERROR_EOF && ret != AVERROR_EXIT)
blog(LOG_WARNING, "MP: av_read_frame failed: %s (%d)", blog(LOG_WARNING, "MP: av_read_frame failed: %s (%d)",
av_err2str(ret), ret); av_err2str(ret), ret);
return ret; return ret;
@ -230,7 +230,7 @@ static bool mp_media_prepare_frames(mp_media_t *m)
while (!mp_media_ready_to_start(m)) { while (!mp_media_ready_to_start(m)) {
if (!m->eof) { if (!m->eof) {
int ret = mp_media_next_packet(m); int ret = mp_media_next_packet(m);
if (ret == AVERROR_EOF) if (ret == AVERROR_EOF || ret == AVERROR_EXIT)
m->eof = true; m->eof = true;
else if (ret < 0) else if (ret < 0)
return false; return false;
@ -577,8 +577,10 @@ static bool init_avformat(mp_media_t *m)
av_dict_set_int(&opts, "buffer_size", m->buffering, 0); av_dict_set_int(&opts, "buffer_size", m->buffering, 0);
m->fmt = avformat_alloc_context(); m->fmt = avformat_alloc_context();
m->fmt->interrupt_callback.callback = interrupt_callback; if (!m->is_local_file) {
m->fmt->interrupt_callback.opaque = m; m->fmt->interrupt_callback.callback = interrupt_callback;
m->fmt->interrupt_callback.opaque = m;
}
int ret = avformat_open_input(&m->fmt, m->path, format, int ret = avformat_open_input(&m->fmt, m->path, format,
opts ? &opts : NULL); opts ? &opts : NULL);

View file

@ -12,6 +12,11 @@ if(MSVC)
w32-pthreads) w32-pthreads)
endif() endif()
if(APPLE)
set(obs-scripting_PLATFORM_DEPS
objc)
endif()
option(DISABLE_LUA "Disable Lua scripting support" OFF) option(DISABLE_LUA "Disable Lua scripting support" OFF)
option(DISABLE_PYTHON "Disable Python scripting support" OFF) option(DISABLE_PYTHON "Disable Python scripting support" OFF)

View file

@ -43,7 +43,8 @@ static const char *startup_script_template = "\
for val in pairs(package.preload) do\n\ for val in pairs(package.preload) do\n\
package.preload[val] = nil\n\ package.preload[val] = nil\n\
end\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"; require \"obslua\"\n";
static const char *get_script_path_func = "\ static const char *get_script_path_func = "\
@ -1310,7 +1311,31 @@ void obs_lua_load(void)
/* ---------------------------------------------- */ /* ---------------------------------------------- */
/* Initialize Lua startup script */ /* 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; startup_script = tmp.array;
dstr_free(&dep_paths); dstr_free(&dep_paths);

View file

@ -32,6 +32,12 @@
#define SO_EXT ".dylib" #define SO_EXT ".dylib"
#endif #endif
#ifdef __APPLE__
#define PYTHON_LIB_SUBDIR "lib/"
#else
#define PYTHON_LIB_SUBDIR ""
#endif
bool import_python(const char *python_path) bool import_python(const char *python_path)
{ {
struct dstr lib_path; struct dstr lib_path;
@ -44,7 +50,7 @@ bool import_python(const char *python_path)
dstr_init_copy(&lib_path, python_path); dstr_init_copy(&lib_path, python_path);
dstr_replace(&lib_path, "\\", "/"); dstr_replace(&lib_path, "\\", "/");
if (!dstr_is_empty(&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); dstr_cat(&lib_path, PYTHON_LIB SO_EXT);

View file

@ -1651,6 +1651,13 @@ bool obs_scripting_load_python(const char *python_path)
add_to_python_path(SCRIPT_DIR); 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"); py_obspython = PyImport_ImportModule("obspython");
bool success = !py_error(); bool success = !py_error();
if (!success) { if (!success) {

View file

@ -42,7 +42,12 @@ if(CMAKE_VERSION VERSION_GREATER 3.7.2)
else() else()
SWIG_ADD_MODULE(obspython python obspython.i ../cstrcache.cpp ../cstrcache.h) SWIG_ADD_MODULE(obspython python obspython.i ../cstrcache.cpp ../cstrcache.h)
endif() 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) function(install_plugin_bin_swig target additional_target)
if(APPLE) if(APPLE)
@ -57,14 +62,7 @@ function(install_plugin_bin_swig target additional_target)
PREFIX "") PREFIX "")
if (APPLE) if (APPLE)
set_property( SET_TARGET_PROPERTIES(${additional_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
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/"
)
endif() endif()
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/obspython.py" install(FILES "${CMAKE_CURRENT_BINARY_DIR}/obspython.py"

View file

@ -27,5 +27,14 @@ else
CLANG_FORMAT=clang-format CLANG_FORMAT=clang-format
fi 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 {} | xargs -I{} -P ${NPROC} ${CLANG_FORMAT} -i -style=file -fallback-style=none {}

View file

@ -178,10 +178,13 @@ void gs_vertex_shader::Rebuild(ID3D11Device *dev)
if (FAILED(hr)) if (FAILED(hr))
throw HRError("Failed to create vertex shader", hr); throw HRError("Failed to create vertex shader", hr);
hr = dev->CreateInputLayout(layoutData.data(), (UINT)layoutData.size(), const UINT layoutSize = (UINT)layoutData.size();
data.data(), data.size(), &layout); if (layoutSize > 0) {
if (FAILED(hr)) hr = dev->CreateInputLayout(layoutData.data(), layoutSize,
throw HRError("Failed to create input layout", hr); data.data(), data.size(), &layout);
if (FAILED(hr))
throw HRError("Failed to create input layout", hr);
}
if (constantSize) { if (constantSize) {
hr = dev->CreateBuffer(&bd, NULL, &constants); hr = dev->CreateBuffer(&bd, NULL, &constants);

View file

@ -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<DISPLAYCONFIG_PATH_INFO> paths(numPath);
std::vector<DISPLAYCONFIG_MODE_INFO> 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) static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter)
{ {
UINT i; UINT i;
@ -806,15 +853,41 @@ static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter)
if (FAILED(output->GetDesc(&desc))) if (FAILED(output->GetDesc(&desc)))
continue; 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, blog(LOG_INFO,
"\t output %u: " "\t output %u: "
"pos={%d, %d}, " "pos={%d, %d}, "
"size={%d, %d}, " "size={%d, %d}, "
"attached=%s", "attached=%s, "
"refresh=%u, "
"name=%ls",
i, rect.left, rect.top, rect.right - rect.left, i, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, 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", blog(LOG_INFO, "\t Shared VRAM: %u",
desc.SharedSystemMemory); 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); LogAdapterMonitors(adapter);
} }
} }

View file

@ -110,7 +110,7 @@ if(WIN32)
endif() endif()
elseif(APPLE) elseif(APPLE)
set(libobs_PLATFORM_SOURCES set(libobs_PLATFORM_SOURCES
obs-cocoa.c obs-cocoa.m
util/threading-posix.c util/threading-posix.c
util/pipe-posix.c util/pipe-posix.c
util/platform-nix.c util/platform-nix.c

View file

@ -29,6 +29,14 @@
#include <IOKit/hid/IOHIDDevice.h> #include <IOKit/hid/IOHIDDevice.h>
#include <IOKit/hid/IOHIDManager.h> #include <IOKit/hid/IOHIDManager.h>
#import <AppKit/AppKit.h>
bool is_in_bundle()
{
NSRunningApplication *app = [NSRunningApplication currentApplication];
return [app bundleIdentifier] != nil;
}
const char *get_module_extension(void) const char *get_module_extension(void)
{ {
return ".so"; return ".so";
@ -51,12 +59,45 @@ void add_default_module_paths(void)
{ {
for (int i = 0; i < module_patterns_size; i++) for (int i = 0; i < module_patterns_size; i++)
obs_add_module_path(module_bin[i], module_data[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) char *find_libobs_data_file(const char *file)
{ {
struct dstr path; 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); dstr_cat(&path, file);
return path.array; return path.array;
} }
@ -111,13 +152,20 @@ static void log_available_memory(void)
memory_available / 1024 / 1024); 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")); pi, sel_registerName("operatingSystem"));
id os = objc_msgSend(pi, sel_registerName("operatingSystemName")); typedef id (*os_name_func)(id, SEL);
const char *name = (const char *)objc_msgSend(os, UTF8String); 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*/) { if (os_id == 5 /*NSMACHOperatingSystem*/) {
blog(LOG_INFO, "OS Name: Mac OS X (%s)", name); 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"); 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, typedef id (*version_func)(id, SEL);
sel_registerName("operatingSystemVersionString")); version_func operatingSystemVersionString = (version_func)objc_msgSend;
const char *version = (const char *)objc_msgSend(vs, UTF8String); 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"); 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) static void log_os(void)
{ {
Class NSProcessInfo = objc_getClass("NSProcessInfo"); Class NSProcessInfo = objc_getClass("NSProcessInfo");
id pi = objc_msgSend((id)NSProcessInfo, typedef id (*func)(id, SEL);
sel_registerName("processInfo")); func processInfo = (func)objc_msgSend;
id pi = processInfo((id)NSProcessInfo, sel_registerName("processInfo"));
SEL UTF8String = sel_registerName("UTF8String"); 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"); Class NSEvent = objc_getClass("NSEvent");
SEL pressedMouseButtons = sel_registerName("pressedMouseButtons"); SEL pressedMouseButtonsSel = sel_registerName("pressedMouseButtons");
NSUInteger buttons = typedef int (*func)(id, SEL);
(NSUInteger)objc_msgSend((id)NSEvent, pressedMouseButtons); func pressedMouseButtons = (func)objc_msgSend;
NSUInteger buttons = (NSUInteger)pressedMouseButtons(
(id)NSEvent, pressedMouseButtonsSel);
*pressed = (buttons & (1 << button)) != 0; *pressed = (buttons & (1 << button)) != 0;
return true; return true;
@ -1699,6 +1754,10 @@ bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *plat,
IOHIDValueRef value = 0; IOHIDValueRef value = 0;
IOHIDDeviceRef device = IOHIDElementGetDevice(element); IOHIDDeviceRef device = IOHIDElementGetDevice(element);
if (device == NULL) {
continue;
}
if (IOHIDDeviceGetValue(device, element, &value) != if (IOHIDDeviceGetValue(device, element, &value) !=
kIOReturnSuccess) { kIOReturnSuccess) {
i += 1; i += 1;

View file

@ -41,7 +41,7 @@
* *
* Reset to zero each major or minor version * 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) \ #define MAKE_SEMANTIC_VERSION(major, minor, patch) \
((major << 24) | (minor << 16) | patch) ((major << 24) | (minor << 16) | patch)

View file

@ -58,9 +58,10 @@ static void *gpu_encode_thread(void *unused)
video_output_inc_texture_frames(video->video); video_output_inc_texture_frames(video->video);
for (size_t i = 0; i < video->gpu_encoders.num; i++) { for (size_t i = 0; i < video->gpu_encoders.num; i++) {
obs_encoder_t *encoder = video->gpu_encoders.array[i]; obs_encoder_t *encoder = obs_encoder_get_ref(
da_push_back(encoders, &encoder); video->gpu_encoders.array[i]);
obs_encoder_addref(encoder); if (encoder)
da_push_back(encoders, &encoder);
} }
pthread_mutex_unlock(&video->gpu_encoder_mutex); pthread_mutex_unlock(&video->gpu_encoder_mutex);

View file

@ -664,7 +664,8 @@ void XCompcapMain::render(gs_effect_t *effect)
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
while (gs_effect_loop(effect, "Draw")) { while (gs_effect_loop(effect, "Draw")) {
xcursor_render(p->cursor); xcursor_render(p->cursor, -p->cur_cut_left,
-p->cur_cut_top);
} }
} }
} }

View file

@ -103,7 +103,7 @@ void xcursor_tick(xcursor_t *data)
XFree(xc); XFree(xc);
} }
void xcursor_render(xcursor_t *data) void xcursor_render(xcursor_t *data, int x_offset, int y_offset)
{ {
if (!data->tex) if (!data->tex)
return; return;
@ -117,7 +117,8 @@ void xcursor_render(xcursor_t *data)
gs_enable_color(true, true, true, false); gs_enable_color(true, true, true, false);
gs_matrix_push(); 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_draw_sprite(data->tex, 0, 0, 0);
gs_matrix_pop(); gs_matrix_pop();

View file

@ -61,7 +61,7 @@ void xcursor_tick(xcursor_t *data);
* *
* This needs to be executed within a valid render context * 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 * Specify offset for the cursor

View file

@ -108,7 +108,6 @@ int randr_screen_count(xcb_connection_t *xcb)
{ {
if (!xcb) if (!xcb)
return 0; return 0;
xcb_screen_t *screen; xcb_screen_t *screen;
screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data; screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data;

View file

@ -324,7 +324,9 @@ static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p,
")", ")",
i, w, h, x, y); 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 */ /* handle missing screen */

@ -1 +1 @@
Subproject commit 2ab0e141512f0235f71347c67ccf80acffa9e69f Subproject commit 7e04f7f29a26a3695f154d5c1a0f60cc1e7f1d8f

View file

@ -183,7 +183,8 @@ static bool parse_params(AVCodecContext *context, char **opts)
*assign = 0; *assign = 0;
value = assign + 1; 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, blog(LOG_WARNING, "Failed to set %s=%s", name,
value); value);
ret = false; ret = false;

View file

@ -1342,7 +1342,7 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes)
return; return;
} }
if (buffer_duration_usec >= DBR_TRIGGER_USEC) { if ((uint64_t)buffer_duration_usec >= DBR_TRIGGER_USEC) {
pthread_mutex_lock(&stream->dbr_mutex); pthread_mutex_lock(&stream->dbr_mutex);
bitrate_changed = dbr_bitrate_lowered(stream); bitrate_changed = dbr_bitrate_lowered(stream);
pthread_mutex_unlock(&stream->dbr_mutex); pthread_mutex_unlock(&stream->dbr_mutex);

View file

@ -1,15 +1,21 @@
project(rtmp-services) project(rtmp-services)
find_package(Libcurl REQUIRED)
include_directories(${LIBCURL_INCLUDE_DIRS})
include_directories(${OBS_JANSSON_INCLUDE_DIRS}) include_directories(${OBS_JANSSON_INCLUDE_DIRS})
set(rtmp-services_SOURCES set(rtmp-services_SOURCES
twitch.c twitch.c
younow.c
rtmp-common.c rtmp-common.c
rtmp-custom.c rtmp-custom.c
rtmp-services-main.c) rtmp-services-main.c)
set(rtmp-services_HEADERS set(rtmp-services_HEADERS
twitch.h twitch.h
younow.h
rtmp-format-ver.h) rtmp-format-ver.h)
set(RTMP_SERVICES_URL set(RTMP_SERVICES_URL
@ -28,10 +34,12 @@ add_library(rtmp-services MODULE
${rtmp-services_SOURCES} ${rtmp-services_SOURCES}
${rtmp-services_HEADERS} ${rtmp-services_HEADERS}
${rtmp-services_config_HEADERS}) ${rtmp-services_config_HEADERS})
target_link_libraries(rtmp-services target_link_libraries(rtmp-services
libobs libobs
file-updater file-updater
${OBS_JANSSON_IMPORT}) ${OBS_JANSSON_IMPORT}
${LIBCURL_LIBRARIES})
target_include_directories(rtmp-services target_include_directories(rtmp-services
PUBLIC PUBLIC

4
plugins/rtmp-services/data/package.json Normal file → Executable file
View file

@ -1,10 +1,10 @@
{ {
"url": "https://obsproject.com/obs2_update/rtmp-services", "url": "https://obsproject.com/obs2_update/rtmp-services",
"version": 112, "version": 116,
"files": [ "files": [
{ {
"name": "services.json", "name": "services.json",
"version": 112 "version": 116
} }
] ]
} }

51
plugins/rtmp-services/data/services.json Normal file → Executable file
View file

@ -573,7 +573,7 @@
"recommended": { "recommended": {
"keyint": 2, "keyint": 2,
"profile": "main", "profile": "main",
"max video bitrate": 4000, "max video bitrate": 6000,
"max audio bitrate": 128 "max audio bitrate": 128
} }
}, },
@ -1465,6 +1465,20 @@
"max audio bitrate": 192 "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", "name": "Camplace",
"servers": [ "servers": [
@ -1500,6 +1514,24 @@
"x264opts": "tune=zerolatency" "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", "name": "Steam",
"common": false, "common": false,
@ -1559,6 +1591,23 @@
"max video bitrate": 7000, "max video bitrate": 7000,
"max audio bitrate": 128 "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"
}
} }
] ]
} }

View file

@ -5,6 +5,7 @@
#include "rtmp-format-ver.h" #include "rtmp-format-ver.h"
#include "twitch.h" #include "twitch.h"
#include "younow.h"
struct rtmp_common { struct rtmp_common {
char *service; char *service;
@ -485,7 +486,9 @@ static void apply_video_encoder_settings(obs_data_t *settings,
obs_data_set_string(settings, "rate_control", "CBR"); obs_data_set_string(settings, "rate_control", "CBR");
item = json_object_get(recommended, "profile"); 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); const char *profile = json_string_value(item);
obs_data_set_string(settings, "profile", profile); 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; return service->server;
} }

View file

@ -0,0 +1,113 @@
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
#include <util/dstr.h>
#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;
}

View file

@ -0,0 +1,3 @@
#pragma once
extern const char *younow_get_ingest(const char *server, const char *key);

View file

@ -128,12 +128,23 @@ void get_window_class(struct dstr *class, HWND hwnd)
/* not capturable or internal windows */ /* not capturable or internal windows */
static const char *internal_microsoft_exes[] = { static const char *internal_microsoft_exes[] = {
"startmenuexperiencehost",
"applicationframehost", "applicationframehost",
"peopleexperiencehost",
"shellexperiencehost", "shellexperiencehost",
"microsoft.notes",
"windowsinternal", "windowsinternal",
"systemsettings",
"textinputhost",
"winstore.app", "winstore.app",
"searchapp",
"video.ui",
"searchui", "searchui",
"lockapp", "lockapp",
"cortana",
"gamebar",
"tabtip",
"time",
NULL, NULL,
}; };

View file

@ -468,6 +468,12 @@ static inline enum speaker_layout convert_speaker_layout(uint8_t channels)
void DShowInput::OnEncodedVideoData(enum AVCodecID id, unsigned char *data, void DShowInput::OnEncodedVideoData(enum AVCodecID id, unsigned char *data,
size_t size, long long ts) 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)) { if (!ffmpeg_decode_valid(video_decoder)) {
/* Only use MJPEG hardware decoding on resolutions higher /* Only use MJPEG hardware decoding on resolutions higher
* than 1920x1080. The reason why is because we want to strike * than 1920x1080. The reason why is because we want to strike

View file

@ -2,6 +2,7 @@
#define WIN32_MEAN_AND_LEAN #define WIN32_MEAN_AND_LEAN
#include <windows.h> #include <windows.h>
#include <initguid.h>
#include <mmdeviceapi.h> #include <mmdeviceapi.h>
#include <audioclient.h> #include <audioclient.h>
#include <propsys.h> #include <propsys.h>

View file

@ -28,6 +28,7 @@ class WASAPISource {
obs_source_t *source; obs_source_t *source;
string device_id; string device_id;
string device_name; string device_name;
string device_sample = "-";
bool isInputDevice; bool isInputDevice;
bool useDeviceTiming = false; bool useDeviceTiming = false;
bool isDefaultDevice = false; bool isDefaultDevice = false;
@ -289,7 +290,8 @@ void WASAPISource::InitCapture()
client->Start(); client->Start();
active = true; 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() void WASAPISource::Initialize()
@ -308,6 +310,22 @@ void WASAPISource::Initialize()
device_name = GetDeviceName(device); 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(); InitClient();
if (!isInputDevice) if (!isInputDevice)
InitRender(); InitRender();