Ctrl K
ComunidadGuías…

server/src/nvenc/nvenc_base.h

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * @file src/nvenc/nvenc_base.h
 * @brief Declarations for abstract platform-agnostic base of standalone NVENC encoder.
 */
#pragma once

// platform includes
#ifdef _WIN32
  #include <winsock2.h>
#endif

// lib includes
#include <ffnvcodec/nvEncodeAPI.h>

// 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<NV_ENCODE_API_FUNCTION_LIST> 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<uint64_t, uint64_t> last_rfi_range;
      logging::min_max_avg_periodic_logger<double> frame_size_logger = {debug, "NvEnc: encoded frame sizes in kB", ""};
      logging::min_max_avg_periodic_logger<double> 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