Changes of Revision 97

obs-studio.changes Changed
x
 
1
@@ -1,4 +1,29 @@
2
 -------------------------------------------------------------------
3
+Sat Oct 22 09:19:53 UTC 2022 - hpj@urpla.net
4
+
5
+- Update to version 28.0.3:
6
+  * win-wasapi: Fix Stop hang
7
+  * frontend-tools: Display dialog when changing Python version
8
+  * frontend-tools: Display Python version in UI
9
+  * UI: Fix AutoRemux not working when FFmpeg output configured
10
+  * win-wasapi: Don't reconnect when inactive
11
+  * libobs: Update version to 28.0.3
12
+  * obs-scripting: Fix compile when python is not found
13
+  * obs-ffmpeg: Fix unpause causing certain encoders to fail
14
+  * libobs: Add function to get encoder pause offset
15
+  * UI: Don't reselect SceneTree items if tree is clearing
16
+  * UI: Remove executable bit from public key file
17
+  * obs-ffmpeg: Fix m3u8 recording in AMF
18
+  * linux-pipewire: Close sessions as we are done with them
19
+  * libobs/media-io: Restore color range conversion
20
+  * CI: Downgrade Sphinx to fix docs build error
21
+  * libobs/media-io: Avoid scaler for range diff
22
+  * linux-capture: Fixup window name/class checking
23
+  * obs-ffmpeg: Cap AMF encoder at 100 Mbps
24
+  * UI: Fix color of popout icon
25
+  * UI: Fix dock titlebar icons not loading
26
+
27
+-------------------------------------------------------------------
28
 Sat Sep 24 16:13:45 UTC 2022 - Hans-Peter Jansen <hpj@urpla.net>
29
 
30
 - Disable cef for other than x86_64 archs
31
obs-studio.spec Changed
10
 
1
@@ -8,7 +8,7 @@
2
 %endif
3
 
4
 Name:           obs-studio
5
-Version:        28.0.2
6
+Version:        28.0.3
7
 Release:        0
8
 Summary:        A recording/broadcasting program
9
 Group:          Productivity/Multimedia/Video/Editors and Convertors
10
_service Changed
10
 
1
@@ -1,7 +1,7 @@
2
 <services>
3
   <service name="tar_scm" mode="disabled">
4
     <param name="versionformat">@PARENT_TAG@</param>
5
-    <param name="revision">refs/tags/28.0.2</param>
6
+    <param name="revision">refs/tags/28.0.3</param>
7
     <param name="url">https://github.com/obsproject/obs-studio.git</param>
8
     <param name="scm">git</param>
9
     <param name="changesgenerate">enable</param>
10
_servicedata Changed
10
 
1
@@ -1,6 +1,6 @@
2
 <servicedata>
3
   <service name="tar_scm">
4
     <param name="url">https://github.com/obsproject/obs-studio.git</param>
5
-    <param name="changesrevision">087ee39e2d8b9240b2ada1492fc7ad85102084ab</param>
6
+    <param name="changesrevision">d21891b3ca1a8607de479687cd2e12a4455525d7</param>
7
   </service>
8
 </servicedata>
9
\ No newline at end of file
10
obs-studio-28.0.2.tar.xz/.github/workflows/docs.yml -> obs-studio-28.0.3.tar.xz/.github/workflows/docs.yml Changed
9
 
1
@@ -42,6 +42,7 @@
2
           build_only: True
3
           target_branch: 'master'
4
           target_path: '../home/_build'
5
+          pre_build_commands: 'pip install -Iv sphinx==5.1.1'
6
       - uses: actions/upload-artifact@v3
7
         with:
8
           name: OBS Studio Documentation (HTML)
9
obs-studio-28.0.2.tar.xz/UI/data/themes/Acri.qss -> obs-studio-28.0.3.tar.xz/UI/data/themes/Acri.qss Changed
12
 
1
@@ -300,8 +300,8 @@
2
     font-size: 10.5pt;
3
     font-weight: bold;
4
 
5
-    titlebar-close-icon: url('./Dark/Close.svg');
6
-    titlebar-normal-icon: url('./Dark/Popout.svg');
7
+    titlebar-close-icon: url('./Dark/close.svg');
8
+    titlebar-normal-icon: url('./Dark/popout.svg');
9
 }
10
 
