New upstream version 24.0.5+dfsg1

This commit is contained in:
Sebastian Ramacher 2019-12-10 20:31:54 +01:00
parent 52fa83f147
commit 4c2ea24267
61 changed files with 710 additions and 130 deletions

1
.gitignore vendored
View file

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

View file

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

View file

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

View file

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

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

@ -0,0 +1,30 @@
<?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>CFBundleSignature</key>
<string>????</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.

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

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

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

View file

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

View file

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

View file

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

View file

@ -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);
}

View file

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

View file

@ -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();

View file

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

View file

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

View file

@ -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();

View file

@ -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();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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)
{
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);
}
}

View file

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

View file

@ -29,6 +29,14 @@
#include <IOKit/hid/IOHIDDevice.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)
{
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;

View file

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

View file

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

View file

@ -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);
}
}
}

View file

@ -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();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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",
"version": 112,
"version": 114,
"files": [
{
"name": "services.json",
"version": 112
"version": 114
}
]
}

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

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

View file

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

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);