226 lines
7.1 KiB
Diff
226 lines
7.1 KiB
Diff
From 50270b270250a2a95a3d5e2799e40331fb4f83e9 Mon Sep 17 00:00:00 2001
|
|
From: Bleuzen <12885163+Bleuzen@users.noreply.github.com>
|
|
Date: Thu, 27 Apr 2023 22:42:49 +0200
|
|
Subject: [PATCH] obs-ffmpeg: Add NVENC AV1 FFmpeg encoder, part 1
|
|
|
|
---
|
|
plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c | 100 ++++++++++++++++++++++++--
|
|
plugins/obs-ffmpeg/obs-ffmpeg.c | 8 ++-
|
|
2 files changed, 100 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c
|
|
index 8cd813837c1e8..35c3822453741 100644
|
|
--- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c
|
|
+++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c
|
|
@@ -35,6 +35,7 @@ struct nvenc_encoder {
|
|
#ifdef ENABLE_HEVC
|
|
bool hevc;
|
|
#endif
|
|
+ bool av1;
|
|
int gpu;
|
|
DARRAY(uint8_t) header;
|
|
DARRAY(uint8_t) sei;
|
|
@@ -61,6 +62,13 @@ static const char *hevc_nvenc_getname(void *unused)
|
|
}
|
|
#endif
|
|
|
|
+#define ENCODER_NAME_AV1 "GPU: NVIDIA NVENC AV1 (FFmpeg)"
|
|
+static const char *av1_nvenc_getname(void *unused)
|
|
+{
|
|
+ UNUSED_PARAMETER(unused);
|
|
+ return ENCODER_NAME_AV1;
|
|
+}
|
|
+
|
|
static inline bool valid_format(enum video_format format)
|
|
{
|
|
switch (format) {
|
|
@@ -286,7 +294,9 @@ static void on_first_packet(void *data, AVPacket *pkt, struct darray *da)
|
|
&enc->sei.array, &enc->sei.num);
|
|
} else
|
|
#endif
|
|
- {
|
|
+ if (enc->av1) {
|
|
+ // TODO: add obs_extract_av1_headers
|
|
+ } else {
|
|
obs_extract_avc_headers(pkt->data, pkt->size,
|
|
(uint8_t **)&da->array, &da->num,
|
|
&enc->header.array, &enc->header.num,
|
|
@@ -305,10 +315,11 @@ static void on_first_packet(void *data, AVPacket *pkt, struct darray *da)
|
|
}
|
|
|
|
static void *nvenc_create_internal(obs_data_t *settings, obs_encoder_t *encoder,
|
|
- bool psycho_aq, bool hevc)
|
|
+ bool psycho_aq, bool hevc, bool av1)
|
|
{
|
|
struct nvenc_encoder *enc = bzalloc(sizeof(*enc));
|
|
|
|
+ enc->av1 = av1;
|
|
#ifdef ENABLE_HEVC
|
|
enc->hevc = hevc;
|
|
if (hevc) {
|
|
@@ -321,7 +332,12 @@ static void *nvenc_create_internal(obs_data_t *settings, obs_encoder_t *encoder,
|
|
#else
|
|
UNUSED_PARAMETER(hevc);
|
|
#endif
|
|
- {
|
|
+ if (av1) {
|
|
+ if (!ffmpeg_video_encoder_init(
|
|
+ &enc->ffve, enc, encoder, "av1_nvenc", "nvenc_av1",
|
|
+ ENCODER_NAME_AV1, on_init_error, on_first_packet))
|
|
+ goto fail;
|
|
+ } else {
|
|
if (!ffmpeg_video_encoder_init(&enc->ffve, enc, encoder,
|
|
"h264_nvenc", "nvenc_h264",
|
|
ENCODER_NAME_H264, on_init_error,
|
|
@@ -365,12 +381,14 @@ static void *h264_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder)
|
|
}
|
|
|
|
bool psycho_aq = obs_data_get_bool(settings, "psycho_aq");
|
|
- void *enc = nvenc_create_internal(settings, encoder, psycho_aq, false);
|
|
+ void *enc = nvenc_create_internal(settings, encoder, psycho_aq, false,
|
|
+ false);
|
|
if ((enc == NULL) && psycho_aq) {
|
|
blog(LOG_WARNING,
|
|
"[NVENC encoder] nvenc_create_internal failed, "
|
|
"trying again without Psycho Visual Tuning");
|
|
- enc = nvenc_create_internal(settings, encoder, false, false);
|
|
+ enc = nvenc_create_internal(settings, encoder, false, false,
|
|
+ false);
|
|
}
|
|
|
|
return enc;
|
|
@@ -404,18 +422,59 @@ static void *hevc_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder)
|
|
}
|
|
|
|
bool psycho_aq = obs_data_get_bool(settings, "psycho_aq");
|
|
- void *enc = nvenc_create_internal(settings, encoder, psycho_aq, true);
|
|
+ void *enc = nvenc_create_internal(settings, encoder, psycho_aq, true,
|
|
+ false);
|
|
if ((enc == NULL) && psycho_aq) {
|
|
blog(LOG_WARNING,
|
|
"[NVENC encoder] nvenc_create_internal failed, "
|
|
"trying again without Psycho Visual Tuning");
|
|
- enc = nvenc_create_internal(settings, encoder, false, true);
|
|
+ enc = nvenc_create_internal(settings, encoder, false, true,
|
|
+ false);
|
|
}
|
|
|
|
return enc;
|
|
}
|
|
#endif
|
|
|
|
+static void *av1_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder)
|
|
+{
|
|
+ video_t *video = obs_encoder_video(encoder);
|
|
+ const struct video_output_info *voi = video_output_get_info(video);
|
|
+ switch (voi->format) {
|
|
+ case VIDEO_FORMAT_I010:
|
|
+ case VIDEO_FORMAT_P010: {
|
|
+ const char *const text =
|
|
+ obs_module_text("NVENC.10bitUnsupported");
|
|
+ obs_encoder_set_last_error(encoder, text);
|
|
+ blog(LOG_ERROR, "[NVENC encoder] %s", text);
|
|
+ return NULL;
|
|
+ }
|
|
+ default:
|
|
+ if (voi->colorspace == VIDEO_CS_2100_PQ ||
|
|
+ voi->colorspace == VIDEO_CS_2100_HLG) {
|
|
+ const char *const text =
|
|
+ obs_module_text("NVENC.8bitUnsupportedHdr");
|
|
+ obs_encoder_set_last_error(encoder, text);
|
|
+ blog(LOG_ERROR, "[NVENC encoder] %s", text);
|
|
+ return NULL;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ bool psycho_aq = obs_data_get_bool(settings, "psycho_aq");
|
|
+ void *enc = nvenc_create_internal(settings, encoder, psycho_aq, false,
|
|
+ true);
|
|
+ if ((enc == NULL) && psycho_aq) {
|
|
+ blog(LOG_WARNING,
|
|
+ "[NVENC encoder] nvenc_create_internal failed, "
|
|
+ "trying again without Psycho Visual Tuning");
|
|
+ enc = nvenc_create_internal(settings, encoder, false, false,
|
|
+ true);
|
|
+ }
|
|
+
|
|
+ return enc;
|
|
+}
|
|
+
|
|
static bool nvenc_encode(void *data, struct encoder_frame *frame,
|
|
struct encoder_packet *packet, bool *received_packet)
|
|
{
|
|
@@ -658,6 +717,12 @@ obs_properties_t *hevc_nvenc_properties_ffmpeg(void *unused)
|
|
}
|
|
#endif
|
|
|
|
+obs_properties_t *av1_nvenc_properties_ffmpeg(void *unused)
|
|
+{
|
|
+ UNUSED_PARAMETER(unused);
|
|
+ return nvenc_properties_internal(CODEC_AV1, true);
|
|
+}
|
|
+
|
|
static bool nvenc_extra_data(void *data, uint8_t **extra_data, size_t *size)
|
|
{
|
|
struct nvenc_encoder *enc = data;
|
|
@@ -719,3 +784,24 @@ struct obs_encoder_info hevc_nvenc_encoder_info = {
|
|
#endif
|
|
};
|
|
#endif
|
|
+
|
|
+struct obs_encoder_info av1_nvenc_encoder_info = {
|
|
+ .id = "ffmpeg_av1_nvenc",
|
|
+ .type = OBS_ENCODER_VIDEO,
|
|
+ .codec = "av1",
|
|
+ .get_name = av1_nvenc_getname,
|
|
+ .create = av1_nvenc_create,
|
|
+ .destroy = nvenc_destroy,
|
|
+ .encode = nvenc_encode,
|
|
+ .update = nvenc_reconfigure,
|
|
+ .get_defaults = av1_nvenc_defaults,
|
|
+ .get_properties = av1_nvenc_properties_ffmpeg,
|
|
+ .get_extra_data = nvenc_extra_data,
|
|
+ .get_sei_data = nvenc_sei_data,
|
|
+ .get_video_info = nvenc_video_info,
|
|
+#ifdef _WIN32
|
|
+ .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL,
|
|
+#else
|
|
+ .caps = OBS_ENCODER_CAP_DYN_BITRATE,
|
|
+#endif
|
|
+};
|
|
diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c
|
|
index da0b2c2b46f7a..e06f8ac7a9192 100644
|
|
--- a/plugins/obs-ffmpeg/obs-ffmpeg.c
|
|
+++ b/plugins/obs-ffmpeg/obs-ffmpeg.c
|
|
@@ -43,6 +43,7 @@ extern struct obs_encoder_info h264_nvenc_encoder_info;
|
|
#ifdef ENABLE_HEVC
|
|
extern struct obs_encoder_info hevc_nvenc_encoder_info;
|
|
#endif
|
|
+extern struct obs_encoder_info av1_nvenc_encoder_info;
|
|
extern struct obs_encoder_info svt_av1_encoder_info;
|
|
extern struct obs_encoder_info aom_av1_encoder_info;
|
|
|
|
@@ -307,8 +308,11 @@ static bool nvenc_supported(bool *out_h264, bool *out_hevc, bool *out_av1)
|
|
if (success) {
|
|
void *const lib = os_dlopen("libnvidia-encode.so.1");
|
|
success = lib != NULL;
|
|
- if (success)
|
|
+ if (success) {
|
|
os_dlclose(lib);
|
|
+ av1 = nvenc_codec_exists("av1_nvenc",
|
|
+ "nvenc_av1");
|
|
+ }
|
|
}
|
|
#else
|
|
void *const lib = os_dlopen("libnvidia-encode.so.1");
|
|
@@ -432,6 +436,8 @@ bool obs_module_load(void)
|
|
if (hevc)
|
|
obs_register_encoder(&hevc_nvenc_encoder_info);
|
|
#endif
|
|
+ if (av1)
|
|
+ obs_register_encoder(&av1_nvenc_encoder_info);
|
|
}
|
|
|
|
#ifdef _WIN32
|