Compare commits
22 commits
master
...
release/28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d21891b3ca | ||
|
|
05b6346250 | ||
|
|
89d9493468 | ||
|
|
cee2e7db3f | ||
|
|
8efb109144 | ||
|
|
539318bd58 | ||
|
|
41d56fd0a7 | ||
|
|
30463ba91c | ||
|
|
4b39ea9df3 | ||
|
|
c3c4adedd5 | ||
|
|
becbb5d885 | ||
|
|
dbab976843 | ||
|
|
e52ad554aa | ||
|
|
fb972b2520 | ||
|
|
972628346f | ||
|
|
b27c899761 | ||
|
|
8e9d9fba24 | ||
|
|
d314eafdc6 | ||
|
|
ea94eeb045 | ||
|
|
19d92a29a2 | ||
|
|
087ee39e2d | ||
|
|
afb755f8a0 |
30 changed files with 280 additions and 56 deletions
1
.github/workflows/docs.yml
vendored
1
.github/workflows/docs.yml
vendored
|
|
@ -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
0
UI/data/OBSPublicRSAKey.pem
Executable file → Normal 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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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?"
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ class ScriptsTool : public QDialog {
|
|||
std::unique_ptr<Ui_ScriptsTool> ui;
|
||||
QWidget *propertiesView = nullptr;
|
||||
|
||||
void updatePythonVersionLabel();
|
||||
|
||||
public:
|
||||
ScriptsTool();
|
||||
~ScriptsTool();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
11
deps/obs-scripting/obs-scripting-python.c
vendored
11
deps/obs-scripting/obs-scripting-python.c
vendored
|
|
@ -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;
|
||||
|
|
|
|||
5
deps/obs-scripting/obs-scripting.c
vendored
5
deps/obs-scripting/obs-scripting.c
vendored
|
|
@ -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
|
||||
|
|
|
|||
1
deps/obs-scripting/obs-scripting.h
vendored
1
deps/obs-scripting/obs-scripting.h
vendored
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue