Compare commits

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

66 commits

Author SHA1 Message Date
PatTheMav
fa50f64c3c
libobs: Trigger bindings injected by Qt directly
Instead of lettings the hotkey thread handle bindings triggered by Qt,
call the callback directly.

By this point, Qt has resolved the modifier state (so no need for the
hotkey thread to do the same), the pressed state is set as well,
the branch takes care of matching the pressed keys.

This also fixes the issue with the hotkey changes introduced by #3914,
which only inject key events when OBS is _not_ in focus (and as such
modifier states are never updated).
2022-08-06 16:36:37 +02:00
Theodore Dubois
2b60f04974
libobs: Rewrite macOS hotkeys implementation
Co-authored-by: Sebastian Beckmann <beckmann.sebastian@outlook.de>
2022-08-06 16:36:26 +02:00
Dossy Shiobara
ce78cd446e mac-capture: Improve window capture performance
Replaces the current implementation of Window Capture, which uses Core
Graphics to render a bitmap raster, with one that accesses the window's
CGImage bitmap directly, blits it, and hands that off as a frame to OBS
instead.

Co-Authored-By: Sebastian Beckmann <beckmann.sebastian@outlook.de>
2022-07-19 23:24:07 +02:00
Kurt Kartaltepe
d9865bdffb libobs-opengl: Use gl helpers in create_dmabuf_image
This replaces direct OpenGL calls to error handling helpers. Previously
this would cause errors to be misattributed to the next OpenGL functions
called.

Fixes DMA-BUF importing returning a texture on failure on KDE+NVIDIA.
2022-04-30 16:41:52 -07:00
jp9000
aab14ba29a libobs: Update version to 27.2.4 2022-03-27 16:29:23 -07:00
Monsteer
48f41c67dc UI: Add missing previousIcon in Rachni theme
The previous icon for the media icons set in the Rachni theme is missing
in the theme's .qss file.
2022-03-24 18:22:59 -07:00
derrod
c263266975 CI: Enable legacy CI for PRs to release branches 2022-03-24 18:22:59 -07:00
derrod
a1ba04536f CI: Fix CEF zip extraction path on Windows 2022-03-24 18:22:59 -07:00
Ryan Foster
ef53407c75 CI: Fix Qt XML Validator workflow
libxml2-utils updated recently but the manifest is out-of-date. Try to
fix it with the suggestion of running apt-get update first.
2022-03-24 18:22:59 -07:00
pkv
928597ec9d obs-libfdk: Set bitstream to ADTS for mpegts output
This sets the aac bitstream to ADTS for the mpegts output.
This fixes a bug with mpegts ouput where the avformat muxer issues
an error with fdk-aac encoder.

Signed-off-by: pkv <pkv@obsproject.com>
2022-03-24 18:22:59 -07:00
jpark37
b718aa41f4 win-waspai: Tighten version check for RTWQ
Getting reports that it isn't working for 1607 and below.

If they continue, we should probably just pull the RTWQ path.
2022-03-24 18:22:59 -07:00
eightball567
0d68aacfb6 mac-capture: Add vbcable to whitelist for loopback devices 2022-03-24 18:22:59 -07:00
cg2121
2056a41ca5 UI: Fix handling of remove signal with projectors
Since the remove signal is coming from another thread,
QMetaObject::invokeMethod has to be used, or corruption
could possibly occur.
2022-03-24 18:22:59 -07:00
jp9000
fb96d637e2 UI: Fix deferred source properties not updating 2022-03-24 18:22:59 -07:00
jp9000
1cfd1bb90a UI: Rename visual update callback variable
Having both a "callback" and "cb" doesn't make sense. One is
specifically for visual updates only, the other is for actual updates,
so use an appropriate name for both
2022-03-24 18:22:59 -07:00
cg2121
63f535c86e UI: Fix mixer hide toggle in studio mode
If the user would toggle hide in mixer in studio mode,
the source would show up in audio mixer, even if it
wasn't active.
2022-03-24 18:22:59 -07:00
Exeldro
27bb608130 libobs: Fix overflow subtracting unsigned numbers
When offset_size is greater than size, size would overflow
2022-03-24 18:22:59 -07:00
Kurt Kartaltepe
4845e88219 UI: Avoid calling obs_source_update multiple times
When we added undo/redo in 86eb7aeb a distinction between Updates and
Visual updates was made, presumably for filters which need special
handling of the update case. These functions were also added to regular
source properties, but didnt use any special code instead calling
obs_source_update an additional time after a short time.

Instead lets remove the 2nd delayed call and only call obs_source_update
once per settings change.
2022-03-24 18:22:59 -07:00
jp9000
09df945fb8 UI: Use get_new_source_name instead of strprintf
Remove a bit of duplicated code, and fix non-Windows compilation. This
is a bit of a workaround to not have to modify cmake, but might as well
reuse a function that already exists anyway rather than duplicate the
code.
2022-03-24 18:22:59 -07:00
jp9000
23bc7c7544 UI: Fix duplicated source names in audio settings
When you add an audio capture source and use the "use existing" feature
in the source selection dialog, and then recreate the same audio source
again in audio settings, it will result in two sources using identical
names, which although relatively harmless can cause issues when doing
things such as trying to find sources by their name.

Fixes obsproject/obs-studio#5621
Closes obsproject/obs-studio#5947
2022-03-24 18:22:59 -07:00
jpark37
4b6170f258 win-dshow: Fix hwdevice_ctx leak 2022-03-24 18:22:59 -07:00
derrod
20332b01fd CI: Move obspython.py to Resources 2022-03-23 15:53:01 +01:00
derrod
afa8527723 obs-scripting: Load obspython.py from Resources on macOS 2022-03-23 15:53:01 +01:00
jp9000
b65ee7e7fb virtualcam-module: Revert changes since 27.1.3 (for now)
This reverts commit 4b0767741a,
3f3f9ed7e6,
0e7c17bd6e, and
865eecb739.
2022-03-02 17:36:49 -08:00
jp9000
399636847d virtualcam-module: Prevent placeholder memory leak 2022-03-02 05:05:13 -08:00
jp9000
9f0650658d virtualcam-module: Only initialize placeholder once 2022-03-02 05:04:47 -08:00
jp9000
201e9df041 libobs: Update version to 27.2.3 2022-03-02 04:26:32 -08:00
jp9000
fc20e7a71b virtualcam-module: Fix incorrect correct res/fps
Apparently the testing that was done was not sufficient enough or did
not check all test cases, so resolution/fps does not get set properly on
the virtualcam.
2022-03-02 04:20:27 -08:00
jpark37
5bd791dea6 UI: Remove conflicting setlocale call
Qt already sets LC_ALL to "" for non-Windows. We set LC_NUMERIC to "C"
afterward for sanity, and we don't want that setting to get smashed.
2022-03-01 12:54:25 -08:00
jpark37
b5f85bc788 UI: Restore LC_NUMERIC to C locale on Mac/Linux
Recommended by QCoreApplication doc for Unix.

Fixes CUBE LUT load, and FFmpeg output args for foreign locales.
2022-03-01 12:54:25 -08:00
jp9000
34b55180ab libobs: Update version to 27.2.2 2022-03-01 11:40:46 -08:00
jp9000
c1ff655dfe virtualcam-module: Remove unnecessarily inlines 2022-03-01 23:29:04 +11:00
jp9000
91759c2a2e virtualcam-module: Stop thread on Stop call
This causes the thread to only start when the IMediaFilter::Run/Pause
calls have been made, and stop whenever either the IMediaFilter::Stop
call has been made, or on destruction, whichever comes first.

