UI: Migrate from libff
This commit is contained in:
parent
182468b373
commit
f0407dd1cd
@ -16,10 +16,6 @@ endif()
|
|||||||
find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat)
|
find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat)
|
||||||
find_package(CURL REQUIRED)
|
find_package(CURL REQUIRED)
|
||||||
|
|
||||||
if(NOT TARGET OBS::libff-util)
|
|
||||||
add_subdirectory("${CMAKE_SOURCE_DIR}/deps/libff" "${CMAKE_BINARY_DIR}/deps/libff")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT TARGET OBS::json11)
|
if(NOT TARGET OBS::json11)
|
||||||
add_subdirectory("${CMAKE_SOURCE_DIR}/deps/json11" "${CMAKE_BINARY_DIR}/deps/json11")
|
add_subdirectory("${CMAKE_SOURCE_DIR}/deps/json11" "${CMAKE_BINARY_DIR}/deps/json11")
|
||||||
endif()
|
endif()
|
||||||
@ -35,7 +31,6 @@ target_link_libraries(
|
|||||||
FFmpeg::avformat
|
FFmpeg::avformat
|
||||||
OBS::libobs
|
OBS::libobs
|
||||||
OBS::frontend-api
|
OBS::frontend-api
|
||||||
OBS::libff-util
|
|
||||||
OBS::json11)
|
OBS::json11)
|
||||||
|
|
||||||
include(cmake/ui-qt.cmake)
|
include(cmake/ui-qt.cmake)
|
||||||
@ -71,6 +66,8 @@ target_sources(
|
|||||||
auth-oauth.cpp
|
auth-oauth.cpp
|
||||||
auth-oauth.hpp
|
auth-oauth.hpp
|
||||||
display-helpers.hpp
|
display-helpers.hpp
|
||||||
|
ffmpeg-utils.cpp
|
||||||
|
ffmpeg-utils.hpp
|
||||||
multiview.cpp
|
multiview.cpp
|
||||||
multiview.hpp
|
multiview.hpp
|
||||||
obf.c
|
obf.c
|
||||||
|
@ -150,10 +150,10 @@ target_sources(
|
|||||||
ui-validation.hpp
|
ui-validation.hpp
|
||||||
multiview.cpp
|
multiview.cpp
|
||||||
multiview.hpp
|
multiview.hpp
|
||||||
|
ffmpeg-utils.cpp
|
||||||
|
ffmpeg-utils.hpp
|
||||||
${CMAKE_SOURCE_DIR}/deps/json11/json11.cpp
|
${CMAKE_SOURCE_DIR}/deps/json11/json11.cpp
|
||||||
${CMAKE_SOURCE_DIR}/deps/json11/json11.hpp
|
${CMAKE_SOURCE_DIR}/deps/json11/json11.hpp
|
||||||
${CMAKE_SOURCE_DIR}/deps/libff/libff/ff-util.c
|
|
||||||
${CMAKE_SOURCE_DIR}/deps/libff/libff/ff-util.h
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/ui-config.h)
|
${CMAKE_CURRENT_BINARY_DIR}/ui-config.h)
|
||||||
|
|
||||||
target_sources(
|
target_sources(
|
||||||
@ -289,7 +289,7 @@ target_sources(obs PRIVATE importers/importers.cpp importers/importers.hpp impor
|
|||||||
|
|
||||||
target_compile_features(obs PRIVATE cxx_std_17)
|
target_compile_features(obs PRIVATE cxx_std_17)
|
||||||
|
|
||||||
target_include_directories(obs PRIVATE ${CMAKE_SOURCE_DIR}/deps/json11 ${CMAKE_SOURCE_DIR}/deps/libff)
|
target_include_directories(obs PRIVATE ${CMAKE_SOURCE_DIR}/deps/json11)
|
||||||
|
|
||||||
target_link_libraries(obs PRIVATE CURL::libcurl FFmpeg::avcodec FFmpeg::avutil FFmpeg::avformat OBS::libobs
|
target_link_libraries(obs PRIVATE CURL::libcurl FFmpeg::avcodec FFmpeg::avutil FFmpeg::avformat OBS::libobs
|
||||||
OBS::frontend-api)
|
OBS::frontend-api)
|
||||||
@ -374,9 +374,6 @@ if(OS_WINDOWS)
|
|||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_link_options(obs PRIVATE "LINKER:/IGNORE:4098" "LINKER:/IGNORE:4099")
|
target_link_options(obs PRIVATE "LINKER:/IGNORE:4098" "LINKER:/IGNORE:4099")
|
||||||
target_link_libraries(obs PRIVATE OBS::w32-pthreads)
|
target_link_libraries(obs PRIVATE OBS::w32-pthreads)
|
||||||
|
|
||||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}../deps/libff/libff/ff-util.c PROPERTIES COMPILE_FLAGS
|
|
||||||
-Dinline=__inline)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||||
|
290
UI/ffmpeg-utils.cpp
Normal file
290
UI/ffmpeg-utils.cpp
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.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 "ffmpeg-utils.hpp"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
static void GetCodecsForId(const FFmpegFormat &format,
|
||||||
|
vector<FFmpegCodec> &codecs, enum AVCodecID id,
|
||||||
|
bool ignore_compaibility)
|
||||||
|
{
|
||||||
|
|
||||||
|
const AVCodec *codec = nullptr;
|
||||||
|
void *i = 0;
|
||||||
|
|
||||||
|
while ((codec = av_codec_iterate(&i)) != nullptr) {
|
||||||
|
if (codec->id != id)
|
||||||
|
continue;
|
||||||
|
// Not an encoding codec
|
||||||
|
if (!av_codec_is_encoder(codec))
|
||||||
|
continue;
|
||||||
|
// Skip if not supported and compatibility check not disabled
|
||||||
|
if (!ignore_compaibility &&
|
||||||
|
!av_codec_get_tag(format.codec_tags, codec->id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FFmpegCodec d{codec->name, codec->long_name, codec->id};
|
||||||
|
|
||||||
|
const AVCodec *base_codec = avcodec_find_encoder(codec->id);
|
||||||
|
if (strcmp(base_codec->name, codec->name) != 0) {
|
||||||
|
d.alias = true;
|
||||||
|
d.base_name = base_codec->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (codec->type) {
|
||||||
|
case AVMEDIA_TYPE_AUDIO:
|
||||||
|
d.type = FFmpegCodecType::AUDIO;
|
||||||
|
break;
|
||||||
|
case AVMEDIA_TYPE_VIDEO:
|
||||||
|
d.type = FFmpegCodecType::VIDEO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
d.type = FFmpegCodecType::UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
codecs.push_back(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<const AVCodecDescriptor *> GetCodecDescriptors()
|
||||||
|
{
|
||||||
|
std::vector<const AVCodecDescriptor *> codecs;
|
||||||
|
|
||||||
|
const AVCodecDescriptor *desc = nullptr;
|
||||||
|
while ((desc = avcodec_descriptor_next(desc)) != nullptr)
|
||||||
|
codecs.push_back(desc);
|
||||||
|
|
||||||
|
return codecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format,
|
||||||
|
bool ignore_compatibility)
|
||||||
|
{
|
||||||
|
vector<FFmpegCodec> codecs;
|
||||||
|
auto codecDescriptors = GetCodecDescriptors();
|
||||||
|
|
||||||
|
if (codecDescriptors.empty())
|
||||||
|
return codecs;
|
||||||
|
|
||||||
|
for (const AVCodecDescriptor *codec : codecDescriptors)
|
||||||
|
GetCodecsForId(format, codecs, codec->id, ignore_compatibility);
|
||||||
|
|
||||||
|
return codecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_output_device(const AVClass *avclass)
|
||||||
|
{
|
||||||
|
if (!avclass)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (avclass->category) {
|
||||||
|
case AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT:
|
||||||
|
case AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT:
|
||||||
|
case AV_CLASS_CATEGORY_DEVICE_OUTPUT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<FFmpegFormat> GetSupportedFormats()
|
||||||
|
{
|
||||||
|
vector<FFmpegFormat> formats;
|
||||||
|
const AVOutputFormat *output_format;
|
||||||
|
|
||||||
|
void *i = 0;
|
||||||
|
while ((output_format = av_muxer_iterate(&i)) != nullptr) {
|
||||||
|
if (is_output_device(output_format->priv_class))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
formats.push_back({
|
||||||
|
output_format->name,
|
||||||
|
output_format->long_name,
|
||||||
|
output_format->mime_type,
|
||||||
|
output_format->extensions,
|
||||||
|
output_format->audio_codec,
|
||||||
|
output_format->video_codec,
|
||||||
|
output_format->codec_tag,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return formats;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_encoder_name(const char *format_name,
|
||||||
|
enum AVCodecID codec_id)
|
||||||
|
{
|
||||||
|
const AVCodec *codec = avcodec_find_encoder(codec_id);
|
||||||
|
if (codec == nullptr && codec_id == AV_CODEC_ID_NONE)
|
||||||
|
return nullptr;
|
||||||
|
else if (codec == nullptr)
|
||||||
|
return format_name;
|
||||||
|
else
|
||||||
|
return codec->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *FFmpegFormat::GetDefaultName(FFmpegCodecType codec_type) const
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (codec_type) {
|
||||||
|
case FFmpegCodecType::AUDIO:
|
||||||
|
return get_encoder_name(name, audio_codec);
|
||||||
|
case FFmpegCodecType::VIDEO:
|
||||||
|
return get_encoder_name(name, video_codec);
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FFCodecAndFormatCompatible(const char *codec, const char *format)
|
||||||
|
{
|
||||||
|
if (!codec || !format)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100)
|
||||||
|
AVOutputFormat *output_format;
|
||||||
|
#else
|
||||||
|
const AVOutputFormat *output_format;
|
||||||
|
#endif
|
||||||
|
output_format = av_guess_format(format, NULL, NULL);
|
||||||
|
if (!output_format)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const AVCodecDescriptor *codec_desc =
|
||||||
|
avcodec_descriptor_get_by_name(codec);
|
||||||
|
if (!codec_desc)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 0, 100)
|
||||||
|
return avformat_query_codec(output_format, codec_desc->id,
|
||||||
|
FF_COMPLIANCE_EXPERIMENTAL) == 1;
|
||||||
|
#else
|
||||||
|
return avformat_query_codec(output_format, codec_desc->id,
|
||||||
|
FF_COMPLIANCE_NORMAL) == 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static const unordered_set<string> builtin_codecs = {
|
||||||
|
"h264", "hevc", "av1", "prores", "aac", "opus",
|
||||||
|
"alac", "flac", "pcm_s16le", "pcm_s24le", "pcm_f32le",
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsBuiltinCodec(const char *codec)
|
||||||
|
{
|
||||||
|
return builtin_codecs.count(codec) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const unordered_map<string, unordered_set<string>> codec_compat = {
|
||||||
|
// Technically our muxer supports HEVC and AV1 as well, but nothing else does
|
||||||
|
{"flv",
|
||||||
|
{
|
||||||
|
"h264",
|
||||||
|
"aac",
|
||||||
|
}},
|
||||||
|
{"mpegts",
|
||||||
|
{
|
||||||
|
"h264",
|
||||||
|
"hevc",
|
||||||
|
"aac",
|
||||||
|
"opus",
|
||||||
|
}},
|
||||||
|
{"hls",
|
||||||
|
// Also using MPEG-TS in our case, but no Opus support
|
||||||
|
{
|
||||||
|
"h264",
|
||||||
|
"hevc",
|
||||||
|
"aac",
|
||||||
|
}},
|
||||||
|
{"mp4",
|
||||||
|
{
|
||||||
|
"h264",
|
||||||
|
"hevc",
|
||||||
|
"av1",
|
||||||
|
"aac",
|
||||||
|
"opus",
|
||||||
|
"alac",
|
||||||
|
"flac",
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100)
|
||||||
|
// PCM in MP4 is only supported in FFmpeg > 6.0
|
||||||
|
"pcm_s16le",
|
||||||
|
"pcm_s24le",
|
||||||
|
"pcm_f32le",
|
||||||
|
#endif
|
||||||
|
}},
|
||||||
|
{"fragmented_mp4",
|
||||||
|
{
|
||||||
|
"h264",
|
||||||
|
"hevc",
|
||||||
|
"av1",
|
||||||
|
"aac",
|
||||||
|
"opus",
|
||||||
|
"alac",
|
||||||
|
"flac",
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100)
|
||||||
|
"pcm_s16le",
|
||||||
|
"pcm_s24le",
|
||||||
|
"pcm_f32le",
|
||||||
|
#endif
|
||||||
|
}},
|
||||||
|
{"mov",
|
||||||
|
{
|
||||||
|
"h264",
|
||||||
|
"hevc",
|
||||||
|
"prores",
|
||||||
|
"aac",
|
||||||
|
"alac",
|
||||||
|
"pcm_s16le",
|
||||||
|
"pcm_s24le",
|
||||||
|
"pcm_f32le",
|
||||||
|
}},
|
||||||
|
{"fragmented_mov",
|
||||||
|
{
|
||||||
|
"h264",
|
||||||
|
"hevc",
|
||||||
|
"prores",
|
||||||
|
"aac",
|
||||||
|
"alac",
|
||||||
|
"pcm_s16le",
|
||||||
|
"pcm_s24le",
|
||||||
|
"pcm_f32le",
|
||||||
|
}},
|
||||||
|
// MKV supports everything
|
||||||
|
{"mkv", {}},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ContainerSupportsCodec(const string &container, const string &codec)
|
||||||
|
{
|
||||||
|
auto iter = codec_compat.find(container);
|
||||||
|
if (iter == codec_compat.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto codecs = iter->second;
|
||||||
|
// Assume everything is supported
|
||||||
|
if (codecs.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return codecs.count(codec) > 0;
|
||||||
|
}
|
84
UI/ffmpeg-utils.hpp
Normal file
84
UI/ffmpeg-utils.hpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.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/>.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qmetatype.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavdevice/avdevice.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FFmpegCodecType { AUDIO, VIDEO, UNKNOWN };
|
||||||
|
|
||||||
|
struct FFmpegFormat {
|
||||||
|
const char *name;
|
||||||
|
const char *long_name;
|
||||||
|
const char *mime_type;
|
||||||
|
const char *extensions;
|
||||||
|
AVCodecID audio_codec;
|
||||||
|
AVCodecID video_codec;
|
||||||
|
const AVCodecTag *const *codec_tags;
|
||||||
|
|
||||||
|
FFmpegFormat() = default;
|
||||||
|
|
||||||
|
const char *GetDefaultName(FFmpegCodecType codec_type) const;
|
||||||
|
|
||||||
|
bool HasAudio() const { return audio_codec != AV_CODEC_ID_NONE; }
|
||||||
|
bool HasVideo() const { return video_codec != AV_CODEC_ID_NONE; }
|
||||||
|
|
||||||
|
bool operator==(const FFmpegFormat &format) const
|
||||||
|
{
|
||||||
|
if (strcmp(name, format.name) != 0)
|
||||||
|
return false;
|
||||||
|
return strcmp(mime_type, format.mime_type) != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(FFmpegFormat)
|
||||||
|
|
||||||
|
struct FFmpegCodec {
|
||||||
|
const char *name;
|
||||||
|
const char *long_name;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
bool alias;
|
||||||
|
const char *base_name;
|
||||||
|
|
||||||
|
FFmpegCodecType type;
|
||||||
|
|
||||||
|
FFmpegCodec() = default;
|
||||||
|
|
||||||
|
bool operator==(const FFmpegCodec &codec) const
|
||||||
|
{
|
||||||
|
if (id != codec.id)
|
||||||
|
return false;
|
||||||
|
return strcmp(name, codec.name) != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(FFmpegCodec)
|
||||||
|
|
||||||
|
std::vector<FFmpegFormat> GetSupportedFormats();
|
||||||
|
std::vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format,
|
||||||
|
bool ignore_compatibility);
|
||||||
|
|
||||||
|
bool FFCodecAndFormatCompatible(const char *codec, const char *format);
|
||||||
|
bool IsBuiltinCodec(const char *codec);
|
||||||
|
bool ContainerSupportsCodec(const std::string &container,
|
||||||
|
const std::string &codec);
|
@ -7585,17 +7585,22 @@ void OBSBasic::AutoRemux(QString input, bool no_show)
|
|||||||
config, isSimpleMode ? "SimpleOutput" : "AdvOut", "RecFormat2");
|
config, isSimpleMode ? "SimpleOutput" : "AdvOut", "RecFormat2");
|
||||||
|
|
||||||
bool audio_is_pcm = strncmp(aCodecName, "pcm", 3) == 0;
|
bool audio_is_pcm = strncmp(aCodecName, "pcm", 3) == 0;
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 5, 100)
|
||||||
/* FFmpeg <= 6.0 cannot remux AV1+PCM into any supported format. */
|
/* FFmpeg <= 6.0 cannot remux AV1+PCM into any supported format. */
|
||||||
if (audio_is_pcm && !ff_supports_pcm_in_mp4() &&
|
if (audio_is_pcm && strcmp(vCodecName, "av1") == 0)
|
||||||
strcmp(vCodecName, "av1") == 0)
|
|
||||||
return;
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Retain original container for fMP4/fMOV */
|
/* Retain original container for fMP4/fMOV */
|
||||||
if (strncmp(format, "fragmented", 10) == 0) {
|
if (strncmp(format, "fragmented", 10) == 0) {
|
||||||
output += "remuxed." + suffix;
|
output += "remuxed." + suffix;
|
||||||
} else if (strcmp(vCodecName, "prores") == 0 ||
|
} else if (strcmp(vCodecName, "prores") == 0) {
|
||||||
(audio_is_pcm && !ff_supports_pcm_in_mp4())) {
|
|
||||||
output += "mov";
|
output += "mov";
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 5, 100)
|
||||||
|
} else if (audio_is_pcm) {
|
||||||
|
output += "mov";
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
output += "mp4";
|
output += "mp4";
|
||||||
}
|
}
|
||||||
|
@ -83,51 +83,6 @@ protected:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used for QVariant in codec comboboxes
|
|
||||||
namespace {
|
|
||||||
static bool StringEquals(QString left, QString right)
|
|
||||||
{
|
|
||||||
return left == right;
|
|
||||||
}
|
|
||||||
struct FormatDesc {
|
|
||||||
const char *name = nullptr;
|
|
||||||
const char *mimeType = nullptr;
|
|
||||||
const ff_format_desc *desc = nullptr;
|
|
||||||
|
|
||||||
inline FormatDesc() = default;
|
|
||||||
inline FormatDesc(const char *name, const char *mimeType,
|
|
||||||
const ff_format_desc *desc = nullptr)
|
|
||||||
: name(name),
|
|
||||||
mimeType(mimeType),
|
|
||||||
desc(desc)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const FormatDesc &f) const
|
|
||||||
{
|
|
||||||
if (!StringEquals(name, f.name))
|
|
||||||
return false;
|
|
||||||
return StringEquals(mimeType, f.mimeType);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct CodecDesc {
|
|
||||||
const char *name = nullptr;
|
|
||||||
int id = 0;
|
|
||||||
|
|
||||||
inline CodecDesc() = default;
|
|
||||||
inline CodecDesc(const char *name, int id) : name(name), id(id) {}
|
|
||||||
|
|
||||||
bool operator==(const CodecDesc &codecDesc) const
|
|
||||||
{
|
|
||||||
if (id != codecDesc.id)
|
|
||||||
return false;
|
|
||||||
return StringEquals(name, codecDesc.name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Q_DECLARE_METATYPE(FormatDesc)
|
|
||||||
Q_DECLARE_METATYPE(CodecDesc)
|
|
||||||
|
|
||||||
static inline bool ResTooHigh(uint32_t cx, uint32_t cy)
|
static inline bool ResTooHigh(uint32_t cx, uint32_t cy)
|
||||||
{
|
{
|
||||||
return cx > 16384 || cy > 16384;
|
return cx > 16384 || cy > 16384;
|
||||||
@ -231,36 +186,35 @@ static inline QString GetComboData(QComboBox *combo)
|
|||||||
|
|
||||||
static int FindEncoder(QComboBox *combo, const char *name, int id)
|
static int FindEncoder(QComboBox *combo, const char *name, int id)
|
||||||
{
|
{
|
||||||
CodecDesc codecDesc(name, id);
|
FFmpegCodec codec{name, nullptr, id};
|
||||||
|
|
||||||
for (int i = 0; i < combo->count(); i++) {
|
for (int i = 0; i < combo->count(); i++) {
|
||||||
QVariant v = combo->itemData(i);
|
QVariant v = combo->itemData(i);
|
||||||
if (!v.isNull()) {
|
if (!v.isNull()) {
|
||||||
if (codecDesc == v.value<CodecDesc>()) {
|
if (codec == v.value<FFmpegCodec>()) {
|
||||||
return i;
|
return i;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CodecDesc GetDefaultCodecDesc(const ff_format_desc *formatDesc,
|
static FFmpegCodec GetDefaultCodec(const FFmpegFormat &format,
|
||||||
ff_codec_type codecType)
|
FFmpegCodecType codecType)
|
||||||
{
|
{
|
||||||
int id = 0;
|
int id = 0;
|
||||||
switch (codecType) {
|
switch (codecType) {
|
||||||
case FF_CODEC_AUDIO:
|
case AUDIO:
|
||||||
id = ff_format_desc_audio(formatDesc);
|
id = format.audio_codec;
|
||||||
break;
|
break;
|
||||||
case FF_CODEC_VIDEO:
|
case VIDEO:
|
||||||
id = ff_format_desc_video(formatDesc);
|
id = format.video_codec;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return CodecDesc();
|
return FFmpegCodec();
|
||||||
}
|
}
|
||||||
|
|
||||||
return CodecDesc(ff_format_desc_get_default_name(formatDesc, codecType),
|
return FFmpegCodec{format.GetDefaultName(codecType), nullptr, id};
|
||||||
id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define INVALID_BITRATE 10000
|
#define INVALID_BITRATE 10000
|
||||||
@ -754,9 +708,6 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
|
|||||||
//Apply button disabled until change.
|
//Apply button disabled until change.
|
||||||
EnableApplyButton(false);
|
EnableApplyButton(false);
|
||||||
|
|
||||||
// Initialize libff library
|
|
||||||
ff_init();
|
|
||||||
|
|
||||||
installEventFilter(new SettingsEventFilter());
|
installEventFilter(new SettingsEventFilter());
|
||||||
|
|
||||||
LoadColorRanges();
|
LoadColorRanges();
|
||||||
@ -1140,25 +1091,21 @@ void OBSBasicSettings::LoadFormats()
|
|||||||
#define FORMAT_STR(str) QTStr("Basic.Settings.Output.Format." str)
|
#define FORMAT_STR(str) QTStr("Basic.Settings.Output.Format." str)
|
||||||
ui->advOutFFFormat->blockSignals(true);
|
ui->advOutFFFormat->blockSignals(true);
|
||||||
|
|
||||||
formats.reset(ff_format_supported());
|
formats = GetSupportedFormats();
|
||||||
const ff_format_desc *format = formats.get();
|
|
||||||
|
for (auto &format : formats) {
|
||||||
|
bool audio = format.HasAudio();
|
||||||
|
bool video = format.HasVideo();
|
||||||
|
|
||||||
while (format != nullptr) {
|
|
||||||
bool audio = ff_format_desc_has_audio(format);
|
|
||||||
bool video = ff_format_desc_has_video(format);
|
|
||||||
FormatDesc formatDesc(ff_format_desc_name(format),
|
|
||||||
ff_format_desc_mime_type(format), format);
|
|
||||||
if (audio || video) {
|
if (audio || video) {
|
||||||
QString itemText(ff_format_desc_name(format));
|
QString itemText(format.name);
|
||||||
if (audio ^ video)
|
if (audio ^ video)
|
||||||
itemText += QString(" (%1)").arg(
|
itemText += QString(" (%1)").arg(
|
||||||
audio ? AUDIO_STR : VIDEO_STR);
|
audio ? AUDIO_STR : VIDEO_STR);
|
||||||
|
|
||||||
ui->advOutFFFormat->addItem(
|
ui->advOutFFFormat->addItem(
|
||||||
itemText, QVariant::fromValue(formatDesc));
|
itemText, QVariant::fromValue(format));
|
||||||
}
|
}
|
||||||
|
|
||||||
format = ff_format_desc_next(format);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->advOutFFFormat->model()->sort(0);
|
ui->advOutFFFormat->model()->sort(0);
|
||||||
@ -1187,26 +1134,22 @@ void OBSBasicSettings::LoadFormats()
|
|||||||
#undef FORMAT_STR
|
#undef FORMAT_STR
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AddCodec(QComboBox *combo, const ff_codec_desc *codec_desc)
|
static void AddCodec(QComboBox *combo, const FFmpegCodec &codec)
|
||||||
{
|
{
|
||||||
QString itemText(ff_codec_desc_name(codec_desc));
|
QString itemText(codec.name);
|
||||||
if (ff_codec_desc_is_alias(codec_desc))
|
if (codec.alias)
|
||||||
itemText += QString(" (%1)").arg(
|
itemText += QString(" (%1)").arg(codec.base_name);
|
||||||
ff_codec_desc_base_name(codec_desc));
|
|
||||||
|
|
||||||
CodecDesc cd(ff_codec_desc_name(codec_desc),
|
combo->addItem(itemText, QVariant::fromValue(codec));
|
||||||
ff_codec_desc_id(codec_desc));
|
|
||||||
|
|
||||||
combo->addItem(itemText, QVariant::fromValue(cd));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define AV_ENCODER_DEFAULT_STR \
|
#define AV_ENCODER_DEFAULT_STR \
|
||||||
QTStr("Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault")
|
QTStr("Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault")
|
||||||
|
|
||||||
static void AddDefaultCodec(QComboBox *combo, const ff_format_desc *formatDesc,
|
static void AddDefaultCodec(QComboBox *combo, const FFmpegFormat &format,
|
||||||
ff_codec_type codecType)
|
FFmpegCodecType codecType)
|
||||||
{
|
{
|
||||||
CodecDesc cd = GetDefaultCodecDesc(formatDesc, codecType);
|
FFmpegCodec cd = GetDefaultCodec(format, codecType);
|
||||||
|
|
||||||
int existingIdx = FindEncoder(combo, cd.name, cd.id);
|
int existingIdx = FindEncoder(combo, cd.name, cd.id);
|
||||||
if (existingIdx >= 0)
|
if (existingIdx >= 0)
|
||||||
@ -1219,48 +1162,41 @@ static void AddDefaultCodec(QComboBox *combo, const ff_format_desc *formatDesc,
|
|||||||
#define AV_ENCODER_DISABLE_STR \
|
#define AV_ENCODER_DISABLE_STR \
|
||||||
QTStr("Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable")
|
QTStr("Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable")
|
||||||
|
|
||||||
void OBSBasicSettings::ReloadCodecs(const ff_format_desc *formatDesc)
|
void OBSBasicSettings::ReloadCodecs(const FFmpegFormat &format)
|
||||||
{
|
{
|
||||||
ui->advOutFFAEncoder->blockSignals(true);
|
ui->advOutFFAEncoder->blockSignals(true);
|
||||||
ui->advOutFFVEncoder->blockSignals(true);
|
ui->advOutFFVEncoder->blockSignals(true);
|
||||||
ui->advOutFFAEncoder->clear();
|
ui->advOutFFAEncoder->clear();
|
||||||
ui->advOutFFVEncoder->clear();
|
ui->advOutFFVEncoder->clear();
|
||||||
|
|
||||||
if (formatDesc == nullptr)
|
bool ignore_compatibility = ui->advOutFFIgnoreCompat->isChecked();
|
||||||
return;
|
vector<FFmpegCodec> supportedCodecs =
|
||||||
|
GetFormatCodecs(format, ignore_compatibility);
|
||||||
|
|
||||||
bool ignore_compatability = ui->advOutFFIgnoreCompat->isChecked();
|
for (auto &codec : supportedCodecs) {
|
||||||
OBSFFCodecDesc codecDescs(
|
switch (codec.type) {
|
||||||
ff_codec_supported(formatDesc, ignore_compatability));
|
case AUDIO:
|
||||||
|
|
||||||
const ff_codec_desc *codec = codecDescs.get();
|
|
||||||
|
|
||||||
while (codec != nullptr) {
|
|
||||||
switch (ff_codec_desc_type(codec)) {
|
|
||||||
case FF_CODEC_AUDIO:
|
|
||||||
AddCodec(ui->advOutFFAEncoder, codec);
|
AddCodec(ui->advOutFFAEncoder, codec);
|
||||||
break;
|
break;
|
||||||
case FF_CODEC_VIDEO:
|
case VIDEO:
|
||||||
AddCodec(ui->advOutFFVEncoder, codec);
|
AddCodec(ui->advOutFFVEncoder, codec);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
codec = ff_codec_desc_next(codec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ff_format_desc_has_audio(formatDesc))
|
if (format.HasAudio())
|
||||||
AddDefaultCodec(ui->advOutFFAEncoder, formatDesc,
|
AddDefaultCodec(ui->advOutFFAEncoder, format,
|
||||||
FF_CODEC_AUDIO);
|
FFmpegCodecType::AUDIO);
|
||||||
if (ff_format_desc_has_video(formatDesc))
|
if (format.HasVideo())
|
||||||
AddDefaultCodec(ui->advOutFFVEncoder, formatDesc,
|
AddDefaultCodec(ui->advOutFFVEncoder, format,
|
||||||
FF_CODEC_VIDEO);
|
FFmpegCodecType::VIDEO);
|
||||||
|
|
||||||
ui->advOutFFAEncoder->model()->sort(0);
|
ui->advOutFFAEncoder->model()->sort(0);
|
||||||
ui->advOutFFVEncoder->model()->sort(0);
|
ui->advOutFFVEncoder->model()->sort(0);
|
||||||
|
|
||||||
QVariant disable = QVariant::fromValue(CodecDesc());
|
QVariant disable = QVariant::fromValue(FFmpegCodec());
|
||||||
|
|
||||||
ui->advOutFFAEncoder->insertItem(0, AV_ENCODER_DISABLE_STR, disable);
|
ui->advOutFFAEncoder->insertItem(0, AV_ENCODER_DISABLE_STR, disable);
|
||||||
ui->advOutFFVEncoder->insertItem(0, AV_ENCODER_DISABLE_STR, disable);
|
ui->advOutFFVEncoder->insertItem(0, AV_ENCODER_DISABLE_STR, disable);
|
||||||
@ -2302,12 +2238,12 @@ void OBSBasicSettings::LoadAdvOutputRecordingEncoderProperties()
|
|||||||
static void SelectFormat(QComboBox *combo, const char *name,
|
static void SelectFormat(QComboBox *combo, const char *name,
|
||||||
const char *mimeType)
|
const char *mimeType)
|
||||||
{
|
{
|
||||||
FormatDesc formatDesc(name, mimeType);
|
FFmpegFormat format{name, mimeType};
|
||||||
|
|
||||||
for (int i = 0; i < combo->count(); i++) {
|
for (int i = 0; i < combo->count(); i++) {
|
||||||
QVariant v = combo->itemData(i);
|
QVariant v = combo->itemData(i);
|
||||||
if (!v.isNull()) {
|
if (!v.isNull()) {
|
||||||
if (formatDesc == v.value<FormatDesc>()) {
|
if (format == v.value<FFmpegFormat>()) {
|
||||||
combo->setCurrentIndex(i);
|
combo->setCurrentIndex(i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2523,14 +2459,14 @@ void OBSBasicSettings::LoadOutputSettings()
|
|||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBSBasicSettings::SetAdvOutputFFmpegEnablement(ff_codec_type encoderType,
|
void OBSBasicSettings::SetAdvOutputFFmpegEnablement(FFmpegCodecType encoderType,
|
||||||
bool enabled,
|
bool enabled,
|
||||||
bool enableEncoder)
|
bool enableEncoder)
|
||||||
{
|
{
|
||||||
bool rescale = config_get_bool(main->Config(), "AdvOut", "FFRescale");
|
bool rescale = config_get_bool(main->Config(), "AdvOut", "FFRescale");
|
||||||
|
|
||||||
switch (encoderType) {
|
switch (encoderType) {
|
||||||
case FF_CODEC_VIDEO:
|
case FFmpegCodecType::VIDEO:
|
||||||
ui->advOutFFVBitrate->setEnabled(enabled);
|
ui->advOutFFVBitrate->setEnabled(enabled);
|
||||||
ui->advOutFFVGOPSize->setEnabled(enabled);
|
ui->advOutFFVGOPSize->setEnabled(enabled);
|
||||||
ui->advOutFFUseRescale->setEnabled(enabled);
|
ui->advOutFFUseRescale->setEnabled(enabled);
|
||||||
@ -2538,7 +2474,7 @@ void OBSBasicSettings::SetAdvOutputFFmpegEnablement(ff_codec_type encoderType,
|
|||||||
ui->advOutFFVEncoder->setEnabled(enabled || enableEncoder);
|
ui->advOutFFVEncoder->setEnabled(enabled || enableEncoder);
|
||||||
ui->advOutFFVCfg->setEnabled(enabled);
|
ui->advOutFFVCfg->setEnabled(enabled);
|
||||||
break;
|
break;
|
||||||
case FF_CODEC_AUDIO:
|
case FFmpegCodecType::AUDIO:
|
||||||
ui->advOutFFABitrate->setEnabled(enabled);
|
ui->advOutFFABitrate->setEnabled(enabled);
|
||||||
ui->advOutFFAEncoder->setEnabled(enabled || enableEncoder);
|
ui->advOutFFAEncoder->setEnabled(enabled || enableEncoder);
|
||||||
ui->advOutFFACfg->setEnabled(enabled);
|
ui->advOutFFACfg->setEnabled(enabled);
|
||||||
@ -3766,13 +3702,13 @@ void OBSBasicSettings::SaveFormat(QComboBox *combo)
|
|||||||
{
|
{
|
||||||
QVariant v = combo->currentData();
|
QVariant v = combo->currentData();
|
||||||
if (!v.isNull()) {
|
if (!v.isNull()) {
|
||||||
FormatDesc desc = v.value<FormatDesc>();
|
auto format = v.value<FFmpegFormat>();
|
||||||
config_set_string(main->Config(), "AdvOut", "FFFormat",
|
config_set_string(main->Config(), "AdvOut", "FFFormat",
|
||||||
desc.name);
|
format.name);
|
||||||
config_set_string(main->Config(), "AdvOut", "FFFormatMimeType",
|
config_set_string(main->Config(), "AdvOut", "FFFormatMimeType",
|
||||||
desc.mimeType);
|
format.mime_type);
|
||||||
|
|
||||||
const char *ext = ff_format_desc_extensions(desc.desc);
|
const char *ext = format.extensions;
|
||||||
string extStr = ext ? ext : "";
|
string extStr = ext ? ext : "";
|
||||||
|
|
||||||
char *comma = strchr(&extStr[0], ',');
|
char *comma = strchr(&extStr[0], ',');
|
||||||
@ -3795,9 +3731,10 @@ void OBSBasicSettings::SaveEncoder(QComboBox *combo, const char *section,
|
|||||||
const char *value)
|
const char *value)
|
||||||
{
|
{
|
||||||
QVariant v = combo->currentData();
|
QVariant v = combo->currentData();
|
||||||
CodecDesc cd;
|
FFmpegCodec cd{};
|
||||||
if (!v.isNull())
|
if (!v.isNull())
|
||||||
cd = v.value<CodecDesc>();
|
cd = v.value<FFmpegCodec>();
|
||||||
|
|
||||||
config_set_int(main->Config(), section,
|
config_set_int(main->Config(), section,
|
||||||
QT_TO_UTF8(QString("%1Id").arg(value)), cd.id);
|
QT_TO_UTF8(QString("%1Id").arg(value)), cd.id);
|
||||||
if (cd.id != 0)
|
if (cd.id != 0)
|
||||||
@ -4437,27 +4374,29 @@ void OBSBasicSettings::on_advOutFFFormat_currentIndexChanged(int idx)
|
|||||||
const QVariant itemDataVariant = ui->advOutFFFormat->itemData(idx);
|
const QVariant itemDataVariant = ui->advOutFFFormat->itemData(idx);
|
||||||
|
|
||||||
if (!itemDataVariant.isNull()) {
|
if (!itemDataVariant.isNull()) {
|
||||||
FormatDesc desc = itemDataVariant.value<FormatDesc>();
|
auto format = itemDataVariant.value<FFmpegFormat>();
|
||||||
SetAdvOutputFFmpegEnablement(
|
SetAdvOutputFFmpegEnablement(FFmpegCodecType::AUDIO,
|
||||||
FF_CODEC_AUDIO, ff_format_desc_has_audio(desc.desc),
|
format.HasAudio(), false);
|
||||||
false);
|
SetAdvOutputFFmpegEnablement(FFmpegCodecType::VIDEO,
|
||||||
SetAdvOutputFFmpegEnablement(
|
format.HasVideo(), false);
|
||||||
FF_CODEC_VIDEO, ff_format_desc_has_video(desc.desc),
|
ReloadCodecs(format);
|
||||||
false);
|
|
||||||
ReloadCodecs(desc.desc);
|
|
||||||
ui->advOutFFFormatDesc->setText(
|
|
||||||
ff_format_desc_long_name(desc.desc));
|
|
||||||
|
|
||||||
CodecDesc defaultAudioCodecDesc =
|
ui->advOutFFFormatDesc->setText(format.long_name);
|
||||||
GetDefaultCodecDesc(desc.desc, FF_CODEC_AUDIO);
|
|
||||||
CodecDesc defaultVideoCodecDesc =
|
FFmpegCodec defaultAudioCodecDesc =
|
||||||
GetDefaultCodecDesc(desc.desc, FF_CODEC_VIDEO);
|
GetDefaultCodec(format, AUDIO);
|
||||||
|
FFmpegCodec defaultVideoCodecDesc =
|
||||||
|
GetDefaultCodec(format, VIDEO);
|
||||||
SelectEncoder(ui->advOutFFAEncoder, defaultAudioCodecDesc.name,
|
SelectEncoder(ui->advOutFFAEncoder, defaultAudioCodecDesc.name,
|
||||||
defaultAudioCodecDesc.id);
|
defaultAudioCodecDesc.id);
|
||||||
SelectEncoder(ui->advOutFFVEncoder, defaultVideoCodecDesc.name,
|
SelectEncoder(ui->advOutFFVEncoder, defaultVideoCodecDesc.name,
|
||||||
defaultVideoCodecDesc.id);
|
defaultVideoCodecDesc.id);
|
||||||
} else {
|
} else {
|
||||||
ReloadCodecs(nullptr);
|
ui->advOutFFAEncoder->blockSignals(true);
|
||||||
|
ui->advOutFFVEncoder->blockSignals(true);
|
||||||
|
ui->advOutFFAEncoder->clear();
|
||||||
|
ui->advOutFFVEncoder->clear();
|
||||||
|
|
||||||
ui->advOutFFFormatDesc->setText(DEFAULT_CONTAINER_STR);
|
ui->advOutFFFormatDesc->setText(DEFAULT_CONTAINER_STR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4466,10 +4405,9 @@ void OBSBasicSettings::on_advOutFFAEncoder_currentIndexChanged(int idx)
|
|||||||
{
|
{
|
||||||
const QVariant itemDataVariant = ui->advOutFFAEncoder->itemData(idx);
|
const QVariant itemDataVariant = ui->advOutFFAEncoder->itemData(idx);
|
||||||
if (!itemDataVariant.isNull()) {
|
if (!itemDataVariant.isNull()) {
|
||||||
CodecDesc desc = itemDataVariant.value<CodecDesc>();
|
auto desc = itemDataVariant.value<FFmpegCodec>();
|
||||||
SetAdvOutputFFmpegEnablement(
|
SetAdvOutputFFmpegEnablement(
|
||||||
FF_CODEC_AUDIO, desc.id != 0 || desc.name != nullptr,
|
AUDIO, desc.id != 0 || desc.name != nullptr, true);
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4477,10 +4415,9 @@ void OBSBasicSettings::on_advOutFFVEncoder_currentIndexChanged(int idx)
|
|||||||
{
|
{
|
||||||
const QVariant itemDataVariant = ui->advOutFFVEncoder->itemData(idx);
|
const QVariant itemDataVariant = ui->advOutFFVEncoder->itemData(idx);
|
||||||
if (!itemDataVariant.isNull()) {
|
if (!itemDataVariant.isNull()) {
|
||||||
CodecDesc desc = itemDataVariant.value<CodecDesc>();
|
auto desc = itemDataVariant.value<FFmpegCodec>();
|
||||||
SetAdvOutputFFmpegEnablement(
|
SetAdvOutputFFmpegEnablement(
|
||||||
FF_CODEC_VIDEO, desc.id != 0 || desc.name != nullptr,
|
VIDEO, desc.id != 0 || desc.name != nullptr, true);
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4994,51 +4931,6 @@ void OBSBasicSettings::AdvOutSplitFileChanged()
|
|||||||
ui->advOutSplitFileSize->setVisible(splitFileType == 1);
|
ui->advOutSplitFileSize->setVisible(splitFileType == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const unordered_set<string> builtin_codecs = {
|
|
||||||
"h264", "hevc", "av1", "prores", "aac", "opus",
|
|
||||||
"alac", "flac", "pcm_s16le", "pcm_s24le", "pcm_f32le"};
|
|
||||||
|
|
||||||
static const unordered_map<string, unordered_set<string>> codec_compat = {
|
|
||||||
// Technically our muxer supports HEVC and AV1 as well, but nothing else does
|
|
||||||
{"flv", {"h264", "aac"}},
|
|
||||||
{"mpegts", {"h264", "hevc", "aac", "opus"}},
|
|
||||||
{"hls",
|
|
||||||
{"h264", "hevc", "aac"}}, // Also using MPEG-TS, but no Opus support
|
|
||||||
{"mp4",
|
|
||||||
{"h264", "hevc", "av1", "aac", "opus", "alac", "flac", "pcm_s16le",
|
|
||||||
"pcm_s24le", "pcm_f32le"}},
|
|
||||||
{"fragmented_mp4",
|
|
||||||
{"h264", "hevc", "av1", "aac", "opus", "alac", "flac", "pcm_s16le",
|
|
||||||
"pcm_s24le", "pcm_f32le"}},
|
|
||||||
{"mov",
|
|
||||||
{"h264", "hevc", "prores", "aac", "alac", "pcm_s16le", "pcm_s24le",
|
|
||||||
"pcm_f32le"}},
|
|
||||||
{"fragmented_mov",
|
|
||||||
{"h264", "hevc", "prores", "aac", "alac", "pcm_s16le", "pcm_s24le",
|
|
||||||
"pcm_f32le"}},
|
|
||||||
// MKV supports everything
|
|
||||||
{"mkv", {}},
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool ContainerSupportsCodec(const string &container, const string &codec)
|
|
||||||
{
|
|
||||||
auto iter = codec_compat.find(container);
|
|
||||||
if (iter == codec_compat.end())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto codecs = iter->second;
|
|
||||||
// Assume everything is supported
|
|
||||||
if (codecs.empty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// PCM in MP4 is only supported in FFmpeg > 6.0
|
|
||||||
if ((container == "mp4" || container == "fragmented_mp4") &&
|
|
||||||
!ff_supports_pcm_in_mp4() && codec.find("pcm_") != string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return codecs.count(codec) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DisableIncompatibleCodecs(QComboBox *cbox, const QString &format,
|
static void DisableIncompatibleCodecs(QComboBox *cbox, const QString &format,
|
||||||
const QString &formatName,
|
const QString &formatName,
|
||||||
const QString &streamEncoder)
|
const QString &streamEncoder)
|
||||||
@ -5063,10 +4955,10 @@ static void DisableIncompatibleCodecs(QComboBox *cbox, const QString &format,
|
|||||||
bool is_compatible =
|
bool is_compatible =
|
||||||
ContainerSupportsCodec(format.toStdString(), codec);
|
ContainerSupportsCodec(format.toStdString(), codec);
|
||||||
/* Fall back to FFmpeg check if codec not one of the built-in ones. */
|
/* Fall back to FFmpeg check if codec not one of the built-in ones. */
|
||||||
if (!is_compatible && !builtin_codecs.count(codec)) {
|
if (!is_compatible && !IsBuiltinCodec(codec)) {
|
||||||
string ext = GetFormatExt(QT_TO_UTF8(format));
|
string ext = GetFormatExt(QT_TO_UTF8(format));
|
||||||
is_compatible =
|
is_compatible =
|
||||||
ff_format_codec_compatible(codec, ext.c_str());
|
FFCodecAndFormatCompatible(codec, ext.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
QStandardItemModel *model =
|
QStandardItemModel *model =
|
||||||
|
@ -24,11 +24,10 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <libff/ff-util.h>
|
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
|
|
||||||
#include "auth-base.hpp"
|
#include "auth-base.hpp"
|
||||||
|
#include "ffmpeg-utils.hpp"
|
||||||
|
|
||||||
class OBSBasic;
|
class OBSBasic;
|
||||||
class QAbstractButton;
|
class QAbstractButton;
|
||||||
@ -69,20 +68,6 @@ public slots:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class OBSFFDeleter {
|
|
||||||
public:
|
|
||||||
void operator()(const ff_format_desc *format)
|
|
||||||
{
|
|
||||||
ff_format_desc_free(format);
|
|
||||||
}
|
|
||||||
void operator()(const ff_codec_desc *codec)
|
|
||||||
{
|
|
||||||
ff_codec_desc_free(codec);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
using OBSFFCodecDesc = std::unique_ptr<const ff_codec_desc, OBSFFDeleter>;
|
|
||||||
using OBSFFFormatDesc = std::unique_ptr<const ff_format_desc, OBSFFDeleter>;
|
|
||||||
|
|
||||||
class OBSBasicSettings : public QDialog {
|
class OBSBasicSettings : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QIcon generalIcon READ GetGeneralIcon WRITE SetGeneralIcon
|
Q_PROPERTY(QIcon generalIcon READ GetGeneralIcon WRITE SetGeneralIcon
|
||||||
@ -135,7 +120,7 @@ private:
|
|||||||
static constexpr uint32_t ENCODER_HIDE_FLAGS =
|
static constexpr uint32_t ENCODER_HIDE_FLAGS =
|
||||||
(OBS_ENCODER_CAP_DEPRECATED | OBS_ENCODER_CAP_INTERNAL);
|
(OBS_ENCODER_CAP_DEPRECATED | OBS_ENCODER_CAP_INTERNAL);
|
||||||
|
|
||||||
OBSFFFormatDesc formats;
|
std::vector<FFmpegFormat> formats;
|
||||||
|
|
||||||
OBSPropertiesView *streamProperties = nullptr;
|
OBSPropertiesView *streamProperties = nullptr;
|
||||||
OBSPropertiesView *streamEncoderProps = nullptr;
|
OBSPropertiesView *streamEncoderProps = nullptr;
|
||||||
@ -239,7 +224,7 @@ private:
|
|||||||
void LoadColorSpaces();
|
void LoadColorSpaces();
|
||||||
void LoadColorFormats();
|
void LoadColorFormats();
|
||||||
void LoadFormats();
|
void LoadFormats();
|
||||||
void ReloadCodecs(const ff_format_desc *formatDesc);
|
void ReloadCodecs(const FFmpegFormat &format);
|
||||||
|
|
||||||
void UpdateColorFormatSpaceWarning();
|
void UpdateColorFormatSpaceWarning();
|
||||||
|
|
||||||
@ -309,7 +294,7 @@ private:
|
|||||||
void LoadAdvOutputRecordingEncoderProperties();
|
void LoadAdvOutputRecordingEncoderProperties();
|
||||||
void LoadAdvOutputFFmpegSettings();
|
void LoadAdvOutputFFmpegSettings();
|
||||||
void LoadAdvOutputAudioSettings();
|
void LoadAdvOutputAudioSettings();
|
||||||
void SetAdvOutputFFmpegEnablement(ff_codec_type encoderType,
|
void SetAdvOutputFFmpegEnablement(FFmpegCodecType encoderType,
|
||||||
bool enabled,
|
bool enabled,
|
||||||
bool enableEncode = false);
|
bool enableEncode = false);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user