Compare commits

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

22 commits

Author SHA1 Message Date
jpark37
d21891b3ca win-wasapi: Fix Stop hang
Fix hang when audio capture is active in the background and stopped by
either closing OBS, or switching scene collections.
2022-10-01 11:28:47 -07:00
Matt Gajownik
05b6346250 frontend-tools: Display dialog when changing Python version
Runtime switching of Python version is not supported.
2022-10-01 07:41:37 -07:00
Matt Gajownik
89d9493468 frontend-tools: Display Python version in UI 2022-10-01 07:41:37 -07:00
PatTheMav
cee2e7db3f UI: Fix AutoRemux not working when FFmpeg output configured
When the Advanced Output configuration is set to use custom FFmpeg
output, automatic remuxing is disabled. Unfortunately this check will
also take place even if Simple Output is used (as the value is set in
the configuration data, but is not "active").

This check ensures that the check for custom FFmpeg output is only
applied when Advanced Output is enabled.
2022-10-01 07:14:06 -07:00
jpark37
8efb109144 win-wasapi: Don't reconnect when inactive
Unnecessary reconnect attempts may cause noticeable hitches.
2022-10-01 07:14:06 -07:00
jp9000
539318bd58 libobs: Update version to 28.0.3 2022-10-01 06:07:25 -07:00
Exeldro
41d56fd0a7 obs-scripting: Fix compile when python is not found 2022-10-01 06:06:50 -07:00
jp9000
30463ba91c obs-ffmpeg: Fix unpause causing certain encoders to fail
Due to the timeout detection, we need to factor in the current pause
offset to prevent pause from affecting the timeout.

Closes obsproject/obs-studio#7313
2022-10-01 06:06:50 -07:00
jp9000
4b39ea9df3 libobs: Add function to get encoder pause offset 2022-10-01 06:06:50 -07:00
gxalpha
c3c4adedd5 UI: Don't reselect SceneTree items if tree is clearing
In the scene tree, we currently prevent deselecting items by
automatically reselecting a new one to workaround a regression in Qt 6.2
that would let users unselect items even in SingleSelection QItemViews.
When clearing however, we explicitly want to unselect the currently
selected item, so we should avoid reselecting it in the SceneTree.
2022-10-01 06:06:50 -07:00
derrod
becbb5d885 UI: Remove executable bit from public key file 2022-10-01 06:06:50 -07:00
shinji3
dbab976843 obs-ffmpeg: Fix m3u8 recording in AMF 2022-10-01 06:06:50 -07:00
Aleix Pol
e52ad554aa linux-pipewire: Close sessions as we are done with them
This way the stream can be cleaned up. Otherwise, we closed them all
when OBS closes which works but is not entirely correct.
2022-09-28 19:33:04 -07:00
jpark37
fb972b2520 libobs/media-io: Restore color range conversion
FFmpeg needs color range during context init to handle conversions.
sws_setColorspaceDetails after the fact is too late.
2022-09-28 19:33:04 -07:00
Matt Gajownik
972628346f CI: Downgrade Sphinx to fix docs build error
This should be reverted once sphinx_rtd_theme is updated.

See https://github.com/readthedocs/sphinx_rtd_theme/issues/1343
2022-09-28 19:33:04 -07:00
jpark37
b27c899761 libobs/media-io: Avoid scaler for range diff
Green tint appears when using converting full NV12 to limited NV12 with
swscale, so just avoid creating scaler by color range for now.
2022-09-28 19:33:04 -07:00
Kurt Kartaltepe
8e9d9fba24 linux-capture: Fixup window name/class checking
There were a couple mistakes here that caused the fallback checks other
than window ID to fail to ever return valid results. This restores this
functionality that was broken since the c++/c transition.

fixes #7404
2022-09-28 19:33:04 -07:00
Ryan Foster
d314eafdc6 obs-ffmpeg: Cap AMF encoder at 100 Mbps
The AMF docs specify that the maximum value for TARGET_BITRATE and
PEAK_BITRATE is 100,000,000 bit/s. Trying to set values above this
maximum value results in AMF choosing from its defaults instead of
capping at the maximum value. Let's cap at 100,000 Kbps in the UI to
prevent users from running in to this.