This potentially will work around a suspected race condition that
appears to be in the WebRTC library where the filter's library will be
released while the filter is in the process of being destroyed, which
can take longer than usual if the join takes too long. Basically, fixes
a reported crash (that doesn't appear to technically be our fault) when
the filter is used with browsers when the virtualcam is deactivating in
web browsers.
2022-03-01 23:28:59 +11:00
jp9000
d23cb52796 win-dshow: Ensure thread is joinable before joining
This may have been the issue behind the reported firefox crashes, but
strangely, the thread should always be joinable here because it's
explicitly always created in the constructor and joined in the
destructor.
2022-03-01 23:28:37 +11:00
jp9000
1acc6c69da obs-scripting: Make callback "removed" variable atomic
Makes the callback variable "removed" atomic, and on script unload,
first sets all callbacks to removed before actually unloading the script
out of a safety precaution. (See note at the bottom for further details)

This minimizes the possibility of a race condition where the script
callback could be called while those callbacks were being removed.

Big note for this change, this change should eventually be replaced with
a reference counting ownership method where script callbacks can hold a
reference and share ownership of the script if it's still alive while
the script callback is being called. That way the script callbacks can
safely execute. May require a fair amount of reworking of the script
object.
2022-03-01 04:06:33 -08:00
jpark37
fea9281a1f libobs/util: Use integer math for Windows timing
Cleaner and faster than double math.
2022-03-01 17:03:31 +11:00
jpark37
2e1a6c9c64 libobs: Clamp video timing for safety
os_gettime_ns and os_sleepto_ns may not match up exactly.
2022-03-01 17:03:27 +11:00
jp9000
2d7cda18e9 libobs/util: Fix rounding error with os_sleepto_ns()
os_sleepto_ns() can occasionally return false on times that the
processor may not have reached yet. The reason is because the
count_target, which converts time_target into a QPC counter, is subject
to a rounding error.

Using numbers I generated from an actual clock cycle on my own CPU, I
can show an example of this occurring: if the clock frequency value is
10000000.0, and you call os_sleepto_ns(42164590320600), it will convert
that number first to a double floating point of its QPC value:
421645903205.99994. Then, because it converts that to a LONGLONG
integer, it of course strips off the decimal point. If you convert
421645903205 *back* to a time value, the new value will be
42164590320500, which is lower than the original value by approximately
100 nanoseconds. While this may seem insignificant, it was apparently
enough to cause the os_sleepto_ns() call in video_sleep() to sometimes
return false despite the current time being lower than the target time,
which would cause it to incorrectly calculate how many frames were
duplicated by subtracting the frame time from the current system time,
divide that by the current frame interval, set the vframe_info.count
value to 0, and thus cause an infinite loop in the encode_gpu()
function because queue_frame now starts returning negative numbers in
perpetuity.

This change fixes some rare reports of users having their video lock up
and disconnect, forcing the user to have to forcibly shut down the
program.

Thanks to Twitch user SNLabat for having the patience to kindly provide
us with a dump file from the freeze, and to Matt for coordinating with
that user to obtain it from them.
2022-02-28 22:22:29 +11:00
Kevin Degeling
db3cee0868 UI: Additional product details
Based on the product description from the main website
2022-02-28 22:22:09 +11:00
obiwac
c09429aa8c linux-v4l2: scandir with alphasort on non-Linux
Sort video device entries with `alphasort` on non-Linux platforms,
as opposed to `versionsort` on Linux.
(`versionsort` is a GNU extension, unavailable on e.g. FreeBSD.)

UI: Fix call to `to_string` on FreeBSD
2022-02-28 22:21:46 +11:00
obiwac
cedd8b9d15 libobs/graphics: gs_query_dmabuf_* on FreeBSD too 2022-02-28 22:21:41 +11:00
gxalpha
46f9a5a8b9 UI: Refresh edit menu on item locked signal 2022-02-28 22:21:30 +11:00
Kurt Kartaltepe
eff0148dd7 linux-v4l2: Fix warnings in mjpeg
Previously the switch did not catch all cases issuing a quite large
warning. Also there was a const-ness warning for codecs on ffmpeg 5.0
that this addresses.
2022-02-28 22:21:14 +11:00
Richard Stanway
bad7edd051 win-wasapi: Fall back to old code if RTWQ fails
Fixes a crash if RTWQ is unavailable, e.g. if the mmcss service is not
running.
2022-02-28 22:20:45 +11:00
Ryan Foster
cc1395857a CI: Update workflow to copy SOVERSION symlinks
Commit 7a5bffc0a6 applied a fix to the
macOS build script. This applies the same fix to the GitHub Actions
workflow that is actually currently used on CI.
2022-02-22 17:17:25 -05:00
Julian Orth
68a8264197 libobs: Map wayland keymap with MAP_PRIVATE
Wayland clients are required to use MAP_PRIVATE starting with version 7
of the wl_seat protocol.

Signed-off-by: Julian Orth <ju.orth@gmail.com>
2022-02-21 14:10:36 -05:00
jp9000
acad9dbaf7 libobs: Update version to 27.2.1 2022-02-20 16:35:43 -08:00
Richard Stanway
bfcc066099 obs-outputs: Set a fixed size socket buffer on Windows 7
Auto tuning apparently doesn't work very well on this version and
af6844f5c2 caused throughput
regressions.
2022-02-21 11:21:20 +11:00
Matt Gajownik
2f48733f3c CI: Bump Windows CEF cache to fix reported version
While the previous cache had the correct changes, the version was
reported incorrectly. This will avoid confusion.
2022-02-21 10:32:40 +11:00
Matt Gajownik
c9dfad7340 CI: Bump Windows CEF cache for new OnAcceleratedPaint2
See 36fdac16a7 for more info.

This only bumps the cache on Windows as the changes are currently
Windows-specific.
2022-02-21 10:32:36 +11:00
jp9000
0f97427ef2 obs-browser: Add support for custom OBS CEF
Our version fixes the system freeze issue, reduces resource usage, and
improves performance back to 3770 levels.

See obsproject/cef@36fdac16a7 for detailed information.
2022-02-20 13:31:05 -08:00
jp9000
3eec65d127 obs-browser: Fix texture recreating every frame 2022-02-20 13:31:05 -08:00
Matt Gajownik
a0c37e7aa9 obs-browser: Fix issues with rendering on Linux/macOS 2022-02-20 17:33:02 +11:00
Kurt Kartaltepe
8ad7d1b08d linux-v4l2: Use decoded MJPEG pixel format
Previously we assumed mjpeg was always decoded to 422 but it seems some
cameras provide frames that decode to different pixel formats such as
420.

This change delays setting the obs frame pixel format until after we
have decoded the v4l2 frame.

fixes #5821
2022-02-20 14:40:20 +11:00
Matt Gajownik
d1f8caeb8d UI: Log 'Hide OBS from capture' on startup & settings change 2022-02-20 13:40:34 +11:00
derrod
94016b459f libobs: Adjust path for legacy browser source block
The current path would prevent the browser source from loading if OBS
itself is in the "Application Support" folder, where it might end up
when being installed via certain distribution platforms.
This adjusts the existing hack to specifically check for the obs-studio
subfolder where the old browser source library would reside.
2022-02-20 12:06:03 +11:00
gxalpha
5f7c4329b2 UI: Refresh edit menu on item select/deselect 2022-02-20 12:05:59 +11:00
PatTheMav
a75687abec CI: Ensure SOVERSION symlinks exist in created App Bundle
Bundles libraries are available via their specific versioned filename
as well as SOVERSION and versionless symlinks. Dylibbundler does _not_
include these symlinks when copying linked libraries into the bundle.

This change ensures that all libraries and their SOVERSION symlinks
exist in the created App Bundle.
2022-02-20 12:05:54 +11:00
PatTheMav
c4455d4de0 CI: Update main workflow file to use fixed obs-deps
Prior version was linked against libxcb, because it was present on
Github Actions macOS runners. Consequently builds on CI will succeed
as the library is always present, will fail on user's machines though.
2022-02-20 12:05:50 +11:00
Matt Gajownik
9c42e9f752 win-wasapi: Only enable work queue on Windows 10+
Fixes #5967
2022-02-20 12:04:56 +11:00
pkv
92bf2ebe93 obs-ffmpeg: Force mpegts format & disable restart on activate for srt & rist
This fixes crashes in media Source when :
- the user forgets to set the input format for srt or rist URLs;
- disabling / enabling / disabling etc Media source with a rist URL.

Signed-off-by: pkv <pkv@obsproject.com>
2022-02-20 12:04:48 +11:00
Florian Zwoch
b6366757c0 linux-capture: Fix for pipewire capture leaking texture handles
Enter the graphics context before calling gs_texture_destroy() or
else the call will fail causing the texture handles to leak.
2022-02-20 12:04:41 +11:00
Matt Gajownik
59a675738f UI: Don't collapse preview in Filters splitter view
Fixes #5959
2022-02-18 20:37:47 +11:00
jp9000
5643e9cbbf obs-browser: Fix sRGB rendering 2022-02-15 23:52:44 -08:00
jp9000
e43f75fc92 obs-browser: Update version to 2.17.10 2022-02-15 23:21:59 -08:00
jp9000
f2628de7f2 obs-browser: Acquire, copy, and release immediately
Instead of holding onto an acquire over the duration of an entire frame,
acquire, copy to a texture, and immediately release the lock.

(Note from Jim: When it comes to synchronization, the idea of acquiring
a lock, and then releasing all the way in the next frame just feels a
bit worrisome. Especially when it's a GPU object. But at this point, I'm
just making shots in the dark in an attempt to eliminate any possible
things that might even have a remote chance to trigger latent bugs in a
GPU driver or the kernel. You know, that whole system freeze thing. This
probably doesn't have anything to do with it, at this point I have
nothing but wild geese to chase. Whatever.)
2022-02-15 23:21:59 -08:00
51 changed files with 568 additions and 1226 deletions

View file

@ -14,6 +14,7 @@ on:
- '**.md'
branches:
- master
- 'release/**'
env:
MACOS_CEF_BUILD_VERSION: '4638'
@ -34,7 +35,7 @@ jobs:
runs-on: [macos-latest]
env:
MIN_MACOS_VERSION: '10.13'
MACOS_DEPS_VERSION: '2022-01-31'
MACOS_DEPS_VERSION: '2022-02-13'
VLC_VERSION: '3.0.8'
SPARKLE_VERSION: '1.23.0'
QT_VERSION: '5.15.2'
@ -211,10 +212,12 @@ jobs:
if [ -d ./OBS.app/Contents/Resources/data/obs-scripting ]; then
mv ./OBS.app/Contents/Resources/data/obs-scripting/obslua.so ./OBS.app/Contents/MacOS/
mv ./OBS.app/Contents/Resources/data/obs-scripting/_obspython.so ./OBS.app/Contents/MacOS/
mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/MacOS/
mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/Resources/
rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/
fi
/bin/cp -cpR /tmp/obsdeps/lib/*.dylib ./OBS.app/Contents/Frameworks
BUNDLE_PLUGINS=(
./OBS.app/Contents/PlugIns/coreaudio-encoder.so
./OBS.app/Contents/PlugIns/decklink-ouput-ui.so
@ -559,7 +562,7 @@ jobs:
CACHE_NAME: 'windows-cef-64-cache'
with:
path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64
key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-1
key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-3
- name: 'Install prerequisite: QT'
if: steps.qt-cache.outputs.cache-hit != 'true'
run: |
@ -579,7 +582,7 @@ jobs:
if: steps.cef-cache.outputs.cache-hit != 'true'
run: |
curl -kL https://cdn-fastly.obsproject.com/downloads/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64.zip -f --retry 5 -o cef.zip
7z x cef.zip -o"${{ github.workspace }}/cmbuild"
7z x cef.zip -o"${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64"
- name: 'Configure'
run: |
mkdir ./build
@ -668,7 +671,7 @@ jobs:
CACHE_NAME: 'cef-32-cache'
with:
path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86
key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-1
key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-3
- name: 'Install prerequisite: QT'
if: steps.qt-cache.outputs.cache-hit != 'true'
run: |
@ -688,7 +691,7 @@ jobs:
if: steps.cef-cache.outputs.cache-hit != 'true'
run: |
curl -kL https://cdn-fastly.obsproject.com/downloads/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86.zip -f --retry 5 -o cef.zip
7z x cef.zip -o"${{ github.workspace }}/cmbuild"
7z x cef.zip -o"${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86"
- name: 'Configure'
run: |
mkdir ./build

View file

@ -19,6 +19,7 @@ jobs:
- name: Install xmllint
run: |
sudo apt-get -qq update
sudo apt-get install --no-install-recommends -y libxml2-utils
- name: Register Annotations

View file

@ -419,9 +419,11 @@ prepare_macos_bundle() {
if [ -d ./OBS.app/Contents/Resources/data/obs-scripting ]; then
/bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obslua.so ./OBS.app/Contents/MacOS/
/bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/_obspython.so ./OBS.app/Contents/MacOS/
/bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/MacOS/
/bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/Resources/
/bin/rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/
fi
# dylibbundler will only copy actually linked files into bundle, but not symlinks
/bin/cp -cpR /tmp/obsdeps/lib/*.dylib ./OBS.app/Contents/Frameworks
bundle_dylibs
install_frameworks

View file

@ -1464,6 +1464,10 @@ QPushButton#sourceFiltersButton {
qproperty-icon: url(./Dark/media/media_next.svg);
}
* [themeID="previousIcon"] {
qproperty-icon: url(./Dark/media/media_previous.svg);
}
/* YouTube Integration */
OBSYoutubeActions {
qproperty-thumbPlaceholder: url(./Dark/sources/image.svg);

View file

@ -474,6 +474,9 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QFrame" name="previewFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">

View file

@ -1156,6 +1156,12 @@ bool OBSApp::InitTheme()
OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)
: QApplication(argc, argv), profilerNameStore(store)
{
/* fix float handling */
#if defined(Q_OS_UNIX)
if (!setlocale(LC_NUMERIC, "C"))
blog(LOG_WARNING, "Failed to set LC_NUMERIC to C locale");
#endif
sleepInhibitor = os_inhibit_sleep_create("OBS Video/audio");
#ifdef __APPLE__
@ -1455,6 +1461,12 @@ bool OBSApp::OBSInit()
blog(LOG_INFO, "Browser Hardware Acceleration: %s",
browserHWAccel ? "true" : "false");
#endif
#ifdef _WIN32
bool hideFromCapture = config_get_bool(globalConfig, "BasicWindow",
"HideOBSWindowsFromCapture");
blog(LOG_INFO, "Hide OBS windows from screen capture: %s",
hideFromCapture ? "true" : "false");
#endif
blog(LOG_INFO, "Qt Version: %s (runtime), %s (compiled)", qVersion(),
QT_VERSION_STR);

View file

@ -136,7 +136,7 @@ const char *RunOnce::thr_name = "OBS runonce";
void PIDFileCheck(bool &already_running)
{
std::string tmpfile_name =
"/tmp/obs-studio.lock." + to_string(geteuid());
"/tmp/obs-studio.lock." + std::to_string(geteuid());
int fd = open(tmpfile_name.c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0600);
if (fd == -1) {
already_running = true;
@ -213,7 +213,6 @@ string GetDefaultVideoSavePath()
vector<string> GetPreferredLocales()
{
setlocale(LC_ALL, "");
vector<string> matched;
string messages = setlocale(LC_MESSAGES, NULL);
if (!messages.size() || messages == "C" || messages == "POSIX")

View file

@ -180,14 +180,15 @@ void OBSPropertiesView::GetScrollPos(int &h, int &v)
OBSPropertiesView::OBSPropertiesView(OBSData settings_, obs_object_t *obj,
PropertiesReloadCallback reloadCallback,
PropertiesUpdateCallback callback_,
PropertiesVisualUpdateCb cb_, int minSize_)
PropertiesVisualUpdateCb visUpdateCb_,
int minSize_)
: VScrollArea(nullptr),
properties(nullptr, obs_properties_destroy),
settings(settings_),
weakObj(obs_object_get_weak_object(obj)),
reloadCallback(reloadCallback),
callback(callback_),
cb(cb_),
visUpdateCb(visUpdateCb_),
minSize(minSize_)
{
setFrameShape(QFrame::NoFrame);
@ -198,14 +199,15 @@ OBSPropertiesView::OBSPropertiesView(OBSData settings_, obs_object_t *obj,
OBSPropertiesView::OBSPropertiesView(OBSData settings_, void *obj,
PropertiesReloadCallback reloadCallback,
PropertiesUpdateCallback callback_,
PropertiesVisualUpdateCb cb_, int minSize_)
PropertiesVisualUpdateCb visUpdateCb_,
int minSize_)
: VScrollArea(nullptr),
properties(nullptr, obs_properties_destroy),
settings(settings_),
rawObj(obj),
reloadCallback(reloadCallback),
callback(callback_),
cb(cb_),
visUpdateCb(visUpdateCb_),
minSize(minSize_)
{
setFrameShape(QFrame::NoFrame);
@ -2028,11 +2030,11 @@ void WidgetInfo::ControlChanged()
blog(LOG_DEBUG, "No update timer or no callback!");
}
if (view->cb && !view->deferUpdate) {
if (view->visUpdateCb && !view->deferUpdate) {
OBSObject strongObj = view->GetObject();
void *obj = strongObj ? strongObj.Get() : view->rawObj;
if (obj)
view->cb(obj, view->settings);
view->visUpdateCb(obj, view->settings);
}
view->SignalChanged();

View file

@ -102,7 +102,7 @@ private:
std::string type;
PropertiesReloadCallback reloadCallback;
PropertiesUpdateCallback callback = nullptr;
PropertiesVisualUpdateCb cb = nullptr;
PropertiesVisualUpdateCb visUpdateCb = nullptr;
int minSize;
std::vector<std::unique_ptr<WidgetInfo>> children;
std::string lastFocused;
@ -188,7 +188,10 @@ public:
inline void UpdateSettings()
{
callback(OBSGetStrongRef(weakObj), nullptr, settings);
if (callback)
callback(OBSGetStrongRef(weakObj), nullptr, settings);
else if (visUpdateCb)
visUpdateCb(OBSGetStrongRef(weakObj), settings);
}
inline bool DeferUpdate() const { return deferUpdate; }

View file

@ -520,6 +520,7 @@ void SourceTreeItem::VisibilityChanged(bool visible)
void SourceTreeItem::LockedChanged(bool locked)
{
lock->setChecked(locked);
OBSBasic::Get()->UpdateEditMenu();
}
void SourceTreeItem::Renamed(const QString &name)
@ -623,12 +624,14 @@ void SourceTreeItem::Select()
{
tree->SelectItem(sceneitem, true);
OBSBasic::Get()->UpdateContextBarDeferred();
OBSBasic::Get()->UpdateEditMenu();
}
void SourceTreeItem::Deselect()
{
tree->SelectItem(sceneitem, false);
OBSBasic::Get()->UpdateContextBarDeferred();
OBSBasic::Get()->UpdateEditMenu();
}
/* ========================================================================= */

View file

@ -1842,6 +1842,7 @@ void OBSBasic::OBSInit()
ui->contextContainer->setVisible(contextVisible);
if (contextVisible)
UpdateContextBar(true);
UpdateEditMenu();
{
ProfileScope("OBSBasic::Load");
@ -3530,6 +3531,8 @@ void OBSBasic::ActivateAudioSource(OBSSource source)
{
if (SourceMixerHidden(source))
return;
if (!obs_source_active(source))
return;
if (!obs_source_audio_active(source))
return;
@ -4411,6 +4414,8 @@ bool OBSBasic::ResetAudio()
return obs_reset_audio(&ai);
}
extern char *get_new_source_name(const char *name, const char *format);
void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId,
const char *deviceDesc, int channel)
{
@ -4434,10 +4439,11 @@ void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId,
}
} else if (!disable) {
BPtr<char> name = get_new_source_name(deviceDesc, "%s (%d)");
settings = obs_data_create();
obs_data_set_string(settings, "device_id", deviceId);
source = obs_source_create(sourceId, deviceDesc, settings,
nullptr);
source = obs_source_create(sourceId, name, settings, nullptr);
obs_set_output_source(channel, source);
}
@ -5369,8 +5375,6 @@ ColorSelect::ColorSelect(QWidget *parent)
void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
{
UpdateEditMenu();
QMenu popup(this);
delete previewProjectorSource;
delete sourceProjector;
@ -7825,11 +7829,6 @@ void OBSBasic::UpdateEditMenu()
ui->actionHorizontalCenter->setEnabled(canTransform);
}
void OBSBasic::on_menuBasic_MainMenu_Edit_aboutToShow()
{
UpdateEditMenu();
}
void OBSBasic::on_actionEditTransform_triggered()
{
if (transformWindow)

View file

@ -966,8 +966,6 @@ private slots:
void on_actionShowCrashLogs_triggered();
void on_actionUploadLastCrashLog_triggered();
void on_menuBasic_MainMenu_Edit_aboutToShow();
void on_actionEditTransform_triggered();
void on_actionCopyTransform_triggered();
void on_actionPasteTransform_triggered();

View file

@ -81,20 +81,10 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_)
OBSDataAutoRelease nd_settings = obs_source_get_settings(source);
obs_data_apply(oldSettings, nd_settings);
auto handle_memory = [](void *vp, obs_data_t *old_settings,
obs_data_t *new_settings) {
obs_source_t *source = reinterpret_cast<obs_source_t *>(vp);
obs_source_update(source, new_settings);
UNUSED_PARAMETER(old_settings);
UNUSED_PARAMETER(vp);
};
view = new OBSPropertiesView(
nd_settings.Get(), source,
(PropertiesReloadCallback)obs_source_properties,
(PropertiesUpdateCallback)handle_memory,
(PropertiesUpdateCallback) nullptr, // No special handling required for undo/redo
(PropertiesVisualUpdateCb)obs_source_update);
view->setMinimumHeight(150);

View file

@ -3008,6 +3008,9 @@ void OBSBasicSettings::SaveGeneralSettings()
main->SetDisplayAffinity(window);
}
}
blog(LOG_INFO, "Hide OBS windows from screen capture: %s",
hide_window ? "true" : "false");
}
#endif
if (WidgetChanged(ui->openStatsOnStartup))

View file

@ -130,7 +130,7 @@ static void AddSource(void *_data, obs_scene_t *scene)
obs_sceneitem_set_visible(sceneitem, data->visible);
}
static char *get_new_source_name(const char *name)
char *get_new_source_name(const char *name, const char *format)
{
struct dstr new_name = {0};
int inc = 0;
@ -143,7 +143,7 @@ static char *get_new_source_name(const char *name)
if (!existing_source)
break;
dstr_printf(&new_name, "%s %d", name, ++inc + 1);
dstr_printf(&new_name, format, name, ++inc + 1);
}
return new_name.array;
@ -160,8 +160,8 @@ static void AddExisting(OBSSource source, bool visible, bool duplicate,
if (duplicate) {
OBSSource from = source;
char *new_name =
get_new_source_name(obs_source_get_name(source));
char *new_name = get_new_source_name(
obs_source_get_name(source), "%s %d");
source = obs_source_duplicate(from, new_name, false);
obs_source_release(source);
bfree(new_name);

View file

@ -626,11 +626,7 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
void OBSProjector::OBSSourceRemoved(void *data, calldata_t *params)
{
OBSProjector *window = reinterpret_cast<OBSProjector *>(data);
OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
main->DeleteProjector(window);
allProjectors.removeAll(window);
QMetaObject::invokeMethod(window, "EscapeTriggered");
UNUSED_PARAMETER(params);
}

View file

@ -9,6 +9,22 @@
<summary>Live streaming and video recording software</summary>
<description>
<p>Free and open source software for video capturing, recording, and live streaming.</p>
<p>Features:</p>
<ul>
<li>High performance real time video/audio capturing and mixing. Create scenes made up of multiple sources including window captures, images, text, browser windows, webcams, capture cards and more.</li>
<li>Set up an unlimited number of scenes you can switch between seamlessly via custom transitions.</li>
<li>Intuitive audio mixer with per-source filters such as noise gate, noise suppression, and gain. Take full control with VST plugin support.</li>
<li>Powerful and easy to use configuration options. Add new Sources, duplicate existing ones, and adjust their properties effortlessly.</li>
<li>Streamlined Settings panel gives you access to a wide array of configuration options to tweak every aspect of your broadcast or recording.</li>
<li>Modular 'Dock' UI allows you to rearrange the layout exactly as you like. You can even pop out each individual Dock to its own window.</li>
</ul>
<p>Create Professional Productions:</p>
<ul>
<li>Choose from a number of different and customizable transitions for when you switch between your scenes or add your own stinger video files.</li>
<li>Set hotkeys for nearly every sort of action, such as switching between scenes, starting/stopping streams or recordings, muting audio sources, push to talk, and more.</li>
<li>Studio Mode lets you preview your scenes and sources before pushing them live. Adjust your scenes and sources or create new ones and ensure they're perfect before your viewers ever see them.</li>
<li>Get a high level view of your production using the Multiview. Monitor 8 different scenes and easily cue or transition to any of them with merely a single or double click.</li>
</ul>
</description>
<url type="homepage">https://obsproject.com</url>
<url type="bugtracker">https://github.com/obsproject/obs-studio/issues</url>
@ -16,7 +32,7 @@
<url type="translate">https://crowdin.com/project/obs-studio</url>
<screenshots>
<screenshot type="default">
<image>https://obsproject.com/assets/images/OBSDemoApp.jpg</image>
<image>https://obsproject.com/assets/images/OBSDemoApp2610.png</image>
</screenshot>
</screenshots>
<content_rating type="oars-1.1"/>

View file

@ -33,9 +33,14 @@ struct script_callback {
obs_script_t *script;
calldata_t extra;
bool removed;
volatile bool removed;
};
static inline bool script_callback_removed(struct script_callback *cb)
{
return os_atomic_load_bool(&cb->removed);
}
static inline void *add_script_callback(struct script_callback **first,
obs_script_t *script, size_t extra_size)
{
@ -54,7 +59,7 @@ static inline void *add_script_callback(struct script_callback **first,
static inline void remove_script_callback(struct script_callback *cb)
{
cb->removed = true;
os_atomic_set_bool(&cb->removed, true);
struct script_callback *next = cb->next;
if (next)

View file

@ -206,7 +206,7 @@ static void frontend_event_callback(enum obs_frontend_event event, void *priv)
struct lua_obs_callback *cb = priv;
lua_State *script = cb->script;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
obs_frontend_remove_event_callback(frontend_event_callback, cb);
return;
}
@ -254,7 +254,7 @@ static void frontend_save_callback(obs_data_t *save_data, bool saving,
struct lua_obs_callback *cb = priv;
lua_State *script = cb->script;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
obs_frontend_remove_save_callback(frontend_save_callback, cb);
return;
}

View file

@ -288,7 +288,7 @@ static void timer_call(struct script_callback *p_cb)
{
struct lua_obs_callback *cb = (struct lua_obs_callback *)p_cb;
if (p_cb->removed)
if (script_callback_removed(p_cb))
return;
lock_callback();
@ -329,7 +329,7 @@ static void obs_lua_main_render_callback(void *priv, uint32_t cx, uint32_t cy)
struct lua_obs_callback *cb = priv;
lua_State *script = cb->script;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
obs_remove_main_render_callback(obs_lua_main_render_callback,
cb);
return;
@ -377,7 +377,7 @@ static void obs_lua_tick_callback(void *priv, float seconds)
struct lua_obs_callback *cb = priv;
lua_State *script = cb->script;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
obs_remove_tick_callback(obs_lua_tick_callback, cb);
return;
}
@ -423,7 +423,7 @@ static void calldata_signal_callback(void *priv, calldata_t *cd)
struct lua_obs_callback *cb = priv;
lua_State *script = cb->script;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
signal_handler_remove_current();
return;
}
@ -505,7 +505,7 @@ static void calldata_signal_callback_global(void *priv, const char *signal,
struct lua_obs_callback *cb = priv;
lua_State *script = cb->script;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
signal_handler_remove_current();
return;
}
@ -663,7 +663,7 @@ static void hotkey_pressed(void *p_cb, bool pressed)
struct lua_obs_callback *cb = p_cb;
lua_State *script = cb->script;
if (cb->base.removed)
if (script_callback_removed(&cb->base))
return;
lock_callback();
@ -689,7 +689,7 @@ static void hotkey_callback(void *p_cb, obs_hotkey_id id, obs_hotkey_t *hotkey,
{
struct lua_obs_callback *cb = p_cb;
if (cb->base.removed)
if (script_callback_removed(&cb->base))
return;
if (pressed)
@ -745,7 +745,7 @@ static bool button_prop_clicked(obs_properties_t *props, obs_property_t *p,
lua_State *script = cb->script;
bool ret = false;
if (cb->base.removed)
if (script_callback_removed(&cb->base))
return false;
lock_callback();
@ -801,7 +801,7 @@ static bool modified_callback(void *p_cb, obs_properties_t *props,
lua_State *script = cb->script;
bool ret = false;
if (cb->base.removed)
if (script_callback_removed(&cb->base))
return false;
lock_callback();
@ -1078,7 +1078,7 @@ static void lua_tick(void *param, float seconds)
struct lua_obs_timer *next = timer->next;
struct lua_obs_callback *cb = lua_obs_timer_cb(timer);
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
lua_obs_timer_remove(timer);
} else {
uint64_t elapsed = ts - timer->last_ts;
@ -1156,6 +1156,25 @@ void obs_lua_script_unload(obs_script_t *s)
lua_State *script = data->script;
/* ---------------------------- */
/* mark callbacks as removed */
pthread_mutex_lock(&data->mutex);
/* XXX: scripts can potentially make callbacks when this happens, so
* this probably still isn't ideal as we can't predict how the
* processor or operating system is going to schedule things. a more
* ideal method would be to reference count the script objects and
* atomically share ownership with callbacks when they're called. */
struct lua_obs_callback *cb =
(struct lua_obs_callback *)data->first_callback;
while (cb) {
os_atomic_set_bool(&cb->base.removed, true);
cb = (struct lua_obs_callback *)cb->base.next;
}
pthread_mutex_unlock(&data->mutex);
/* ---------------------------- */
/* undefine source types */
@ -1189,8 +1208,7 @@ void obs_lua_script_unload(obs_script_t *s)
/* ---------------------------- */
/* remove all callbacks */
struct lua_obs_callback *cb =
(struct lua_obs_callback *)data->first_callback;
cb = (struct lua_obs_callback *)data->first_callback;
while (cb) {
struct lua_obs_callback *next =
(struct lua_obs_callback *)cb->base.next;

View file

@ -280,7 +280,7 @@ static void frontend_save_callback(obs_data_t *save_data, bool saving,
{
struct python_obs_callback *cb = priv;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
obs_frontend_remove_save_callback(frontend_save_callback, cb);
return;
}
@ -355,7 +355,7 @@ static void frontend_event_callback(enum obs_frontend_event event, void *priv)
{
struct python_obs_callback *cb = priv;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
obs_frontend_remove_event_callback(frontend_event_callback, cb);
return;
}

View file

@ -431,7 +431,7 @@ static void timer_call(struct script_callback *p_cb)
{
struct python_obs_callback *cb = (struct python_obs_callback *)p_cb;
if (p_cb->removed)
if (script_callback_removed(p_cb))
return;
lock_callback(cb);
@ -476,7 +476,7 @@ static void obs_python_tick_callback(void *priv, float seconds)
{
struct python_obs_callback *cb = priv;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
obs_remove_tick_callback(obs_python_tick_callback, cb);
return;
}
@ -546,7 +546,7 @@ static void calldata_signal_callback(void *priv, calldata_t *cd)
{
struct python_obs_callback *cb = priv;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
signal_handler_remove_current();
return;
}
@ -652,7 +652,7 @@ static void calldata_signal_callback_global(void *priv, const char *signal,
{
struct python_obs_callback *cb = priv;
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
signal_handler_remove_current();
return;
}
@ -767,7 +767,7 @@ static void hotkey_pressed(void *p_cb, bool pressed)
{
struct python_obs_callback *cb = p_cb;
if (cb->base.removed)
if (script_callback_removed(&cb->base))
return;
lock_callback(cb);
@ -803,7 +803,7 @@ static void hotkey_callback(void *p_cb, obs_hotkey_id id, obs_hotkey_t *hotkey,
{
struct python_obs_callback *cb = p_cb;
if (cb->base.removed)
if (script_callback_removed(&cb->base))
return;
if (pressed)
@ -873,7 +873,7 @@ static bool button_prop_clicked(obs_properties_t *props, obs_property_t *p,
struct python_obs_callback *cb = p_cb;
bool ret = false;
if (cb->base.removed)
if (script_callback_removed(&cb->base))
return false;
lock_callback(cb);
@ -937,7 +937,7 @@ static bool modified_callback(void *p_cb, obs_properties_t *props,
struct python_obs_callback *cb = p_cb;
bool ret = false;
if (cb->base.removed)
if (script_callback_removed(&cb->base))
return false;
lock_callback(cb);
@ -1333,6 +1333,24 @@ void obs_python_script_unload(obs_script_t *s)
if (!s->loaded || !python_loaded)
return;
/* ---------------------------- */
/* mark callbacks as removed */
lock_python();
/* XXX: scripts can potentially make callbacks when this happens, so
* this probably still isn't ideal as we can't predict how the
* processor or operating system is going to schedule things. a more
* ideal method would be to reference count the script objects and
* atomically share ownership with callbacks when they're called. */
struct script_callback *cb = data->first_callback;
while (cb) {
os_atomic_set_bool(&cb->removed, true);
cb = cb->next;
}
unlock_python();
/* ---------------------------- */
/* unhook tick function */
@ -1350,7 +1368,7 @@ void obs_python_script_unload(obs_script_t *s)
data->next_tick = NULL;
}
lock_python();
relock_python();
Py_XDECREF(data->tick);
Py_XDECREF(data->save);
@ -1364,7 +1382,7 @@ void obs_python_script_unload(obs_script_t *s)
/* ---------------------------- */
/* remove all callbacks */
struct script_callback *cb = data->first_callback;
cb = data->first_callback;
while (cb) {
struct script_callback *next = cb->next;
remove_script_callback(cb);
@ -1532,7 +1550,7 @@ static void python_tick(void *param, float seconds)
struct python_obs_timer *next = timer->next;
struct python_obs_callback *cb = python_obs_timer_cb(timer);
if (cb->base.removed) {
if (script_callback_removed(&cb->base)) {
python_obs_timer_remove(timer);
} else {
uint64_t elapsed = ts - timer->last_ts;
@ -1658,10 +1676,24 @@ bool obs_scripting_load_python(const char *python_path)
bfree(absolute_script_path);
#if __APPLE__
char *exec_path = os_get_executable_path_ptr("");
if (exec_path)
add_to_python_path(exec_path);
bfree(exec_path);
char *absolute_exec_path = os_get_executable_path_ptr("");
if (absolute_exec_path != NULL) {
add_to_python_path(absolute_exec_path);
struct dstr resources_path;
dstr_init_move_array(&resources_path, absolute_exec_path);
dstr_cat(&resources_path, "../Resources");
char *absolute_resources_path = os_get_abs_path_ptr(resources_path.array);
if (absolute_resources_path != NULL) {
add_to_python_path(absolute_resources_path);
bfree(absolute_resources_path);
}
dstr_free(&resources_path);
bfree(absolute_exec_path);
}
#endif
py_obspython = PyImport_ImportModule("obspython");

View file

@ -195,6 +195,7 @@ static inline bool py_error_(const char *func, int line)
#define py_error() py_error_(__FUNCTION__, __LINE__)
#define lock_python() PyGILState_STATE gstate = PyGILState_Ensure()
#define relock_python() gstate = PyGILState_Ensure()
#define unlock_python() PyGILState_Release(gstate)
struct py_source;

View file

@ -199,17 +199,23 @@ gl_egl_create_dmabuf_image(EGLDisplay egl_display, unsigned int width,
return NULL;
}
texture = gs_texture_create(width, height, color_format, 1, NULL,
GS_DYNAMIC);
if ((texture = gs_texture_create(width, height, color_format, 1, NULL,
GS_DYNAMIC)) == NULL) {
return NULL;
}
const GLuint gltex = *(GLuint *)gs_texture_get_obj(texture);
glBindTexture(GL_TEXTURE_2D, gltex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl_bind_texture(GL_TEXTURE_2D, gltex);
gl_tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl_tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image);
if (!gl_success("glEGLImageTargetTexture2DOES")) {
gs_texture_destroy(texture);
texture = NULL;
}
glBindTexture(GL_TEXTURE_2D, 0);
gl_bind_texture(GL_TEXTURE_2D, 0);
eglDestroyImage(egl_display, egl_image);
return texture;

View file

@ -936,7 +936,7 @@ EXPORT gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width,
EXPORT void gs_register_loss_callbacks(const struct gs_device_loss *callbacks);
EXPORT void gs_unregister_loss_callbacks(void *data);
#elif __linux__
#elif defined(__linux__) || defined(__FreeBSD__)
EXPORT gs_texture_t *gs_texture_create_from_dmabuf(
unsigned int width, unsigned int height, uint32_t drm_format,

File diff suppressed because it is too large Load diff

View file

@ -41,7 +41,7 @@
*
* Reset to zero each major or minor version
*/
#define LIBOBS_API_PATCH_VER 0
#define LIBOBS_API_PATCH_VER 4
#define MAKE_SEMANTIC_VERSION(major, minor, patch) \
((major << 24) | (minor << 16) | patch)

View file

@ -1086,13 +1086,15 @@ static inline void push_back_audio(struct obs_encoder *encoder,
struct audio_data *data, size_t size,
size_t offset_size)
{
if (offset_size >= size)
return;
size -= offset_size;
/* push in to the circular buffer */
if (size)
for (size_t i = 0; i < encoder->planes; i++)
circlebuf_push_back(&encoder->audio_input_buffer[i],
data->data[i] + offset_size, size);
for (size_t i = 0; i < encoder->planes; i++)
circlebuf_push_back(&encoder->audio_input_buffer[i],
data->data[i] + offset_size, size);
}
static inline size_t calc_offset_size(struct obs_encoder *encoder,

View file

@ -1289,8 +1289,14 @@ static inline bool inject_hotkey(void *data, size_t idx,
event->strict_modifiers)) {
bool pressed = binding->key.key == event->hotkey.key &&
event->pressed;
handle_binding(binding, event->hotkey.modifiers, false,
event->strict_modifiers, &pressed);
if (binding->key.key == OBS_KEY_NONE)
pressed = true;
if (pressed) {
binding->modifiers_match = true;
if (!binding->pressed)
press_released_binding(binding);
}
}
return true;

View file

@ -110,7 +110,7 @@ int obs_open_module(obs_module_t **module, const char *path,
/* HACK: Do not load obsolete obs-browser build on macOS; the
* obs-browser plugin used to live in the Application Support
* directory. */
if (astrstri(path, "Library/Application Support") != NULL &&
if (astrstri(path, "Library/Application Support/obs-studio") != NULL &&
astrstri(path, "obs-browser") != NULL) {
blog(LOG_WARNING, "Ignoring old obs-browser.so version");
return MODULE_ERROR;

View file

@ -84,7 +84,7 @@ static void platform_keyboard_keymap(void *data, struct wl_keyboard *keyboard,
UNUSED_PARAMETER(format);
obs_hotkeys_platform_t *plat = (obs_hotkeys_platform_t *)data;
char *keymap_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
char *keymap_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (keymap_shm == MAP_FAILED) {
close(fd);
return;

View file

@ -723,7 +723,12 @@ static inline void video_sleep(struct obs_core_video *video, bool raw_active,
*p_time = t;
count = 1;
} else {
count = (int)((os_gettime_ns() - cur_time) / interval_ns);
const uint64_t udiff = os_gettime_ns() - cur_time;
int64_t diff;
memcpy(&diff, &udiff, sizeof(diff));
const uint64_t clamped_diff =
(diff > (int64_t)interval_ns) ? diff : interval_ns;
count = (int)(clamped_diff / interval_ns);
*p_time = cur_time + interval_ns * count;
}

View file

@ -20,11 +20,13 @@
#include <shlobj.h>
#include <intrin.h>
#include <psapi.h>
#include <math.h>
#include "base.h"
#include "platform.h"
#include "darray.h"
#include "dstr.h"
#include "util_uint64.h"
#include "windows/win-registry.h"
#include "windows/win-version.h"
@ -331,9 +333,9 @@ void os_cpu_usage_info_destroy(os_cpu_usage_info_t *info)
bool os_sleepto_ns(uint64_t time_target)
{
const double freq = (double)get_clockfreq();
const uint64_t freq = get_clockfreq();
const LONGLONG count_target =
(LONGLONG)((double)time_target * freq / 1000000000.0);
util_mul_div64(time_target, freq, 1000000000);
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
@ -370,14 +372,9 @@ void os_sleep_ms(uint32_t duration)
uint64_t os_gettime_ns(void)
{
LARGE_INTEGER current_time;
double time_val;
QueryPerformanceCounter(&current_time);
time_val = (double)current_time.QuadPart;
time_val *= 1000000000.0;
time_val /= (double)get_clockfreq();
return (uint64_t)time_val;
return util_mul_div64(current_time.QuadPart, 1000000000,
get_clockfreq());
}
/* returns [folder]\[name] on windows */

View file

@ -298,8 +298,10 @@ static void destroy_session(obs_pipewire_data *obs_pw)
}
g_clear_pointer(&obs_pw->sender_name, bfree);
obs_enter_graphics();
g_clear_pointer(&obs_pw->cursor.texture, gs_texture_destroy);
g_clear_pointer(&obs_pw->texture, gs_texture_destroy);
obs_leave_graphics();
g_cancellable_cancel(obs_pw->cancellable);
g_clear_object(&obs_pw->cancellable);
}

View file

@ -79,8 +79,6 @@ static inline enum video_format v4l2_to_obs_video_format(uint_fast32_t format)
#endif
case V4L2_PIX_FMT_BGR24:
return VIDEO_FORMAT_BGR3;
case V4L2_PIX_FMT_MJPEG:
return VIDEO_FORMAT_I422;
default:
return VIDEO_FORMAT_NONE;
}

View file

@ -148,13 +148,6 @@ static void v4l2_prep_obs_frame(struct v4l2_data *data,
plane_offsets[1] = data->linesize * data->height;
plane_offsets[2] = data->linesize * data->height * 5 / 4;
break;
case V4L2_PIX_FMT_MJPEG:
frame->linesize[0] = 0;
frame->linesize[1] = 0;
frame->linesize[2] = 0;
plane_offsets[1] = 0;
plane_offsets[2] = 0;
break;
default:
frame->linesize[0] = data->linesize;
break;
@ -479,7 +472,8 @@ static void v4l2_format_list(int dev, obs_property_t *prop)
dstr_cat(&buffer, " (Emulated)");
if (v4l2_to_obs_video_format(fmt.pixelformat) !=
VIDEO_FORMAT_NONE) {
VIDEO_FORMAT_NONE ||
fmt.pixelformat == V4L2_PIX_FMT_MJPEG) {
obs_property_list_add_int(prop, buffer.array,
fmt.pixelformat);
blog(LOG_INFO, "Pixelformat: %s (available)",
@ -1003,7 +997,8 @@ static void v4l2_init(struct v4l2_data *data)
blog(LOG_ERROR, "Unable to set format");
goto fail;
}
if (v4l2_to_obs_video_format(data->pixfmt) == VIDEO_FORMAT_NONE) {
if (v4l2_to_obs_video_format(data->pixfmt) == VIDEO_FORMAT_NONE &&
data->pixfmt != V4L2_PIX_FMT_MJPEG) {
blog(LOG_ERROR, "Selected video format not supported");
goto fail;
}

View file

@ -45,7 +45,6 @@ int v4l2_init_mjpeg(struct v4l2_mjpeg_decoder *decoder)
}
decoder->context->flags2 |= AV_CODEC_FLAG2_FAST;
decoder->context->pix_fmt = AV_PIX_FMT_YUVJ422P;
if (avcodec_open2(decoder->context, decoder->codec, NULL) < 0) {
blog(LOG_ERROR, "failed to open codec");
@ -76,7 +75,6 @@ void v4l2_destroy_mjpeg(struct v4l2_mjpeg_decoder *decoder)
int v4l2_decode_mjpeg(struct obs_source_frame *out, uint8_t *data,
size_t length, struct v4l2_mjpeg_decoder *decoder)
{
decoder->packet->data = data;
decoder->packet->size = length;
if (avcodec_send_packet(decoder->context, decoder->packet) < 0) {
@ -94,5 +92,22 @@ int v4l2_decode_mjpeg(struct obs_source_frame *out, uint8_t *data,
out->linesize[i] = decoder->frame->linesize[i];
}
switch (decoder->context->pix_fmt) {
case AV_PIX_FMT_YUVJ422P:
case AV_PIX_FMT_YUV422P:
out->format = VIDEO_FORMAT_I422;
break;
case AV_PIX_FMT_YUVJ420P:
case AV_PIX_FMT_YUV420P:
out->format = VIDEO_FORMAT_I420;
break;
case AV_PIX_FMT_YUVJ444P:
case AV_PIX_FMT_YUV444P:
out->format = VIDEO_FORMAT_I444;
break;
default:
break;
}
return 0;
}

View file

@ -29,7 +29,7 @@ extern "C" {
* Data structure for mjpeg decoding
*/
struct v4l2_mjpeg_decoder {
AVCodec *codec;
const AVCodec *codec;
AVCodecContext *context;
AVPacket *packet;
AVFrame *frame;

View file

@ -185,7 +185,14 @@ static bool virtualcam_start(void *data)
return false;
}
n = scandir("/dev", &list, scanfilter, versionsort);
n = scandir("/dev", &list, scanfilter,
#if defined(__linux__)
versionsort
#else
alphasort
#endif
);
if (n == -1)
return false;

View file

@ -15,7 +15,8 @@ static inline bool device_is_input(char *device)
astrstri(device, "ishowu") == NULL &&
astrstri(device, "blackhole") == NULL &&
astrstri(device, "loopback") == NULL &&
astrstri(device, "groundcontrol") == NULL;
astrstri(device, "groundcontrol") == NULL &&
astrstri(device, "vbcable") == NULL;
}
static inline bool enum_success(OSStatus stat, const char *msg)

View file

@ -50,28 +50,28 @@ static inline void capture_frame(struct window_capture *wc)
size_t width = CGImageGetWidth(img);
size_t height = CGImageGetHeight(img);
CGRect rect = {{0, 0}, {width, height}};
da_resize(wc->buffer, width * height * 4);
uint8_t *data = wc->buffer.array;
if (!width || !height || CGImageGetBitsPerPixel(img) != 32 ||
CGImageGetBitsPerComponent(img) != 8) {
CGImageRelease(img);
return;
}
CGContextRef cg_context = CGBitmapContextCreate(
data, width, height, 8, width * 4, wc->color_space,
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
CGContextSetBlendMode(cg_context, kCGBlendModeCopy);
CGContextDrawImage(cg_context, rect, img);
CGContextRelease(cg_context);
CGImageRelease(img);
CGDataProviderRef provider = CGImageGetDataProvider(img);
CFDataRef data = CGDataProviderCopyData(provider);
struct obs_source_frame frame = {
.format = VIDEO_FORMAT_BGRA,
.width = width,
.height = height,
.data[0] = data,
.linesize[0] = width * 4,
.data[0] = (uint8_t *)CFDataGetBytePtr(data),
.linesize[0] = CGImageGetBytesPerRow(img),
.timestamp = ts,
};
obs_source_output_video(wc->source, &frame);
CGImageRelease(img);
CFRelease(data);
}
static void *capture_thread(void *data)

@ -1 +1 @@
Subproject commit b0d687937af876b52b69c46e276b4ab601b07f0e
Subproject commit 915761778ec1eae99e740ad4bf63b40db3142ee2

View file

@ -386,6 +386,15 @@ static void ffmpeg_source_tick(void *data, float seconds)
}
}
#define SRT_PROTO "srt"
#define RIST_PROTO "rist"
static bool requires_mpegts(char *path)
{
return !astrcmpi_n(path, SRT_PROTO, sizeof(SRT_PROTO) - 1) ||
!astrcmpi_n(path, RIST_PROTO, sizeof(RIST_PROTO) - 1);
}
static void ffmpeg_source_update(void *data, obs_data_t *settings)
{
struct ffmpeg_source *s = data;
@ -406,6 +415,10 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings)
input = (char *)obs_data_get_string(settings, "input");
input_format =
(char *)obs_data_get_string(settings, "input_format");
if (requires_mpegts(input)) {
input_format = "mpegts";
obs_data_set_string(settings, "input_format", "mpegts");
}
s->reconnect_delay_sec =
(int)obs_data_get_int(settings, "reconnect_delay_sec");
s->reconnect_delay_sec = s->reconnect_delay_sec == 0
@ -429,7 +442,9 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings)
s->is_clear_on_media_end =
obs_data_get_bool(settings, "clear_on_media_end");
s->restart_on_activate =
obs_data_get_bool(settings, "restart_on_activate");
!astrcmpi_n(input, RIST_PROTO, sizeof(RIST_PROTO) - 1)
? false
: obs_data_get_bool(settings, "restart_on_activate");
s->range = (enum video_range_type)obs_data_get_int(settings,
"color_range");
s->is_linear_alpha = obs_data_get_bool(settings, "linear_alpha");

View file

@ -96,6 +96,8 @@ static void *libfdk_create(obs_data_t *settings, obs_encoder_t *encoder)
int bitrate = (int)obs_data_get_int(settings, "bitrate") * 1000;
int afterburner = obs_data_get_bool(settings, "afterburner") ? 1 : 0;
audio_t *audio = obs_encoder_audio(encoder);
bool set_to_ADTS = obs_data_get_bool(settings, "set_to_ADTS");
int transmux = set_to_ADTS ? 2 : 0;
int mode = 0;
AACENC_ERROR err;
@ -159,7 +161,9 @@ static void *libfdk_create(obs_data_t *settings, obs_encoder_t *encoder)
aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATEMODE, 0));
CHECK_LIBFDK(
aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATE, bitrate));
CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, 0));
CHECK_LIBFDK(
aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, transmux));
CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AFTERBURNER,
afterburner));

View file

@ -16,6 +16,9 @@
******************************************************************************/
#include "rtmp-stream.h"
#ifdef _WIN32
#include <util/windows/win-version.h>
#endif
#ifndef SEC_TO_NSEC
#define SEC_TO_NSEC 1000000000ULL
@ -605,6 +608,24 @@ static void *send_thread(void *data)
os_set_thread_name("rtmp-stream: send_thread");
#if defined(_WIN32)
// Despite MSDN claiming otherwise, send buffer auto tuning on
// Windows 7 doesn't seem to work very well.
if (get_win_ver_int() == 0x601) {
DWORD cur_sendbuf_size;
DWORD desired_sendbuf_size = 524288;
socklen_t int_size = sizeof(int);
if (!getsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET,
SO_SNDBUF, (char *)&cur_sendbuf_size,
&int_size) &&
cur_sendbuf_size < desired_sendbuf_size) {
setsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET,
SO_SNDBUF, (char *)&desired_sendbuf_size,
sizeof(desired_sendbuf_size));
}
}
log_sndbuf_size(stream);
#endif

View file

@ -122,6 +122,7 @@ static void rtmp_custom_apply_settings(void *data, obs_data_t *video_settings,
strncmp(service->server, RTMP_PROTOCOL, strlen(RTMP_PROTOCOL)) !=
0) {
obs_data_set_bool(video_settings, "repeat_headers", true);
obs_data_set_bool(audio_settings, "set_to_ADTS", true);
}
}

View file

@ -61,6 +61,7 @@ static void init_hw_decoder(struct ffmpeg_decode *d)
}
if (hw_ctx) {
d->hw_device_ctx = hw_ctx;
d->decoder->hw_device_ctx = av_buffer_ref(hw_ctx);
d->hw = true;
}
@ -117,6 +118,9 @@ void ffmpeg_decode_free(struct ffmpeg_decode *decode)
if (decode->frame)
av_frame_free(&decode->frame);
if (decode->hw_device_ctx)
av_buffer_unref(&decode->hw_device_ctx);
if (decode->packet_buffer)
bfree(decode->packet_buffer);

View file

@ -37,6 +37,7 @@ extern "C" {
#endif
struct ffmpeg_decode {
AVBufferRef *hw_device_ctx;
AVCodecContext *decoder;
AVCodec *codec;

View file

@ -123,6 +123,9 @@ static bool load_placeholder_internal()
bool initialize_placeholder()
{
if (initialized)
return true;
GdiplusStartupInput si;
ULONG_PTR token;
GdiplusStartup(&token, &si, nullptr);

View file

@ -112,7 +112,8 @@ VCamFilter::VCamFilter()
VCamFilter::~VCamFilter()
{
SetEvent(thread_stop);
th.join();
if (th.joinable())
th.join();
video_queue_close(vq);
if (placeholder.scaled_data)

View file

@ -7,6 +7,7 @@
#include <util/windows/ComPtr.hpp>
#include <util/windows/WinHandle.hpp>
#include <util/windows/CoTaskMemPtr.hpp>
#include <util/windows/win-version.h>
#include <util/threading.h>
#include <util/util_uint64.h>
@ -324,7 +325,19 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_,
/* OBS will already load DLL on startup if it exists */
const HMODULE rtwq_module = GetModuleHandle(L"RTWorkQ.dll");
rtwq_supported = rtwq_module != NULL;
// while RTWQ was introduced in Win 8.1, it silently fails
// to capture Desktop Audio for some reason. Disable for now.
struct win_version_info win1703 = {};
win1703.major = 10;
win1703.minor = 0;
win1703.build = 15063;
win1703.revis = 0;
struct win_version_info ver;
get_win_ver(&ver);
if (win_version_compare(&ver, &win1703) >= 0)
rtwq_supported = rtwq_module != NULL;
if (rtwq_supported) {
rtwq_unlock_work_queue =
(PFN_RtwqUnlockWorkQueue)GetProcAddress(
@ -341,46 +354,54 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_,
(PFN_RtwqPutWaitingWorkItem)GetProcAddress(
rtwq_module, "RtwqPutWaitingWorkItem");
hr = rtwq_create_async_result(nullptr, &startCapture, nullptr,
&startCaptureAsyncResult);
if (FAILED(hr)) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw HRError(
"Could not create startCaptureAsyncResult", hr);
}
try {
hr = rtwq_create_async_result(nullptr, &startCapture,
nullptr,
&startCaptureAsyncResult);
if (FAILED(hr)) {
throw HRError(
"Could not create startCaptureAsyncResult",
hr);
}
hr = rtwq_create_async_result(nullptr, &sampleReady, nullptr,
&sampleReadyAsyncResult);
if (FAILED(hr)) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw HRError("Could not create sampleReadyAsyncResult",
hr);
}
hr = rtwq_create_async_result(nullptr, &sampleReady,
nullptr,
&sampleReadyAsyncResult);
if (FAILED(hr)) {
throw HRError(
"Could not create sampleReadyAsyncResult",
hr);
}
hr = rtwq_create_async_result(nullptr, &restart, nullptr,
&restartAsyncResult);
if (FAILED(hr)) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw HRError("Could not create restartAsyncResult",
hr);
}
hr = rtwq_create_async_result(nullptr, &restart,
nullptr,
&restartAsyncResult);
if (FAILED(hr)) {
throw HRError(
"Could not create restartAsyncResult",
hr);
}
DWORD taskId = 0;
DWORD id = 0;
hr = rtwq_lock_shared_work_queue(L"Capture", 0, &taskId, &id);
if (FAILED(hr)) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw HRError("RtwqLockSharedWorkQueue failed", hr);
}
DWORD taskId = 0;
DWORD id = 0;
hr = rtwq_lock_shared_work_queue(L"Capture", 0, &taskId,
&id);
if (FAILED(hr)) {
throw HRError("RtwqLockSharedWorkQueue failed",
hr);
}
startCapture.SetQueueId(id);
sampleReady.SetQueueId(id);
restart.SetQueueId(id);
} else {
startCapture.SetQueueId(id);
sampleReady.SetQueueId(id);
restart.SetQueueId(id);
} catch (HRError &err) {
blog(LOG_ERROR, "RTWQ setup failed: %s (0x%08X)",
err.str, err.hr);
rtwq_supported = false;
}
}
if (!rtwq_supported) {
captureThread = CreateThread(nullptr, 0,
WASAPISource::CaptureThread, this,
0, nullptr);