11
 QDockWidget::title {
12
obs-studio-28.0.2.tar.xz/UI/data/themes/Grey.qss -> obs-studio-28.0.3.tar.xz/UI/data/themes/Grey.qss Changed
12
 
1
@@ -300,8 +300,8 @@
2
     font-size: 10.5pt;
3
     font-weight: bold;
4
 
5
-    titlebar-close-icon: url('./Dark/Close.svg');
6
-    titlebar-normal-icon: url('./Dark/Popout.svg');
7
+    titlebar-close-icon: url('./Dark/close.svg');
8
+    titlebar-normal-icon: url('./Dark/popout.svg');
9
 }
10
 
11
 QDockWidget::title {
12
obs-studio-28.0.2.tar.xz/UI/data/themes/Light.qss -> obs-studio-28.0.3.tar.xz/UI/data/themes/Light.qss Changed
12
 
1
@@ -300,8 +300,8 @@
2
     font-size: 10.5pt;
3
     font-weight: bold;
4
 
5
-    titlebar-close-icon: url('./Light/Close.svg');
6
-    titlebar-normal-icon: url('./Light/Popout.svg');
7
+    titlebar-close-icon: url('./Light/close.svg');
8
+    titlebar-normal-icon: url('./Light/popout.svg');
9
 }
10
 
11
 QDockWidget::title {
12
obs-studio-28.0.2.tar.xz/UI/data/themes/Light/popout.svg -> obs-studio-28.0.3.tar.xz/UI/data/themes/Light/popout.svg Changed
4
 
1
@@ -1,1 +1,1 @@
2
-<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>
3
+<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>
4
obs-studio-28.0.2.tar.xz/UI/data/themes/Rachni.qss -> obs-studio-28.0.3.tar.xz/UI/data/themes/Rachni.qss Changed
12
 
1
@@ -302,8 +302,8 @@
2
     font-size: 10.5pt;
3
     font-weight: bold;
4
 
5
-    titlebar-close-icon: url('./Dark/Close.svg');
6
-    titlebar-normal-icon: url('./Dark/Popout.svg');
7
+    titlebar-close-icon: url('./Dark/close.svg');
8
+    titlebar-normal-icon: url('./Dark/popout.svg');
9
 }
10
 
11
 QDockWidget::title {
12
obs-studio-28.0.2.tar.xz/UI/data/themes/Yami.qss -> obs-studio-28.0.3.tar.xz/UI/data/themes/Yami.qss Changed
12
 
1
@@ -304,8 +304,8 @@
2
     font-size: 10.5pt;
3
     font-weight: bold;
4
 
5
-    titlebar-close-icon: url('./Dark/Close.svg');
6
-    titlebar-normal-icon: url('./Dark/Popout.svg');
7
+    titlebar-close-icon: url('./Dark/close.svg');
8
+    titlebar-normal-icon: url('./Dark/popout.svg');
9
 }
10
 
11
 QDockWidget::title {
12
obs-studio-28.0.2.tar.xz/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini -> obs-studio-28.0.3.tar.xz/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini Changed
12
 
1
@@ -39,6 +39,10 @@
2
 PythonSettings.PythonInstallPath32bit="Python Install Path (32bit)"
3
 PythonSettings.PythonInstallPath64bit="Python Install Path (64bit)"
4
 PythonSettings.BrowsePythonPath="Browse Python Path"
5
+PythonSettings.PythonVersion="Loaded Python Version: %1"
6
+PythonSettings.PythonNotLoaded="Python not currently loaded"
7
+PythonSettings.AlreadyLoaded.Title="Python Already Loaded"
8
+PythonSettings.AlreadyLoaded.Message="A copy of Python %1 is already loaded. To load the newly selected Python version, please restart OBS."
9
 ScriptLogWindow="Script Log"
10
 Description="Description"
11
 ScriptDescriptionLink.Text="Open this link in your default web browser?"
12
obs-studio-28.0.2.tar.xz/UI/frontend-plugins/frontend-tools/forms/scripts.ui -> obs-studio-28.0.3.tar.xz/UI/frontend-plugins/frontend-tools/forms/scripts.ui Changed
15
 
1
@@ -269,6 +269,13 @@
2
         </layout>
3
        </item>
4
        <item>
5
+        <widget class="QLabel" name="pythonVersionLabel">
6
+         <property name="text">
7
+          <string/>
8
+         </property>
9
+        </widget>
10
+       </item>
11
+       <item>
12
         <spacer name="verticalSpacer">
13
          <property name="orientation">
14
           <enum>Qt::Vertical</enum>
15
obs-studio-28.0.2.tar.xz/UI/frontend-plugins/frontend-tools/scripts.cpp -> obs-studio-28.0.3.tar.xz/UI/frontend-plugins/frontend-tools/scripts.cpp Changed
61
 
1
@@ -194,6 +194,7 @@
2
        config_get_string(config, "Python", "Path" ARCH_NAME);
3
    ui->pythonPath->setText(path);
4
    ui->pythonPathLabel->setText(obs_module_text(PYTHONPATH_LABEL_TEXT));
5
+   updatePythonVersionLabel();
6
 #else
7
    delete ui->pythonSettingsTab;
8
    ui->pythonSettingsTab = nullptr;
9
@@ -219,6 +220,20 @@
10
               ui->scripts->currentRow());
