Skip to content

Commit

Permalink
enhance H264EncoderNvCodec by adding a buffer threshold property (#360)
Browse files Browse the repository at this point in the history
* enhance H264EncoderNvCodec by adding a buffer threshold property

* add default threshold as a #define

* add comments for doxygen documentation

---------

Co-authored-by: Yashraj <[email protected]>
  • Loading branch information
kushaljain-apra and yashrajsapra authored Jul 29, 2024
1 parent 19190c3 commit 7b852c8
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 15 deletions.
58 changes: 49 additions & 9 deletions base/include/H264EncoderNVCodec.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
69 changes: 69 additions & 0 deletions base/include/H264EncoderNVCodecHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<frame_sp(size_t)> makeFrame, std::function<void(frame_sp& ,frame_sp&)> 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<Detail> mDetail;
};
11 changes: 10 additions & 1 deletion base/src/H264EncoderNVCodec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
78 changes: 73 additions & 5 deletions base/src/H264EncoderNVCodecHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
{ \
Expand Down Expand Up @@ -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),
Expand All @@ -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));

Expand Down Expand Up @@ -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++)
Expand Down Expand Up @@ -572,21 +575,79 @@ 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 << ">";
}

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;

Expand All @@ -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;
Expand All @@ -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()
Expand Down

0 comments on commit 7b852c8

Please sign in to comment.