/** * @file src/nvenc/nvenc_base.h * @brief Declarations for abstract platform-agnostic base of standalone NVENC encoder. */ #pragma once // platform includes #ifdef _WIN32 #include #endif // lib includes #include // local includes #include "nvenc_colorspace.h" #include "nvenc_config.h" #include "nvenc_encoded_frame.h" #include "src/logging.h" #include "src/video.h" /** * @brief Standalone NVENC encoder */ namespace nvenc { /** * @brief Abstract platform-agnostic base of standalone NVENC encoder. * Derived classes perform platform-specific operations. */ class nvenc_base { public: /** * @param device_type Underlying device type used by derived class. */ explicit nvenc_base(NV_ENC_DEVICE_TYPE device_type); virtual ~nvenc_base(); nvenc_base(const nvenc_base &) = delete; nvenc_base &operator=(const nvenc_base &) = delete; /** * @brief Create the encoder. * @param config NVENC encoder configuration. * @param client_config Stream configuration requested by the client. * @param colorspace YUV colorspace. * @param buffer_format Platform-agnostic input surface format. * @return `true` on success, `false` on error */ bool create_encoder( const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format, const SS_HDR_METADATA *initial_hdr_metadata = nullptr ); /** * @brief Destroy the encoder. * Derived classes classes call it in the destructor. */ bool destroy_encoder(); /** * @brief Encode the next frame using platform-specific input surface. * @param frame_index Frame index that uniquely identifies the frame. * Afterwards serves as parameter for `invalidate_ref_frames()`. * No restrictions on the first frame index, but later frame indexes must be subsequent. * @param force_idr Whether to encode frame as forced IDR. * @return Encoded frame. */ nvenc_encoded_frame encode_frame(uint64_t frame_index, bool force_idr); /** * @brief Perform reference frame invalidation (RFI) procedure. * @param first_frame First frame index of the invalidation range. * @param last_frame Last frame index of the invalidation range. * @return `true` on success, `false` on error. * After error next frame must be encoded with `force_idr = true`. */ bool invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame); /** * @brief Reconfigure the live encoder with a new average/max bitrate (no rebuild). * @param bitrate_kbps New bitrate in kbps. * @return `true` if reconfigured in place; `false` if the caller must rebuild the encoder. */ bool set_bitrate(int bitrate_kbps); /** * @brief Replace HDR mastering-display and content-light metadata for subsequent frames. * @param metadata Metadata in Sunshine's control-channel units. */ void set_hdr_metadata(const SS_HDR_METADATA &metadata); protected: /** Enter/leave a platform device context around complete NVENC API transactions. */ virtual bool enter_context() { return true; } virtual void leave_context() { } /** Native backends may retain the session handle to quarantine dependent resources after teardown failure. */ virtual bool preserve_encoder_on_destroy_failure() const noexcept { return false; } class context_guard_t { public: explicit context_guard_t(nvenc_base &owner): owner(owner), active(owner.enter_context()) { } ~context_guard_t() { if (active) { owner.leave_context(); } } explicit operator bool() const noexcept { return active; } private: nvenc_base &owner; bool active; }; /** * @brief Required. Used for loading NvEnc library and setting `nvenc` variable with `NvEncodeAPICreateInstance()`. * Called during `create_encoder()` if `nvenc` variable is not initialized. * @return `true` on success, `false` on error */ virtual bool init_library(uint32_t api_version) = 0; /** * @brief Required. Used for creating outside-facing input surface, * registering this surface with `nvenc->nvEncRegisterResource()` and setting `registered_input_buffer` variable. * Called during `create_encoder()`. * @return `true` on success, `false` on error */ virtual bool create_and_register_input_buffer() = 0; /** * @brief Optional. Override if you must perform additional operations on the registered input surface in the beginning of `encode_frame()`. * Typically used for interop copy. * @return `true` on success, `false` on error */ virtual bool synchronize_input_buffer() { return true; } /** * @brief Optional. Override if you want to create encoder in async mode. * In this case must also set `async_event_handle` variable. * @param timeout_ms Wait timeout in milliseconds * @return `true` on success, `false` on timeout or error */ virtual bool wait_for_async_event(uint32_t timeout_ms) { return false; } bool nvenc_failed(NVENCSTATUS status); const NV_ENC_DEVICE_TYPE device_type; void *encoder = nullptr; struct { uint32_t width = 0; uint32_t height = 0; NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; int video_format = -1; uint32_t ref_frames_in_dpb = 0; bool rfi = false; } encoder_params; std::string last_nvenc_error_string; // Derived classes set these variables void *device = nullptr; ///< Platform-specific handle of encoding device. ///< Should be set in constructor or `init_library()`. std::shared_ptr nvenc; ///< Function pointers list produced by `NvEncodeAPICreateInstance()`. ///< Should be set in `init_library()`. NV_ENC_REGISTERED_PTR registered_input_buffer = nullptr; ///< Platform-specific input surface registered with `NvEncRegisterResource()`. ///< Should be set in `create_and_register_input_buffer()`. void *async_event_handle = nullptr; ///< (optional) Platform-specific handle of event object event. ///< Can be set in constructor or `init_library()`, must override `wait_for_async_event()`. uint32_t selected_api_version = 0; ///< API version selected after runtime probing. NVENCSTATUS last_nvenc_status = NV_ENC_SUCCESS; private: NV_ENC_OUTPUT_PTR output_bitstream = nullptr; struct { uint64_t last_encoded_frame_index = 0; bool rfi_needs_confirmation = false; std::pair last_rfi_range; logging::min_max_avg_periodic_logger frame_size_logger = {debug, "NvEnc: encoded frame sizes in kB", ""}; logging::min_max_avg_periodic_logger encode_latency_logger = {debug, "NvEnc: native encode call latency", "ms"}; } encoder_state; // Saved during create_encoder() so set_bitrate() can drive nvEncReconfigureEncoder(). // saved_init_params.encodeConfig points at current_enc_config. NV_ENC_INITIALIZE_PARAMS saved_init_params {}; NV_ENC_CONFIG current_enc_config {}; bool hdr_metadata_valid = false; SS_HDR_METADATA hdr_metadata {}; }; } // namespace nvenc