11
 }
12
 
13
+void ScriptsTool::updatePythonVersionLabel()
14
+{
15
+   QString label;
16
+   if (obs_scripting_python_loaded()) {
17
+       char version8;
18
+       obs_scripting_python_version(version, sizeof(version));
19
+       label = QString(obs_module_text("PythonSettings.PythonVersion"))
20
+               .arg(version);
21
+   } else {
22
+       label = obs_module_text("PythonSettings.PythonNotLoaded");
23
+   }
24
+   ui->pythonVersionLabel->setText(label);
25
+}
26
+
27
 void ScriptsTool::RemoveScript(const char *path)
28
 {
29
    for (size_t i = 0; i < scriptData->scripts.size(); i++) {
30
@@ -455,11 +470,29 @@
31
 
32
    ui->pythonPath->setText(newPath);
33
 
34
-   if (obs_scripting_python_loaded())
35
+   bool loaded = obs_scripting_python_loaded();
36
+
37
+   if (loaded && !newPath.isEmpty() && curPath.compare(newPath) != 0) {
38
+       char version8;
39
+       obs_scripting_python_version(version, sizeof(version));
40
+       QString message =
41
+           QString(obs_module_text(
42
+                   "PythonSettings.AlreadyLoaded.Message"))
43
+               .arg(version);
44
+       OBSMessageBox::information(
45
+           this,
46
+           obs_module_text("PythonSettings.AlreadyLoaded.Title"),
47
+           message);
48
        return;
49
+   } else if (loaded) {
50
+       return;
51
+   }
52
+
53
    if (!obs_scripting_load_python(path))
54
        return;
55
 
56
+   updatePythonVersionLabel();
57
+
58
    for (OBSScript &script : scriptData->scripts) {
59
        enum obs_script_lang lang = obs_script_get_lang(script);
60
        if (lang == OBS_SCRIPT_LANG_PYTHON) {
61
obs-studio-28.0.2.tar.xz/UI/frontend-plugins/frontend-tools/scripts.hpp -> obs-studio-28.0.3.tar.xz/UI/frontend-plugins/frontend-tools/scripts.hpp Changed
10
 
1
@@ -31,6 +31,8 @@
2
    std::unique_ptr<Ui_ScriptsTool> ui;
3
    QWidget *propertiesView = nullptr;
4
 
5
+   void updatePythonVersionLabel();
6
+
7
 public:
8
    ScriptsTool();
9
    ~ScriptsTool();
10
obs-studio-28.0.2.tar.xz/UI/item-widget-helpers.cpp -> obs-studio-28.0.3.tar.xz/UI/item-widget-helpers.cpp Changed
16
 
1
@@ -37,10 +37,14 @@
2
 
3
 void ClearListItems(QListWidget *widget)
4
 {
5
+   // Workaround for the SceneTree workaround for QTBUG-105870
6
+   widget->setProperty("clearing", true);
7
+
8
    widget->setCurrentItem(nullptr, QItemSelectionModel::Clear);
9
 
10
    for (int i = 0; i < widget->count(); i++)
11
        delete widget->itemWidget(widget->item(i));
12
 
13
    widget->clear();
14
+   widget->setProperty("clearing", false);
15
 }
16
obs-studio-28.0.2.tar.xz/UI/scene-tree.cpp -> obs-studio-28.0.3.tar.xz/UI/scene-tree.cpp Changed
10
 
1
@@ -252,6 +252,7 @@
2
 void SceneTree::selectionChanged(const QItemSelection &selected,
3
                 const QItemSelection &deselected)
4
 {
5
-   if (selected.count() == 0 && deselected.count() > 0)
6
+   if (selected.count() == 0 && deselected.count() > 0 &&
7
+       !property("clearing").toBool())
8
        setCurrentRow(deselected.indexes().front().row());
9
 }
10
obs-studio-28.0.2.tar.xz/UI/window-basic-main.cpp -> obs-studio-28.0.3.tar.xz/UI/window-basic-main.cpp Changed
39
 
1
@@ -7122,17 +7122,31 @@
2
 
3
 void OBSBasic::AutoRemux(QString input, bool no_show)
4
 {
5
-   bool autoRemux = config_get_bool(Config(), "Video", "AutoRemux");
6
+   auto config = Config();
7
+
8
+   bool autoRemux = config_get_bool(config, "Video", "AutoRemux");
9
 
10
    if (!autoRemux)
11
        return;
12
 
13
-   const char *recType = config_get_string(Config(), "AdvOut", "RecType");
14
-
15
-   bool ffmpegOutput = astrcmpi(recType, "FFmpeg") == 0;
16
+   bool isSimpleMode = false;
17
 
18
-   if (ffmpegOutput)
19
-       return;
20
+   const char *mode = config_get_string(config, "Output", "Mode");
21
+   if (!mode) {
22
+       isSimpleMode = true;
23
+   } else {
24
+       isSimpleMode = strcmp(mode, "Simple") == 0;
25
+   }
26
+
27
+   if (!isSimpleMode) {
28
+       const char *recType =
29
+           config_get_string(config, "AdvOut", "RecType");
30
+
31
+       bool ffmpegOutput = astrcmpi(recType, "FFmpeg") == 0;
32
+
33
+       if (ffmpegOutput)
34
+           return;
35
+   }
36
 
37
    if (input.isEmpty())
38
        return;
39
obs-studio-28.0.2.tar.xz/deps/obs-scripting/obs-scripting-python.c -> obs-studio-28.0.3.tar.xz/deps/obs-scripting/obs-scripting-python.c Changed
19
 
1
@@ -1590,6 +1590,17 @@
2
    return (bool)RUNTIME_LINK;
3
 }
4
 
5
+void obs_scripting_python_version(char *version, size_t version_length)
6
+{
7
+#if RUNTIME_LINK
8
+   snprintf(version, version_length, "%d.%d", python_version.major,
9
+        python_version.minor);
10
+#else
11
+   snprintf(version, version_length, "%d.%d", PY_MAJOR_VERSION,
12
+        PY_MINOR_VERSION);
13
+#endif
14
+}
15
+
16
 bool obs_scripting_python_loaded(void)
17
 {
18
    return python_loaded;
19
obs-studio-28.0.2.tar.xz/deps/obs-scripting/obs-scripting.c -> obs-studio-28.0.3.tar.xz/deps/obs-scripting/obs-scripting.c Changed
11
 
1
@@ -458,4 +458,9 @@
2
 {
3
    return (bool)true;
4
 }
5
+
6
+void obs_scripting_python_version(char *version, size_t version_length)
7
+{
8
+   version0 = 0;
9
+}
10
 #endif
11
obs-studio-28.0.2.tar.xz/deps/obs-scripting/obs-scripting.h -> obs-studio-28.0.3.tar.xz/deps/obs-scripting/obs-scripting.h Changed
9
 
1
@@ -46,6 +46,7 @@
2
                       void *param);
3
 
4
 EXPORT bool obs_scripting_python_runtime_linked(void);
5
+EXPORT void obs_scripting_python_version(char *version, size_t version_length);
6
 EXPORT bool obs_scripting_python_loaded(void);
7
 EXPORT bool obs_scripting_load_python(const char *python_path);
8
 
9
obs-studio-28.0.2.tar.xz/libobs/media-io/video-scaler-ffmpeg.c -> obs-studio-28.0.3.tar.xz/libobs/media-io/video-scaler-ffmpeg.c Changed
41
 
1
@@ -19,6 +19,7 @@
2
 #include "video-scaler.h"
3
 
4
 #include <libavutil/imgutils.h>
5
+#include <libavutil/opt.h>
6
 #include <libswscale/swscale.h>
7
 
8
 struct video_scaler {
9
@@ -188,16 +189,27 @@
10
        goto fail;
11
    }
12
 
13
-   scaler->swscale = sws_getCachedContext(NULL, src->width, src->height,
14
-                          format_src, dst->width,
15
-                          dst->height, format_dst,
16
-                          scale_type, NULL, NULL, NULL);
17
+   scaler->swscale = sws_alloc_context();
18
    if (!scaler->swscale) {
19
        blog(LOG_ERROR, "video_scaler_create: Could not create "
20
                "swscale");
21
        goto fail;
22
    }
23
 
24
+   av_opt_set_int(scaler->swscale, "sws_flags", scale_type, 0);
25
+   av_opt_set_int(scaler->swscale, "srcw", src->width, 0);
26
+   av_opt_set_int(scaler->swscale, "srch", src->height, 0);
27
+   av_opt_set_int(scaler->swscale, "dstw", dst->width, 0);
28
+   av_opt_set_int(scaler->swscale, "dsth", dst->height, 0);
29
+   av_opt_set_int(scaler->swscale, "src_format", format_src, 0);
30
+   av_opt_set_int(scaler->swscale, "dst_format", format_dst, 0);
31
+   av_opt_set_int(scaler->swscale, "src_range", range_src, 0);
32
+   av_opt_set_int(scaler->swscale, "dst_range", range_dst, 0);
33
+   if (sws_init_context(scaler->swscale, NULL, NULL) < 0) {
34
+       blog(LOG_ERROR, "video_scaler_create: sws_init_context failed");
35
+       goto fail;
36
+   }
37
+
38
    ret = sws_setColorspaceDetails(scaler->swscale, coeff_src, range_src,
39
                       coeff_dst, range_dst, 0, FIXED_1_0,
40
                       FIXED_1_0);
41
obs-studio-28.0.2.tar.xz/libobs/obs-config.h -> obs-studio-28.0.3.tar.xz/libobs/obs-config.h Changed
10
 
1
@@ -41,7 +41,7 @@
2
  *
3
  * Reset to zero each major or minor version
4
  */
5
-#define LIBOBS_API_PATCH_VER 2
6
+#define LIBOBS_API_PATCH_VER 3
7
 
8
 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \
9
    ((major << 24) | (minor << 16) | patch)
10
obs-studio-28.0.2.tar.xz/libobs/obs-encoder.c -> obs-studio-28.0.3.tar.xz/libobs/obs-encoder.c Changed
10
 
1
@@ -1534,3 +1534,8 @@
2
    else
3
        encoder->last_error_message = NULL;
4
 }
5
+
6
+uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder)
7
+{
8
+   return encoder ? encoder->pause.ts_offset : 0;
9
+}
10
obs-studio-28.0.2.tar.xz/libobs/obs.h -> obs-studio-28.0.3.tar.xz/libobs/obs.h Changed
10
 
1
@@ -2413,6 +2413,8 @@
2
 EXPORT void obs_encoder_set_last_error(obs_encoder_t *encoder,
3
                       const char *message);
4
 
5
+EXPORT uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder);
6
+
7
 /* ------------------------------------------------------------------------- */
8
 /* Stream Services */
9
 
10
obs-studio-28.0.2.tar.xz/plugins/linux-capture/xcomposite-input.c -> obs-studio-28.0.3.tar.xz/plugins/linux-capture/xcomposite-input.c Changed
35
 
1
@@ -310,11 +310,10 @@
2
    char *wcls = bzalloc(strEnd - thirdStr + 1);
3
    memcpy(wname, secondStr, secondMark - secondStr);
4
    memcpy(wcls, thirdStr, strEnd - thirdStr);
5
-   xcb_window_t wid = (xcb_window_t)strtol(str, NULL, 10);
6
+   ret = (xcb_window_t)strtol(str, NULL, 10);
7
 
8
    // first try to find a match by the window-id
9
-   if (da_find(tlw, &wid, 0) != DARRAY_INVALID) {
10
-       ret = wid;
11
+   if (da_find(tlw, &ret, 0) != DARRAY_INVALID) {
12
        goto cleanup2;
13
    }
14
 
15
@@ -325,8 +324,8 @@
16
 
17
        struct dstr cwname = xcomp_window_name(conn, disp, cwin);
18
        struct dstr cwcls = xcomp_window_class(conn, cwin);
19
-       bool found = (strcmp(wname, cwname.array) == 0 &&
20
-                 strcmp(wcls, cwcls.array));
21
+       bool found = strcmp(wname, cwname.array) == 0 &&
22
+                strcmp(wcls, cwcls.array) == 0;
23
 
24
        dstr_free(&cwname);
25
        dstr_free(&cwcls);
26
@@ -345,7 +344,7 @@
27
    bfree(wcls);
28
 cleanup1:
29
    da_free(tlw);
30
-   return wid;
31
+   return ret;
32
 }
33
 
34
 void xcomp_cleanup_pixmap(Display *disp, struct xcompcap *s)
35
obs-studio-28.0.2.tar.xz/plugins/linux-pipewire/screencast-portal.c -> obs-studio-28.0.3.tar.xz/plugins/linux-pipewire/screencast-portal.c Changed
22
 
1
@@ -627,6 +627,20 @@
2
    g_clear_pointer(&capture->restore_token, bfree);
3
    g_clear_pointer(&capture->obs_pw, obs_pipewire_destroy);
4
 
5
+   if (capture->session_handle) {
6
+       blog(LOG_DEBUG, "pipewire Cleaning previous session %s",
7
+            capture->session_handle);
8
+       g_dbus_connection_call(portal_get_dbus_connection(),
9
+                      "org.freedesktop.portal.Desktop",
10
+                      capture->session_handle,
11
+                      "org.freedesktop.portal.Session",
12
+                      "Close", NULL, NULL,
13
+                      G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL,
14
+                      NULL);
15
+
16
+       g_clear_pointer(&capture->session_handle, g_free);
17
+   }
18
+
19
    init_screencast_capture(capture);
20
 
21
    return false;
22
obs-studio-28.0.2.tar.xz/plugins/obs-ffmpeg/obs-ffmpeg-mux.c -> obs-studio-28.0.3.tar.xz/plugins/obs-ffmpeg/obs-ffmpeg-mux.c Changed
74
 
1
@@ -375,18 +375,33 @@
2
    stream->found_audiopacket->track_idx = true;
3
 }