Fixes GitHub Issue obsproject/obs-studio#7423.
2022-09-28 19:33:04 -07:00
cg2121
ea94eeb045 UI: Fix color of popout icon
In the light theme, the dock popout icon was the wrong color,
basically making it invisible.
2022-09-28 19:33:04 -07:00
cg2121
19d92a29a2 UI: Fix dock titlebar icons not loading
The icons had the wrong name in the qss files. The docks were missing
the icons on at least Linux. I have no idea why the dock icons still
worked on Windows though.
2022-09-28 19:33:04 -07:00
jp9000
087ee39e2d libobs: Update version to 28.0.2 2022-09-22 09:40:51 -07:00
pkv
afb755f8a0 obs-filter: Update model for NVIDIA Audio FX
This updates the model when swapping effects with NVIDIA Audio Effects.
This fixes a bug where the model was not updated. :(

Signed-off-by: pkv <pkv@obsproject.com>
2022-09-22 09:28:38 -07:00
30 changed files with 280 additions and 56 deletions

View file

@ -42,6 +42,7 @@ jobs:
build_only: True
target_branch: 'master'
target_path: '../home/_build'
pre_build_commands: 'pip install -Iv sphinx==5.1.1'
- uses: actions/upload-artifact@v3
with:
name: OBS Studio Documentation (HTML)

0
UI/data/OBSPublicRSAKey.pem Executable file → Normal file
View file

View file

@ -300,8 +300,8 @@ QDockWidget {
font-size: 10.5pt;
font-weight: bold;
titlebar-close-icon: url('./Dark/Close.svg');
titlebar-normal-icon: url('./Dark/Popout.svg');
titlebar-close-icon: url('./Dark/close.svg');
titlebar-normal-icon: url('./Dark/popout.svg');
}
QDockWidget::title {

View file

@ -300,8 +300,8 @@ QDockWidget {
font-size: 10.5pt;
font-weight: bold;
titlebar-close-icon: url('./Dark/Close.svg');
titlebar-normal-icon: url('./Dark/Popout.svg');
titlebar-close-icon: url('./Dark/close.svg');
titlebar-normal-icon: url('./Dark/popout.svg');
}
QDockWidget::title {

View file

@ -300,8 +300,8 @@ QDockWidget {
font-size: 10.5pt;
font-weight: bold;
titlebar-close-icon: url('./Light/Close.svg');
titlebar-normal-icon: url('./Light/Popout.svg');
titlebar-close-icon: url('./Light/close.svg');
titlebar-normal-icon: url('./Light/popout.svg');
}
QDockWidget::title {

View file

@ -1 +1 @@
<svg class="feather feather-plus" fill="none" stroke="#FFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" version="1.1" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="24" height="20" /><rect x="2" y="2" width="24" height="4" style="fill:#fff" /><rect x="14" y="18" width="24" height="20" /><rect x="14" y="18" width="24" height="4" style="fill:#fff" /></svg>
<svg class="feather feather-plus" fill="none" stroke="#202020" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" version="1.1" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="24" height="20" /><rect x="2" y="2" width="24" height="4" style="fill:#202020" /><rect x="14" y="18" width="24" height="20" /><rect x="14" y="18" width="24" height="4" style="fill:#202020" /></svg>

Before

Width:  |  Height:  |  Size: 410 B

After

Width:  |  Height:  |  Size: 419 B

Before After
Before After

View file

@ -302,8 +302,8 @@ QDockWidget {
font-size: 10.5pt;
font-weight: bold;
titlebar-close-icon: url('./Dark/Close.svg');
titlebar-normal-icon: url('./Dark/Popout.svg');
titlebar-close-icon: url('./Dark/close.svg');
titlebar-normal-icon: url('./Dark/popout.svg');
}
QDockWidget::title {

View file

@ -304,8 +304,8 @@ QDockWidget {
font-size: 10.5pt;
font-weight: bold;
titlebar-close-icon: url('./Dark/Close.svg');
titlebar-normal-icon: url('./Dark/Popout.svg');
titlebar-close-icon: url('./Dark/close.svg');
titlebar-normal-icon: url('./Dark/popout.svg');
}
QDockWidget::title {

View file

@ -39,6 +39,10 @@ PythonSettings="Python Settings"
PythonSettings.PythonInstallPath32bit="Python Install Path (32bit)"
PythonSettings.PythonInstallPath64bit="Python Install Path (64bit)"
PythonSettings.BrowsePythonPath="Browse Python Path"
PythonSettings.PythonVersion="Loaded Python Version: %1"
PythonSettings.PythonNotLoaded="Python not currently loaded"
PythonSettings.AlreadyLoaded.Title="Python Already Loaded"
PythonSettings.AlreadyLoaded.Message="A copy of Python %1 is already loaded. To load the newly selected Python version, please restart OBS."
ScriptLogWindow="Script Log"
Description="Description"
ScriptDescriptionLink.Text="Open this link in your default web browser?"

View file

@ -268,6 +268,13 @@
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="pythonVersionLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View file

@ -194,6 +194,7 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool)
config_get_string(config, "Python", "Path" ARCH_NAME);
ui->pythonPath->setText(path);
ui->pythonPathLabel->setText(obs_module_text(PYTHONPATH_LABEL_TEXT));
updatePythonVersionLabel();
#else
delete ui->pythonSettingsTab;
ui->pythonSettingsTab = nullptr;
@ -219,6 +220,20 @@ ScriptsTool::~ScriptsTool()
ui->scripts->currentRow());
}
void ScriptsTool::updatePythonVersionLabel()
{
QString label;
if (obs_scripting_python_loaded()) {
char version[8];
obs_scripting_python_version(version, sizeof(version));
label = QString(obs_module_text("PythonSettings.PythonVersion"))
.arg(version);
} else {
label = obs_module_text("PythonSettings.PythonNotLoaded");
}
ui->pythonVersionLabel->setText(label);
}
void ScriptsTool::RemoveScript(const char *path)
{
for (size_t i = 0; i < scriptData->scripts.size(); i++) {
@ -455,11 +470,29 @@ void ScriptsTool::on_pythonPathBrowse_clicked()
ui->pythonPath->setText(newPath);
if (obs_scripting_python_loaded())
bool loaded = obs_scripting_python_loaded();
if (loaded && !newPath.isEmpty() && curPath.compare(newPath) != 0) {
char version[8];
obs_scripting_python_version(version, sizeof(version));
QString message =
QString(obs_module_text(
"PythonSettings.AlreadyLoaded.Message"))
.arg(version);
OBSMessageBox::information(
this,
obs_module_text("PythonSettings.AlreadyLoaded.Title"),
message);
return;
} else if (loaded) {
return;
}
if (!obs_scripting_load_python(path))
return;
updatePythonVersionLabel();
for (OBSScript &script : scriptData->scripts) {
enum obs_script_lang lang = obs_script_get_lang(script);
if (lang == OBS_SCRIPT_LANG_PYTHON) {

View file

@ -31,6 +31,8 @@ class ScriptsTool : public QDialog {
std::unique_ptr<Ui_ScriptsTool> ui;
QWidget *propertiesView = nullptr;
void updatePythonVersionLabel();
public:
ScriptsTool();
~ScriptsTool();

View file

@ -37,10 +37,14 @@ void DeleteListItem(QListWidget *widget, QListWidgetItem *item)
void ClearListItems(QListWidget *widget)
{
// Workaround for the SceneTree workaround for QTBUG-105870
widget->setProperty("clearing", true);
widget->setCurrentItem(nullptr, QItemSelectionModel::Clear);
for (int i = 0; i < widget->count(); i++)
delete widget->itemWidget(widget->item(i));
widget->clear();
widget->setProperty("clearing", false);
}

View file

@ -252,6 +252,7 @@ void SceneTree::rowsInserted(const QModelIndex &parent, int start, int end)
void SceneTree::selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected)
{
if (selected.count() == 0 && deselected.count() > 0)
if (selected.count() == 0 && deselected.count() > 0 &&
!property("clearing").toBool())
setCurrentRow(deselected.indexes().front().row());
}

View file

@ -7122,17 +7122,31 @@ void OBSBasic::StreamingStop(int code, QString last_error)
void OBSBasic::AutoRemux(QString input, bool no_show)
{
bool autoRemux = config_get_bool(Config(), "Video", "AutoRemux");
auto config = Config();
bool autoRemux = config_get_bool(config, "Video", "AutoRemux");
if (!autoRemux)
return;
const char *recType = config_get_string(Config(), "AdvOut", "RecType");
bool isSimpleMode = false;
bool ffmpegOutput = astrcmpi(recType, "FFmpeg") == 0;
const char *mode = config_get_string(config, "Output", "Mode");
if (!mode) {
isSimpleMode = true;
} else {
isSimpleMode = strcmp(mode, "Simple") == 0;
}
if (ffmpegOutput)
return;
if (!isSimpleMode) {
const char *recType =
config_get_string(config, "AdvOut", "RecType");
bool ffmpegOutput = astrcmpi(recType, "FFmpeg") == 0;
if (ffmpegOutput)
return;
}
if (input.isEmpty())
return;

View file

@ -1590,6 +1590,17 @@ bool obs_scripting_python_runtime_linked(void)
return (bool)RUNTIME_LINK;
}
void obs_scripting_python_version(char *version, size_t version_length)
{
#if RUNTIME_LINK
snprintf(version, version_length, "%d.%d", python_version.major,
python_version.minor);
#else
snprintf(version, version_length, "%d.%d", PY_MAJOR_VERSION,
PY_MINOR_VERSION);
#endif
}
bool obs_scripting_python_loaded(void)
{
return python_loaded;

View file

@ -458,4 +458,9 @@ bool obs_scripting_python_runtime_linked(void)
{
return (bool)true;
}
void obs_scripting_python_version(char *version, size_t version_length)
{
version[0] = 0;
}
#endif

View file

@ -46,6 +46,7 @@ EXPORT void obs_scripting_set_log_callback(scripting_log_handler_t handler,
void *param);
EXPORT bool obs_scripting_python_runtime_linked(void);
EXPORT void obs_scripting_python_version(char *version, size_t version_length);
EXPORT bool obs_scripting_python_loaded(void);
EXPORT bool obs_scripting_load_python(const char *python_path);

View file

@ -19,6 +19,7 @@
#include "video-scaler.h"
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
struct video_scaler {
@ -188,16 +189,27 @@ int video_scaler_create(video_scaler_t **scaler_out,
goto fail;
}
scaler->swscale = sws_getCachedContext(NULL, src->width, src->height,
format_src, dst->width,
dst->height, format_dst,
scale_type, NULL, NULL, NULL);
scaler->swscale = sws_alloc_context();
if (!scaler->swscale) {
blog(LOG_ERROR, "video_scaler_create: Could not create "
"swscale");
goto fail;
}
av_opt_set_int(scaler->swscale, "sws_flags", scale_type, 0);
av_opt_set_int(scaler->swscale, "srcw", src->width, 0);
av_opt_set_int(scaler->swscale, "srch", src->height, 0);
av_opt_set_int(scaler->swscale, "dstw", dst->width, 0);
av_opt_set_int(scaler->swscale, "dsth", dst->height, 0);
av_opt_set_int(scaler->swscale, "src_format", format_src, 0);
av_opt_set_int(scaler->swscale, "dst_format", format_dst, 0);
av_opt_set_int(scaler->swscale, "src_range", range_src, 0);
av_opt_set_int(scaler->swscale, "dst_range", range_dst, 0);
if (sws_init_context(scaler->swscale, NULL, NULL) < 0) {
blog(LOG_ERROR, "video_scaler_create: sws_init_context failed");
goto fail;
}
ret = sws_setColorspaceDetails(scaler->swscale, coeff_src, range_src,
coeff_dst, range_dst, 0, FIXED_1_0,
FIXED_1_0);

View file

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

View file

@ -1534,3 +1534,8 @@ void obs_encoder_set_last_error(obs_encoder_t *encoder, const char *message)
else
encoder->last_error_message = NULL;
}
uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder)
{
return encoder ? encoder->pause.ts_offset : 0;
}

View file

@ -2413,6 +2413,8 @@ EXPORT const char *obs_encoder_get_last_error(obs_encoder_t *encoder);
EXPORT void obs_encoder_set_last_error(obs_encoder_t *encoder,
const char *message);
EXPORT uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder);
/* ------------------------------------------------------------------------- */
/* Stream Services */

View file

@ -310,11 +310,10 @@ xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp,
char *wcls = bzalloc(strEnd - thirdStr + 1);
memcpy(wname, secondStr, secondMark - secondStr);
memcpy(wcls, thirdStr, strEnd - thirdStr);
xcb_window_t wid = (xcb_window_t)strtol(str, NULL, 10);
ret = (xcb_window_t)strtol(str, NULL, 10);
// first try to find a match by the window-id
if (da_find(tlw, &wid, 0) != DARRAY_INVALID) {
ret = wid;
if (da_find(tlw, &ret, 0) != DARRAY_INVALID) {
goto cleanup2;
}
@ -325,8 +324,8 @@ xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp,
struct dstr cwname = xcomp_window_name(conn, disp, cwin);
struct dstr cwcls = xcomp_window_class(conn, cwin);
bool found = (strcmp(wname, cwname.array) == 0 &&
strcmp(wcls, cwcls.array));
bool found = strcmp(wname, cwname.array) == 0 &&
strcmp(wcls, cwcls.array) == 0;
dstr_free(&cwname);
dstr_free(&cwcls);
@ -345,7 +344,7 @@ cleanup2:
bfree(wcls);
cleanup1:
da_free(tlw);
return wid;
return ret;
}
void xcomp_cleanup_pixmap(Display *disp, struct xcompcap *s)

View file

@ -627,6 +627,20 @@ static bool reload_session_cb(obs_properties_t *properties,
g_clear_pointer(&capture->restore_token, bfree);
g_clear_pointer(&capture->obs_pw, obs_pipewire_destroy);
if (capture->session_handle) {
blog(LOG_DEBUG, "[pipewire] Cleaning previous session %s",
capture->session_handle);
g_dbus_connection_call(portal_get_dbus_connection(),
"org.freedesktop.portal.Desktop",
capture->session_handle,
"org.freedesktop.portal.Session",
"Close", NULL, NULL,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL,
NULL);
g_clear_pointer(&capture->session_handle, g_free);
}
init_screencast_capture(capture);
return false;

View file

@ -375,18 +375,33 @@ inline static void ts_offset_update(struct ffmpeg_muxer *stream,
stream->found_audio[packet->track_idx] = true;
}
static bool ffmpeg_mux_start(void *data)
static inline void update_encoder_settings(struct ffmpeg_muxer *stream,
const char *path)
{
struct ffmpeg_muxer *stream = data;
obs_data_t *settings;
const char *path;
obs_encoder_t *vencoder = obs_output_get_video_encoder(stream->output);
const char *ext = strrchr(path, '.');
/* if using m3u8, repeat headers */
if (ext && strcmp(ext, ".m3u8") == 0) {
obs_data_t *settings = obs_encoder_get_settings(vencoder);
obs_data_set_bool(settings, "repeat_headers", true);
obs_encoder_update(vencoder, settings);
obs_data_release(settings);
}
}
static inline bool ffmpeg_mux_start_internal(struct ffmpeg_muxer *stream,
obs_data_t *settings)
{
const char *path = obs_data_get_string(settings, "path");
update_encoder_settings(stream, path);
if (!obs_output_can_begin_data_capture(stream->output, 0))
return false;
if (!obs_output_initialize_encoders(stream->output, 0))
return false;
settings = obs_output_get_settings(stream->output);
if (stream->is_network) {
obs_service_t *service;
service = obs_output_get_service(stream->output);
@ -395,7 +410,6 @@ static bool ffmpeg_mux_start(void *data)
path = obs_service_get_url(service);
stream->split_file = false;
} else {
path = obs_data_get_string(settings, "path");
stream->max_time =
obs_data_get_int(settings, "max_time_sec") * 1000000LL;
@ -427,7 +441,6 @@ static bool ffmpeg_mux_start(void *data)
}
start_pipe(stream, path);
obs_data_release(settings);
if (!stream->pipe) {
obs_output_set_last_error(
@ -446,6 +459,17 @@ static bool ffmpeg_mux_start(void *data)
return true;
}
static bool ffmpeg_mux_start(void *data)
{
struct ffmpeg_muxer *stream = data;
obs_data_t *settings = obs_output_get_settings(stream->output);
bool success = ffmpeg_mux_start_internal(stream, settings);
obs_data_release(settings);
return success;
}
int deactivate(struct ffmpeg_muxer *stream, int code)
{
int ret = -1;

View file

@ -221,6 +221,8 @@ bool ffmpeg_video_encode(struct ffmpeg_video_encoder *enc,
AVPacket av_pkt = {0};
bool timeout = false;
const int64_t cur_ts = (int64_t)os_gettime_ns();
const int64_t pause_offset =
(int64_t)obs_encoder_get_pause_offset(enc->encoder);
int got_packet;
int ret;
@ -270,7 +272,7 @@ bool ffmpeg_video_encode(struct ffmpeg_video_encoder *enc,
debug("cur: %lld, packet: %lld, diff: %lld", cur_ts,
recv_ts_nsec, cur_ts - recv_ts_nsec);
#endif
if ((cur_ts - recv_ts_nsec) > TIMEOUT_MAX_NSEC) {
if ((cur_ts - recv_ts_nsec - pause_offset) > TIMEOUT_MAX_NSEC) {
char timeout_str[16];
snprintf(timeout_str, sizeof(timeout_str), "%d",
TIMEOUT_MAX_SEC);

View file

@ -964,7 +964,7 @@ static obs_properties_t *amf_properties_internal(bool hevc)
obs_property_set_modified_callback(p, rate_control_modified);
p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"),
50, 300000, 50);
50, 100000, 50);
obs_property_int_set_suffix(p, " Kbps");
obs_properties_add_int(props, "cqp", obs_module_text("NVENC.CQLevel"),
@ -1158,6 +1158,11 @@ static bool amf_avc_init(void *data, obs_data_t *settings)
: 250;
set_avc_property(enc, IDR_PERIOD, gop_size);
bool repeat_headers = obs_data_get_bool(settings, "repeat_headers");
if (repeat_headers)
set_avc_property(enc, HEADER_INSERTION_SPACING, gop_size);
set_avc_property(enc, DE_BLOCKING_FILTER, true);
const char *ffmpeg_opts = obs_data_get_string(settings, "ffmpeg_opts");

View file

@ -119,6 +119,7 @@ struct noise_suppress_data {
char *model;
bool nvafx_initialized;
const char *fx;
char *sdk_path;
/* Resampler */
audio_resampler_t *nvafx_resampler;
@ -217,6 +218,7 @@ static void noise_suppress_destroy(void *data)
audio_resampler_destroy(ng->nvafx_resampler_back);
}
bfree(ng->model);
bfree(ng->sdk_path);
bfree((void *)ng->fx);
if (ng->nvafx_enabled) {
if (ng->use_nvafx)
@ -425,7 +427,25 @@ static inline enum speaker_layout convert_speaker_layout(uint8_t channels)
return SPEAKERS_UNKNOWN;
}
}
#ifdef LIBNVAFX_ENABLED
static void set_model(void *data, const char *method)
{
struct noise_suppress_data *ng = data;
const char *file;
if (strcmp(NVAFX_EFFECT_DEREVERB, method) == 0)
file = NVAFX_EFFECT_DEREVERB_MODEL;
else if (strcmp(NVAFX_EFFECT_DEREVERB_DENOISER, method) == 0)
file = NVAFX_EFFECT_DEREVERB_DENOISER_MODEL;
else
file = NVAFX_EFFECT_DENOISER_MODEL;
size_t size = strlen(ng->sdk_path) + strlen(file) + 1;
char *buffer = (char *)bmalloc(size);
strcpy(buffer, ng->sdk_path);
strcat(buffer, file);
ng->model = buffer;
}
#endif
static void noise_suppress_update(void *data, obs_data_t *s)
{
struct noise_suppress_data *ng = data;
@ -444,6 +464,8 @@ static void noise_suppress_update(void *data, obs_data_t *s)
strcmp(method, S_METHOD_NVAFX_DEREVERB) == 0 ||
strcmp(method, S_METHOD_NVAFX_DEREVERB_DENOISER) == 0;
#ifdef LIBNVAFX_ENABLED
if (nvafx_requested)
set_model(ng, method);
float intensity = (float)obs_data_get_double(s, S_NVAFX_INTENSITY);
if (ng->use_nvafx && ng->nvafx_initialized) {
if (intensity != ng->intensity_ratio &&
@ -469,6 +491,7 @@ static void noise_suppress_update(void *data, obs_data_t *s)
bfree((void *)ng->fx);
ng->fx = bstrdup(method);
ng->intensity_ratio = intensity;
set_model(ng, method);
os_atomic_set_bool(&ng->reinit_done, false);
pthread_mutex_lock(&ng->nvafx_mutex);
for (int i = 0; i < (int)ng->channels; i++) {
@ -736,13 +759,9 @@ static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter)
ng->nvafx_enabled = false;
do_log(LOG_ERROR, "NVAFX redist is not installed.");
} else {
const char *file = "\\models\\denoiser_48k.trtpkg";
size_t size = strlen(sdk_path) + strlen(file) + 1;
char *buffer = (char *)bmalloc(size);
strcpy(buffer, sdk_path);
strcat(buffer, file);
ng->model = buffer;
size_t size = sizeof(sdk_path) + 1;
ng->sdk_path = bmalloc(size);
strcpy(ng->sdk_path, sdk_path);
ng->nvafx_enabled = true;
ng->nvafx_initialized = false;
ng->nvafx_loading = false;

View file

@ -18,6 +18,12 @@ static HMODULE nv_cuda = NULL;
#define NVAFX_EFFECT_AEC "aec"
#define NVAFX_EFFECT_SUPERRES "superres"
/** Model paths */
#define NVAFX_EFFECT_DENOISER_MODEL "\\models\\denoiser_48k.trtpkg"
#define NVAFX_EFFECT_DEREVERB_MODEL "\\models\\dereverb_48k.trtpkg"
#define NVAFX_EFFECT_DEREVERB_DENOISER_MODEL \
"\\models\\dereverb_denoiser_48k.trtpkg"
#define NVAFX_CHAINED_EFFECT_DENOISER_16k_SUPERRES_16k_TO_48k \
"denoiser16k_superres16kto48k"
#define NVAFX_CHAINED_EFFECT_DEREVERB_16k_SUPERRES_16k_TO_48k \

View file

@ -182,7 +182,7 @@ class WASAPISource {
std::atomic<bool> isDefaultDevice = false;
bool previouslyFailed = false;
WinHandle reconnectThread;
WinHandle reconnectThread = NULL;
class CallbackStartCapture : public ARtwqAsyncCallback {
public:
@ -238,6 +238,7 @@ class WASAPISource {
WinHandle stopSignal;
WinHandle receiveSignal;
WinHandle restartSignal;
WinHandle reconnectExitSignal;
WinHandle exitSignal;
WinHandle initSignal;
DWORD reconnectDuration = 0;
@ -298,6 +299,9 @@ public:
void Update(obs_data_t *settings);
void OnWindowChanged(obs_data_t *settings);
void Activate();
void Deactivate();
void SetDefaultDevice(EDataFlow flow, ERole role, LPCWSTR id);
void OnStartCapture();
@ -390,6 +394,10 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_,
if (!restartSignal.Valid())
throw "Could not create restart signal";
reconnectExitSignal = CreateEvent(nullptr, true, false, nullptr);
if (!reconnectExitSignal.Valid())
throw "Could not create reconnect exit signal";
exitSignal = CreateEvent(nullptr, true, false, nullptr);
if (!exitSignal.Valid())
throw "Could not create exit signal";
@ -402,11 +410,6 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_,
if (!reconnectSignal.Valid())
throw "Could not create reconnect signal";
reconnectThread = CreateThread(
nullptr, 0, WASAPISource::ReconnectThread, this, 0, nullptr);
if (!reconnectThread.Valid())
throw "Failed to create reconnect thread";
notify = new WASAPINotify(this);
if (!notify)
throw "Could not create WASAPINotify";
@ -532,11 +535,19 @@ void WASAPISource::Stop()
if (rtwq_supported)
SetEvent(receiveSignal);
WaitForSingleObject(idleSignal, INFINITE);
if (reconnectThread.Valid()) {
WaitForSingleObject(idleSignal, INFINITE);
} else {
const HANDLE sigs[] = {reconnectSignal, idleSignal};
WaitForMultipleObjects(_countof(sigs), sigs, false, INFINITE);
}
SetEvent(exitSignal);
WaitForSingleObject(reconnectThread, INFINITE);
if (reconnectThread.Valid()) {
SetEvent(reconnectExitSignal);
WaitForSingleObject(reconnectThread, INFINITE);
}
if (rtwq_supported)
rtwq_unlock_work_queue(sampleReady.GetQueueId());
@ -657,6 +668,25 @@ void WASAPISource::OnWindowChanged(obs_data_t *settings)
SetEvent(restartSignal);
}
void WASAPISource::Activate()
{
if (!reconnectThread.Valid()) {
ResetEvent(reconnectExitSignal);
reconnectThread = CreateThread(nullptr, 0,
WASAPISource::ReconnectThread,
this, 0, nullptr);
}
}
void WASAPISource::Deactivate()
{
if (reconnectThread.Valid()) {
SetEvent(reconnectExitSignal);
WaitForSingleObject(reconnectThread, INFINITE);
reconnectThread = NULL;
}
}
ComPtr<IMMDevice> WASAPISource::InitDevice(IMMDeviceEnumerator *enumerator,
bool isDefaultDevice,
SourceType type,
@ -1005,10 +1035,15 @@ DWORD WINAPI WASAPISource::ReconnectThread(LPVOID param)
WASAPISource *source = (WASAPISource *)param;
const HANDLE sigs[] = {
source->exitSignal,
source->reconnectExitSignal,
source->reconnectSignal,
};
const HANDLE reconnect_sigs[] = {
source->reconnectExitSignal,
source->stopSignal,
};
bool exit = false;
while (!exit) {
const DWORD ret = WaitForMultipleObjects(_countof(sigs), sigs,
@ -1020,8 +1055,10 @@ DWORD WINAPI WASAPISource::ReconnectThread(LPVOID param)
default:
assert(ret == (WAIT_OBJECT_0 + 1));
if (source->reconnectDuration > 0) {
WaitForSingleObject(source->stopSignal,
source->reconnectDuration);
WaitForMultipleObjects(
_countof(reconnect_sigs),
reconnect_sigs, false,
source->reconnectDuration);
}
source->Start();
}
@ -1426,6 +1463,16 @@ static void UpdateWASAPISource(void *obj, obs_data_t *settings)
static_cast<WASAPISource *>(obj)->Update(settings);
}
static void ActivateWASAPISource(void *obj)
{
static_cast<WASAPISource *>(obj)->Activate();
}
static void DeactivateWASAPISource(void *obj)
{
static_cast<WASAPISource *>(obj)->Deactivate();
}
static bool UpdateWASAPIMethod(obs_properties_t *props, obs_property_t *,
obs_data_t *settings)
{
@ -1542,6 +1589,8 @@ void RegisterWASAPIInput()
info.create = CreateWASAPIInput;
info.destroy = DestroyWASAPISource;
info.update = UpdateWASAPISource;
info.activate = ActivateWASAPISource;
info.deactivate = DeactivateWASAPISource;
info.get_defaults = GetWASAPIDefaultsInput;
info.get_properties = GetWASAPIPropertiesInput;
info.icon_type = OBS_ICON_TYPE_AUDIO_INPUT;
@ -1559,6 +1608,8 @@ void RegisterWASAPIDeviceOutput()
info.create = CreateWASAPIDeviceOutput;
info.destroy = DestroyWASAPISource;
info.update = UpdateWASAPISource;
info.activate = ActivateWASAPISource;
info.deactivate = DeactivateWASAPISource;
info.get_defaults = GetWASAPIDefaultsDeviceOutput;
info.get_properties = GetWASAPIPropertiesDeviceOutput;
info.icon_type = OBS_ICON_TYPE_AUDIO_OUTPUT;
@ -1576,6 +1627,8 @@ void RegisterWASAPIProcessOutput()
info.create = CreateWASAPIProcessOutput;
info.destroy = DestroyWASAPISource;
info.update = UpdateWASAPISource;
info.activate = ActivateWASAPISource;
info.deactivate = DeactivateWASAPISource;
info.get_defaults = GetWASAPIDefaultsProcessOutput;
info.get_properties = GetWASAPIPropertiesProcessOutput;
info.icon_type = OBS_ICON_TYPE_PROCESS_AUDIO_OUTPUT;