diff --git a/src/video.cpp b/src/video.cpp index f786aeb59f0..920ce1a4857 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -14,7 +14,6 @@ extern "C" { #include #include #include -#include } #include "cbs.h" @@ -51,12 +50,6 @@ namespace video { av_buffer_unref(&ref); } - using avcodec_ctx_t = util::safe_ptr; - using avcodec_frame_t = util::safe_ptr; - using avcodec_buffer_t = util::safe_ptr; - using sws_t = util::safe_ptr; - using img_event_t = std::shared_ptr>>; - namespace nv { enum class profile_h264_e : int { @@ -87,11 +80,6 @@ namespace video { }; } // namespace qsv - platf::mem_type_e - map_base_dev_type(AVHWDeviceType type); - platf::pix_fmt_e - map_pix_fmt(AVPixelFormat fmt); - util::Either dxgi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *); util::Either @@ -288,137 +276,6 @@ namespace video { ALWAYS_REPROBE = 1 << 9, // This is an encoder of last resort and we want to aggressively probe for a better one }; - struct encoder_platform_formats_t { - virtual ~encoder_platform_formats_t() = default; - platf::mem_type_e dev_type; - platf::pix_fmt_e pix_fmt_8bit, pix_fmt_10bit; - }; - - struct encoder_platform_formats_avcodec: encoder_platform_formats_t { - using init_buffer_function_t = std::function(platf::avcodec_encode_device_t *)>; - - encoder_platform_formats_avcodec( - const AVHWDeviceType &avcodec_base_dev_type, - const AVHWDeviceType &avcodec_derived_dev_type, - const AVPixelFormat &avcodec_dev_pix_fmt, - const AVPixelFormat &avcodec_pix_fmt_8bit, - const AVPixelFormat &avcodec_pix_fmt_10bit, - const init_buffer_function_t &init_avcodec_hardware_input_buffer_function): - avcodec_base_dev_type { avcodec_base_dev_type }, - avcodec_derived_dev_type { avcodec_derived_dev_type }, - avcodec_dev_pix_fmt { avcodec_dev_pix_fmt }, - avcodec_pix_fmt_8bit { avcodec_pix_fmt_8bit }, - avcodec_pix_fmt_10bit { avcodec_pix_fmt_10bit }, - init_avcodec_hardware_input_buffer { init_avcodec_hardware_input_buffer_function } { - dev_type = map_base_dev_type(avcodec_base_dev_type); - pix_fmt_8bit = map_pix_fmt(avcodec_pix_fmt_8bit); - pix_fmt_10bit = map_pix_fmt(avcodec_pix_fmt_10bit); - } - - AVHWDeviceType avcodec_base_dev_type, avcodec_derived_dev_type; - AVPixelFormat avcodec_dev_pix_fmt; - AVPixelFormat avcodec_pix_fmt_8bit, avcodec_pix_fmt_10bit; - - init_buffer_function_t init_avcodec_hardware_input_buffer; - }; - - struct encoder_platform_formats_nvenc: encoder_platform_formats_t { - encoder_platform_formats_nvenc( - const platf::mem_type_e &dev_type, - const platf::pix_fmt_e &pix_fmt_8bit, - const platf::pix_fmt_e &pix_fmt_10bit) { - encoder_platform_formats_t::dev_type = dev_type; - encoder_platform_formats_t::pix_fmt_8bit = pix_fmt_8bit; - encoder_platform_formats_t::pix_fmt_10bit = pix_fmt_10bit; - } - }; - - struct encoder_t { - std::string_view name; - enum flag_e { - PASSED, // Is supported - REF_FRAMES_RESTRICT, // Set maximum reference frames - CBR, // Some encoders don't support CBR, if not supported --> attempt constant quantatication parameter instead - DYNAMIC_RANGE, // hdr - VUI_PARAMETERS, // AMD encoder with VAAPI doesn't add VUI parameters to SPS - MAX_FLAGS - }; - - static std::string_view - from_flag(flag_e flag) { -#define _CONVERT(x) \ - case flag_e::x: \ - return #x##sv - switch (flag) { - _CONVERT(PASSED); - _CONVERT(REF_FRAMES_RESTRICT); - _CONVERT(CBR); - _CONVERT(DYNAMIC_RANGE); - _CONVERT(VUI_PARAMETERS); - _CONVERT(MAX_FLAGS); - } -#undef _CONVERT - - return "unknown"sv; - } - - struct option_t { - KITTY_DEFAULT_CONSTR_MOVE(option_t) - option_t(const option_t &) = default; - - std::string name; - std::variant *, std::function, std::string, std::string *> value; - - option_t(std::string &&name, decltype(value) &&value): - name { std::move(name) }, value { std::move(value) } {} - }; - - const std::unique_ptr platform_formats; - - struct { - std::vector common_options; - std::vector sdr_options; - std::vector hdr_options; - std::vector fallback_options; - - // QP option to set in the case that CBR/VBR is not supported - // by the encoder. If CBR/VBR is guaranteed to be supported, - // don't specify this option to avoid wasteful encoder probing. - std::optional qp; - - std::string name; - std::bitset capabilities; - - bool - operator[](flag_e flag) const { - return capabilities[(std::size_t) flag]; - } - - std::bitset::reference - operator[](flag_e flag) { - return capabilities[(std::size_t) flag]; - } - } av1, hevc, h264; - - uint32_t flags; - }; - - struct encode_session_t { - virtual ~encode_session_t() = default; - - virtual int - convert(platf::img_t &img) = 0; - - virtual void - request_idr_frame() = 0; - - virtual void - request_normal_frame() = 0; - - virtual void - invalidate_ref_frames(int64_t first_frame, int64_t last_frame) = 0; - }; - class avcodec_encode_session_t: public encode_session_t { public: avcodec_encode_session_t() = default; @@ -586,7 +443,7 @@ namespace video { auto capture_thread_sync = safe::make_shared(start_capture_sync, end_capture_sync); #ifdef _WIN32 - static encoder_t nvenc { + encoder_t nvenc { "nvenc"sv, std::make_unique( platf::mem_type_e::dxgi, @@ -630,7 +487,7 @@ namespace video { PARALLEL_ENCODING | REF_FRAMES_INVALIDATION // flags }; #elif !defined(__APPLE__) - static encoder_t nvenc { + encoder_t nvenc { "nvenc"sv, std::make_unique( #ifdef _WIN32 @@ -718,7 +575,7 @@ namespace video { #endif #ifdef _WIN32 - static encoder_t quicksync { + encoder_t quicksync { "quicksync"sv, std::make_unique( AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_QSV, @@ -799,7 +656,7 @@ namespace video { PARALLEL_ENCODING | CBR_WITH_VBR | RELAXED_COMPLIANCE | NO_RC_BUF_LIMIT }; - static encoder_t amdvce { + encoder_t amdvce { "amdvce"sv, std::make_unique( AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_NONE, @@ -871,7 +728,7 @@ namespace video { }; #endif - static encoder_t software { + encoder_t software { "software"sv, std::make_unique( AV_HWDEVICE_TYPE_NONE, AV_HWDEVICE_TYPE_NONE, @@ -936,7 +793,7 @@ namespace video { }; #ifdef __linux__ - static encoder_t vaapi { + encoder_t vaapi { "vaapi"sv, std::make_unique( AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_NONE, @@ -1004,7 +861,7 @@ namespace video { #endif #ifdef __APPLE__ - static encoder_t videotoolbox { + encoder_t videotoolbox { "videotoolbox"sv, std::make_unique( AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_HWDEVICE_TYPE_NONE, diff --git a/src/video.h b/src/video.h index fec5c38b343..eb8eabc358d 100644 --- a/src/video.h +++ b/src/video.h @@ -11,11 +11,181 @@ extern "C" { #include +#include } struct AVPacket; namespace video { + platf::mem_type_e + map_base_dev_type(AVHWDeviceType type); + platf::pix_fmt_e + map_pix_fmt(AVPixelFormat fmt); + + void + free_ctx(AVCodecContext *ctx); + void + free_frame(AVFrame *frame); + void + free_buffer(AVBufferRef *ref); + + using avcodec_ctx_t = util::safe_ptr; + using avcodec_frame_t = util::safe_ptr; + using avcodec_buffer_t = util::safe_ptr; + using sws_t = util::safe_ptr; + using img_event_t = std::shared_ptr>>; + + struct encoder_platform_formats_t { + virtual ~encoder_platform_formats_t() = default; + platf::mem_type_e dev_type; + platf::pix_fmt_e pix_fmt_8bit, pix_fmt_10bit; + }; + + struct encoder_platform_formats_avcodec: encoder_platform_formats_t { + using init_buffer_function_t = std::function(platf::avcodec_encode_device_t *)>; + + encoder_platform_formats_avcodec( + const AVHWDeviceType &avcodec_base_dev_type, + const AVHWDeviceType &avcodec_derived_dev_type, + const AVPixelFormat &avcodec_dev_pix_fmt, + const AVPixelFormat &avcodec_pix_fmt_8bit, + const AVPixelFormat &avcodec_pix_fmt_10bit, + const init_buffer_function_t &init_avcodec_hardware_input_buffer_function): + avcodec_base_dev_type { avcodec_base_dev_type }, + avcodec_derived_dev_type { avcodec_derived_dev_type }, + avcodec_dev_pix_fmt { avcodec_dev_pix_fmt }, + avcodec_pix_fmt_8bit { avcodec_pix_fmt_8bit }, + avcodec_pix_fmt_10bit { avcodec_pix_fmt_10bit }, + init_avcodec_hardware_input_buffer { init_avcodec_hardware_input_buffer_function } { + dev_type = map_base_dev_type(avcodec_base_dev_type); + pix_fmt_8bit = map_pix_fmt(avcodec_pix_fmt_8bit); + pix_fmt_10bit = map_pix_fmt(avcodec_pix_fmt_10bit); + } + + AVHWDeviceType avcodec_base_dev_type, avcodec_derived_dev_type; + AVPixelFormat avcodec_dev_pix_fmt; + AVPixelFormat avcodec_pix_fmt_8bit, avcodec_pix_fmt_10bit; + + init_buffer_function_t init_avcodec_hardware_input_buffer; + }; + + struct encoder_platform_formats_nvenc: encoder_platform_formats_t { + encoder_platform_formats_nvenc( + const platf::mem_type_e &dev_type, + const platf::pix_fmt_e &pix_fmt_8bit, + const platf::pix_fmt_e &pix_fmt_10bit) { + encoder_platform_formats_t::dev_type = dev_type; + encoder_platform_formats_t::pix_fmt_8bit = pix_fmt_8bit; + encoder_platform_formats_t::pix_fmt_10bit = pix_fmt_10bit; + } + }; + + struct encoder_t { + std::string_view name; + enum flag_e { + PASSED, // Is supported + REF_FRAMES_RESTRICT, // Set maximum reference frames + CBR, // Some encoders don't support CBR, if not supported --> attempt constant quantatication parameter instead + DYNAMIC_RANGE, // hdr + VUI_PARAMETERS, // AMD encoder with VAAPI doesn't add VUI parameters to SPS + MAX_FLAGS + }; + + static std::string_view + from_flag(flag_e flag) { +#define _CONVERT(x) \ + case flag_e::x: \ + std::string_view(#x) + switch (flag) { + _CONVERT(PASSED); + _CONVERT(REF_FRAMES_RESTRICT); + _CONVERT(CBR); + _CONVERT(DYNAMIC_RANGE); + _CONVERT(VUI_PARAMETERS); + _CONVERT(MAX_FLAGS); + } +#undef _CONVERT + + return { "unknown" }; + } + + struct option_t { + KITTY_DEFAULT_CONSTR_MOVE(option_t) + option_t(const option_t &) = default; + + std::string name; + std::variant *, std::function, std::string, std::string *> value; + + option_t(std::string &&name, decltype(value) &&value): + name { std::move(name) }, value { std::move(value) } {} + }; + + const std::unique_ptr platform_formats; + + struct codec_t { + std::vector common_options; + std::vector sdr_options; + std::vector hdr_options; + std::vector fallback_options; + + // QP option to set in the case that CBR/VBR is not supported + // by the encoder. If CBR/VBR is guaranteed to be supported, + // don't specify this option to avoid wasteful encoder probing. + std::optional qp; + + std::string name; + std::bitset capabilities; + + bool + operator[](flag_e flag) const { + return capabilities[(std::size_t) flag]; + } + + std::bitset::reference + operator[](flag_e flag) { + return capabilities[(std::size_t) flag]; + } + } av1, hevc, h264; + + uint32_t flags; + }; + + struct encode_session_t { + virtual ~encode_session_t() = default; + + virtual int + convert(platf::img_t &img) = 0; + + virtual void + request_idr_frame() = 0; + + virtual void + request_normal_frame() = 0; + + virtual void + invalidate_ref_frames(int64_t first_frame, int64_t last_frame) = 0; + }; + + // encoders + extern encoder_t software; + +#if !defined(__APPLE__) + extern encoder_t nvenc; // available for windows and linux +#endif + +#ifdef _WIN32 + extern encoder_t amdvce; + extern encoder_t quicksync; +#endif + +#ifdef __linux__ + extern encoder_t vaapi; +#endif + +#ifdef __APPLE__ + extern encoder_t videotoolbox; +#endif + struct packet_raw_t { virtual ~packet_raw_t() = default; @@ -154,6 +324,8 @@ namespace video { config_t config, void *channel_data); + bool + validate_encoder(encoder_t &encoder, bool expect_failure); int probe_encoders(); } // namespace video