4
 
5
-static bool ffmpeg_mux_start(void *data)
6
+static inline void update_encoder_settings(struct ffmpeg_muxer *stream,
7
+                      const char *path)
8
 {
9
-   struct ffmpeg_muxer *stream = data;
10
-   obs_data_t *settings;
11
-   const char *path;
12
+   obs_encoder_t *vencoder = obs_output_get_video_encoder(stream->output);
13
+   const char *ext = strrchr(path, '.');
14
+
15
+   /* if using m3u8, repeat headers */
16
+   if (ext && strcmp(ext, ".m3u8") == 0) {
17
+       obs_data_t *settings = obs_encoder_get_settings(vencoder);
18
+       obs_data_set_bool(settings, "repeat_headers", true);
19
+       obs_encoder_update(vencoder, settings);
20
+       obs_data_release(settings);
21
+   }
22
+}
23
+
24
+static inline bool ffmpeg_mux_start_internal(struct ffmpeg_muxer *stream,
25
+                        obs_data_t *settings)
26
+{
27
+   const char *path = obs_data_get_string(settings, "path");
28
+
29
+   update_encoder_settings(stream, path);
30
 
31
    if (!obs_output_can_begin_data_capture(stream->output, 0))
32
        return false;
33
    if (!obs_output_initialize_encoders(stream->output, 0))
34
        return false;
35
 
36
-   settings = obs_output_get_settings(stream->output);
37
    if (stream->is_network) {
38
        obs_service_t *service;
39
        service = obs_output_get_service(stream->output);
40
@@ -395,7 +410,6 @@
41
        path = obs_service_get_url(service);
42
        stream->split_file = false;
43
    } else {
44
-       path = obs_data_get_string(settings, "path");
45
 
46
        stream->max_time =
47
            obs_data_get_int(settings, "max_time_sec") * 1000000LL;
48
@@ -427,7 +441,6 @@
49
    }
50
 
51
    start_pipe(stream, path);
52
-   obs_data_release(settings);
53
 
54
    if (!stream->pipe) {
55
        obs_output_set_last_error(
56
@@ -446,6 +459,17 @@
57
    return true;
58
 }
59
 
60
+static bool ffmpeg_mux_start(void *data)
61
+{
62
+   struct ffmpeg_muxer *stream = data;
63
+
64
+   obs_data_t *settings = obs_output_get_settings(stream->output);
65
+   bool success = ffmpeg_mux_start_internal(stream, settings);
66
+   obs_data_release(settings);
67
+
68
+   return success;
69
+}
70
+
71
 int deactivate(struct ffmpeg_muxer *stream, int code)
72
 {
73
    int ret = -1;
74
obs-studio-28.0.2.tar.xz/plugins/obs-ffmpeg/obs-ffmpeg-video-encoders.c -> obs-studio-28.0.3.tar.xz/plugins/obs-ffmpeg/obs-ffmpeg-video-encoders.c Changed
19
 
1
@@ -221,6 +221,8 @@
2
    AVPacket av_pkt = {0};
3
    bool timeout = false;
4
    const int64_t cur_ts = (int64_t)os_gettime_ns();
5
+   const int64_t pause_offset =
6
+       (int64_t)obs_encoder_get_pause_offset(enc->encoder);
7
    int got_packet;
8
    int ret;
9
 
10
@@ -270,7 +272,7 @@
11
        debug("cur: %lld, packet: %lld, diff: %lld", cur_ts,
12
              recv_ts_nsec, cur_ts - recv_ts_nsec);
13
 #endif
14
-       if ((cur_ts - recv_ts_nsec) > TIMEOUT_MAX_NSEC) {
15
+       if ((cur_ts - recv_ts_nsec - pause_offset) > TIMEOUT_MAX_NSEC) {
16
            char timeout_str16;
17
            snprintf(timeout_str, sizeof(timeout_str), "%d",
18
                 TIMEOUT_MAX_SEC);
19
obs-studio-28.0.2.tar.xz/plugins/obs-ffmpeg/texture-amf.cpp -> obs-studio-28.0.3.tar.xz/plugins/obs-ffmpeg/texture-amf.cpp Changed
22
 
1
@@ -964,7 +964,7 @@
2
    obs_property_set_modified_callback(p, rate_control_modified);
3
 
4
    p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"),
5
-                  50, 300000, 50);
6
+                  50, 100000, 50);
7
    obs_property_int_set_suffix(p, " Kbps");
8
 
9
    obs_properties_add_int(props, "cqp", obs_module_text("NVENC.CQLevel"),
10
@@ -1158,6 +1158,11 @@
11
                    : 250;
12
 
13
    set_avc_property(enc, IDR_PERIOD, gop_size);
14
+
15
+   bool repeat_headers = obs_data_get_bool(settings, "repeat_headers");
16
+   if (repeat_headers)
17
+       set_avc_property(enc, HEADER_INSERTION_SPACING, gop_size);
18
+
19
    set_avc_property(enc, DE_BLOCKING_FILTER, true);
20
 
21
    const char *ffmpeg_opts = obs_data_get_string(settings, "ffmpeg_opts");
22
obs-studio-28.0.2.tar.xz/plugins/win-wasapi/win-wasapi.cpp -> obs-studio-28.0.3.tar.xz/plugins/win-wasapi/win-wasapi.cpp Changed
173
 
1
@@ -182,7 +182,7 @@
2
    std::atomic<bool> isDefaultDevice = false;
3
 
4
    bool previouslyFailed = false;
5
-   WinHandle reconnectThread;
6
+   WinHandle reconnectThread = NULL;
7
 
8
    class CallbackStartCapture : public ARtwqAsyncCallback {
9
    public:
10
@@ -238,6 +238,7 @@
11
    WinHandle stopSignal;
12
    WinHandle receiveSignal;
13
    WinHandle restartSignal;
14
+   WinHandle reconnectExitSignal;
15
    WinHandle exitSignal;
16
    WinHandle initSignal;
17
    DWORD reconnectDuration = 0;
18
@@ -298,6 +299,9 @@
19
    void Update(obs_data_t *settings);
20
    void OnWindowChanged(obs_data_t *settings);
21
 
22
+   void Activate();
23
+   void Deactivate();
24
+
25
    void SetDefaultDevice(EDataFlow flow, ERole role, LPCWSTR id);
26
 
27
    void OnStartCapture();
28
@@ -390,6 +394,10 @@
29
    if (!restartSignal.Valid())
30
        throw "Could not create restart signal";
31
 
32
+   reconnectExitSignal = CreateEvent(nullptr, true, false, nullptr);
33
+   if (!reconnectExitSignal.Valid())
34
+       throw "Could not create reconnect exit signal";
35
+
36
    exitSignal = CreateEvent(nullptr, true, false, nullptr);
37
    if (!exitSignal.Valid())
38
        throw "Could not create exit signal";
39
@@ -402,11 +410,6 @@
40
    if (!reconnectSignal.Valid())
41
        throw "Could not create reconnect signal";
42
 
43
-   reconnectThread = CreateThread(
44
-       nullptr, 0, WASAPISource::ReconnectThread, this, 0, nullptr);
45
-   if (!reconnectThread.Valid())
46
-       throw "Failed to create reconnect thread";
47
-
48
    notify = new WASAPINotify(this);
49
    if (!notify)
50
        throw "Could not create WASAPINotify";
51
@@ -532,11 +535,19 @@
52
    if (rtwq_supported)
53
        SetEvent(receiveSignal);
54
 
55
-   WaitForSingleObject(idleSignal, INFINITE);
56
+   if (reconnectThread.Valid()) {
57
+       WaitForSingleObject(idleSignal, INFINITE);
58
+   } else {
59
+       const HANDLE sigs = {reconnectSignal, idleSignal};
60
+       WaitForMultipleObjects(_countof(sigs), sigs, false, INFINITE);
61
+   }
62
 
63
    SetEvent(exitSignal);
64
 
65
-   WaitForSingleObject(reconnectThread, INFINITE);
66
+   if (reconnectThread.Valid()) {
67
+       SetEvent(reconnectExitSignal);
68
+       WaitForSingleObject(reconnectThread, INFINITE);
69
+   }
70
 
71
    if (rtwq_supported)
72
        rtwq_unlock_work_queue(sampleReady.GetQueueId());
73
@@ -657,6 +668,25 @@
74
        SetEvent(restartSignal);
75
 }
76
 
77
+void WASAPISource::Activate()
78
+{
79
+   if (!reconnectThread.Valid()) {
80
+       ResetEvent(reconnectExitSignal);
81
+       reconnectThread = CreateThread(nullptr, 0,
82
+                          WASAPISource::ReconnectThread,
83
+                          this, 0, nullptr);
84
+   }
85
+}
86
+
87
+void WASAPISource::Deactivate()
88
+{
89
+   if (reconnectThread.Valid()) {
90
+       SetEvent(reconnectExitSignal);
91
+       WaitForSingleObject(reconnectThread, INFINITE);
92
+       reconnectThread = NULL;
93
+   }
94
+}
95
+
96
 ComPtr<IMMDevice> WASAPISource::InitDevice(IMMDeviceEnumerator *enumerator,
97
                       bool isDefaultDevice,
98
                       SourceType type,
99
@@ -1005,10 +1035,15 @@
100
    WASAPISource *source = (WASAPISource *)param;
101
 
102
    const HANDLE sigs = {
103
-       source->exitSignal,
104
+       source->reconnectExitSignal,
105
        source->reconnectSignal,
106
    };
107
 
108
+   const HANDLE reconnect_sigs = {
109
+       source->reconnectExitSignal,
110
+       source->stopSignal,
111
+   };
112
+
113
    bool exit = false;
114
    while (!exit) {
115
        const DWORD ret = WaitForMultipleObjects(_countof(sigs), sigs,
116
@@ -1020,8 +1055,10 @@
117
        default:
118
            assert(ret == (WAIT_OBJECT_0 + 1));
119
            if (source->reconnectDuration > 0) {
120
-               WaitForSingleObject(source->stopSignal,
121
-                           source->reconnectDuration);
122
+               WaitForMultipleObjects(
123
+                   _countof(reconnect_sigs),
124
+                   reconnect_sigs, false,
125
+                   source->reconnectDuration);
126
            }
127
            source->Start();
128
        }
129
@@ -1426,6 +1463,16 @@
130
    static_cast<WASAPISource *>(obj)->Update(settings);
131
 }
132
 
133
+static void ActivateWASAPISource(void *obj)
134
+{
135
+   static_cast<WASAPISource *>(obj)->Activate();
136
+}
137
+
138
+static void DeactivateWASAPISource(void *obj)
139
+{
140
+   static_cast<WASAPISource *>(obj)->Deactivate();
141
+}
142
+
143
 static bool UpdateWASAPIMethod(obs_properties_t *props, obs_property_t *,
144
                   obs_data_t *settings)
145
 {
146
@@ -1542,6 +1589,8 @@
147
    info.create = CreateWASAPIInput;
148
    info.destroy = DestroyWASAPISource;
149
    info.update = UpdateWASAPISource;
150
+   info.activate = ActivateWASAPISource;
151
+   info.deactivate = DeactivateWASAPISource;
152
    info.get_defaults = GetWASAPIDefaultsInput;
153
    info.get_properties = GetWASAPIPropertiesInput;
154
    info.icon_type = OBS_ICON_TYPE_AUDIO_INPUT;
155
@@ -1559,6 +1608,8 @@
156
    info.create = CreateWASAPIDeviceOutput;
157
    info.destroy = DestroyWASAPISource;
158
    info.update = UpdateWASAPISource;
159
+   info.activate = ActivateWASAPISource;
160
+   info.deactivate = DeactivateWASAPISource;
161
    info.get_defaults = GetWASAPIDefaultsDeviceOutput;
162
    info.get_properties = GetWASAPIPropertiesDeviceOutput;
163
    info.icon_type = OBS_ICON_TYPE_AUDIO_OUTPUT;
164
@@ -1576,6 +1627,8 @@
165
    info.create = CreateWASAPIProcessOutput;
166
    info.destroy = DestroyWASAPISource;
167
    info.update = UpdateWASAPISource;
168
+   info.activate = ActivateWASAPISource;
169
+   info.deactivate = DeactivateWASAPISource;
170
    info.get_defaults = GetWASAPIDefaultsProcessOutput;
171
    info.get_properties = GetWASAPIPropertiesProcessOutput;
172
    info.icon_type = OBS_ICON_TYPE_PROCESS_AUDIO_OUTPUT;
173