From 7b852c87b7df72765b3f4a3d2b7a212b1d8e6e05 Mon Sep 17 00:00:00 2001 From: Kushal Jain <155632770+kushaljain-apra@users.noreply.github.com> Date: Mon, 29 Jul 2024 19:30:00 +0530 Subject: [PATCH] enhance H264EncoderNvCodec by adding a buffer threshold property (#360) * enhance H264EncoderNvCodec by adding a buffer threshold property * add default threshold as a #define * add comments for doxygen documentation --------- Co-authored-by: Yashraj <88146397+yashrajsapra@users.noreply.github.com> --- base/include/H264EncoderNVCodec.h | 58 +++++++++++++++--- base/include/H264EncoderNVCodecHelper.h | 69 ++++++++++++++++++++++ base/src/H264EncoderNVCodec.cpp | 11 +++- base/src/H264EncoderNVCodecHelper.cpp | 78 +++++++++++++++++++++++-- 4 files changed, 201 insertions(+), 15 deletions(-) diff --git a/base/include/H264EncoderNVCodec.h b/base/include/H264EncoderNVCodec.h index cd94f3f93..f81cccfdc 100644 --- a/base/include/H264EncoderNVCodec.h +++ b/base/include/H264EncoderNVCodec.h @@ -3,30 +3,70 @@ #include "Module.h" #include "CudaCommon.h" +/** + * @brief Properties for the H264 encoder using NVCodec. + */ class H264EncoderNVCodecProps : public ModuleProps { public: + /** + * @enum H264CodecProfile + * @brief Enum representing different H.264 codec profiles. + */ enum H264CodecProfile { - BASELINE, - MAIN, - HIGH, + BASELINE, /**< Baseline profile */ + MAIN, /**< Main profile */ + HIGH, /**< High profile */ }; + /** + * @brief Constructor for H264EncoderNVCodecProps with all parameters. + * + * @param _bitRateKbps Bit rate in kilobits per second. + * @param _cuContext CUDA context. + * @param _gopLength Group of Pictures (GOP) length. + * @param _frameRate Frame rate. + * @param _vProfile Video profile from H264CodecProfile enum. + * @param _enableBFrames Enable or disable B-frames. + */ H264EncoderNVCodecProps(const uint32_t &_bitRateKbps, const apracucontext_sp& _cuContext, const uint32_t &_gopLength,const uint32_t &_frameRate,H264CodecProfile _vProfile,bool _enableBFrames) : cuContext(_cuContext), gopLength(_gopLength), frameRate(_frameRate), bitRateKbps(_bitRateKbps), vProfile(_vProfile), enableBFrames(_enableBFrames) { } + + /** + * @brief Constructor for H264EncoderNVCodecProps with default bit rate. + * + * @param _cuContext CUDA context. + */ H264EncoderNVCodecProps(apracucontext_sp& _cuContext) : bitRateKbps(0), cuContext(_cuContext) { + } + /** + * @brief Constructor for H264EncoderNVCodecProps with buffer threshold. + * + * @param _bitRateKbps Bit rate in kilobits per second. + * @param _cuContext CUDA context. + * @param _gopLength Group of Pictures (GOP) length. + * @param _frameRate Frame rate. + * @param _vProfile Video profile from H264CodecProfile enum. + * @param _enableBFrames Enable or disable B-frames. + * @param _bufferThres Buffer threshold. + */ + H264EncoderNVCodecProps(const uint32_t &_bitRateKbps, const apracucontext_sp& _cuContext, const uint32_t &_gopLength,const uint32_t &_frameRate,H264CodecProfile _vProfile,bool _enableBFrames, uint32_t &_bufferThres) + : cuContext(_cuContext), gopLength(_gopLength), frameRate(_frameRate), bitRateKbps(_bitRateKbps), vProfile(_vProfile), enableBFrames(_enableBFrames), bufferThres(_bufferThres) + { } - H264CodecProfile vProfile= H264EncoderNVCodecProps::BASELINE; - bool enableBFrames=false; - uint32_t gopLength = 30; - uint32_t bitRateKbps = 1000; - uint32_t frameRate = 30; - apracucontext_sp cuContext; + + H264CodecProfile vProfile = H264EncoderNVCodecProps::BASELINE; /**< Video profile. */ + bool enableBFrames = false; /**< Enable or disable B-frames. */ + uint32_t gopLength = 30; /**< Group of Pictures (GOP) length. */ + uint32_t bitRateKbps = 1000; /**< Bit rate in kilobits per second. */ + uint32_t frameRate = 30; /**< Frame rate. */ + apracucontext_sp cuContext; /**< CUDA context. */ + uint32_t bufferThres = 30; /**< Buffer threshold. */ }; class H264EncoderNVCodec : public Module diff --git a/base/include/H264EncoderNVCodecHelper.h b/base/include/H264EncoderNVCodecHelper.h index 277c583eb..86268ed0d 100644 --- a/base/include/H264EncoderNVCodecHelper.h +++ b/base/include/H264EncoderNVCodecHelper.h @@ -5,19 +5,88 @@ #include "CommonDefs.h" #include "CudaCommon.h" #include "H264EncoderNVCodec.h" + +/** + * @brief Helper class for H264 encoding using NVCodec. + */ class H264EncoderNVCodecHelper { public: + /** + * @brief Constructor for H264EncoderNVCodecHelper. + * + * @param _bitRateKbps Bit rate in kilobits per second. + * @param _cuContext CUDA context. + * @param _gopLength Group of Pictures (GOP) length. + * @param _frameRate Frame rate. + * @param _profile Video profile from H264EncoderNVCodecProps::H264CodecProfile enum. + * @param enableBFrames Enable or disable B-frames. + */ H264EncoderNVCodecHelper(uint32_t _bitRateKbps, apracucontext_sp& _cuContext, uint32_t _gopLength, uint32_t _frameRate,H264EncoderNVCodecProps::H264CodecProfile _profile, bool enableBFrames); + + /** + * @brief Constructor for H264EncoderNVCodecHelper with buffer threshold. + * + * @param _bitRateKbps Bit rate in kilobits per second. + * @param _cuContext CUDA context. + * @param _gopLength Group of Pictures (GOP) length. + * @param _frameRate Frame rate. + * @param _profile Video profile from H264EncoderNVCodecProps::H264CodecProfile enum. + * @param enableBFrames Enable or disable B-frames. + * @param _bufferThres Buffer threshold. + */ + H264EncoderNVCodecHelper(uint32_t _bitRateKbps, apracucontext_sp& _cuContext, uint32_t _gopLength, uint32_t _frameRate,H264EncoderNVCodecProps::H264CodecProfile _profile, bool enableBFrames, uint32_t _bufferThres); + + /** + * @brief Destructor for H264EncoderNVCodecHelper. + */ ~H264EncoderNVCodecHelper(); + /** + * @brief Initialize the encoder. + * + * @param width Frame width. + * @param height Frame height. + * @param pitch Frame pitch. + * @param imageType Type of the image from ImageMetadata::ImageType. + * @param makeFrame Function to create a frame. + * @param send Function to send a frame. + * @return True if initialization was successful, false otherwise. + */ bool init(uint32_t width, uint32_t height, uint32_t pitch, ImageMetadata::ImageType imageType, std::function makeFrame, std::function send); + /** + * @brief Process a frame. + * + * @param frame Frame to process. + * @return True if the frame was processed successfully, false otherwise. + */ bool process(frame_sp &frame); + + /** + * @brief End encoding. + */ void endEncode(); + + /** + * @brief Get SPS and PPS data. + * + * @param buffer Pointer to buffer to store SPS and PPS data. + * @param size Size of the buffer. + * @param width Width of the frame. + * @param height Height of the frame. + * @return True if SPS and PPS data was retrieved successfully, false otherwise. + */ bool getSPSPPS(void*& buffer, size_t& size, int& width, int& height); private: + /** + * @brief Internal detail class. + */ class Detail; + + /** + * @brief Shared pointer to the internal detail class. + */ boost::shared_ptr mDetail; }; \ No newline at end of file diff --git a/base/src/H264EncoderNVCodec.cpp b/base/src/H264EncoderNVCodec.cpp index 7f8dcc616..9ace6c4e3 100644 --- a/base/src/H264EncoderNVCodec.cpp +++ b/base/src/H264EncoderNVCodec.cpp @@ -13,12 +13,21 @@ #include "nvEncodeAPI.h" #include "Command.h" +#define DEFAULT_BUFFER_THRESHOLD 30 + class H264EncoderNVCodec::Detail { public: Detail(H264EncoderNVCodecProps &_props) : mProps(_props) { - helper.reset(new H264EncoderNVCodecHelper(_props.bitRateKbps, _props.cuContext,_props.gopLength,_props.frameRate,_props.vProfile,_props.enableBFrames)); + if(_props.bufferThres == DEFAULT_BUFFER_THRESHOLD) + { + helper.reset(new H264EncoderNVCodecHelper(_props.bitRateKbps, _props.cuContext,_props.gopLength,_props.frameRate,_props.vProfile,_props.enableBFrames)); + } + else + { + helper.reset(new H264EncoderNVCodecHelper(_props.bitRateKbps, _props.cuContext,_props.gopLength,_props.frameRate,_props.vProfile,_props.enableBFrames,_props.bufferThres)); + } } ~Detail() diff --git a/base/src/H264EncoderNVCodecHelper.cpp b/base/src/H264EncoderNVCodecHelper.cpp index d9415e6ee..c1463b240 100644 --- a/base/src/H264EncoderNVCodecHelper.cpp +++ b/base/src/H264EncoderNVCodecHelper.cpp @@ -31,6 +31,8 @@ static inline bool operator!=(const GUID &guid1, const GUID &guid2) { } #endif +#define DEFAULT_BUFFER_THRESHOLD 30 + #define NVENC_API_CALL( nvencAPI ) \ do \ { \ @@ -204,7 +206,7 @@ class H264EncoderNVCodecHelper::Detail } } public: - Detail(uint32_t& bitRateKbps, apracucontext_sp& cuContext, uint32_t& gopLength, uint32_t& frameRate,H264EncoderNVCodecProps::H264CodecProfile profile,bool enableBFrames) : + Detail(uint32_t& bitRateKbps, apracucontext_sp& cuContext, uint32_t& gopLength, uint32_t& frameRate,H264EncoderNVCodecProps::H264CodecProfile profile,bool enableBFrames, uint32_t& bufferThres) : m_nWidth(0), m_nHeight(0), m_eBufferFormat(NV_ENC_BUFFER_FORMAT_UNDEFINED), @@ -214,7 +216,8 @@ class H264EncoderNVCodecHelper::Detail m_nGopLength(gopLength), m_nFrameRate(frameRate), m_nProfile(profile), - m_bEnableBFrames(enableBFrames) + m_bEnableBFrames(enableBFrames), + m_nBufferThres(bufferThres) { m_nvcodecResources.reset(new NVCodecResources(cuContext)); @@ -472,7 +475,7 @@ class H264EncoderNVCodecHelper::Detail { NVENC_API_CALL(m_nvcodecResources->m_nvenc.nvEncInitializeEncoder(m_nvcodecResources->m_hEncoder, &m_initializeParams)); - m_nEncoderBuffer = m_encodeConfig.frameIntervalP + m_encodeConfig.rcParams.lookaheadDepth + 30; + m_nEncoderBuffer = m_encodeConfig.frameIntervalP + m_encodeConfig.rcParams.lookaheadDepth + DEFAULT_BUFFER_THRESHOLD; m_nvcodecResources->m_nFreeOutputBitstreams = m_nEncoderBuffer; for (int i = 0; i < m_nEncoderBuffer; i++) @@ -572,9 +575,35 @@ class H264EncoderNVCodecHelper::Detail } } + /** + * @brief Checks if there are free output bitstreams available and allocates more if needed. + * + * This function checks the availability of free output bitstreams. If there are no free output bitstreams and the number + * of busy streams is below a predefined threshold, it allocates additional output bitstreams to the encoder buffer. + * + * @details + * The function first checks the number of busy output bitstreams. If there are no free output bitstreams and the number + * of busy streams is below `m_nBufferThres`, it calculates the buffer length to be allocated and doubles the output buffers + * by calling `doubleOutputBuffers`. It then logs the allocation and increments the count of free output bitstreams. + * If there are free output bitstreams, it logs a message indicating the current state. + * + * @return true if there are free output bitstreams available, false otherwise. + * + * @note This function modifies the state of `m_nvcodecResources->m_nFreeOutputBitstreams`. + * @warning Ensure thread safety when calling this function in a multi-threaded environment. + */ + bool is_not_empty() const { - if (!m_nvcodecResources->m_nFreeOutputBitstreams) + uint32_t busyStreams = m_nvcodecResources->m_nBusyOutputBitstreams; + if (!m_nvcodecResources->m_nFreeOutputBitstreams && busyStreams < m_nBufferThres) + { + uint32_t bufferLength = min(busyStreams, m_nBufferThres - busyStreams); + doubleOutputBuffers(bufferLength); + LOG_INFO << "Allocated <" << bufferLength << "> outputbitstreams to the encoder buffer."; + m_nvcodecResources->m_nFreeOutputBitstreams += bufferLength; + } + else { LOG_INFO << "waiting for free outputbitstream<> busy streams<" << m_nvcodecResources->m_nBusyOutputBitstreams << ">"; } @@ -582,11 +611,43 @@ class H264EncoderNVCodecHelper::Detail return m_nvcodecResources->m_nFreeOutputBitstreams > 0; } + /** + * @brief Checks if there are any busy output bitstreams or if the encoder is not running. + * + * This function returns true if there are any busy output bitstreams or if the encoder is not running. + * It helps determine if there are any output bitstreams currently being processed or if the encoding process has stopped. + * + * @return true if there are busy output bitstreams or the encoder is not running, false otherwise. + * + * @note This function does not modify any state. + */ + bool is_output_available() const { return m_nvcodecResources->m_nBusyOutputBitstreams > 0 || !m_bRunning; } + /** + * @brief Allocates additional output bitstream buffers. + * + * This function allocates a specified number of additional output bitstream buffers and adds them to the encoder buffer. + * It uses the NVIDIA Video Codec SDK to create the bitstream buffers and updates the internal queue of output bitstreams. + * + * @param bufferLength The number of additional output bitstream buffers to allocate. + * + * @note This function is called internally by `is_not_empty` when more buffers are needed. + * @warning Ensure that the NVIDIA Video Codec SDK is properly initialized before calling this function. + */ + void doubleOutputBuffers(uint32_t bufferLength) const + { + for (int i = 0; i < bufferLength; i++) + { + NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER }; + NVENC_API_CALL(m_nvcodecResources->m_nvenc.nvEncCreateBitstreamBuffer(m_nvcodecResources->m_hEncoder, &createBitstreamBuffer)); + m_nvcodecResources->m_qBitstreamOutputBitstream.push_back(createBitstreamBuffer.bitstreamBuffer); + } + } + std::thread m_thread; bool m_bRunning; @@ -600,6 +661,7 @@ class H264EncoderNVCodecHelper::Detail uint32_t m_nFrameRate; H264EncoderNVCodecProps::H264CodecProfile m_nProfile; bool m_bEnableBFrames; + uint32_t m_nBufferThres; NV_ENC_INITIALIZE_PARAMS m_initializeParams; NV_ENC_CONFIG m_encodeConfig; @@ -621,7 +683,13 @@ class H264EncoderNVCodecHelper::Detail H264EncoderNVCodecHelper::H264EncoderNVCodecHelper(uint32_t _bitRateKbps, apracucontext_sp& _cuContext, uint32_t _gopLength, uint32_t _frameRate, H264EncoderNVCodecProps::H264CodecProfile _profile, bool enableBFrames) { - mDetail.reset(new Detail(_bitRateKbps, _cuContext,_gopLength,_frameRate,_profile,enableBFrames)); + uint32_t _bufferThres = 30; + mDetail.reset(new Detail(_bitRateKbps, _cuContext,_gopLength,_frameRate,_profile,enableBFrames,_bufferThres)); +} + +H264EncoderNVCodecHelper::H264EncoderNVCodecHelper(uint32_t _bitRateKbps, apracucontext_sp& _cuContext, uint32_t _gopLength, uint32_t _frameRate, H264EncoderNVCodecProps::H264CodecProfile _profile, bool enableBFrames, uint32_t _bufferThres) +{ + mDetail.reset(new Detail(_bitRateKbps, _cuContext,_gopLength,_frameRate,_profile,enableBFrames,_bufferThres)); } H264EncoderNVCodecHelper::~H264EncoderNVCodecHelper()