commit 688e1e675ffe3c43321e6666f7abfad22d891dc1
Author: Neal Gompa <ngompa(a)fedoraproject.org>
Date: Thu May 4 06:10:49 2023 -0400
Initial import to RPM Fusion
.gitignore | 1 +
...ntly-reference-the-software-H264-encoder-.patch | 104 ++++
...Add-initial-support-for-the-OpenH264-H.26.patch | 346 +++++++++++++
...ort-for-OpenH264-as-the-worst-case-fallba.patch | 545 +++++++++++++++++++++
...studio-deps-Add-license-declaration-files.patch | 218 +++++++++
obs-studio-freeworld.spec | 271 ++++++++++
...io-obs-qsv11-Add-license-declaration-file.patch | 79 +++
obs-studio-websocket-use-system-qrcodegencpp.patch | 107 ++++
sources | 2 +
9 files changed, 1673 insertions(+)
---
diff --git a/.gitignore b/.gitignore
index e69de29..f6a3a77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/*.tar.gz
diff --git a/0001-UI-Consistently-reference-the-software-H264-encoder-.patch
b/0001-UI-Consistently-reference-the-software-H264-encoder-.patch
new file mode 100644
index 0000000..e976f5c
--- /dev/null
+++ b/0001-UI-Consistently-reference-the-software-H264-encoder-.patch
@@ -0,0 +1,104 @@
+From 04d3a21c6e72cd5574f7333adf548012d912c1ee Mon Sep 17 00:00:00 2001
+From: Neal Gompa <neal(a)gompa.dev>
+Date: Sat, 7 Jan 2023 23:15:13 -0500
+Subject: [PATCH 1/3] UI: Consistently reference the software H264 encoder
+ properly
+
+The code here assumes that the only software encoder is the x264-based
+H.264 encoder. That may not always remain true. This change adjusts
+the encoder string to indicate that it's an H.264 encoder from x264.
+---
+ UI/data/locale/en-US.ini | 4 ++--
+ UI/window-basic-auto-config-test.cpp | 6 +++---
+ UI/window-basic-settings-stream.cpp | 2 +-
+ UI/window-basic-settings.cpp | 7 ++++---
+ 4 files changed, 10 insertions(+), 9 deletions(-)
+
+diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini
+index 4c03aa7db..e44d99660 100644
+--- a/UI/data/locale/en-US.ini
++++ b/UI/data/locale/en-US.ini
+@@ -980,7 +980,7 @@ Basic.Settings.Output.Simple.Warn.Encoder="Warning: Recording
with a software en
+ Basic.Settings.Output.Simple.Warn.Lossless="Warning: Lossless quality generates
tremendously large file sizes! Lossless quality can use upward of 7 gigabytes of disk
space per minute at high resolutions and framerates. Lossless is not recommended for long
recordings unless you have a very large amount of disk space available."
+ Basic.Settings.Output.Simple.Warn.Lossless.Msg="Are you sure you want to use
lossless quality?"
+ Basic.Settings.Output.Simple.Warn.Lossless.Title="Lossless quality warning!"
+-Basic.Settings.Output.Simple.Encoder.Software="Software (x264)"
++Basic.Settings.Output.Simple.Encoder.Software.X264.H264="Software (x264)"
+ Basic.Settings.Output.Simple.Encoder.Hardware.QSV.H264="Hardware (QSV,
H.264)"
+ Basic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1="Hardware (QSV, AV1)"
+ Basic.Settings.Output.Simple.Encoder.Hardware.AMD.H264="Hardware (AMD,
H.264)"
+@@ -991,7 +991,7 @@
Basic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1="Hardware (NVENC, AV1)"
+ Basic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC="Hardware (NVENC,
HEVC)"
+ Basic.Settings.Output.Simple.Encoder.Hardware.Apple.H264="Hardware (Apple,
H.264)"
+ Basic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC="Hardware (Apple,
HEVC)"
+-Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 low CPU usage
preset, increases file size)"
++Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU.X264.H264="Software (x264 low
CPU usage preset, increases file size)"
+ Basic.Settings.Output.Simple.Codec.AAC="AAC"
+ Basic.Settings.Output.Simple.Codec.AAC.Default="AAC (Default)"
+ Basic.Settings.Output.Simple.Codec.Opus="Opus"
+diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp
+index 2d89d7347..09979c031 100644
+--- a/UI/window-basic-auto-config-test.cpp
++++ b/UI/window-basic-auto-config-test.cpp
+@@ -993,7 +993,7 @@ void AutoConfigTestPage::TestRecordingEncoderThread()
+ }
+
+ #define ENCODER_TEXT(x) "Basic.Settings.Output.Simple.Encoder." x
+-#define ENCODER_SOFTWARE ENCODER_TEXT("Software")
++#define ENCODER_X264 ENCODER_TEXT("Software.X264.H264")
+ #define ENCODER_NVENC ENCODER_TEXT("Hardware.NVENC.H264")
+ #define ENCODER_QSV ENCODER_TEXT("Hardware.QSV.H264")
+ #define ENCODER_AMD ENCODER_TEXT("Hardware.AMD.H264")
+@@ -1033,7 +1033,7 @@ void AutoConfigTestPage::FinalizeResults()
+ auto encName = [](AutoConfig::Encoder enc) -> QString {
+ switch (enc) {
+ case AutoConfig::Encoder::x264:
+- return QTStr(ENCODER_SOFTWARE);
++ return QTStr(ENCODER_X264);
+ case AutoConfig::Encoder::NVENC:
+ return QTStr(ENCODER_NVENC);
+ case AutoConfig::Encoder::QSV:
+@@ -1046,7 +1046,7 @@ void AutoConfigTestPage::FinalizeResults()
+ return QTStr(QUALITY_SAME);
+ }
+
+- return QTStr(ENCODER_SOFTWARE);
++ return QTStr(ENCODER_X264);
+ };
+
+ auto newLabel = [this](const char *str) -> QLabel * {
+diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp
+index f6e0817a0..b056938e7 100644
+--- a/UI/window-basic-settings-stream.cpp
++++ b/UI/window-basic-settings-stream.cpp
+@@ -1585,7 +1585,7 @@ void OBSBasicSettings::ResetEncoders(bool streamOnly)
+
+ #define ENCODER_STR(str) QTStr("Basic.Settings.Output.Simple.Encoder." str)
+
+- ui->simpleOutStrEncoder->addItem(ENCODER_STR("Software"),
++ ui->simpleOutStrEncoder->addItem(ENCODER_STR("Software.X264.H264"),
+ QString(SIMPLE_ENCODER_X264));
+ if (service_supports_encoder(vcodecs, "obs_qsv11"))
+ ui->simpleOutStrEncoder->addItem(
+diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
+index fb170bfc1..48bb4bac6 100644
+--- a/UI/window-basic-settings.cpp
++++ b/UI/window-basic-settings.cpp
+@@ -5286,10 +5286,11 @@ void OBSBasicSettings::FillSimpleRecordingValues()
+ ADD_QUALITY("HQ");
+ ADD_QUALITY("Lossless");
+
+- ui->simpleOutRecEncoder->addItem(ENCODER_STR("Software"),
++ ui->simpleOutRecEncoder->addItem(ENCODER_STR("Software.X264.H264"),
+ QString(SIMPLE_ENCODER_X264));
+- ui->simpleOutRecEncoder->addItem(ENCODER_STR("SoftwareLowCPU"),
+- QString(SIMPLE_ENCODER_X264_LOWCPU));
++ ui->simpleOutRecEncoder->addItem(
++ ENCODER_STR("SoftwareLowCPU.X264.H264"),
++ QString(SIMPLE_ENCODER_X264_LOWCPU));
+ if (EncoderAvailable("obs_qsv11"))
+ ui->simpleOutRecEncoder->addItem(
+ ENCODER_STR("Hardware.QSV.H264"),
+--
+2.39.2
+
diff --git a/0002-obs-ffmpeg-Add-initial-support-for-the-OpenH264-H.26.patch
b/0002-obs-ffmpeg-Add-initial-support-for-the-OpenH264-H.26.patch
new file mode 100644
index 0000000..0a80a79
--- /dev/null
+++ b/0002-obs-ffmpeg-Add-initial-support-for-the-OpenH264-H.26.patch
@@ -0,0 +1,346 @@
+From 4517d8d8bb4c43af1f5b757773a5f9550bd23d37 Mon Sep 17 00:00:00 2001
+From: Neal Gompa <neal(a)gompa.dev>
+Date: Sun, 26 Mar 2023 06:06:31 -0400
+Subject: [PATCH 2/3] obs-ffmpeg: Add initial support for the OpenH264 H.264
+ software codec
+
+This allows users to leverage the OpenH264 codec from Cisco to encode
+H.264 video content. It is significantly reduced in capability from
+alternatives, but it does the job.
+
+This also provides a framework for adding support for other H.264
+software codecs provided through FFmpeg.
+---
+ plugins/obs-ffmpeg/CMakeLists.txt | 1 +
+ plugins/obs-ffmpeg/cmake/legacy.cmake | 1 +
+ plugins/obs-ffmpeg/data/locale/en-US.ini | 3 +
+ plugins/obs-ffmpeg/obs-ffmpeg-h264.c | 260 +++++++++++++++++++++++
+ plugins/obs-ffmpeg/obs-ffmpeg.c | 2 +
+ 5 files changed, 267 insertions(+)
+ create mode 100644 plugins/obs-ffmpeg/obs-ffmpeg-h264.c
+
+diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt
+index 3eba00932..f97622c22 100644
+--- a/plugins/obs-ffmpeg/CMakeLists.txt
++++ b/plugins/obs-ffmpeg/CMakeLists.txt
+@@ -34,6 +34,7 @@ target_sources(
+ obs-ffmpeg-video-encoders.c
+ obs-ffmpeg-audio-encoders.c
+ obs-ffmpeg-av1.c
++ obs-ffmpeg-h264.c
+ obs-ffmpeg-nvenc.c
+ obs-ffmpeg-output.c
+ obs-ffmpeg-mux.c
+diff --git a/plugins/obs-ffmpeg/cmake/legacy.cmake
b/plugins/obs-ffmpeg/cmake/legacy.cmake
+index 5540676ea..62f2cc7a1 100644
+--- a/plugins/obs-ffmpeg/cmake/legacy.cmake
++++ b/plugins/obs-ffmpeg/cmake/legacy.cmake
+@@ -40,6 +40,7 @@ target_sources(
+ obs-ffmpeg-video-encoders.c
+ obs-ffmpeg-audio-encoders.c
+ obs-ffmpeg-av1.c
++ obs-ffmpeg-h264.c
+ obs-ffmpeg-nvenc.c
+ obs-ffmpeg-output.c
+ obs-ffmpeg-mux.c
+diff --git a/plugins/obs-ffmpeg/data/locale/en-US.ini
b/plugins/obs-ffmpeg/data/locale/en-US.ini
+index 55bbe919d..0363ed82b 100644
+--- a/plugins/obs-ffmpeg/data/locale/en-US.ini
++++ b/plugins/obs-ffmpeg/data/locale/en-US.ini
+@@ -109,4 +109,7 @@ NVENC.CheckDrivers="Try installing the latest <a
href=\"https://obsproject.com/g
+
+ AV1.8bitUnsupportedHdr="OBS does not support 8-bit output of Rec. 2100."
+
++H264.UnsupportedVideoFormat="Only video formats using 8-bit color are
supported."
++H264.UnsupportedColorSpace="Only the Rec. 709 color space is supported."
++
+ ReconnectDelayTime="Reconnect Delay"
+diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-h264.c b/plugins/obs-ffmpeg/obs-ffmpeg-h264.c
+new file mode 100644
+index 000000000..179a61ccf
+--- /dev/null
++++ b/plugins/obs-ffmpeg/obs-ffmpeg-h264.c
+@@ -0,0 +1,260 @@
++/******************************************************************************
++ Copyright (C) 2023 by Neal Gompa <neal(a)gompa.dev>
++ Partly derived from obs-ffmpeg-av1.c by Hugh Bailey <obs.jim(a)gmail.com>
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <
http://www.gnu.org/licenses/>.
++******************************************************************************/
++
++#include "obs-ffmpeg-video-encoders.h"
++
++#define do_log(level, format, ...) \
++ blog(level, "[H.264 encoder: '%s'] " format, \
++ obs_encoder_get_name(enc->ffve.encoder), ##__VA_ARGS__)
++
++#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__)
++#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
++#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
++#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
++
++enum h264_encoder_type {
++ H264_ENCODER_TYPE_OH264,
++};
++
++struct h264_encoder {
++ struct ffmpeg_video_encoder ffve;
++ enum h264_encoder_type type;
++
++ DARRAY(uint8_t) header;
++};
++
++static const char *oh264_getname(void *unused)
++{
++ UNUSED_PARAMETER(unused);
++ return "OpenH264";
++}
++
++static void h264_video_info(void *data, struct video_scale_info *info)
++{
++ UNUSED_PARAMETER(data);
++
++ // OpenH264 only supports I420
++ info->format = VIDEO_FORMAT_I420;
++}
++
++static bool h264_update(struct h264_encoder *enc, obs_data_t *settings)
++{
++ const char *profile = obs_data_get_string(settings, "profile");
++ int bitrate = (int)obs_data_get_int(settings, "bitrate");
++ int keyint_sec = 0; // This is not supported by OpenH264
++ const char *rc_mode = "quality"; // We only want to use quality mode
++ int allow_skip_frames = 1; // This is required for quality mode
++
++ video_t *video = obs_encoder_video(enc->ffve.encoder);
++ const struct video_output_info *voi = video_output_get_info(video);
++ struct video_scale_info info;
++
++ info.format = voi->format;
++ info.colorspace = voi->colorspace;
++ info.range = voi->range;
++
++ enc->ffve.context->thread_count = 0;
++
++ h264_video_info(enc, &info);
++
++ av_opt_set(enc->ffve.context->priv_data, "rc_mode", rc_mode, 0);
++ av_opt_set(enc->ffve.context->priv_data, "profile", profile, 0);
++ av_opt_set_int(enc->ffve.context->priv_data, "allow_skip_frames",
++ allow_skip_frames, 0);
++
++ const char *ffmpeg_opts = obs_data_get_string(settings, "ffmpeg_opts");
++ ffmpeg_video_encoder_update(&enc->ffve, bitrate, keyint_sec, voi, &info,
++ ffmpeg_opts);
++ info("settings:\n"
++ "\tencoder: %s\n"
++ "\trc_mode: %s\n"
++ "\tbitrate: %d\n"
++ "\tprofile: %s\n"
++ "\twidth: %d\n"
++ "\theight: %d\n"
++ "\tffmpeg opts: %s\n",
++ enc->ffve.enc_name, rc_mode, bitrate, profile,
++ enc->ffve.context->width, enc->ffve.height, ffmpeg_opts);
++
++ enc->ffve.context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
++ return ffmpeg_video_encoder_init_codec(&enc->ffve);
++}
++
++static void h264_destroy(void *data)
++{
++ struct h264_encoder *enc = data;
++
++ ffmpeg_video_encoder_free(&enc->ffve);
++ da_free(enc->header);
++ bfree(enc);
++}
++
++static void on_first_packet(void *data, AVPacket *pkt, struct darray *da)
++{
++ struct h264_encoder *enc = data;
++
++ da_copy_array(enc->header, enc->ffve.context->extradata,
++ enc->ffve.context->extradata_size);
++
++ darray_copy_array(1, da, pkt->data, pkt->size);
++}
++
++static void *h264_create_internal(obs_data_t *settings, obs_encoder_t *encoder,
++ const char *enc_lib, const char *enc_name)
++{
++ video_t *video = obs_encoder_video(encoder);
++ const struct video_output_info *voi = video_output_get_info(video);
++
++ switch (voi->format) {
++ // planar 4:2:0 formats
++ case VIDEO_FORMAT_I420: // three-plane
++ case VIDEO_FORMAT_NV12: // two-plane, luma and packed chroma
++ // packed 4:2:2 formats
++ case VIDEO_FORMAT_YVYU:
++ case VIDEO_FORMAT_YUY2: // YUYV
++ case VIDEO_FORMAT_UYVY:
++ // packed uncompressed formats
++ case VIDEO_FORMAT_RGBA:
++ case VIDEO_FORMAT_BGRA:
++ case VIDEO_FORMAT_BGRX:
++ case VIDEO_FORMAT_BGR3:
++ case VIDEO_FORMAT_Y800: // grayscale
++ // planar 4:4:4
++ case VIDEO_FORMAT_I444:
++ // planar 4:2:2
++ case VIDEO_FORMAT_I422:
++ // planar 4:2:0 with alpha
++ case VIDEO_FORMAT_I40A:
++ // planar 4:2:2 with alpha
++ case VIDEO_FORMAT_I42A:
++ // planar 4:4:4 with alpha
++ case VIDEO_FORMAT_YUVA:
++ // packed 4:4:4 with alpha
++ case VIDEO_FORMAT_AYUV:
++ break;
++ default:; // Make the compiler do the right thing
++ const char *const text =
++ obs_module_text("H264.UnsupportedVideoFormat");
++ obs_encoder_set_last_error(encoder, text);
++ blog(LOG_ERROR, "[H.264 encoder] %s", text);
++ return NULL;
++ }
++
++ switch (voi->colorspace) {
++ case VIDEO_CS_DEFAULT:
++ case VIDEO_CS_709:
++ break;
++ default:; // Make the compiler do the right thing
++ const char *const text =
++ obs_module_text("H264.UnsupportedColorSpace");
++ obs_encoder_set_last_error(encoder, text);
++ blog(LOG_ERROR, "[H.264 encoder] %s", text);
++ return NULL;
++ }
++
++ struct h264_encoder *enc = bzalloc(sizeof(*enc));
++
++ if (strcmp(enc_lib, "libopenh264") == 0)
++ enc->type = H264_ENCODER_TYPE_OH264;
++
++ if (!ffmpeg_video_encoder_init(&enc->ffve, enc, encoder, enc_lib, NULL,
++ enc_name, NULL, on_first_packet))
++ goto fail;
++ if (!h264_update(enc, settings))
++ goto fail;
++
++ return enc;
++
++fail:
++ h264_destroy(enc);
++ return NULL;
++}
++
++static void *oh264_create(obs_data_t *settings, obs_encoder_t *encoder)
++{
++ return h264_create_internal(settings, encoder, "libopenh264",
++ "OpenH264");
++}
++
++static bool h264_encode(void *data, struct encoder_frame *frame,
++ struct encoder_packet *packet, bool *received_packet)
++{
++ struct h264_encoder *enc = data;
++ return ffmpeg_video_encode(&enc->ffve, frame, packet, received_packet);
++}
++
++void h264_defaults(obs_data_t *settings)
++{
++ obs_data_set_default_int(settings, "bitrate", 2500);
++ obs_data_set_default_string(settings, "profile", "main");
++}
++
++obs_properties_t *h264_properties(enum h264_encoder_type type)
++{
++ UNUSED_PARAMETER(type); // Only one encoder right now...
++ obs_properties_t *props = obs_properties_create();
++ obs_property_t *p;
++
++ p = obs_properties_add_list(props, "profile",
++ obs_module_text("Profile"),
++ OBS_COMBO_TYPE_LIST,
++ OBS_COMBO_FORMAT_STRING);
++ obs_property_list_add_string(p, "constrained_baseline",
++ "constrained_baseline");
++ obs_property_list_add_string(p, "main", "main");
++ obs_property_list_add_string(p, "high", "high");
++
++ p = obs_properties_add_int(props, "bitrate",
obs_module_text("Bitrate"),
++ 50, 300000, 50);
++ obs_property_int_set_suffix(p, " Kbps");
++
++ obs_properties_add_text(props, "ffmpeg_opts",
++ obs_module_text("FFmpegOpts"),
++ OBS_TEXT_DEFAULT);
++
++ return props;
++}
++
++obs_properties_t *oh264_properties(void *unused)
++{
++ UNUSED_PARAMETER(unused);
++ return h264_properties(H264_ENCODER_TYPE_OH264);
++}
++
++static bool h264_extra_data(void *data, uint8_t **extra_data, size_t *size)
++{
++ struct h264_encoder *enc = data;
++
++ *extra_data = enc->header.array;
++ *size = enc->header.num;
++ return true;
++}
++
++struct obs_encoder_info oh264_encoder_info = {
++ .id = "ffmpeg_openh264",
++ .type = OBS_ENCODER_VIDEO,
++ .codec = "h264",
++ .get_name = oh264_getname,
++ .create = oh264_create,
++ .destroy = h264_destroy,
++ .encode = h264_encode,
++ .get_defaults = h264_defaults,
++ .get_properties = oh264_properties,
++ .get_extra_data = h264_extra_data,
++ .get_video_info = h264_video_info,
++};
+diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c
+index da0b2c2b4..a01a729c8 100644
+--- a/plugins/obs-ffmpeg/obs-ffmpeg.c
++++ b/plugins/obs-ffmpeg/obs-ffmpeg.c
+@@ -39,6 +39,7 @@ extern struct obs_encoder_info pcm24_encoder_info;
+ extern struct obs_encoder_info pcm32_encoder_info;
+ extern struct obs_encoder_info alac_encoder_info;
+ extern struct obs_encoder_info flac_encoder_info;
++extern struct obs_encoder_info oh264_encoder_info;
+ extern struct obs_encoder_info h264_nvenc_encoder_info;
+ #ifdef ENABLE_HEVC
+ extern struct obs_encoder_info hevc_nvenc_encoder_info;
+@@ -387,6 +388,7 @@ bool obs_module_load(void)
+ obs_register_output(&ffmpeg_hls_muxer);
+ obs_register_output(&replay_buffer);
+ obs_register_encoder(&aac_encoder_info);
++ register_encoder_if_available(&oh264_encoder_info, "libopenh264");
+ register_encoder_if_available(&svt_av1_encoder_info, "libsvtav1");
+ register_encoder_if_available(&aom_av1_encoder_info, "libaom-av1");
+ obs_register_encoder(&opus_encoder_info);
+--
+2.39.2
+
diff --git a/0003-UI-Add-support-for-OpenH264-as-the-worst-case-fallba.patch
b/0003-UI-Add-support-for-OpenH264-as-the-worst-case-fallba.patch
new file mode 100644
index 0000000..cb52514
--- /dev/null
+++ b/0003-UI-Add-support-for-OpenH264-as-the-worst-case-fallba.patch
@@ -0,0 +1,545 @@
+From 30f84455969071ff9aa826a44438a0402dd15123 Mon Sep 17 00:00:00 2001
+From: Neal Gompa <neal(a)gompa.dev>
+Date: Tue, 28 Mar 2023 05:08:49 -0400
+Subject: [PATCH 3/3] UI: Add support for OpenH264 as the worst-case fallback
+
+OpenH264 exists as the codec of last resort, so it is implemented
+such that it is only used as the software codec if x264 is not
+available.
+---
+ UI/data/locale/en-US.ini | 1 +
+ UI/window-basic-auto-config-test.cpp | 50 +++++++++++++++++++---------
+ UI/window-basic-auto-config.cpp | 15 ++++++++-
+ UI/window-basic-auto-config.hpp | 3 ++
+ UI/window-basic-main-outputs.cpp | 6 ++--
+ UI/window-basic-main-profiles.cpp | 34 +++++++++++--------
+ UI/window-basic-main.cpp | 20 +++++++----
+ UI/window-basic-main.hpp | 3 +-
+ UI/window-basic-settings-stream.cpp | 16 ++++++---
+ UI/window-basic-settings.cpp | 23 ++++++++++---
+ 10 files changed, 123 insertions(+), 48 deletions(-)
+
+diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini
+index e44d99660..a5ba5d661 100644
+--- a/UI/data/locale/en-US.ini
++++ b/UI/data/locale/en-US.ini
+@@ -980,6 +980,7 @@ Basic.Settings.Output.Simple.Warn.Encoder="Warning: Recording
with a software en
+ Basic.Settings.Output.Simple.Warn.Lossless="Warning: Lossless quality generates
tremendously large file sizes! Lossless quality can use upward of 7 gigabytes of disk
space per minute at high resolutions and framerates. Lossless is not recommended for long
recordings unless you have a very large amount of disk space available."
+ Basic.Settings.Output.Simple.Warn.Lossless.Msg="Are you sure you want to use
lossless quality?"
+ Basic.Settings.Output.Simple.Warn.Lossless.Title="Lossless quality warning!"
++Basic.Settings.Output.Simple.Encoder.Software.OpenH264.H264="Software
(OpenH264)"
+ Basic.Settings.Output.Simple.Encoder.Software.X264.H264="Software (x264)"
+ Basic.Settings.Output.Simple.Encoder.Hardware.QSV.H264="Hardware (QSV,
H.264)"
+ Basic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1="Hardware (QSV, AV1)"
+diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp
+index 09979c031..c791c8b8b 100644
+--- a/UI/window-basic-auto-config-test.cpp
++++ b/UI/window-basic-auto-config-test.cpp
+@@ -199,7 +199,8 @@ void AutoConfigTestPage::TestBandwidthThread()
+ : "rtmp_common";
+
+ OBSEncoderAutoRelease vencoder = obs_video_encoder_create(
+- "obs_x264", "test_x264", nullptr, nullptr);
++ (wiz->x264Available ? "obs_x264" : "ffmpeg_openh264"),
++ "test_h264", nullptr, nullptr);
+ OBSEncoderAutoRelease aencoder = obs_audio_encoder_create(
+ "ffmpeg_aac", "test_aac", nullptr, 0, nullptr);
+ OBSServiceAutoRelease service = obs_service_create(
+@@ -238,10 +239,11 @@ void AutoConfigTestPage::TestBandwidthThread()
+ obs_data_set_string(service_settings, "key", key.c_str());
+
+ obs_data_set_int(vencoder_settings, "bitrate", wiz->startingBitrate);
+- obs_data_set_string(vencoder_settings, "rate_control", "CBR");
+- obs_data_set_string(vencoder_settings, "preset", "veryfast");
+- obs_data_set_int(vencoder_settings, "keyint_sec", 2);
+-
++ if (wiz->x264Available) {
++ obs_data_set_string(vencoder_settings, "rate_control", "CBR");
++ obs_data_set_string(vencoder_settings, "preset", "veryfast");
++ obs_data_set_int(vencoder_settings, "keyint_sec", 2);
++ }
+ obs_data_set_int(aencoder_settings, "bitrate", 32);
+
+ OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
+@@ -567,7 +569,8 @@ bool AutoConfigTestPage::TestSoftwareEncoding()
+ /* create obs objects */
+
+ OBSEncoderAutoRelease vencoder = obs_video_encoder_create(
+- "obs_x264", "test_x264", nullptr, nullptr);
++ (wiz->x264Available ? "obs_x264" : "ffmpeg_openh264"),
++ "test_h264", nullptr, nullptr);
+ OBSEncoderAutoRelease aencoder = obs_audio_encoder_create(
+ "ffmpeg_aac", "test_aac", nullptr, 0, nullptr);
+ OBSOutputAutoRelease output =
+@@ -581,17 +584,25 @@ bool AutoConfigTestPage::TestSoftwareEncoding()
+ obs_data_set_int(aencoder_settings, "bitrate", 32);
+
+ if (wiz->type != AutoConfig::Type::Recording) {
+- obs_data_set_int(vencoder_settings, "keyint_sec", 2);
++ if (wiz->x264Available) {
++ obs_data_set_int(vencoder_settings, "keyint_sec", 2);
++ obs_data_set_string(vencoder_settings, "rate_control",
++ "CBR");
++ obs_data_set_string(vencoder_settings, "preset",
++ "veryfast");
++ }
+ obs_data_set_int(vencoder_settings, "bitrate",
+ wiz->idealBitrate);
+- obs_data_set_string(vencoder_settings, "rate_control", "CBR");
+ obs_data_set_string(vencoder_settings, "profile", "main");
+- obs_data_set_string(vencoder_settings, "preset", "veryfast");
+ } else {
+- obs_data_set_int(vencoder_settings, "crf", 20);
+- obs_data_set_string(vencoder_settings, "rate_control", "CRF");
++ if (wiz->x264Available) {
++ obs_data_set_int(vencoder_settings, "crf", 20);
++ obs_data_set_string(vencoder_settings, "rate_control",
++ "CRF");
++ obs_data_set_string(vencoder_settings, "preset",
++ "veryfast");
++ }
+ obs_data_set_string(vencoder_settings, "profile", "high");
+- obs_data_set_string(vencoder_settings, "preset", "veryfast");
+ }
+
+ /* -----------------------------------*/
+@@ -944,7 +955,10 @@ void AutoConfigTestPage::TestStreamEncoderThread()
+ else
+ wiz->streamingEncoder = AutoConfig::Encoder::AMD;
+ } else {
+- wiz->streamingEncoder = AutoConfig::Encoder::x264;
++ if (wiz->x264Available)
++ wiz->streamingEncoder = AutoConfig::Encoder::x264;
++ else
++ wiz->streamingEncoder = AutoConfig::Encoder::OpenH264;
+ }
+
+ if (preferHardware && !softwareTested &&
wiz->hardwareEncodingAvailable)
+@@ -979,7 +993,10 @@ void AutoConfigTestPage::TestRecordingEncoderThread()
+ else
+ wiz->recordingEncoder = AutoConfig::Encoder::AMD;
+ } else {
+- wiz->recordingEncoder = AutoConfig::Encoder::x264;
++ if (wiz->x264Available)
++ wiz->streamingEncoder = AutoConfig::Encoder::x264;
++ else
++ wiz->streamingEncoder = AutoConfig::Encoder::OpenH264;
+ }
+
+ if (wiz->recordingEncoder != AutoConfig::Encoder::NVENC) {
+@@ -993,6 +1010,7 @@ void AutoConfigTestPage::TestRecordingEncoderThread()
+ }
+
+ #define ENCODER_TEXT(x) "Basic.Settings.Output.Simple.Encoder." x
++#define ENCODER_OPENH264 ENCODER_TEXT("Software.OpenH264.H264")
+ #define ENCODER_X264 ENCODER_TEXT("Software.X264.H264")
+ #define ENCODER_NVENC ENCODER_TEXT("Hardware.NVENC.H264")
+ #define ENCODER_QSV ENCODER_TEXT("Hardware.QSV.H264")
+@@ -1032,6 +1050,8 @@ void AutoConfigTestPage::FinalizeResults()
+
+ auto encName = [](AutoConfig::Encoder enc) -> QString {
+ switch (enc) {
++ case AutoConfig::Encoder::OpenH264:
++ return QTStr(ENCODER_OPENH264);
+ case AutoConfig::Encoder::x264:
+ return QTStr(ENCODER_X264);
+ case AutoConfig::Encoder::NVENC:
+@@ -1046,7 +1066,7 @@ void AutoConfigTestPage::FinalizeResults()
+ return QTStr(QUALITY_SAME);
+ }
+
+- return QTStr(ENCODER_X264);
++ return QTStr(ENCODER_OPENH264);
+ };
+
+ auto newLabel = [this](const char *str) -> QLabel * {
+diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp
+index 3e9c36685..eace18067 100644
+--- a/UI/window-basic-auto-config.cpp
++++ b/UI/window-basic-auto-config.cpp
+@@ -961,6 +961,7 @@ AutoConfig::AutoConfig(QWidget *parent) : QWizard(parent)
+ streamPage->ui->bitrate->setValue(bitrate);
+ streamPage->ServiceChanged();
+
++ TestSoftwareEncoding();
+ TestHardwareEncoding();
+ if (!hardwareEncodingAvailable) {
+ delete streamPage->ui->preferHardware;
+@@ -989,6 +990,16 @@ AutoConfig::~AutoConfig()
+ EnableThreadedMessageBoxes(false);
+ }
+
++void AutoConfig::TestSoftwareEncoding()
++{
++ size_t idx = 0;
++ const char *id;
++ while (obs_enum_encoder_types(idx++, &id)) {
++ if (strcmp(id, "obs_x264") == 0)
++ x264Available = true;
++ }
++}
++
+ void AutoConfig::TestHardwareEncoding()
+ {
+ size_t idx = 0;
+@@ -1061,8 +1072,10 @@ inline const char *AutoConfig::GetEncoderId(Encoder enc)
+ return SIMPLE_ENCODER_AMD;
+ case Encoder::Apple:
+ return SIMPLE_ENCODER_APPLE_H264;
+- default:
++ case Encoder::x264:
+ return SIMPLE_ENCODER_X264;
++ default:
++ return SIMPLE_ENCODER_OPENH264;
+ }
+ };
+
+diff --git a/UI/window-basic-auto-config.hpp b/UI/window-basic-auto-config.hpp
+index eb50701ff..e581791dd 100644
+--- a/UI/window-basic-auto-config.hpp
++++ b/UI/window-basic-auto-config.hpp
+@@ -43,6 +43,7 @@ class AutoConfig : public QWizard {
+ };
+
+ enum class Encoder {
++ OpenH264,
+ x264,
+ NVENC,
+ QSV,
+@@ -91,6 +92,7 @@ class AutoConfig : public QWizard {
+ bool qsvAvailable = false;
+ bool vceAvailable = false;
+ bool appleAvailable = false;
++ bool x264Available = false;
+
+ int startingBitrate = 2500;
+ bool customServer = false;
+@@ -106,6 +108,7 @@ class AutoConfig : public QWizard {
+ int specificFPSNum = 0;
+ int specificFPSDen = 0;
+
++ void TestSoftwareEncoding();
+ void TestHardwareEncoding();
+ bool CanTestServer(const char *server);
+
+diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp
+index 737ab966d..a4df630c4 100644
+--- a/UI/window-basic-main-outputs.cpp
++++ b/UI/window-basic-main-outputs.cpp
+@@ -515,7 +515,9 @@ void SimpleOutput::LoadStreamingPreset_Lossy(const char *encoderId)
+ /* mistakes have been made to lead us to this. */
+ const char *get_simple_output_encoder(const char *encoder)
+ {
+- if (strcmp(encoder, SIMPLE_ENCODER_X264) == 0) {
++ if (strcmp(encoder, SIMPLE_ENCODER_OPENH264) == 0) {
++ return "ffmpeg_openh264";
++ } else if (strcmp(encoder, SIMPLE_ENCODER_X264) == 0) {
+ return "obs_x264";
+ } else if (strcmp(encoder, SIMPLE_ENCODER_X264_LOWCPU) == 0) {
+ return "obs_x264";
+@@ -549,7 +551,7 @@ const char *get_simple_output_encoder(const char *encoder)
+ #endif
+ }
+
+- return "obs_x264";
++ return "ffmpeg_openh264";
+ }
+
+ void SimpleOutput::LoadRecordingPreset()
+diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp
+index 4941359ea..1f3ffdc1d 100644
+--- a/UI/window-basic-main-profiles.cpp
++++ b/UI/window-basic-main-profiles.cpp
+@@ -794,7 +794,7 @@ void OBSBasic::ChangeProfile()
+
+ Auth::Load();
+
+- CheckForSimpleModeX264Fallback();
++ CheckForSimpleModeH264Fallback();
+
+ blog(LOG_INFO, "Switched to profile '%s' (%s)", newName, newDir);
+ blog(LOG_INFO, "------------------------------------------------");
+@@ -815,12 +815,13 @@ void OBSBasic::ChangeProfile()
+ }
+ }
+
+-void OBSBasic::CheckForSimpleModeX264Fallback()
++void OBSBasic::CheckForSimpleModeH264Fallback()
+ {
+ const char *curStreamEncoder =
+ config_get_string(basicConfig, "SimpleOutput", "StreamEncoder");
+ const char *curRecEncoder =
+ config_get_string(basicConfig, "SimpleOutput", "RecEncoder");
++ bool x264_supported = false;
+ bool qsv_supported = false;
+ bool qsv_av1_supported = false;
+ bool amd_supported = false;
+@@ -837,7 +838,9 @@ void OBSBasic::CheckForSimpleModeX264Fallback()
+ const char *id;
+
+ while (obs_enum_encoder_types(idx++, &id)) {
+- if (strcmp(id, "amd_amf_h264") == 0)
++ if (strcmp(id, "obs_x264") == 0)
++ x264_supported = true;
++ else if (strcmp(id, "amd_amf_h264") == 0)
+ amd_supported = true;
+ else if (strcmp(id, "obs_qsv11") == 0)
+ qsv_supported = true;
+@@ -865,68 +868,73 @@ void OBSBasic::CheckForSimpleModeX264Fallback()
+ #endif
+ }
+
++ // Check to see whether x264 is available
++ const char *fallback_encoder_name = (x264_supported
++ ? SIMPLE_ENCODER_X264
++ : SIMPLE_ENCODER_OPENH264);
++
+ auto CheckEncoder = [&](const char *&name) {
+ if (strcmp(name, SIMPLE_ENCODER_QSV) == 0) {
+ if (!qsv_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ } else if (strcmp(name, SIMPLE_ENCODER_QSV_AV1) == 0) {
+ if (!qsv_av1_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ } else if (strcmp(name, SIMPLE_ENCODER_NVENC) == 0) {
+ if (!nve_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ } else if (strcmp(name, SIMPLE_ENCODER_NVENC_AV1) == 0) {
+ if (!nve_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ #ifdef ENABLE_HEVC
+ } else if (strcmp(name, SIMPLE_ENCODER_AMD_HEVC) == 0) {
+ if (!amd_hevc_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ } else if (strcmp(name, SIMPLE_ENCODER_NVENC_HEVC) == 0) {
+ if (!nve_hevc_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ #endif
+ } else if (strcmp(name, SIMPLE_ENCODER_AMD) == 0) {
+ if (!amd_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ } else if (strcmp(name, SIMPLE_ENCODER_AMD_AV1) == 0) {
+ if (!amd_av1_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ } else if (strcmp(name, SIMPLE_ENCODER_APPLE_H264) == 0) {
+ if (!apple_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ #ifdef ENABLE_HEVC
+ } else if (strcmp(name, SIMPLE_ENCODER_APPLE_HEVC) == 0) {
+ if (!apple_hevc_supported) {
+ changed = true;
+- name = SIMPLE_ENCODER_X264;
++ name = fallback_encoder_name;
+ return false;
+ }
+ #endif
+diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp
+index 6d9375eb4..c6aae8c7b 100644
+--- a/UI/window-basic-main.cpp
++++ b/UI/window-basic-main.cpp
+@@ -1379,6 +1379,8 @@ extern void CheckExistingCookieId();
+ #define DEFAULT_CONTAINER "fragmented_mp4"
+ #endif
+
++extern bool EncoderAvailable(const char *encoder);
++
+ bool OBSBasic::InitBasicConfigDefaults()
+ {
+ QList<QScreen *> screens = QGuiApplication::screens();
+@@ -1549,7 +1551,10 @@ bool OBSBasic::InitBasicConfigDefaults()
+ config_set_default_bool(basicConfig, "AdvOut", "UseRescale",
false);
+ config_set_default_uint(basicConfig, "AdvOut", "TrackIndex", 1);
+ config_set_default_uint(basicConfig, "AdvOut", "VodTrackIndex",
2);
+- config_set_default_string(basicConfig, "AdvOut", "Encoder",
"obs_x264");
++
++ bool useX264 = EncoderAvailable("obs_x264");
++ config_set_default_string(basicConfig, "AdvOut", "Encoder",
++ (useX264 ? "obs_x264" : "ffmpeg_openh264"));
+
+ config_set_default_string(basicConfig, "AdvOut", "RecType",
"Standard");
+
+@@ -1672,7 +1677,6 @@ bool OBSBasic::InitBasicConfigDefaults()
+ return true;
+ }
+
+-extern bool EncoderAvailable(const char *encoder);
+ extern bool update_nvenc_presets(ConfigFile &config);
+
+ void OBSBasic::InitBasicConfigDefaults2()
+@@ -1681,12 +1685,14 @@ void OBSBasic::InitBasicConfigDefaults2()
+ "Pre23Defaults");
+ bool useNV = EncoderAvailable("ffmpeg_nvenc") && !oldEncDefaults;
+
++ bool useX264 = EncoderAvailable("obs_x264");
++ const char *h264_fallback =
++ (useX264 ? SIMPLE_ENCODER_X264 : SIMPLE_ENCODER_OPENH264);
++
+ config_set_default_string(basicConfig, "SimpleOutput",
"StreamEncoder",
+- useNV ? SIMPLE_ENCODER_NVENC
+- : SIMPLE_ENCODER_X264);
++ useNV ? SIMPLE_ENCODER_NVENC : h264_fallback);
+ config_set_default_string(basicConfig, "SimpleOutput",
"RecEncoder",
+- useNV ? SIMPLE_ENCODER_NVENC
+- : SIMPLE_ENCODER_X264);
++ useNV ? SIMPLE_ENCODER_NVENC : h264_fallback);
+
+ const char *aac_default = "ffmpeg_aac";
+ if (EncoderAvailable("CoreAudio_AAC"))
+@@ -1967,7 +1973,7 @@ void OBSBasic::OBSInit()
+
+ InitBasicConfigDefaults2();
+
+- CheckForSimpleModeX264Fallback();
++ CheckForSimpleModeH264Fallback();
+
+ blog(LOG_INFO, STARTUP_SEPARATOR);
+
+diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp
+index cbce69832..74c6eb144 100644
+--- a/UI/window-basic-main.hpp
++++ b/UI/window-basic-main.hpp
+@@ -66,6 +66,7 @@ class OBSBasicVCamConfig;
+
+ #define SIMPLE_ENCODER_X264 "x264"
+ #define SIMPLE_ENCODER_X264_LOWCPU "x264_lowcpu"
++#define SIMPLE_ENCODER_OPENH264 "ffmpeg_openh264"
+ #define SIMPLE_ENCODER_QSV "qsv"
+ #define SIMPLE_ENCODER_QSV_AV1 "qsv_av1"
+ #define SIMPLE_ENCODER_NVENC "nvenc"
+@@ -434,7 +435,7 @@ private:
+ void DeleteProfile(const char *profile_name, const char *profile_dir);
+ void RefreshProfiles();
+ void ChangeProfile();
+- void CheckForSimpleModeX264Fallback();
++ void CheckForSimpleModeH264Fallback();
+
+ void SaveProjectNow();
+
+diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp
+index b056938e7..548e5f45f 100644
+--- a/UI/window-basic-settings-stream.cpp
++++ b/UI/window-basic-settings-stream.cpp
+@@ -1362,7 +1362,9 @@ static QString get_adv_fallback(const QString &enc)
+ return "com.apple.videotoolbox.videoencoder.ave.avc";
+ if (enc == "obs_qsv11_av1")
+ return "obs_qsv11";
+- return "obs_x264";
++ if (EncoderAvailable("obs_x264"))
++ return "obs_x264";
++ return "ffmpeg_openh264";
+ }
+
+ static QString get_adv_audio_fallback(const QString &enc)
+@@ -1391,7 +1393,9 @@ static QString get_simple_fallback(const QString &enc)
+ return SIMPLE_ENCODER_APPLE_H264;
+ if (enc == SIMPLE_ENCODER_QSV_AV1)
+ return SIMPLE_ENCODER_QSV;
+- return SIMPLE_ENCODER_X264;
++ if (EncoderAvailable("obs_x264"))
++ return SIMPLE_ENCODER_X264;
++ return SIMPLE_ENCODER_OPENH264;
+ }
+
+ bool OBSBasicSettings::ServiceSupportsCodecCheck()
+@@ -1585,8 +1589,12 @@ void OBSBasicSettings::ResetEncoders(bool streamOnly)
+
+ #define ENCODER_STR(str) QTStr("Basic.Settings.Output.Simple.Encoder." str)
+
+- ui->simpleOutStrEncoder->addItem(ENCODER_STR("Software.X264.H264"),
+- QString(SIMPLE_ENCODER_X264));
++ ui->simpleOutStrEncoder->addItem(ENCODER_STR("Software.OpenH264.H264"),
++ QString(SIMPLE_ENCODER_OPENH264));
++ if (service_supports_encoder(vcodecs, "obs_x264"))
++ ui->simpleOutStrEncoder->addItem(
++ ENCODER_STR("Software.X264.H264"),
++ QString(SIMPLE_ENCODER_X264));
+ if (service_supports_encoder(vcodecs, "obs_qsv11"))
+ ui->simpleOutStrEncoder->addItem(
+ ENCODER_STR("Hardware.QSV.H264"),
+diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
+index 48bb4bac6..51fe280db 100644
+--- a/UI/window-basic-settings.cpp
++++ b/UI/window-basic-settings.cpp
+@@ -3831,6 +3831,11 @@ void OBSBasicSettings::SaveOutputSettings()
+ do. This only exists to make sure that the x264 preset doesn't
+ get overwritten with empty data. */
+ presetType = "ApplePreset";
++ else if (encoder == SIMPLE_ENCODER_OPENH264)
++ /* The OpenH264 encoder does not have presets like the other encoders
++ do. This only exists to make sure that the x264 preset doesn't
++ get overwritten with empty data. */
++ presetType = "OpenH264Preset";
+ else
+ presetType = "Preset";
+
+@@ -5286,11 +5291,16 @@ void OBSBasicSettings::FillSimpleRecordingValues()
+ ADD_QUALITY("HQ");
+ ADD_QUALITY("Lossless");
+
+- ui->simpleOutRecEncoder->addItem(ENCODER_STR("Software.X264.H264"),
+- QString(SIMPLE_ENCODER_X264));
+- ui->simpleOutRecEncoder->addItem(
+- ENCODER_STR("SoftwareLowCPU.X264.H264"),
+- QString(SIMPLE_ENCODER_X264_LOWCPU));
++ ui->simpleOutRecEncoder->addItem(ENCODER_STR("Software.OpenH264.H264"),
++ QString(SIMPLE_ENCODER_OPENH264));
++ if (EncoderAvailable("obs_x264")) {
++ ui->simpleOutRecEncoder->addItem(
++ ENCODER_STR("Software.X264.H264"),
++ QString(SIMPLE_ENCODER_X264));
++ ui->simpleOutRecEncoder->addItem(
++ ENCODER_STR("SoftwareLowCPU.X264.H264"),
++ QString(SIMPLE_ENCODER_X264_LOWCPU));
++ }
+ if (EncoderAvailable("obs_qsv11"))
+ ui->simpleOutRecEncoder->addItem(
+ ENCODER_STR("Hardware.QSV.H264"),
+@@ -5463,6 +5473,9 @@ void OBSBasicSettings::SimpleStreamingEncoderChanged()
+
+ defaultPreset = "balanced";
+ preset = curAMDAV1Preset;
++ } else if (encoder == SIMPLE_ENCODER_OPENH264) {
++ ui->simpleOutPreset->setVisible(false);
++ ui->simpleOutPresetLabel->setVisible(false);
+ } else {
+
+ #define PRESET_STR(val) \
+--
+2.39.2
+
diff --git a/obs-studio-deps-Add-license-declaration-files.patch
b/obs-studio-deps-Add-license-declaration-files.patch
new file mode 100644
index 0000000..d1e460c
--- /dev/null
+++ b/obs-studio-deps-Add-license-declaration-files.patch
@@ -0,0 +1,218 @@
+From b6be0b4bf5b0b2f26c8143641881cde23ac8ba9c Mon Sep 17 00:00:00 2001
+From: Neal Gompa <neal(a)gompa.dev>
+Date: Wed, 26 Apr 2023 07:56:35 -0400
+Subject: [PATCH 1/2] deps: Add license declaration files
+
+---
+ deps/blake2/LICENSE.blake2 | 19 ++++++++++
+ deps/libff/LICENSE.libff | 18 ++++++++++
+ deps/media-playback/LICENSE.media-playback | 19 ++++++++++
+ libobs/graphics/libnsgif/LICENSE.libnsgif | 33 ++++++++++++++++++
+ libobs/util/simde/LICENSE.simde | 40 ++++++++++++++++++++++
+ plugins/decklink/LICENSE.decklink-sdk | 30 ++++++++++++++++
+ 6 files changed, 159 insertions(+)
+ create mode 100644 deps/blake2/LICENSE.blake2
+ create mode 100644 deps/libff/LICENSE.libff
+ create mode 100644 deps/media-playback/LICENSE.media-playback
+ create mode 100644 libobs/graphics/libnsgif/LICENSE.libnsgif
+ create mode 100644 libobs/util/simde/LICENSE.simde
+ create mode 100644 plugins/decklink/LICENSE.decklink-sdk
+
+diff --git a/deps/blake2/LICENSE.blake2 b/deps/blake2/LICENSE.blake2
+new file mode 100644
+index 000000000..3a8abc787
+--- /dev/null
++++ b/deps/blake2/LICENSE.blake2
+@@ -0,0 +1,19 @@
++BLAKE2 is licensed to permit usage under the terms of the CC0, the OpenSSL License,
++ or the Apache Public License 2.0 at the user's option.
++
++The license grant is reproduced in full below.
++
++/*
++ BLAKE2 reference source code package - reference C implementations
++
++ Copyright 2012, Samuel Neves <sneves(a)dei.uc.pt>. You may use this under the
++ terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
++ your option. The terms of these licenses can be found at:
++
++ - CC0 1.0 Universal :
http://creativecommons.org/publicdomain/zero/1.0
++ - OpenSSL license :
https://www.openssl.org/source/license.html
++ - Apache 2.0 :
http://www.apache.org/licenses/LICENSE-2.0
++
++ More information about the BLAKE2 hash function can be found at
++
https://blake2.net.
++*/
+diff --git a/deps/libff/LICENSE.libff b/deps/libff/LICENSE.libff
+new file mode 100644
+index 000000000..b6f51157a
+--- /dev/null
++++ b/deps/libff/LICENSE.libff
+@@ -0,0 +1,18 @@
++libff is licensed under the ISC license. The license terms are fully
++reproduced below:
++
++/*
++ * Copyright (c) 2015 John R. Bradley <jrb(a)turrettech.com>
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
+diff --git a/deps/media-playback/LICENSE.media-playback
b/deps/media-playback/LICENSE.media-playback
+new file mode 100644
+index 000000000..17d74f4b3
+--- /dev/null
++++ b/deps/media-playback/LICENSE.media-playback
+@@ -0,0 +1,19 @@
++media-playback is licensed under the ISC license. The license terms are fully
++reproduced below:
++
++
++/*
++ * Copyright (c) 2023 Hugh Bailey <obs.jim(a)gmail.com>
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
+diff --git a/libobs/graphics/libnsgif/LICENSE.libnsgif
b/libobs/graphics/libnsgif/LICENSE.libnsgif
+new file mode 100644
+index 000000000..9b28c0b45
+--- /dev/null
++++ b/libobs/graphics/libnsgif/LICENSE.libnsgif
+@@ -0,0 +1,33 @@
++libnsgif is licensed under the MIT license. The licensing statement
++and the full license are reproduced below.
++
++/*
++ * Copyright 2003 James Bursa <bursa(a)users.sourceforge.net>
++ * Copyright 2004 John Tytgat <John.Tytgat(a)aaug.net>
++ * Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
++ * Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
++ *
++ * This file is part of NetSurf's libnsgif,
http://www.netsurf-browser.org/
++ * Licenced under the MIT License,
++ *
http://www.opensource.org/licenses/mit-license.php
++ */
++
++The MIT License
++
++Permission is hereby granted, free of charge, to any person obtaining a copy
++of this software and associated documentation files (the "Software"), to deal
++in the Software without restriction, including without limitation the rights
++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++copies of the Software, and to permit persons to whom the Software is
++furnished to do so, subject to the following conditions:
++
++The above copyright notice and this permission notice shall be included in
++all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++THE SOFTWARE.
+diff --git a/libobs/util/simde/LICENSE.simde b/libobs/util/simde/LICENSE.simde
+new file mode 100644
+index 000000000..78d482e75
+--- /dev/null
++++ b/libobs/util/simde/LICENSE.simde
+@@ -0,0 +1,40 @@
++simde is licensed as a combination of MIT and CC0 code.
++
++License notices for both are reproduced below:
++
++/* SPDX-License-Identifier: MIT
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ * Copyright:
++ * 2017-2020 Evan Nemerson <evan(a)nemerson.com>
++ */
++
++/* Portable Snippets -
https://gitub.com/nemequ/portable-snippets
++ * Created by Evan Nemerson <evan(a)nemerson.com>
++ *
++ * To the extent possible under law, the authors have waived all
++ * copyright and related or neighboring rights to this code. For
++ * details, see the Creative Commons Zero 1.0 Universal license at
++ *
https://creativecommons.org/publicdomain/zero/1.0/
++ *
++ * SPDX-License-Identifier: CC0-1.0
++ */
+diff --git a/plugins/decklink/LICENSE.decklink-sdk
b/plugins/decklink/LICENSE.decklink-sdk
+new file mode 100644
+index 000000000..d320d805b
+--- /dev/null
++++ b/plugins/decklink/LICENSE.decklink-sdk
+@@ -0,0 +1,30 @@
++decklink-sdk is licensed under to Boost Software License 1.0 (BSL-1.0).
++
++The license text is reproduced in full below:
++
++/* -LICENSE-START-
++** Copyright (c) 2020 Blackmagic Design
++**
++** Permission is hereby granted, free of charge, to any person or organization
++** obtaining a copy of the software and accompanying documentation covered by
++** this license (the "Software") to use, reproduce, display, distribute,
++** execute, and transmit the Software, and to prepare derivative works of the
++** Software, and to permit third-parties to whom the Software is furnished to
++** do so, all subject to the following:
++**
++** The copyright notices in the Software and this entire statement, including
++** the above license grant, this restriction and the following disclaimer,
++** must be included in all copies of the Software, in whole or in part, and
++** all derivative works of the Software, unless such copies or derivative
++** works are solely in the form of machine-executable object code generated by
++** a source language processor.
++**
++** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
++** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
++** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
++** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++** DEALINGS IN THE SOFTWARE.
++** -LICENSE-END-
++*/
+--
+2.40.0
+
diff --git a/obs-studio-freeworld.spec b/obs-studio-freeworld.spec
new file mode 100644
index 0000000..84874a5
--- /dev/null
+++ b/obs-studio-freeworld.spec
@@ -0,0 +1,271 @@
+%ifarch %{power64} s390x
+# LuaJIT is not available for POWER and IBM Z
+%bcond_with lua_scripting
+%else
+%bcond_without lua_scripting
+%endif
+
+%global obswebsocket_version 5.2.2
+%global origname obs-studio
+
+%if "%{__isa_bits}" == "64"
+%global lib64_suffix ()(64bit)
+%endif
+%global libvlc_soversion 5
+
+Name: obs-studio-freeworld
+Version: 29.1.0
+Release: 1%{?dist}
+Summary: Open Broadcaster Software Studio -- Freeworld plugins
+
+# OBS itself is GPL-2.0-or-later, while various plugin dependencies are of various other
licenses
+# The licenses for those dependencies are captured with the bundled provides statements
+License: GPL-2.0-or-later and ISC and MIT and BSD-1-Clause and BSD-2-Clause and
BSD-3-Clause and BSL-1.0 and LGPL-2.1-or-later and CC0-1.0 and (CC0-1.0 or OpenSSL or
Apache-2.0) and LicenseRef-Fedora-Public-Domain and (BSD-3-Clause or GPL-2.0-only)
+URL:
https://obsproject.com/
+%if 0%{?snapdate}
+Source0:
https://github.com/obsproject/obs-studio/archive/%{commit}/%{origname}-%{...
+%else
+Source0:
https://github.com/obsproject/obs-studio/archive/%{version_no_tilde}/%{or...
+%endif
+Source1:
https://github.com/obsproject/obs-websocket/archive/%{obswebsocket_versio...
+
+# Backports from upstream
+
+# Proposed upstream
+## From:
https://github.com/obsproject/obs-studio/pull/8529
+Patch0101: 0001-UI-Consistently-reference-the-software-H264-encoder-.patch
+Patch0102: 0002-obs-ffmpeg-Add-initial-support-for-the-OpenH264-H.26.patch
+Patch0103: 0003-UI-Add-support-for-OpenH264-as-the-worst-case-fallba.patch
+
+
+# Downstream Fedora patches
+## Use system qrcodegencpp
+Patch1001: obs-studio-websocket-use-system-qrcodegencpp.patch
+## Add license declarations for bundled deps
+Patch9001: obs-studio-deps-Add-license-declaration-files.patch
+## Add license declaration for obs-qsv11
+Patch9002: obs-studio-obs-qsv11-Add-license-declaration-file.patch
+
+
+BuildRequires: gcc
+BuildRequires: cmake >= 3.16
+BuildRequires: ninja-build
+BuildRequires: libappstream-glib
+BuildRequires: desktop-file-utils
+
+BuildRequires: alsa-lib-devel
+BuildRequires: asio-devel
+BuildRequires: fdk-aac-free-devel
+BuildRequires: ffmpeg-devel
+BuildRequires: fontconfig-devel
+BuildRequires: freetype-devel
+BuildRequires: jansson-devel >= 2.5
+BuildRequires: json-devel
+BuildRequires: libcurl-devel
+BuildRequires: libdrm-devel
+BuildRequires: libGL-devel
+BuildRequires: libglvnd-devel
+BuildRequires: librist-devel
+BuildRequires: srt-devel
+BuildRequires: libuuid-devel
+BuildRequires: libv4l-devel
+BuildRequires: libva-devel
+BuildRequires: libX11-devel
+BuildRequires: libxcb-devel
+BuildRequires: libXcomposite-devel
+BuildRequires: libXinerama-devel
+BuildRequires: libxkbcommon-devel
+%if %{with lua_scripting}
+BuildRequires: luajit-devel
+%endif
+BuildRequires: mbedtls-devel
+BuildRequires: pciutils-devel
+BuildRequires: pipewire-devel
+BuildRequires: pipewire-jack-audio-connection-kit-devel
+BuildRequires: pulseaudio-libs-devel
+BuildRequires: python3-devel
+BuildRequires: libqrcodegencpp-devel
+BuildRequires: qt6-qtbase-devel
+BuildRequires: qt6-qtbase-private-devel
+BuildRequires: qt6-qtsvg-devel
+BuildRequires: qt6-qtwayland-devel
+BuildRequires: speexdsp-devel
+BuildRequires: swig
+BuildRequires: systemd-devel
+BuildRequires: wayland-devel
+BuildRequires: websocketpp-devel
+
+# Ensure QtWayland is installed when libwayland-client is installed
+Requires: (qt6-qtwayland%{?_isa} if libwayland-client%{?_isa})
+# For icon folder heirarchy
+Requires: hicolor-icon-theme
+
+# These are modified sources that can't be easily unbundled
+## License: MIT and CC0-1.0
+## Newer version in Fedora with the same licensing
+## Request filed upstream for fixing it:
https://github.com/simd-everywhere/simde/issues/999
+Provides: bundled(simde) = 0.7.1
+## License: BSL-1.0
+Provides: bundled(decklink-sdk)
+## License: CC0-1.0 or OpenSSL or Apache-2.0
+Provides: bundled(blake2)
+## License: MIT
+Provides: bundled(json11)
+## License: MIT
+Provides: bundled(libcaption)
+## License: ISC
+Provides: bundled(libff)
+## License: BSD-1-Clause
+Provides: bundled(uthash)
+## License: BSD-3-Clause
+Provides: bundled(rnnoise)
+## License: LGPL-2.1-or-later and LicenseRef-Fedora-Public-Domain
+Provides: bundled(librtmp)
+## License: MIT
+Provides: bundled(libnsgif)
+## License: MIT
+## Windows only dependency
+## Support for Linux will also unbundle it
+## Cf.
https://github.com/obsproject/obs-studio/pull/8327
+Provides: bundled(intel-mediasdk)
+
+%description
+Open Broadcaster Software is free and open source
+software for video recording and live streaming.
+
+# --------------------------------------------------------------------------
+
+%package -n obs-studio-plugin-x264
+Summary: Open Broadcaster Software Studio - x264 encoding plugin
+License: GPL-2.0-or-later
+BuildRequires: x264-devel
+Requires: obs-studio%{?_isa} >= %{version}
+Supplements: obs-studio%{?_isa}
+
+%description -n obs-studio-plugin-x264
+Open Broadcaster Software is free and open source software
+for video recording and live streaming.
+
+This package contains the plugin for using the x264 encoder for
+streaming or recording AVC/H.264 video.
+
+%files -n obs-studio-plugin-x264
+%license COPYING
+%{_libdir}/obs-plugins/obs-x264.so
+%{_datadir}/obs/obs-plugins/obs-x264/
+
+# --------------------------------------------------------------------------
+
+%package -n obs-studio-plugin-vlc-video
+Summary: Open Broadcaster Software Studio - VLC-based video plugin
+License: GPL-2.0-or-later
+BuildRequires: vlc-devel
+# We dlopen() libvlc
+Requires: libvlc.so.%{libvlc_soversion}%{?lib64_suffix}
+Requires: obs-studio%{?_isa} >= %{version}
+Supplements: obs-studio%{?_isa}
+
+
+%description -n obs-studio-plugin-vlc-video
+Open Broadcaster Software is free and open source software
+for video recording and live streaming.
+
+This package contains the plugin for using VLC to embed video
+as an overlay in a video stream or recording.
+
+%files -n obs-studio-plugin-vlc-video
+%license COPYING
+%{_libdir}/obs-plugins/vlc-video.so
+%{_datadir}/obs/obs-plugins/vlc-video/
+
+# --------------------------------------------------------------------------
+
+
+%prep
+%setup -q -n %{origname}-%{?snapdate:%{commit}}%{!?snapdate:%{version_no_tilde}}
+# Prepare plugins/obs-websocket
+tar -xf %{SOURCE1} -C plugins/obs-websocket --strip-components=1
+%autopatch -p1
+
+# rpmlint reports E: hardcoded-library-path
+# replace OBS_MULTIARCH_SUFFIX by LIB_SUFFIX
+sed -e 's|OBS_MULTIARCH_SUFFIX|LIB_SUFFIX|g' -i cmake/Modules/ObsHelpers.cmake
+
+# Kill rpath settings
+sed -e '\|set(CMAKE_INSTALL_RPATH
"${CMAKE_INSTALL_PREFIX}/${OBS_LIBRARY_DESTINATION}")|d' -i
cmake/Modules/ObsHelpers_Linux.cmake
+
+# touch the missing submodules
+touch plugins/obs-browser/CMakeLists.txt
+
+# remove -Werror flag to mitigate FTBFS with ffmpeg 5.1
+sed -e 's|-Werror-implicit-function-declaration||g' -i
cmake/Modules/CompilerConfig.cmake
+sed -e '/-Werror/d' -i cmake/Modules/CompilerConfig.cmake
+
+# Removing unused third-party deps
+rm -rf deps/w32-pthreads
+rm -rf deps/ipc-util
+rm -rf deps/jansson
+
+# Remove unneeded EGL/KHR files
+rm -rf deps/glad/include/{EGL,KHR}
+sed -e 's|include/EGL/eglplatform.h||g' -i deps/glad/CMakeLists.txt
+
+# Collect license files
+mkdir -p .fedora-rpm/licenses/deps
+mkdir -p .fedora-rpm/licenses/plugins
+cp plugins/obs-filters/rnnoise/COPYING .fedora-rpm/licenses/deps/rnnoise-COPYING
+cp plugins/obs-websocket/LICENSE .fedora-rpm/licenses/plugins/obs-websocket-LICENSE
+cp plugins/obs-outputs/librtmp/COPYING .fedora-rpm/licenses/deps/librtmp-COPYING
+cp deps/json11/LICENSE.txt .fedora-rpm/licenses/deps/json11-LICENSE.txt
+cp deps/libcaption/LICENSE.txt .fedora-rpm/licenses/deps/libcaption-LICENSE.txt
+cp plugins/obs-qsv11/QSV11-License-Clarification-Email.txt
.fedora-rpm/licenses/plugins/QSV11-License-Clarification-Email.txt
+cp deps/uthash/uthash/LICENSE .fedora-rpm/licenses/deps/uthash-LICENSE
+cp deps/blake2/LICENSE.blake2 .fedora-rpm/licenses/deps/
+cp deps/libff/LICENSE.libff .fedora-rpm/licenses/deps/
+cp deps/media-playback/LICENSE.media-playback .fedora-rpm/licenses/deps/
+cp libobs/graphics/libnsgif/LICENSE.libnsgif .fedora-rpm/licenses/deps/
+cp libobs/util/simde/LICENSE.simde .fedora-rpm/licenses/deps/
+cp plugins/decklink/LICENSE.decklink-sdk .fedora-rpm/licenses/deps
+cp plugins/obs-qsv11/obs-qsv11-LICENSE.txt .fedora-rpm/licenses/plugins/
+
+
+%build
+%cmake -DOBS_VERSION_OVERRIDE=%{version_no_tilde} \
+ -DUNIX_STRUCTURE=1 -GNinja \
+ -DCMAKE_SKIP_RPATH=1 \
+ -DBUILD_BROWSER=OFF \
+ -DENABLE_JACK=ON \
+ -DENABLE_LIBFDK=ON \
+ -DENABLE_AJA=OFF \
+%if ! %{with lua_scripting}
+ -DDISABLE_LUA=ON \
+%endif
+ -DOpenGL_GL_PREFERENCE=GLVND
+%cmake_build
+
+
+%install
+%cmake_install
+
+mkdir -p preserve/%{_libdir}/obs-plugins
+mkdir -p preserve/%{_datadir}/obs/obs-plugins
+
+# Preserve plugin files to install
+mv %{buildroot}%{_libdir}/obs-plugins/obs-x264.so preserve/%{_libdir}/obs-plugins
+mv %{buildroot}%{_datadir}/obs/obs-plugins/obs-x264 preserve/%{_datadir}/obs/obs-plugins
+mv %{buildroot}%{_libdir}/obs-plugins/vlc-video.so preserve/%{_libdir}/obs-plugins
+mv %{buildroot}%{_datadir}/obs/obs-plugins/vlc-video
preserve/%{_datadir}/obs/obs-plugins
+
+# Purge installed buildroot
+rm -rf %{buildroot}/*
+
+# Re-install preserved plugins
+mv preserve/%{_prefix} %{buildroot}
+
+
+%changelog
+* Thu May 04 2023 Neal Gompa <ngompa(a)fedoraproject.org> - 29.1.0-1
+- Update to 29.1.0 final
+
+* Wed Apr 19 2023 Neal Gompa <ngompa(a)fedoraproject.org> - 29.1.0~beta4-0.1
+- Initial split package from obs-studio
diff --git a/obs-studio-obs-qsv11-Add-license-declaration-file.patch
b/obs-studio-obs-qsv11-Add-license-declaration-file.patch
new file mode 100644
index 0000000..0504446
--- /dev/null
+++ b/obs-studio-obs-qsv11-Add-license-declaration-file.patch
@@ -0,0 +1,79 @@
+From fbfa25ade86c26088333b3c5e6c39d4a61fc748f Mon Sep 17 00:00:00 2001
+From: Neal Gompa <neal(a)gompa.dev>
+Date: Wed, 26 Apr 2023 07:56:42 -0400
+Subject: [PATCH 2/2] obs-qsv11: Add license declaration file
+
+---
+ plugins/obs-qsv11/obs-qsv11-LICENSE.txt | 60 +++++++++++++++++++++++++
+ 1 file changed, 60 insertions(+)
+ create mode 100644 plugins/obs-qsv11/obs-qsv11-LICENSE.txt
+
+diff --git a/plugins/obs-qsv11/obs-qsv11-LICENSE.txt
b/plugins/obs-qsv11/obs-qsv11-LICENSE.txt
+new file mode 100644
+index 000000000..2e4f30941
+--- /dev/null
++++ b/plugins/obs-qsv11/obs-qsv11-LICENSE.txt
+@@ -0,0 +1,60 @@
++The OBS QSV plugin is licensed under either the BSD-3-Clause or GPL-2.0-only licenses.
++
++The license declaration is reproduced below:
++
++/*
++
++This is provided under a dual BSD/GPLv2 license. When using or
++redistributing this, you may do so under either license.
++
++GPL LICENSE SUMMARY
++
++Copyright(c) Oct. 2015 Intel Corporation.
++
++This program is free software; you can redistribute it and/or modify
++it under the terms of version 2 of the GNU General Public License as
++published by the Free Software Foundation.
++
++This program is distributed in the hope that it will be useful, but
++WITHOUT ANY WARRANTY; without even the implied warranty of
++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++General Public License for more details.
++
++Contact Information:
++
++Seung-Woo Kim, seung-woo.kim(a)intel.com
++705 5th Ave S #500, Seattle, WA 98104
++
++BSD LICENSE
++
++Copyright(c) <date> Intel Corporation.
++
++Redistribution and use in source and binary forms, with or without
++modification, are permitted provided that the following conditions
++are met:
++
++* Redistributions of source code must retain the above copyright
++notice, this list of conditions and the following disclaimer.
++
++* Redistributions in binary form must reproduce the above copyright
++notice, this list of conditions and the following disclaimer in
++the documentation and/or other materials provided with the
++distribution.
++
++* Neither the name of Intel Corporation nor the names of its
++contributors may be used to endorse or promote products derived
++from this software without specific prior written permission.
++
++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++*/
++
+--
+2.40.0
+
diff --git a/obs-studio-websocket-use-system-qrcodegencpp.patch
b/obs-studio-websocket-use-system-qrcodegencpp.patch
new file mode 100644
index 0000000..736a77c
--- /dev/null
+++ b/obs-studio-websocket-use-system-qrcodegencpp.patch
@@ -0,0 +1,107 @@
+From 1e2fc3ade587a7a7c24e4238996ca382c4c0f719 Mon Sep 17 00:00:00 2001
+From: Neal Gompa <ngompa(a)fedoraproject.org>
+Date: Tue, 27 Dec 2022 09:15:08 -0500
+Subject: [PATCH] CMake: Use the system version of QRCodeGenCPP dependency
+
+---
+ CMakeLists.txt | 9 +++++++--
+ cmake/legacy.cmake | 12 ++++++++----
+ src/forms/ConnectInfo.cpp | 2 +-
+ 3 files changed, 16 insertions(+), 7 deletions(-)
+
+diff --git a/plugins/obs-websocket/CMakeLists.txt b/plugins/obs-websocket/CMakeLists.txt
+index 871f92b..288cc87 100644
+--- a/plugins/obs-websocket/CMakeLists.txt
++++ b/plugins/obs-websocket/CMakeLists.txt
+@@ -13,7 +13,7 @@ endif()
+
+ # Submodule deps check
+ if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/deps/qr/cpp/QrCode.hpp")
+- message(FATAL_ERROR "obs-websocket submodule deps not available.")
++ message(INFO "obs-websocket submodule deps not available.")
+ endif()
+
+ # Find Qt
+@@ -22,6 +22,9 @@ find_qt(COMPONENTS Core Widgets Svg Network)
+ # Find nlohmann JSON
+ find_package(nlohmann_json 3 REQUIRED)
+
++# Find qrcodegencpp
++find_package(qrcodegencpp REQUIRED)
++
+ # Find WebSocket++
+ find_package(Websocketpp 0.8 REQUIRED)
+
+@@ -157,7 +160,9 @@ target_link_libraries(
+ Qt::Network
+ nlohmann_json::nlohmann_json
+ Websocketpp::Websocketpp
+- Asio::Asio)
++ Asio::Asio
++ qrcodegencpp
++ )
+
+ set_target_properties_obs(
+ obs-websocket
+diff --git a/plugins/obs-websocket/cmake/legacy.cmake
b/plugins/obs-websocket/cmake/legacy.cmake
+index ab25ec7..98ca704 100644
+--- a/plugins/obs-websocket/cmake/legacy.cmake
++++ b/plugins/obs-websocket/cmake/legacy.cmake
+@@ -10,7 +10,7 @@ endif()
+
+ # Submodule deps check
+ if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/deps/qr/cpp/QrCode.hpp)
+- obs_status(FATAL_ERROR "obs-websocket submodule deps not available.")
++ obs_status(INFO "obs-websocket submodule deps not available.")
+ endif()
+
+ # Plugin tests flag
+@@ -22,6 +22,9 @@ find_qt(COMPONENTS Core Widgets Svg Network)
+ # Find nlohmann JSON
+ find_package(nlohmann_json 3 REQUIRED)
+
++# Find qrcodegencpp
++find_package(qrcodegencpp REQUIRED)
++
+ # Find WebSocket++
+ find_package(Websocketpp 0.8 REQUIRED)
+
+@@ -129,8 +132,7 @@ target_sources(
+ src/utils/Compat.cpp
+ src/utils/Compat.h
+ src/utils/Utils.h
+- deps/qr/cpp/QrCode.cpp
+- deps/qr/cpp/QrCode.hpp)
++ )
+
+ target_link_libraries(
+ obs-websocket
+@@ -142,7 +144,9 @@ target_link_libraries(
+ Qt::Network
+ nlohmann_json::nlohmann_json
+ Websocketpp::Websocketpp
+- Asio::Asio)
++ Asio::Asio
++ qrcodegencpp
++ )
+
+ target_compile_features(obs-websocket PRIVATE cxx_std_17)
+
+diff --git a/plugins/obs-websocket/src/forms/ConnectInfo.cpp
b/plugins/obs-websocket/src/forms/ConnectInfo.cpp
+index ddb979d..89a04c4 100644
+--- a/plugins/obs-websocket/src/forms/ConnectInfo.cpp
++++ b/plugins/obs-websocket/src/forms/ConnectInfo.cpp
+@@ -21,9 +21,9 @@ with this program. If not, see <
https://www.gnu.org/licenses/>
+ #include <QPainter>
+ #include <QUrl>
+ #include <obs-module.h>
++#include <qrcodegencpp/QrCode.hpp>
+
+ #include "ConnectInfo.h"
+-#include "../../deps/qr/cpp/QrCode.hpp"
+ #include "../obs-websocket.h"
+ #include "../Config.h"
+ #include "../utils/Platform.h"
+--
+2.39.2
+
diff --git a/sources b/sources
index e69de29..2b83218 100644
--- a/sources
+++ b/sources
@@ -0,0 +1,2 @@
+SHA512 (obs-studio-29.1.0.tar.gz) =
39978eb1872d8fc3903db654b0f227f9ff6d66283261f4a5d736b3404ab9549708c468f18f98402ad3e2759ed3e937b56a4fb4eae600bd226cf6b3e7dafe6b0d
+SHA512 (obs-websocket-5.2.2.tar.gz) =
7678e247b8cc4d9cbe650a7b45a63b47a0752ba8e08e3b02bc7bdff42ec7d122eff9bad9139edb0f268379e495bae5e8f05dd177c6f935c7b47c02858727cb28