From b7c5df3c4bbd30bbca91f6e9bdb2bf4d606c46f5 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 31 Aug 2024 03:17:18 +0200 Subject: Faster startup (faster video codec query), fix some video codec callback logic --- src/capture/nvfbc.c | 12 +- src/encoder/video/cuda.c | 236 ++++++++++++++++++++-- src/encoder/video/vaapi.c | 24 ++- src/main.cpp | 498 ++++++++++++++++++---------------------------- 4 files changed, 430 insertions(+), 340 deletions(-) (limited to 'src') diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c index 1944dcc..80fbaa8 100644 --- a/src/capture/nvfbc.c +++ b/src/capture/nvfbc.c @@ -1,6 +1,5 @@ #include "../../include/capture/nvfbc.h" #include "../../external/NvFBC.h" -#include "../../include/cuda.h" #include "../../include/egl.h" #include "../../include/utils.h" #include "../../include/color_conversion.h" @@ -24,7 +23,6 @@ typedef struct { bool fbc_handle_created; bool capture_session_created; - gsr_cuda cuda; NVFBC_TOGL_SETUP_PARAMS setup_params; bool supports_direct_cursor; @@ -290,7 +288,6 @@ static int gsr_capture_nvfbc_setup_session(gsr_capture_nvfbc *cap_nvfbc) { static void gsr_capture_nvfbc_stop(gsr_capture_nvfbc *cap_nvfbc) { gsr_capture_nvfbc_destroy_session_and_handle(cap_nvfbc); - gsr_cuda_unload(&cap_nvfbc->cuda); if(cap_nvfbc->library) { dlclose(cap_nvfbc->library); cap_nvfbc->library = NULL; @@ -304,15 +301,8 @@ static void gsr_capture_nvfbc_stop(gsr_capture_nvfbc *cap_nvfbc) { static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame *frame) { gsr_capture_nvfbc *cap_nvfbc = cap->priv; - if(!cap_nvfbc->params.use_software_video_encoder) { - if(!gsr_cuda_load(&cap_nvfbc->cuda, cap_nvfbc->params.egl->x11.dpy, cap_nvfbc->params.overclock)) - return -1; - } - - if(!gsr_capture_nvfbc_load_library(cap)) { - gsr_cuda_unload(&cap_nvfbc->cuda); + if(!gsr_capture_nvfbc_load_library(cap)) return -1; - } cap_nvfbc->x = max_int(cap_nvfbc->params.pos.x, 0); cap_nvfbc->y = max_int(cap_nvfbc->params.pos.y, 0); diff --git a/src/encoder/video/cuda.c b/src/encoder/video/cuda.c index 53abe78..69e570f 100644 --- a/src/encoder/video/cuda.c +++ b/src/encoder/video/cuda.c @@ -1,11 +1,13 @@ #include "../../../include/encoder/video/cuda.h" #include "../../../include/egl.h" #include "../../../include/cuda.h" +#include "../../../external/nvEncodeAPI.h" #include #include #include +#include typedef struct { gsr_video_encoder_cuda_params params; @@ -122,22 +124,224 @@ static bool gsr_video_encoder_cuda_setup_textures(gsr_video_encoder_cuda *self, return true; } +static void* open_nvenc_library(void) { + dlerror(); /* clear */ + void *lib = dlopen("libnvidia-encode.so.1", RTLD_LAZY); + if(!lib) { + lib = dlopen("libnvidia-encode.so", RTLD_LAZY); + if(!lib) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs failed: failed to load libnvidia-encode.so/libnvidia-encode.so.1, error: %s\n", dlerror()); + return NULL; + } + } + return lib; +} + +static bool profile_is_h264(const GUID *profile_guid) { + const GUID *h264_guids[] = { + &NV_ENC_H264_PROFILE_BASELINE_GUID, + &NV_ENC_H264_PROFILE_MAIN_GUID, + &NV_ENC_H264_PROFILE_HIGH_GUID, + &NV_ENC_H264_PROFILE_PROGRESSIVE_HIGH_GUID, + &NV_ENC_H264_PROFILE_CONSTRAINED_HIGH_GUID + }; + + for(int i = 0; i < 5; ++i) { + if(memcmp(profile_guid, h264_guids[i], sizeof(GUID)) == 0) + return true; + } + + return false; +} + +static bool profile_is_hevc(const GUID *profile_guid) { + const GUID *h264_guids[] = { + &NV_ENC_HEVC_PROFILE_MAIN_GUID, + }; + + for(int i = 0; i < 1; ++i) { + if(memcmp(profile_guid, h264_guids[i], sizeof(GUID)) == 0) + return true; + } + + return false; +} + +static bool profile_is_hevc_10bit(const GUID *profile_guid) { + const GUID *h264_guids[] = { + &NV_ENC_HEVC_PROFILE_MAIN10_GUID, + }; + + for(int i = 0; i < 1; ++i) { + if(memcmp(profile_guid, h264_guids[i], sizeof(GUID)) == 0) + return true; + } + + return false; +} + +static bool profile_is_av1(const GUID *profile_guid) { + const GUID *h264_guids[] = { + &NV_ENC_AV1_PROFILE_MAIN_GUID, + }; + + for(int i = 0; i < 1; ++i) { + if(memcmp(profile_guid, h264_guids[i], sizeof(GUID)) == 0) + return true; + } + + return false; +} + +static bool encoder_get_supported_profiles(const NV_ENCODE_API_FUNCTION_LIST *function_list, void *nvenc_encoder, const GUID *encoder_guid, gsr_supported_video_codecs *supported_video_codecs) { + bool success = false; + GUID *profile_guids = NULL; + + uint32_t profile_guid_count = 0; + if(function_list->nvEncGetEncodeProfileGUIDCount(nvenc_encoder, *encoder_guid, &profile_guid_count) != NV_ENC_SUCCESS) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: nvEncGetEncodeProfileGUIDCount failed, error: %s\n", function_list->nvEncGetLastErrorString(nvenc_encoder)); + goto fail; + } + + if(profile_guid_count == 0) + goto fail; + + profile_guids = calloc(profile_guid_count, sizeof(GUID)); + if(!profile_guids) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: failed to allocate %d guids\n", (int)profile_guid_count); + goto fail; + } + + if(function_list->nvEncGetEncodeProfileGUIDs(nvenc_encoder, *encoder_guid, profile_guids, profile_guid_count, &profile_guid_count) != NV_ENC_SUCCESS) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: nvEncGetEncodeProfileGUIDs failed, error: %s\n", function_list->nvEncGetLastErrorString(nvenc_encoder)); + goto fail; + } + + for(uint32_t i = 0; i < profile_guid_count; ++i) { + if(profile_is_h264(&profile_guids[i])) { + supported_video_codecs->h264 = true; + } else if(profile_is_hevc(&profile_guids[i])) { + supported_video_codecs->hevc = true; + } else if(profile_is_hevc_10bit(&profile_guids[i])) { + supported_video_codecs->hevc_hdr = true; + supported_video_codecs->hevc_10bit = true; + } else if(profile_is_av1(&profile_guids[i])) { + supported_video_codecs->av1 = true; + supported_video_codecs->av1_hdr = true; + supported_video_codecs->av1_10bit = true; + } + } + + success = true; + fail: + + if(profile_guids) + free(profile_guids); + + return success; +} + +static bool get_supported_video_codecs(const NV_ENCODE_API_FUNCTION_LIST *function_list, void *nvenc_encoder, gsr_supported_video_codecs *supported_video_codecs) { + bool success = false; + GUID *encoder_guids = NULL; + *supported_video_codecs = (gsr_supported_video_codecs){0}; + + uint32_t encode_guid_count = 0; + if(function_list->nvEncGetEncodeGUIDCount(nvenc_encoder, &encode_guid_count) != NV_ENC_SUCCESS) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: nvEncGetEncodeGUIDCount failed, error: %s\n", function_list->nvEncGetLastErrorString(nvenc_encoder)); + goto fail; + } + + if(encode_guid_count == 0) + goto fail; + + encoder_guids = calloc(encode_guid_count, sizeof(GUID)); + if(!encoder_guids) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: failed to allocate %d guids\n", (int)encode_guid_count); + goto fail; + } + + if(function_list->nvEncGetEncodeGUIDs(nvenc_encoder, encoder_guids, encode_guid_count, &encode_guid_count) != NV_ENC_SUCCESS) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: nvEncGetEncodeGUIDs failed, error: %s\n", function_list->nvEncGetLastErrorString(nvenc_encoder)); + goto fail; + } + + for(uint32_t i = 0; i < encode_guid_count; ++i) { + encoder_get_supported_profiles(function_list, nvenc_encoder, &encoder_guids[i], supported_video_codecs); + } + + success = true; + fail: + + if(encoder_guids) + free(encoder_guids); + + return success; +} + +#define NVENCAPI_VERSION_470 (11 | (1 << 24)) + static gsr_supported_video_codecs gsr_video_encoder_cuda_get_supported_codecs(gsr_video_encoder *encoder, bool cleanup) { (void)encoder; - (void)cleanup; - //gsr_video_encoder_cuda *encoder_cuda = encoder->priv; - // TODO: Query support - return (gsr_supported_video_codecs) { - .h264 = true, - .hevc = true, - .hevc_hdr = true, - .hevc_10bit = true, - .av1 = true, - .av1_hdr = true, - .av1_10bit = true, - .vp8 = false, - .vp9 = false - }; + + void *nvenc_lib = NULL; + gsr_cuda cuda; + memset(&cuda, 0, sizeof(cuda)); + gsr_supported_video_codecs supported_video_codecs = {0}; + + if(!gsr_cuda_load(&cuda, NULL, false)) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: failed to load cuda\n"); + goto done; + } + + nvenc_lib = open_nvenc_library(); + if(!nvenc_lib) + goto done; + + typedef NVENCSTATUS NVENCAPI (*FUNC_NvEncodeAPICreateInstance)(NV_ENCODE_API_FUNCTION_LIST *functionList); + FUNC_NvEncodeAPICreateInstance NvEncodeAPICreateInstance = (FUNC_NvEncodeAPICreateInstance)dlsym(nvenc_lib, "NvEncodeAPICreateInstance"); + if(!NvEncodeAPICreateInstance) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: failed to find NvEncodeAPICreateInstance in libnvidia-encode.so\n"); + goto done; + } + + NV_ENCODE_API_FUNCTION_LIST function_list; + memset(&function_list, 0, sizeof(function_list)); + function_list.version = NV_ENCODE_API_FUNCTION_LIST_VER; + if(NvEncodeAPICreateInstance(&function_list) != NV_ENC_SUCCESS) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: NvEncodeAPICreateInstance failed\n"); + goto done; + } + + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params; + memset(¶ms, 0, sizeof(params)); + params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; + params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; + params.device = cuda.cu_ctx; + params.apiVersion = NVENCAPI_VERSION; + + void *nvenc_encoder = NULL; + if(function_list.nvEncOpenEncodeSessionEx(¶ms, &nvenc_encoder) != NV_ENC_SUCCESS) { + // Old nvidia gpus dont support the new nvenc api (which is required for av1). + // In such cases fallback to old api version if possible and try again. + params.apiVersion = NVENCAPI_VERSION_470; + if(function_list.nvEncOpenEncodeSessionEx(¶ms, &nvenc_encoder) != NV_ENC_SUCCESS) { + fprintf(stderr, "gsr error: gsr_video_encoder_cuda_get_supported_codecs: NvEncOpenEncodeSessionEx failed\n"); + goto done; + } + } + + get_supported_video_codecs(&function_list, nvenc_encoder, &supported_video_codecs); + + done: + if(cleanup) { + function_list.nvEncDestroyEncoder(nvenc_encoder); + if(nvenc_lib) + dlclose(nvenc_lib); + gsr_cuda_unload(&cuda); + } + + return supported_video_codecs; } static void gsr_video_encoder_cuda_stop(gsr_video_encoder_cuda *self, AVCodecContext *video_codec_context); @@ -145,8 +349,8 @@ static void gsr_video_encoder_cuda_stop(gsr_video_encoder_cuda *self, AVCodecCon static bool gsr_video_encoder_cuda_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) { gsr_video_encoder_cuda *encoder_cuda = encoder->priv; - // TODO: Force set overclock to false if wayland - if(!gsr_cuda_load(&encoder_cuda->cuda, encoder_cuda->params.egl->x11.dpy, encoder_cuda->params.overclock)) { + const bool overclock = gsr_egl_get_display_server(encoder_cuda->params.egl) == GSR_DISPLAY_SERVER_X11 ? encoder_cuda->params.overclock : false; + if(!gsr_cuda_load(&encoder_cuda->cuda, encoder_cuda->params.egl->x11.dpy, overclock)) { fprintf(stderr, "gsr error: gsr_video_encoder_cuda_start: failed to load cuda\n"); gsr_video_encoder_cuda_stop(encoder_cuda, video_codec_context); return false; diff --git a/src/encoder/video/vaapi.c b/src/encoder/video/vaapi.c index 2185ebc..a6c45f9 100644 --- a/src/encoder/video/vaapi.c +++ b/src/encoder/video/vaapi.c @@ -246,16 +246,18 @@ static bool get_supported_video_codecs(VADisplay va_dpy, gsr_supported_video_cod int va_major = 0; int va_minor = 0; - if(vaInitialize(va_dpy, &va_major, &va_minor) != VA_STATUS_SUCCESS) - return false; + if(vaInitialize(va_dpy, &va_major, &va_minor) != VA_STATUS_SUCCESS) { + fprintf(stderr, "gsr error: gsr_video_encoder_vaapi_get_supported_codecs: vaInitialize failed\n"); + goto fail; + } int num_profiles = vaMaxNumProfiles(va_dpy); if(num_profiles <= 0) - goto done; + goto fail; profile_list = calloc(num_profiles, sizeof(VAProfile)); if(!profile_list || vaQueryConfigProfiles(va_dpy, profile_list, &num_profiles) != VA_STATUS_SUCCESS) - goto done; + goto fail; for(int i = 0; i < num_profiles; ++i) { if(profile_is_h264(profile_list[i])) { @@ -285,7 +287,7 @@ static bool get_supported_video_codecs(VADisplay va_dpy, gsr_supported_video_cod } success = true; - done: + fail: if(profile_list) free(profile_list); @@ -299,16 +301,22 @@ static gsr_supported_video_codecs gsr_video_encoder_vaapi_get_supported_codecs(g gsr_video_encoder_vaapi *encoder_vaapi = encoder->priv; gsr_supported_video_codecs supported_video_codecs = {0}; - const int drm_fd = open(encoder_vaapi->params.egl->card_path, O_RDWR); + char render_path[128]; + if(!gsr_card_path_get_render_path(encoder_vaapi->params.egl->card_path, render_path)) { + fprintf(stderr, "gsr error: gsr_video_encoder_vaapi_get_supported_codecs: failed to get /dev/dri/renderDXXX file from %s\n", encoder_vaapi->params.egl->card_path); + return supported_video_codecs; + } + + const int drm_fd = open(render_path, O_RDWR); if(drm_fd == -1) { - fprintf(stderr, "gsr error: gsr_video_encoder_vaapi_get_supported_codecs: failed to open device %s\n", encoder_vaapi->params.egl->card_path); + fprintf(stderr, "gsr error: gsr_video_encoder_vaapi_get_supported_codecs: failed to open device %s\n", render_path); return supported_video_codecs; } VADisplay va_dpy = vaGetDisplayDRM(drm_fd); if(va_dpy) { if(!get_supported_video_codecs(va_dpy, &supported_video_codecs, cleanup)) - fprintf(stderr, "gsr error: gsr_video_encoder_vaapi_get_supported_codecs: failed to query supported video codecs for device %s\n", encoder_vaapi->params.egl->card_path); + fprintf(stderr, "gsr error: gsr_video_encoder_vaapi_get_supported_codecs: failed to query supported video codecs for device %s\n", render_path); } if(cleanup) diff --git a/src/main.cpp b/src/main.cpp index 1e0827f..8ab34ba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -584,170 +584,6 @@ static AVCodecContext *create_video_codec_context(AVPixelFormat pix_fmt, return codec_context; } -static bool vaapi_create_codec_context(AVCodecContext *video_codec_context, const char *card_path) { - char render_path[128]; - if(!gsr_card_path_get_render_path(card_path, render_path)) { - fprintf(stderr, "gsr error: failed to get /dev/dri/renderDXXX file from %s\n", card_path); - return false; - } - - AVBufferRef *device_ctx; - if(av_hwdevice_ctx_create(&device_ctx, AV_HWDEVICE_TYPE_VAAPI, render_path, NULL, 0) < 0) { - fprintf(stderr, "Error: Failed to create hardware device context\n"); - return false; - } - - AVBufferRef *frame_context = av_hwframe_ctx_alloc(device_ctx); - if(!frame_context) { - fprintf(stderr, "Error: Failed to create hwframe context\n"); - av_buffer_unref(&device_ctx); - return false; - } - - AVHWFramesContext *hw_frame_context = - (AVHWFramesContext *)frame_context->data; - hw_frame_context->width = video_codec_context->width; - hw_frame_context->height = video_codec_context->height; - hw_frame_context->sw_format = AV_PIX_FMT_NV12; - hw_frame_context->format = video_codec_context->pix_fmt; - hw_frame_context->device_ref = device_ctx; - hw_frame_context->device_ctx = (AVHWDeviceContext*)device_ctx->data; - - //hw_frame_context->initial_pool_size = 1; - - if (av_hwframe_ctx_init(frame_context) < 0) { - fprintf(stderr, "Error: Failed to initialize hardware frame context " - "(note: ffmpeg version needs to be > 4.0)\n"); - av_buffer_unref(&device_ctx); - //av_buffer_unref(&frame_context); - return false; - } - - video_codec_context->hw_device_ctx = av_buffer_ref(device_ctx); - video_codec_context->hw_frames_ctx = av_buffer_ref(frame_context); - return true; -} - -static bool check_if_codec_valid_for_hardware(const AVCodec *codec, gsr_gpu_vendor vendor, const char *card_path) { - // Do not use AV_PIX_FMT_CUDA because we dont want to do full check with hardware context - AVCodecContext *codec_context = create_video_codec_context(vendor == GSR_GPU_VENDOR_NVIDIA ? AV_PIX_FMT_YUV420P : AV_PIX_FMT_VAAPI, VideoQuality::VERY_HIGH, 60, codec, false, vendor, FramerateMode::CONSTANT, false, GSR_COLOR_RANGE_LIMITED, 2, false, BitrateMode::QP); - if(!codec_context) - return false; - - codec_context->width = 512; - codec_context->height = 512; - - if(vendor != GSR_GPU_VENDOR_NVIDIA) { - if(!vaapi_create_codec_context(codec_context, card_path)) { - avcodec_free_context(&codec_context); - return false; - } - } - - bool success = false; - success = avcodec_open2(codec_context, codec_context->codec, NULL) == 0; - if(codec_context->hw_device_ctx) - av_buffer_unref(&codec_context->hw_device_ctx); - if(codec_context->hw_frames_ctx) - av_buffer_unref(&codec_context->hw_frames_ctx); - avcodec_free_context(&codec_context); - return success; -} - -static const AVCodec* find_h264_software_encoder() { - return avcodec_find_encoder_by_name("libx264"); -} - -static const AVCodec* find_h264_encoder(gsr_gpu_vendor vendor, const char *card_path) { - const AVCodec *codec = avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "h264_nvenc" : "h264_vaapi"); - if(!codec) - codec = avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "nvenc_h264" : "vaapi_h264"); - - if(!codec) - return nullptr; - - static bool checked = false; - static bool checked_success = true; - if(!checked) { - checked = true; - if(!check_if_codec_valid_for_hardware(codec, vendor, card_path)) - checked_success = false; - } - return checked_success ? codec : nullptr; -} - -static const AVCodec* find_hevc_encoder(gsr_gpu_vendor vendor, const char *card_path) { - const AVCodec *codec = avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "hevc_nvenc" : "hevc_vaapi"); - if(!codec) - codec = avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "nvenc_hevc" : "vaapi_hevc"); - - if(!codec) - return nullptr; - - static bool checked = false; - static bool checked_success = true; - if(!checked) { - checked = true; - if(!check_if_codec_valid_for_hardware(codec, vendor, card_path)) - checked_success = false; - } - return checked_success ? codec : nullptr; -} - -static const AVCodec* find_av1_encoder(gsr_gpu_vendor vendor, const char *card_path) { - // Workaround bug with av1 nvidia in older ffmpeg versions that causes the whole application to crash - // when avcodec_open2 is opened with av1_nvenc - if(vendor == GSR_GPU_VENDOR_NVIDIA && LIBAVCODEC_BUILD < AV_VERSION_INT(60, 30, 100)) { - return nullptr; - } - - const AVCodec *codec = avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "av1_nvenc" : "av1_vaapi"); - if(!codec) - codec = avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "nvenc_av1" : "vaapi_av1"); - - if(!codec) - return nullptr; - - static bool checked = false; - static bool checked_success = true; - if(!checked) { - checked = true; - if(!check_if_codec_valid_for_hardware(codec, vendor, card_path)) - checked_success = false; - } - return checked_success ? codec : nullptr; -} - -static const AVCodec* find_vp8_encoder(gsr_gpu_vendor vendor, const char *card_path) { - const AVCodec *codec = avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "vp8_nvenc" : "vp8_vaapi"); - if(!codec) - return nullptr; - - static bool checked = false; - static bool checked_success = true; - if(!checked) { - checked = true; - if(!check_if_codec_valid_for_hardware(codec, vendor, card_path)) - checked_success = false; - } - return checked_success ? codec : nullptr; -} - -static const AVCodec* find_vp9_encoder(gsr_gpu_vendor vendor, const char *card_path) { - const AVCodec *codec = avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "vp9_nvenc" : "vp9_vaapi"); - if(!codec) - return nullptr; - - static bool checked = false; - static bool checked_success = true; - if(!checked) { - checked = true; - if(!check_if_codec_valid_for_hardware(codec, vendor, card_path)) - checked_success = false; - } - return checked_success ? codec : nullptr; -} - static void open_audio(AVCodecContext *audio_codec_context) { AVDictionary *options = nullptr; av_dict_set(&options, "strict", "experimental", 0); @@ -1803,56 +1639,81 @@ static void list_gpu_info(gsr_egl *egl) { } } -static void list_supported_video_codecs(gsr_egl *egl, bool wayland) { -#if 1 - if(find_h264_encoder(egl->gpu_info.vendor, egl->card_path)) - puts("h264"); - if(find_h264_software_encoder()) - puts("h264_software"); - if(find_hevc_encoder(egl->gpu_info.vendor, egl->card_path)) { - puts("hevc"); - if(wayland) - puts("hevc_hdr"); // TODO: Verify if it's actually supported - puts("hevc_10bit"); // TODO: Verify if it's actually supported +static const AVCodec* get_ffmpeg_video_codec(VideoCodec video_codec, gsr_gpu_vendor vendor) { + switch(video_codec) { + case VideoCodec::H264: + return avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "h264_nvenc" : "h264_vaapi"); + case VideoCodec::HEVC: + case VideoCodec::HEVC_HDR: + case VideoCodec::HEVC_10BIT: + return avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "hevc_nvenc" : "hevc_vaapi"); + case VideoCodec::AV1: + case VideoCodec::AV1_HDR: + case VideoCodec::AV1_10BIT: + return avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "av1_nvenc" : "av1_vaapi"); + case VideoCodec::VP8: + return avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "vp8_nvenc" : "vp8_vaapi"); + case VideoCodec::VP9: + return avcodec_find_encoder_by_name(vendor == GSR_GPU_VENDOR_NVIDIA ? "vp9_nvenc" : "vp9_vaapi"); } - if(find_av1_encoder(egl->gpu_info.vendor, egl->card_path)) { - puts("av1"); - if(wayland) - puts("av1_hdr"); // TODO: Verify if it's actually supported - puts("av1_10bit"); // TODO: Verify if it's actually supported + return nullptr; +} + +static void set_supported_video_codecs_ffmpeg(gsr_supported_video_codecs *supported_video_codecs, gsr_gpu_vendor vendor) { + if(!get_ffmpeg_video_codec(VideoCodec::H264, vendor)) { + supported_video_codecs->h264 = false; } - if(find_vp8_encoder(egl->gpu_info.vendor, egl->card_path)) - puts("vp8"); - if(find_vp9_encoder(egl->gpu_info.vendor, egl->card_path)) - puts("vp9"); -#else + + if(!get_ffmpeg_video_codec(VideoCodec::HEVC, vendor)) { + supported_video_codecs->hevc = false; + supported_video_codecs->hevc_hdr = false; + supported_video_codecs->hevc_10bit = false; + } + + if(!get_ffmpeg_video_codec(VideoCodec::AV1, vendor)) { + supported_video_codecs->av1 = false; + supported_video_codecs->av1_hdr = false; + supported_video_codecs->av1_10bit = false; + } + + if(!get_ffmpeg_video_codec(VideoCodec::VP8, vendor)) { + supported_video_codecs->vp8 = false; + } + + if(!get_ffmpeg_video_codec(VideoCodec::VP9, vendor)) { + supported_video_codecs->vp9 = false; + } +} + +static void list_supported_video_codecs(gsr_egl *egl, bool wayland) { // Dont clean it up on purpose to increase shutdown speed gsr_video_encoder *video_encoder = create_video_encoder(egl, false, GSR_COLOR_DEPTH_8_BITS, false); if(!video_encoder) return; - const gsr_supported_video_codecs supported_video_codecs = gsr_video_encoder_get_supported_codecs(video_encoder, false); + gsr_supported_video_codecs supported_video_codecs = gsr_video_encoder_get_supported_codecs(video_encoder, false); + set_supported_video_codecs_ffmpeg(&supported_video_codecs, egl->gpu_info.vendor); + if(supported_video_codecs.h264) puts("h264"); - if(find_h264_software_encoder()) + if(avcodec_find_encoder_by_name("libx264")) puts("h264_software"); - if(supported_video_codecs.hevc) { + if(supported_video_codecs.hevc) puts("hevc"); - if(wayland) - puts("hevc_hdr"); // TODO: Verify if it's actually supported - puts("hevc_10bit"); // TODO: Verify if it's actually supported - } - if(supported_video_codecs.av1) { + if(supported_video_codecs.hevc_hdr && wayland) + puts("hevc_hdr"); + if(supported_video_codecs.hevc_10bit) + puts("hevc_10bit"); + if(supported_video_codecs.av1) puts("av1"); - if(wayland) - puts("av1_hdr"); // TODO: Verify if it's actually supported - puts("av1_10bit"); // TODO: Verify if it's actually supported - } + if(supported_video_codecs.av1_hdr && wayland) + puts("av1_hdr"); + if(supported_video_codecs.av1_10bit) + puts("av1_10bit"); if(supported_video_codecs.vp8) puts("vp8"); if(supported_video_codecs.vp9) puts("vp9"); -#endif } static bool monitor_capture_use_drm(gsr_egl *egl, bool wayland) { @@ -1992,7 +1853,7 @@ static void list_audio_devices_command() { _exit(0); } -static gsr_capture* create_capture_impl(const char *window_str, const char *screen_region, bool wayland, gsr_egl *egl, int fps, bool overclock, VideoCodec video_codec, gsr_color_range color_range, +static gsr_capture* create_capture_impl(const char *window_str, const char *screen_region, bool wayland, gsr_egl *egl, int fps, VideoCodec video_codec, gsr_color_range color_range, bool record_cursor, bool track_damage, bool use_software_video_encoder, bool restore_portal_session, const char *portal_session_token_filepath, gsr_color_depth color_depth) { @@ -2112,7 +1973,6 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre nvfbc_params.pos = { 0, 0 }; nvfbc_params.size = { 0, 0 }; nvfbc_params.direct_capture = direct_capture; - nvfbc_params.overclock = overclock; nvfbc_params.color_depth = color_depth; nvfbc_params.color_range = color_range; nvfbc_params.record_cursor = record_cursor; @@ -2284,95 +2144,93 @@ static AudioCodec select_audio_codec_with_fallback(AudioCodec audio_codec, const return audio_codec; } -static const AVCodec* select_video_codec_with_fallback(VideoCodec video_codec, const char *video_codec_to_use, const char *file_extension, bool use_software_video_encoder, const gsr_egl *egl) { - const bool video_codec_auto = strcmp(video_codec_to_use, "auto") == 0; - if(video_codec_auto) { - if(strcmp(file_extension, "webm") == 0) { - fprintf(stderr, "Info: using vp8 encoder because a codec was not specified and the file extension is .webm\n"); - video_codec_to_use = "vp8"; - video_codec = VideoCodec::VP8; - } else { - fprintf(stderr, "Info: using h264 encoder because a codec was not specified\n"); - video_codec_to_use = "h264"; - video_codec = VideoCodec::H264; - } +static const char* video_codec_to_string(VideoCodec video_codec) { + switch(video_codec) { + case VideoCodec::H264: return "h264"; + case VideoCodec::HEVC: return "hevc"; + case VideoCodec::HEVC_HDR: return "hevc_hdr"; + case VideoCodec::HEVC_10BIT: return "hevc_10bit"; + case VideoCodec::AV1: return "av1"; + case VideoCodec::AV1_HDR: return "av1_hdr"; + case VideoCodec::AV1_10BIT: return "av1_10bit"; + case VideoCodec::VP8: return "vp8"; + case VideoCodec::VP9: return "vp9"; } + return ""; +} - // TODO: Allow hevc, vp9 and av1 in (enhanced) flv (supported since ffmpeg 6.1) - const bool is_flv = strcmp(file_extension, "flv") == 0; - if(is_flv) { - if(video_codec != VideoCodec::H264) { - video_codec_to_use = "h264"; - video_codec = VideoCodec::H264; - fprintf(stderr, "Warning: hevc/av1 is not compatible with flv, falling back to h264 instead.\n"); - } +static const AVCodec* pick_video_codec(VideoCodec *video_codec, gsr_egl *egl, bool use_software_video_encoder, bool video_codec_auto, const char *video_codec_to_use, bool is_flv) { + // TODO: software encoder for hevc, av1, vp8 and vp9 - // if(audio_codec != AudioCodec::AAC) { - // audio_codec_to_use = "aac"; - // audio_codec = AudioCodec::AAC; - // fprintf(stderr, "Warning: flv only supports aac, falling back to aac instead.\n"); - // } + gsr_video_encoder *video_encoder = create_video_encoder(egl, false, GSR_COLOR_DEPTH_8_BITS, use_software_video_encoder); + if(!video_encoder) { + fprintf(stderr, "Error: failed to create video encoder\n"); + _exit(1); } - const bool is_hls = strcmp(file_extension, "m3u8") == 0; - if(is_hls) { - if(video_codec_is_av1(video_codec)) { - video_codec_to_use = "hevc"; - video_codec = VideoCodec::HEVC; - fprintf(stderr, "Warning: av1 is not compatible with hls (m3u8), falling back to hevc instead.\n"); - } - - // if(audio_codec != AudioCodec::AAC) { - // audio_codec_to_use = "aac"; - // audio_codec = AudioCodec::AAC; - // fprintf(stderr, "Warning: hls (m3u8) only supports aac, falling back to aac instead.\n"); - // } - } + const gsr_supported_video_codecs supported_video_codecs = gsr_video_encoder_get_supported_codecs(video_encoder, true); + const AVCodec *video_codec_f = nullptr; - if(use_software_video_encoder && video_codec != VideoCodec::H264) { - fprintf(stderr, "Error: \"-encoder cpu\" option is currently only available when using h264 codec option (-k)\n"); - usage(); - } + // TODO: Cleanup + // gsr_video_encoder_destroy - const AVCodec *video_codec_f = nullptr; - switch(video_codec) { + switch(*video_codec) { case VideoCodec::H264: { - if(use_software_video_encoder) { - video_codec_f = find_h264_software_encoder(); - } else { - video_codec_f = find_h264_encoder(egl->gpu_info.vendor, egl->card_path); - } + if(use_software_video_encoder) + video_codec_f = avcodec_find_encoder_by_name("libx264"); + else if(supported_video_codecs.h264) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); break; } - case VideoCodec::HEVC: - case VideoCodec::HEVC_HDR: - case VideoCodec::HEVC_10BIT: - // TODO: software encoder - video_codec_f = find_hevc_encoder(egl->gpu_info.vendor, egl->card_path); + case VideoCodec::HEVC: { + if(supported_video_codecs.hevc) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); break; - case VideoCodec::AV1: - case VideoCodec::AV1_HDR: - case VideoCodec::AV1_10BIT: - // TODO: software encoder - video_codec_f = find_av1_encoder(egl->gpu_info.vendor, egl->card_path); + } + case VideoCodec::HEVC_HDR: { + if(supported_video_codecs.hevc_hdr) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); break; - case VideoCodec::VP8: - // TODO: software encoder - video_codec_f = find_vp8_encoder(egl->gpu_info.vendor, egl->card_path); + } + case VideoCodec::HEVC_10BIT: { + if(supported_video_codecs.hevc_10bit) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); break; - case VideoCodec::VP9: - // TODO: software encoder - video_codec_f = find_vp9_encoder(egl->gpu_info.vendor, egl->card_path); + } + case VideoCodec::AV1: { + if(supported_video_codecs.av1) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); + break; + } + case VideoCodec::AV1_HDR: { + if(supported_video_codecs.av1_hdr) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); + break; + } + case VideoCodec::AV1_10BIT: { + if(supported_video_codecs.av1_10bit) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); + break; + } + case VideoCodec::VP8: { + if(supported_video_codecs.vp8) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); break; + } + case VideoCodec::VP9: { + if(supported_video_codecs.vp9) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); + break; + } } if(!video_codec_auto && !video_codec_f && !is_flv) { - switch(video_codec) { + switch(*video_codec) { case VideoCodec::H264: { fprintf(stderr, "Warning: selected video codec h264 is not supported, trying hevc instead\n"); video_codec_to_use = "hevc"; - video_codec = VideoCodec::HEVC; - video_codec_f = find_hevc_encoder(egl->gpu_info.vendor, egl->card_path); + if(supported_video_codecs.hevc) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); break; } case VideoCodec::HEVC: @@ -2380,8 +2238,9 @@ static const AVCodec* select_video_codec_with_fallback(VideoCodec video_codec, c case VideoCodec::HEVC_10BIT: { fprintf(stderr, "Warning: selected video codec hevc is not supported, trying h264 instead\n"); video_codec_to_use = "h264"; - video_codec = VideoCodec::H264; - video_codec_f = find_h264_encoder(egl->gpu_info.vendor, egl->card_path); + *video_codec = VideoCodec::H264; + if(supported_video_codecs.h264) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); break; } case VideoCodec::AV1: @@ -2389,51 +2248,27 @@ static const AVCodec* select_video_codec_with_fallback(VideoCodec video_codec, c case VideoCodec::AV1_10BIT: { fprintf(stderr, "Warning: selected video codec av1 is not supported, trying h264 instead\n"); video_codec_to_use = "h264"; - video_codec = VideoCodec::H264; - video_codec_f = find_h264_encoder(egl->gpu_info.vendor, egl->card_path); + *video_codec = VideoCodec::H264; + if(supported_video_codecs.h264) + video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor); break; } case VideoCodec::VP8: case VideoCodec::VP9: - // TODO: + // TODO: Cant fallback to other codec because webm only supports vp8/vp9 break; } } - if(!video_codec_f) { - const char *video_codec_name = ""; - switch(video_codec) { - case VideoCodec::H264: { - video_codec_name = "h264"; - break; - } - case VideoCodec::HEVC: - case VideoCodec::HEVC_HDR: - case VideoCodec::HEVC_10BIT: { - video_codec_name = "hevc"; - break; - } - case VideoCodec::AV1: - case VideoCodec::AV1_HDR: - case VideoCodec::AV1_10BIT: { - video_codec_name = "av1"; - break; - } - case VideoCodec::VP8: { - video_codec_name = "vp8"; - break; - } - case VideoCodec::VP9: { - video_codec_name = "vp9"; - break; - } - } + (void)video_codec_to_use; + if(!video_codec_f) { + const char *video_codec_name = video_codec_to_string(*video_codec); fprintf(stderr, "Error: your gpu does not support '%s' video codec. If you are sure that your gpu does support '%s' video encoding and you are using an AMD/Intel GPU,\n" " then make sure you have installed the GPU specific vaapi packages (intel-media-driver, libva-intel-driver, libva-mesa-driver and linux-firmware).\n" " It's also possible that your distro has disabled hardware accelerated video encoding for '%s' video codec.\n" " This may be the case on corporate distros such as Manjaro, Fedora or OpenSUSE.\n" - " You can test this by running 'vainfo | grep VAEntrypointEncSlice' to see if it matches any H264/HEVC profile.\n" + " You can test this by running 'vainfo | grep VAEntrypointEncSlice' to see if it matches any H264/HEVC/AV1/VP8/VP9 profile.\n" " On such distros, you need to manually install mesa from source to enable H264/HEVC hardware acceleration, or use a more user friendly distro. Alternatively record with AV1 if supported by your GPU.\n" " You can alternatively use the flatpak version of GPU Screen Recorder (https://flathub.org/apps/com.dec05eba.gpu_screen_recorder) which bypasses system issues with patented H264/HEVC codecs.\n" " Make sure you have mesa-extra freedesktop runtime installed when using the flatpak (this should be the default), which can be installed with this command:\n" @@ -2445,6 +2280,59 @@ static const AVCodec* select_video_codec_with_fallback(VideoCodec video_codec, c return video_codec_f; } +static const AVCodec* select_video_codec_with_fallback(VideoCodec *video_codec, const char *video_codec_to_use, const char *file_extension, bool use_software_video_encoder, gsr_egl *egl) { + const bool video_codec_auto = strcmp(video_codec_to_use, "auto") == 0; + if(video_codec_auto) { + if(strcmp(file_extension, "webm") == 0) { + fprintf(stderr, "Info: using vp8 encoder because a codec was not specified and the file extension is .webm\n"); + video_codec_to_use = "vp8"; + *video_codec = VideoCodec::VP8; + } else { + fprintf(stderr, "Info: using h264 encoder because a codec was not specified\n"); + video_codec_to_use = "h264"; + *video_codec = VideoCodec::H264; + } + } + + // TODO: Allow hevc, vp9 and av1 in (enhanced) flv (supported since ffmpeg 6.1) + const bool is_flv = strcmp(file_extension, "flv") == 0; + if(is_flv) { + if(*video_codec != VideoCodec::H264) { + video_codec_to_use = "h264"; + *video_codec = VideoCodec::H264; + fprintf(stderr, "Warning: hevc/av1 is not compatible with flv, falling back to h264 instead.\n"); + } + + // if(audio_codec != AudioCodec::AAC) { + // audio_codec_to_use = "aac"; + // audio_codec = AudioCodec::AAC; + // fprintf(stderr, "Warning: flv only supports aac, falling back to aac instead.\n"); + // } + } + + const bool is_hls = strcmp(file_extension, "m3u8") == 0; + if(is_hls) { + if(video_codec_is_av1(*video_codec)) { + video_codec_to_use = "hevc"; + *video_codec = VideoCodec::HEVC; + fprintf(stderr, "Warning: av1 is not compatible with hls (m3u8), falling back to hevc instead.\n"); + } + + // if(audio_codec != AudioCodec::AAC) { + // audio_codec_to_use = "aac"; + // audio_codec = AudioCodec::AAC; + // fprintf(stderr, "Warning: hls (m3u8) only supports aac, falling back to aac instead.\n"); + // } + } + + if(use_software_video_encoder && *video_codec != VideoCodec::H264) { + fprintf(stderr, "Error: \"-encoder cpu\" option is currently only available when using h264 codec option (-k)\n"); + usage(); + } + + return pick_video_codec(video_codec, egl, use_software_video_encoder, video_codec_auto, video_codec_to_use, is_flv); +} + int main(int argc, char **argv) { signal(SIGINT, stop_handler); signal(SIGUSR1, save_replay_handler); @@ -3009,10 +2897,10 @@ int main(int argc, char **argv) { const double target_fps = 1.0 / (double)fps; audio_codec = select_audio_codec_with_fallback(audio_codec, file_extension, uses_amix); - const AVCodec *video_codec_f = select_video_codec_with_fallback(video_codec, video_codec_to_use, file_extension.c_str(), use_software_video_encoder, &egl); + const AVCodec *video_codec_f = select_video_codec_with_fallback(&video_codec, video_codec_to_use, file_extension.c_str(), use_software_video_encoder, &egl); const gsr_color_depth color_depth = video_codec_to_bit_depth(video_codec); - gsr_capture *capture = create_capture_impl(window_str, screen_region, wayland, &egl, fps, overclock, video_codec, color_range, record_cursor, framerate_mode == FramerateMode::CONTENT, use_software_video_encoder, restore_portal_session, portal_session_token_filepath, color_depth); + gsr_capture *capture = create_capture_impl(window_str, screen_region, wayland, &egl, fps, video_codec, color_range, record_cursor, framerate_mode == FramerateMode::CONTENT, use_software_video_encoder, restore_portal_session, portal_session_token_filepath, color_depth); // (Some?) livestreaming services require at least one audio track to work. // If not audio is provided then create one silent audio track. -- cgit v1.2.3