Expose APIs for dynamic configuration

- Allow video bitrate and framerate to change on the fly
- A configuration option to automatically adjust the capture size based on
  window attributes from Xserver
- Config option to accept VBV buffer size for h264 CBR mode
This commit is contained in:
PMohanJ 2026-01-26 21:15:07 +05:30
parent 448e1c801d
commit 1882f175a7
4 changed files with 499 additions and 112 deletions

View File

@ -123,12 +123,16 @@ settings.jpeg_quality = 75 # Quality for changed stripes (0-100)
settings.paint_over_jpeg_quality = 90 # Quality for static "paint-over" stripes (0-100)
# --- H.264 Settings ---
settings.h264_crf = 25 # CRF value (0-51, lower is better quality/higher bitrate)
settings.h264_paintover_crf = 18 # CRF for H.264 paintover on static content. Must be lower than h264_crf to activate.
settings.h264_paintover_burst_frames = 5 # Number of high-quality frames to send in a burst when a paintover is triggered.
settings.h264_fullcolor = False # Use I444 (full color) instead of I420 for software encoding
settings.h264_fullframe = True # Encode full frames (required for HW accel) instead of just changed stripes
settings.h264_streaming_mode = False # Bypass all VNC logic and work like a normal video encoder, higher constant CPU usage for fullscreen gaming/videos
settings.h264_crf = 25 # CRF value (0-51, lower is better quality/higher bitrate)
settings.h264_paintover_crf = 18 # CRF for H.264 paintover on static content. Must be lower than h264_crf to activate.
settings.h264_paintover_burst_frames = 5 # Number of high-quality frames to send in a burst when a paintover is triggered.
settings.h264_fullcolor = False # Use I444 (full color) instead of I420 for software encoding
settings.h264_fullframe = True # Encode full frames (required for HW accel) instead of just changed stripes
settings.h264_streaming_mode = False # Bypass all VNC logic and work like a normal video encoder, higher constant CPU usage for fullscreen gaming/videos
settings.h264_cbr_mode = False # Switches to CBR mode and ignores CRF value. Used in conjunction with h264_bitrate_kbps.
settings.h264_bitrate_kbps = 4000 # Target bitrate for CBR mode. Required when h264_cbr_mode is enabled.
settings.h264_vbv_buffer_size_kb = 400 # Optional VBV buffer size in kilobits for custom buffer size.
settings.auto_adjust_screen_capture_size = True # Allow pixelflux to adjust its capture width and height.
# --- Hardware Acceleration ---
# >= 0: Enable GPU Encoding on /dev/dri/renderD(128 + index)

View File

@ -63,6 +63,15 @@ base_capture_settings.h264_fullframe = False
base_capture_settings.h264_streaming_mode = False
# Pass a vaapi node index 0 = renderD128, -1 to disable
base_capture_settings.vaapi_render_node_index = -1
# Switches to CBR mode and ignores CRF value. Used in conjunction with h264_bitrate_kbps.
base_capture_settings.h264_cbr_mode = False
# Target bitrate in kbps for CBR mode. Required when h264_cbr_mode is enabled.
base_capture_settings.h264_bitrate_kbps = 4000
# Optional VBV buffer size in kilobits for custom buffer size.
base_capture_settings.h264_vbv_buffer_size_kb = 400
# Allow pixelflux to adjust its capture width and height. Overrides provided width and height when enabled.
base_capture_settings.auto_adjust_screen_capture_size = True
#
# --- Change Detection & Optimization ---
# Use a higher quality setting for static regions that haven't changed for a while.

View File

@ -30,6 +30,10 @@ class CaptureSettings(ctypes.Structure):
("vaapi_render_node_index", ctypes.c_int),
("use_cpu", ctypes.c_bool),
("debug_logging", ctypes.c_bool),
("h264_cbr_mode", ctypes.c_bool),
("h264_bitrate_kbps", ctypes.c_int),
("h264_vbv_buffer_size_kb", ctypes.c_int),
("auto_adjust_screen_capture_size", ctypes.c_bool),
]
class StripeEncodeResult(ctypes.Structure):
@ -69,9 +73,15 @@ if _legacy_lib:
stop_capture_c.argtypes = [ctypes.c_void_p]
free_stripe_encode_result_data = _legacy_lib.free_stripe_encode_result_data
free_stripe_encode_result_data.argtypes = [ctypes.POINTER(StripeEncodeResult)]
request_idr = _legacy_lib.screen_capture_request_idr
request_idr = _legacy_lib.request_idr
request_idr.argtypes = [ctypes.c_void_p]
update_video_bitrate_c = _legacy_lib.update_video_bitrate
update_video_bitrate_c.argtypes = [ctypes.c_void_p, ctypes.c_int]
update_framerate_c = _legacy_lib.update_framerate
update_framerate_c.argtypes = [ctypes.c_void_p, ctypes.c_double]
update_vbv_buffer_size_c = _legacy_lib.update_vbv_buffer_size
update_vbv_buffer_size_c.argtypes = [ctypes.c_void_p, ctypes.c_int]
_GLOBAL_WAYLAND_BACKEND = None
if os.environ.get("PIXELFLUX_WAYLAND") == "true":
try:
@ -225,3 +235,15 @@ class ScreenCapture:
def request_idr_frame(self):
if self._is_capturing and self._module:
request_idr(self._module)
def update_video_bitrate(self, bitrate):
if self._is_capturing and self._module:
update_video_bitrate_c(self._module, bitrate)
def update_framerate(self, fps):
if self._is_capturing and self._module:
update_framerate_c(self._module, ctypes.c_double(fps))
def update_vbv_buf_size(self, buffer_size):
if self._is_capturing and self._module:
update_vbv_buffer_size_c(self._module, buffer_size)

View File

@ -113,6 +113,8 @@ struct NvencEncoderState {
int initialized_width = 0;
int initialized_height = 0;
int initialized_qp = -1;
bool cbr_mode = false;
int initialized_bitrate_kbps = 0;
NV_ENC_BUFFER_FORMAT initialized_buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED;
CUcontext cuda_context = nullptr;
@ -146,6 +148,8 @@ struct VaapiEncoderState {
int initialized_qp = -1;
bool initialized_is_444 = false;
unsigned int frame_count = 0;
bool initialized_cbr = false;
int initialized_bitrate_kbps = 0;
};
@ -178,6 +182,8 @@ struct MinimalEncoderStore {
std::vector<int> initialized_colorspaces;
std::vector<bool> initialized_full_range_flags;
std::vector<bool> force_idr_flags;
std::vector<bool> initialized_cbr_flags;
std::vector<int> initialized_bitrates;
std::mutex store_mutex;
/**
@ -198,6 +204,8 @@ struct MinimalEncoderStore {
initialized_colorspaces.resize(new_size, 0);
initialized_full_range_flags.resize(new_size, false);
force_idr_flags.resize(new_size, false);
initialized_cbr_flags.resize(new_size, false);
initialized_bitrates.resize(new_size, 0);
}
}
@ -224,6 +232,8 @@ struct MinimalEncoderStore {
initialized_colorspaces.clear();
initialized_full_range_flags.clear();
force_idr_flags.clear();
initialized_cbr_flags.clear();
initialized_bitrates.clear();
}
/**
@ -290,6 +300,10 @@ struct CaptureSettings {
int vaapi_render_node_index;
bool use_cpu;
bool debug_logging;
bool h264_cbr_mode;
int h264_bitrate_kbps;
int h264_vbv_buffer_size_kb;
bool auto_adjust_screen_capture_size;
/**
* @brief Default constructor for CaptureSettings.
@ -320,7 +334,11 @@ struct CaptureSettings {
watermark_location_enum(WatermarkLocation::NONE),
vaapi_render_node_index(-1),
use_cpu(false),
debug_logging(false) {}
debug_logging(false),
h264_cbr_mode(false),
h264_bitrate_kbps(4000),
h264_vbv_buffer_size_kb(0),
auto_adjust_screen_capture_size(false) {}
/**
* @brief Parameterized constructor for CaptureSettings.
@ -338,9 +356,21 @@ struct CaptureSettings {
* @param dbd Damage block duration.
* @param om Output mode (JPEG or H.264).
* @param crf H.264 Constant Rate Factor.
* @param h264_po_crf H.264 paint-over CRF.
* @param h264_po_burst H.264 paint-over burst frames.
* @param h264_fc H.264 full color (I444) flag.
* @param h264_ff H.264 full frame encoding flag.
* @param h264_sm H.264 streaming mode flag.
* @param capture_cursor Capture cursor flag.
* @param wm_path Watermark image file path.
* @param wm_loc Watermark location enum.
* @param vaapi_idx VA-API render node index.
* @param use_cpu_flag Force CPU encoding flag.
* @param debug_log Enable debug logging flag.
* @param cbr_mode H.264 CBR mode flag.
* @param bitrate_kbps H.264 CBR bitrate in kbps.
* @param vbv_buffer_size_kb H.264 VBV buffer size in kb.
* @param adjust_size Auto-adjust screen capture size flag.
*/
CaptureSettings(int cw, int ch, int cx, int cy, double fps, int jq,
int pojq, bool upoq, int potf, int dbt, int dbd,
@ -349,7 +379,8 @@ struct CaptureSettings {
bool capture_cursor = false,
const char* wm_path = nullptr,
WatermarkLocation wm_loc = WatermarkLocation::NONE,
int vaapi_idx = -1, bool use_cpu_flag = false, bool debug_log = false)
int vaapi_idx = -1, bool use_cpu_flag = false, bool debug_log = false,
bool cbr_mode = false, int bitrate_kbps = 4000, int vbv_buffer_size_kb = 0, bool adjust_size = false)
: capture_width(cw),
capture_height(ch),
capture_x(cx),
@ -373,7 +404,11 @@ struct CaptureSettings {
watermark_location_enum(wm_loc),
vaapi_render_node_index(vaapi_idx),
use_cpu(use_cpu_flag),
debug_logging(debug_log) {}
debug_logging(debug_log),
h264_cbr_mode(cbr_mode),
h264_bitrate_kbps(bitrate_kbps),
h264_vbv_buffer_size_kb(vbv_buffer_size_kb),
auto_adjust_screen_capture_size(adjust_size) {}
};
/**
@ -652,6 +687,11 @@ StripeEncodeResult encode_stripe_jpeg(
* @param current_crf_setting The H.264 CRF (Constant Rate Factor) to use for encoding.
* @param colorspace_setting An integer indicating input YUV format (420 for I420, 444 for I444).
* @param use_full_range True if full range color should be signaled in VUI, false for limited range.
* @param h264_streaming_mode True to enable streaming mode optimizations.
* @param force_idr True to force the encoder to generate an IDR (key) frame.
* @param is_cbr True to enable Constant Bitrate (CBR) mode, false for CRF mode.
* @param bitrate_kbps Target bitrate in kbps when CBR mode is enabled.
* @param vbv_buffer_size_kb VBV buffer size in kb for CBR mode (0 for auto/default).
* @return A StripeEncodeResult containing the H.264 NAL units, or an empty result on failure.
* The result data includes a custom 10-byte header: type tag (0x04), frame type,
* frame_id (uint16_t), stripe_y_start (uint16_t), width (uint16_t), height (uint16_t),
@ -672,7 +712,10 @@ StripeEncodeResult encode_stripe_h264(
int colorspace_setting,
bool use_full_range,
bool h264_streaming_mode,
bool force_id);
bool force_id,
bool is_cbr,
int bitrate_kbps,
int vbv_buffer_size_kb);
/**
* @brief Calculates a 64-bit XXH3 hash for a stripe of YUV data.
@ -686,6 +729,8 @@ StripeEncodeResult encode_stripe_h264(
* @param height Height of the stripe.
* @param is_i420 True if the YUV format is I420 (chroma planes are half width/height),
* false if I444 (chroma planes are full width/height).
* @param use_fullframe_hashing True to use full-frame hashing (samples every 12th row),
* false to hash every row.
* @return A 64-bit hash value of the stripe data, or 0 on error.
*/
uint64_t calculate_yuv_stripe_hash(const uint8_t* y_plane_stripe_start, int y_stride,
@ -740,6 +785,10 @@ public:
WatermarkLocation watermark_location_internal;
bool use_cpu = false;
bool debug_logging = false;
bool h264_cbr_mode = false;
int h264_bitrate_kbps = 4000;
int h264_vbv_buffer_size_kb = 0;
bool auto_adjust_screen_capture_size = false;
std::atomic<bool> stop_requested;
std::thread capture_thread;
@ -782,10 +831,10 @@ private:
mutable std::mutex watermark_data_mutex_;
void reset_nvenc_encoder();
bool initialize_nvenc_encoder(int width, int height, int target_qp, double fps, bool use_yuv444);
bool initialize_nvenc_encoder(int width, int height, int target_qp, double fps, bool use_yuv444, bool is_cbr, int bitrate_kbps, int vbv_buffer_size_kb);
StripeEncodeResult encode_fullframe_nvenc(int width, int height, const uint8_t* y_plane, int y_stride, const uint8_t* u_plane, int u_stride, const uint8_t* v_plane, int v_stride, bool is_i444, int frame_counter, bool force_idr_frame);
void reset_vaapi_encoder();
bool initialize_vaapi_encoder(int render_node_idx, int width, int height, int qp, bool use_yuv444);
bool initialize_vaapi_encoder(int render_node_idx, int width, int height, int qp, bool use_yuv444, bool is_cbr, int bitrate_kbps, int vbv_buffer_size_kb);
StripeEncodeResult encode_fullframe_vaapi(int width, int height, double fps, const uint8_t* y_plane, int y_stride, const uint8_t* u_plane, int u_stride, const uint8_t* v_plane, int v_stride, bool is_i444, int frame_counter, bool force_idr_frame);
void load_watermark_image();
@ -879,6 +928,11 @@ public:
std::lock_guard<std::mutex> data_lock(watermark_data_mutex_);
watermark_loaded_ = false;
}
h264_cbr_mode = new_settings.h264_cbr_mode;
h264_bitrate_kbps = new_settings.h264_bitrate_kbps;
h264_vbv_buffer_size_kb = new_settings.h264_vbv_buffer_size_kb;
auto_adjust_screen_capture_size = new_settings.auto_adjust_screen_capture_size;
}
/**
@ -893,24 +947,74 @@ public:
capture_width, capture_height, capture_x, capture_y, target_fps,
jpeg_quality, paint_over_jpeg_quality, use_paint_over_quality,
paint_over_trigger_frames, damage_block_threshold,
damage_block_duration, output_mode, h264_crf,
h264_paintover_crf, h264_paintover_burst_frames,
h264_fullcolor, h264_fullframe, h264_streaming_mode, capture_cursor,
watermark_path_internal.c_str(), watermark_location_internal,
vaapi_render_node_index, use_cpu, debug_logging
);
damage_block_duration, output_mode, h264_crf, h264_paintover_crf,
h264_paintover_burst_frames, h264_fullcolor, h264_fullframe, h264_streaming_mode,
capture_cursor, watermark_path_internal.c_str(), watermark_location_internal,
vaapi_render_node_index, use_cpu, debug_logging, h264_cbr_mode, h264_bitrate_kbps,
h264_vbv_buffer_size_kb, auto_adjust_screen_capture_size);
}
void request_idr(){
/**
* @brief Requests the next encoded frame to be an IDR (key) frame.
* This function is thread-safe. It sets flags that will cause the next
* encoded frame to be an IDR frame in the appropriate encoder backend.
*/
void request_idr() {
if (debug_logging) {
const char* backend = use_cpu ? "CPU" : (nvenc_operational ? "NVENC" : (vaapi_operational ? "VAAPI" : "None"));
std::cout << "Request IDR -> " << backend << std::endl;
std::cout << "[pixelflux] Request IDR -> " << backend << std::endl;
}
if (use_cpu) force_next_idr_ = true;
else if (nvenc_operational) nvenc_force_next_idr_ = true;
else if (vaapi_operational) vaapi_force_next_idr_ = true;
}
/**
* @brief Updates the target video bitrate for H.264 CBR mode.
* This function is thread-safe. If CBR mode is not enabled, it does nothing.
* @param bitrate The new target bitrate in kbps.
*/
void update_video_bitrate(int bitrate) {
if (!h264_cbr_mode) return;
if (debug_logging) {
std::cout << "[pixelflux] Updating video bitrate from " << h264_bitrate_kbps << " to " << bitrate << std::endl;
}
std::lock_guard<std::mutex> lock(settings_mutex);
h264_bitrate_kbps = static_cast<int>(std::abs(bitrate));
}
/**
* @brief Updates the VBV buffer size for H.264 CBR mode.
* This function is thread-safe. If CBR mode is not enabled, it does nothing.
* @param vbv_buffer_size_kb The new VBV buffer size in kb
*/
void update_vbv_buffer_size(int vbv_buffer_size_kb) {
if (!h264_cbr_mode) return;
if (debug_logging) {
std::cout << "[pixelflux] Updating VBV buffer size from " << h264_vbv_buffer_size_kb << " to " << vbv_buffer_size_kb << std::endl;
}
std::lock_guard<std::mutex> lock(settings_mutex);
h264_vbv_buffer_size_kb = static_cast<int>(std::abs(vbv_buffer_size_kb));
}
/**
* @brief Updates the target framerate for video encoding.
* This function is thread-safe.
* @param fps The new target frames per second.
*/
void update_framerate(double fps) {
if (debug_logging) {
std::cout << "[pixelflux] Updating video framerate from " << target_fps << " to " << fps << std::endl;
}
std::lock_guard<std::mutex> lock(settings_mutex);
target_fps = static_cast<double>(std::abs(fps));
}
};
/**
@ -1012,20 +1116,26 @@ void ScreenCaptureModule::reset_nvenc_encoder() {
* This function is thread-safe. It sets up a new encoder session if one
* is not already active. If an active session exists with the correct
* dimensions and color format, it attempts a lightweight reconfiguration for
* the target QP (quality). If dimensions or color format have changed, it
* the target QP (quality) or bitrate. If dimensions or color format have changed, it
* performs a full teardown and re-initialization.
* @param width The target encoding width.
* @param height The target encoding height.
* @param target_qp The target Quantization Parameter (lower is higher quality).
* @param target_qp The target Quantization Parameter (lower is higher quality) for CRF mode.
* @param fps The target frames per second, used for rate control hints.
* @param use_yuv444 True to configure for YUV 4:4:4, false for NV12 (4:2:0).
* @param is_cbr True to enable Constant Bitrate (CBR) mode, false for CRF mode.
* @param bitrate_kbps Target bitrate in kbps when CBR mode is enabled.
* @param vbv_buffer_size_kb VBV buffer size in kb for CBR mode (0 for auto/default).
* @return True if the encoder is successfully initialized or reconfigured, false otherwise.
*/
bool ScreenCaptureModule::initialize_nvenc_encoder(int width,
int height,
int target_qp,
double fps,
bool use_yuv444) {
bool use_yuv444,
bool is_cbr,
int bitrate_kbps,
int vbv_buffer_size_kb) {
std::lock_guard<std::mutex> lock(nvenc_mutex_);
NV_ENC_BUFFER_FORMAT target_buffer_format =
@ -1034,35 +1144,65 @@ bool ScreenCaptureModule::initialize_nvenc_encoder(int width,
if (nvenc_state_.initialized && nvenc_state_.initialized_width == width &&
nvenc_state_.initialized_height == height &&
nvenc_state_.initialized_qp == target_qp &&
nvenc_state_.initialized_buffer_format == target_buffer_format) {
nvenc_state_.initialized_buffer_format == target_buffer_format &&
nvenc_state_.cbr_mode == is_cbr &&
nvenc_state_.initialized_bitrate_kbps == bitrate_kbps) {
return true;
}
if (nvenc_state_.initialized && nvenc_state_.initialized_width == width &&
nvenc_state_.initialized_height == height &&
nvenc_state_.initialized_buffer_format == target_buffer_format) {
nvenc_state_.initialized_buffer_format == target_buffer_format &&
nvenc_state_.cbr_mode == is_cbr) {
NV_ENC_RECONFIGURE_PARAMS reconfigure_params = {0};
NV_ENC_CONFIG new_config = nvenc_state_.encode_config;
bool reconfig_needed = false;
if (is_cbr) {
if (nvenc_state_.initialized_bitrate_kbps != bitrate_kbps) reconfig_needed = true;
} else {
if (nvenc_state_.initialized_qp != target_qp) reconfig_needed = true;
}
reconfigure_params.version = NV_ENC_RECONFIGURE_PARAMS_VER;
reconfigure_params.reInitEncodeParams = nvenc_state_.init_params;
reconfigure_params.reInitEncodeParams.encodeConfig = &new_config;
new_config.rcParams.constQP.qpInterP = target_qp;
new_config.rcParams.constQP.qpIntra = target_qp;
new_config.rcParams.constQP.qpInterB = target_qp;
bool is_quality_increasing = (target_qp < nvenc_state_.initialized_qp);
reconfigure_params.forceIDR = is_quality_increasing;
if (reconfig_needed) {
NV_ENC_RECONFIGURE_PARAMS reconfigure_params = {0};
NV_ENC_CONFIG new_config = nvenc_state_.encode_config;
NVENCSTATUS status = nvenc_state_.nvenc_funcs.nvEncReconfigureEncoder(
reconfigure_params.version = NV_ENC_RECONFIGURE_PARAMS_VER;
reconfigure_params.reInitEncodeParams = nvenc_state_.init_params;
reconfigure_params.reInitEncodeParams.encodeConfig = &new_config;
if (is_cbr) {
new_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;
uint32_t bps = static_cast<uint32_t>(bitrate_kbps * 1000);
new_config.rcParams.averageBitRate = bps;
new_config.rcParams.maxBitRate = bps;
if (vbv_buffer_size_kb > 0) {
new_config.rcParams.vbvBufferSize = static_cast<uint32_t>(vbv_buffer_size_kb * 1000);
} else {
new_config.rcParams.vbvBufferSize = (bps + 9) / 10;
}
reconfigure_params.forceIDR = false;
} else {
new_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
new_config.rcParams.constQP.qpInterP = target_qp;
new_config.rcParams.constQP.qpIntra = target_qp;
new_config.rcParams.constQP.qpInterB = target_qp;
bool is_quality_increasing = (target_qp < nvenc_state_.initialized_qp);
reconfigure_params.forceIDR = is_quality_increasing;
}
NVENCSTATUS status = nvenc_state_.nvenc_funcs.nvEncReconfigureEncoder(
nvenc_state_.encoder_session, &reconfigure_params);
if (status == NV_ENC_SUCCESS) {
if (status == NV_ENC_SUCCESS) {
nvenc_state_.initialized_qp = target_qp;
nvenc_state_.cbr_mode = is_cbr;
nvenc_state_.initialized_bitrate_kbps = bitrate_kbps;
nvenc_state_.encode_config = new_config;
return true;
}
} else {
return true;
}
}
@ -1175,16 +1315,30 @@ bool ScreenCaptureModule::initialize_nvenc_encoder(int width,
nvenc_state_.encode_config.profileGUID =
use_yuv444 ? NV_ENC_H264_PROFILE_HIGH_444_GUID : NV_ENC_H264_PROFILE_HIGH_GUID;
nvenc_state_.encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
nvenc_state_.encode_config.rcParams.constQP.qpInterP = target_qp;
nvenc_state_.encode_config.rcParams.constQP.qpIntra = target_qp;
nvenc_state_.encode_config.rcParams.constQP.qpInterB = target_qp;
if (is_cbr) {
nvenc_state_.encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;
uint32_t bps = static_cast<uint32_t>(bitrate_kbps * 1000);
nvenc_state_.encode_config.rcParams.averageBitRate = bps;
nvenc_state_.encode_config.rcParams.maxBitRate = bps;
if (vbv_buffer_size_kb > 0) {
nvenc_state_.encode_config.rcParams.vbvBufferSize = static_cast<uint32_t>(vbv_buffer_size_kb * 1000);
} else {
nvenc_state_.encode_config.rcParams.vbvBufferSize = bps * 0.1;
}
} else {
nvenc_state_.encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
nvenc_state_.encode_config.rcParams.constQP.qpInterP = target_qp;
nvenc_state_.encode_config.rcParams.constQP.qpIntra = target_qp;
nvenc_state_.encode_config.rcParams.constQP.qpInterB = target_qp;
}
nvenc_state_.encode_config.gopLength = NVENC_INFINITE_GOPLENGTH;
nvenc_state_.encode_config.frameIntervalP = 1;
NV_ENC_CONFIG_H264* h264_cfg = &nvenc_state_.encode_config.encodeCodecConfig.h264Config;
h264_cfg->chromaFormatIDC = use_yuv444 ? 3 : 1;
h264_cfg->h264VUIParameters.videoFullRangeFlag = use_yuv444 ? 1 : 0;
h264_cfg->repeatSPSPPS = 1;
nvenc_state_.init_params.encodeConfig = &nvenc_state_.encode_config;
status = nvenc_state_.nvenc_funcs.nvEncInitializeEncoder(nvenc_state_.encoder_session,
@ -1239,6 +1393,8 @@ bool ScreenCaptureModule::initialize_nvenc_encoder(int width,
nvenc_state_.initialized_height = height;
nvenc_state_.initialized_qp = target_qp;
nvenc_state_.initialized_buffer_format = target_buffer_format;
nvenc_state_.cbr_mode = is_cbr;
nvenc_state_.initialized_bitrate_kbps = bitrate_kbps;
nvenc_state_.initialized = true;
return true;
}
@ -1432,14 +1588,22 @@ void ScreenCaptureModule::reset_vaapi_encoder() {
* @param qp The target Quantization Parameter for Constant QP (CQP) rate control.
* @param use_yuv444 If true, configures the encoder for YUV 4:4:4 input;
* otherwise, configures for YUV 4:2:0 (NV12).
* @param is_cbr True to enable Constant Bitrate (CBR) mode, false for CQP mode.
* @param bitrate_kbps Target bitrate in kbps when CBR mode is enabled.
* @param vbv_buffer_size_kb VBV buffer size in kb for CBR mode (0 for auto/default).
* @return True if the encoder was successfully initialized, false otherwise.
*/
bool ScreenCaptureModule::initialize_vaapi_encoder(int render_node_idx, int width, int height, int qp, bool use_yuv444) {
bool ScreenCaptureModule::initialize_vaapi_encoder(int render_node_idx, int width, int height, int qp, bool use_yuv444, bool is_cbr, int bitrate_kbps, int vbv_buffer_size_kb) {
std::unique_lock<std::mutex> lock(vaapi_mutex_);
if (vaapi_state_.initialized && vaapi_state_.initialized_width == width &&
vaapi_state_.initialized_height == height && vaapi_state_.initialized_qp == qp &&
vaapi_state_.initialized_is_444 == use_yuv444) {
return true;
vaapi_state_.initialized_height == height &&
vaapi_state_.initialized_is_444 == use_yuv444 &&
vaapi_state_.initialized_cbr == is_cbr) {
if (is_cbr) {
if (vaapi_state_.initialized_bitrate_kbps == bitrate_kbps) return true;
} else {
if (vaapi_state_.initialized_qp == qp) return true;
}
}
if (vaapi_state_.initialized) {
lock.unlock();
@ -1485,8 +1649,23 @@ bool ScreenCaptureModule::initialize_vaapi_encoder(int render_node_idx, int widt
} else {
av_opt_set_int(vaapi_state_.codec_ctx, "profile", FF_PROFILE_H264_HIGH, 0);
}
av_opt_set(vaapi_state_.codec_ctx->priv_data, "rc_mode", "CQP", 0);
av_opt_set_int(vaapi_state_.codec_ctx->priv_data, "qp", qp, 0);
if (is_cbr) {
av_opt_set(vaapi_state_.codec_ctx->priv_data, "rc_mode", "CBR", 0);
int64_t bps = static_cast<int64_t>(bitrate_kbps) * 1000;
vaapi_state_.codec_ctx->bit_rate = bps;
vaapi_state_.codec_ctx->rc_max_rate = bps;
if (vbv_buffer_size_kb > 0) {
vaapi_state_.codec_ctx->rc_buffer_size = static_cast<int64_t>(vbv_buffer_size_kb) * 1000;
} else {
vaapi_state_.codec_ctx->rc_buffer_size = (bps + 9) / 10;
}
vaapi_state_.codec_ctx->rc_min_rate = bps;
} else {
av_opt_set(vaapi_state_.codec_ctx->priv_data, "rc_mode", "CQP", 0);
av_opt_set_int(vaapi_state_.codec_ctx->priv_data, "qp", qp, 0);
}
vaapi_state_.hw_frames_ctx = av_hwframe_ctx_alloc(vaapi_state_.hw_device_ctx);
if (!vaapi_state_.hw_frames_ctx) {
std::cerr << "VAAPI_INIT: Failed to create hardware frames context." << std::endl;
@ -1526,10 +1705,12 @@ bool ScreenCaptureModule::initialize_vaapi_encoder(int render_node_idx, int widt
vaapi_state_.initialized_qp = qp;
vaapi_state_.initialized_is_444 = use_yuv444;
vaapi_state_.frame_count = 0;
vaapi_state_.initialized_cbr = is_cbr;
vaapi_state_.initialized_bitrate_kbps = bitrate_kbps;
if (debug_logging) {
std::cout << "VAAPI_INIT: Encoder initialized successfully via FFmpeg for "
<< width << "x" << height << " " << (use_yuv444 ? "YUV444P" : "NV12")
<< " with QP " << qp << "." << std::endl;
<< (is_cbr ? "with CBR:" + std::to_string(bitrate_kbps) : " with QP: " + std::to_string(qp)) << "." << std::endl;
}
return true;
}
@ -1815,6 +1996,10 @@ void ScreenCaptureModule::capture_loop() {
WatermarkLocation local_watermark_location_setting;
bool local_use_cpu;
bool local_debug_logging;
bool local_current_h264_cbr_mode;
int local_current_h264_bitrate_kbps;
int local_current_h264_vbv_buffer_size_kb;
bool local_current_auto_adjust_screen_capture_size;
{
std::lock_guard<std::mutex> lock(settings_mutex);
@ -1842,6 +2027,10 @@ void ScreenCaptureModule::capture_loop() {
local_debug_logging = debug_logging;
local_watermark_path_setting = watermark_path_internal;
local_watermark_location_setting = watermark_location_internal;
local_current_h264_cbr_mode = h264_cbr_mode;
local_current_h264_bitrate_kbps = h264_bitrate_kbps;
local_current_h264_vbv_buffer_size_kb = h264_vbv_buffer_size_kb;
local_current_auto_adjust_screen_capture_size = auto_adjust_screen_capture_size;
}
if (local_current_output_mode == OutputMode::H264) {
if (local_capture_width_actual % 2 != 0 && local_capture_width_actual > 0) {
@ -1861,14 +2050,22 @@ void ScreenCaptureModule::capture_loop() {
if (!local_use_cpu && local_vaapi_render_node_index >= 0 &&
local_current_output_mode == OutputMode::H264 && local_current_h264_fullframe) {
if (this->initialize_vaapi_encoder(local_vaapi_render_node_index, local_capture_width_actual,
local_capture_height_actual, local_current_h264_crf, local_current_h264_fullcolor)) {
if (this->initialize_vaapi_encoder(local_vaapi_render_node_index,
local_capture_width_actual,
local_capture_height_actual,
local_current_h264_crf,
local_current_h264_fullcolor,
local_current_h264_cbr_mode,
local_current_h264_bitrate_kbps,
local_current_h264_vbv_buffer_size_kb)) {
this->vaapi_operational = true;
this->vaapi_force_next_idr_ = true;
std::cout << "VAAPI Encoder Initialized successfully." << std::endl;
} else {
std::cerr << "VAAPI Encoder initialization failed. Falling back to CPU." << std::endl;
local_use_cpu = true;
std::lock_guard<std::mutex> lock(settings_mutex);
this->use_cpu = true;
}
} else {
if (!local_use_cpu && this->is_nvidia_system_detected &&
@ -1877,13 +2074,18 @@ void ScreenCaptureModule::capture_loop() {
local_capture_height_actual,
local_current_h264_crf,
local_current_target_fps,
local_current_h264_fullcolor)) {
local_current_h264_fullcolor,
local_current_h264_cbr_mode,
local_current_h264_bitrate_kbps,
local_current_h264_vbv_buffer_size_kb)) {
this->nvenc_operational = true;
this->nvenc_force_next_idr_ = true;
std::cout << "NVENC Encoder Initialized successfully." << std::endl;
} else {
std::cerr << "NVENC Encoder initialization failed. Falling back to x264." << std::endl;
local_use_cpu = true;
std::lock_guard<std::mutex> lock(settings_mutex);
this->use_cpu = true;
}
} else {
if (!this->nvenc_operational && this->nvenc_state_.initialized) {
@ -1892,6 +2094,61 @@ void ScreenCaptureModule::capture_loop() {
}
}
std::chrono::duration < double > target_frame_duration_seconds =
std::chrono::duration < double > (1.0 / local_current_target_fps);
auto next_frame_time =
std::chrono::high_resolution_clock::now() + target_frame_duration_seconds;
const int MAX_ATTACH_ATTEMPTS = 5;
const int RETRY_BACKOFF_MS = 500;
char* display_env = std::getenv("DISPLAY");
const char* display_name = display_env ? display_env : ":0";
Display* display = XOpenDisplay(display_name);
if (!display) {
std::cerr << "Error: Failed to open X display " << display_name << std::endl;
return;
}
Window root_window = DefaultRootWindow(display);
int screen = DefaultScreen(display);
XWindowAttributes attributes;
if (XGetWindowAttributes(display, root_window, &attributes)) {
if (local_current_auto_adjust_screen_capture_size) {
std::cout << "[pixelflux] auto_adjust_screen_capture_size is enabled, ignoring requested capture size "
<< local_capture_width_actual << "x" << local_capture_height_actual
<< " and resetting x and y offset to 0" << std::endl;
local_capture_width_actual = attributes.width;
local_capture_height_actual = attributes.height;
local_capture_x_offset = 0;
local_capture_y_offset = 0;
std::lock_guard<std::mutex> lock(settings_mutex);
this->capture_width = attributes.width;
this->capture_height = attributes.height;
} else {
if (local_capture_width_actual > attributes.width) {
local_capture_width_actual = attributes.width;
local_capture_x_offset = 0;
}
if (local_capture_height_actual > attributes.height) {
local_capture_height_actual = attributes.height;
local_capture_y_offset = 0;
}
if (local_capture_x_offset + local_capture_width_actual > attributes.width) {
local_capture_x_offset = attributes.width - local_capture_width_actual;
}
if (local_capture_y_offset + local_capture_height_actual > attributes.height) {
local_capture_y_offset = attributes.height - local_capture_height_actual;
}
if (local_capture_x_offset < 0) local_capture_x_offset = 0;
if (local_capture_y_offset < 0) local_capture_y_offset = 0;
}
}
this->yuv_planes_are_i444_ = local_current_h264_fullcolor;
if (local_current_output_mode == OutputMode::H264) {
bool use_nv12_planes = !local_use_cpu && local_current_h264_fullframe && !local_current_h264_fullcolor &&
@ -1931,46 +2188,6 @@ void ScreenCaptureModule::capture_loop() {
load_watermark_image();
}
std::chrono::duration < double > target_frame_duration_seconds =
std::chrono::duration < double > (1.0 / local_current_target_fps);
auto next_frame_time =
std::chrono::high_resolution_clock::now() + target_frame_duration_seconds;
const int MAX_ATTACH_ATTEMPTS = 5;
const int RETRY_BACKOFF_MS = 500;
char* display_env = std::getenv("DISPLAY");
const char* display_name = display_env ? display_env : ":0";
Display* display = XOpenDisplay(display_name);
if (!display) {
std::cerr << "Error: Failed to open X display " << display_name << std::endl;
return;
}
Window root_window = DefaultRootWindow(display);
int screen = DefaultScreen(display);
XWindowAttributes attributes;
if (XGetWindowAttributes(display, root_window, &attributes)) {
if (local_capture_width_actual > attributes.width) {
local_capture_width_actual = attributes.width;
local_capture_x_offset = 0;
}
if (local_capture_height_actual > attributes.height) {
local_capture_height_actual = attributes.height;
local_capture_y_offset = 0;
}
if (local_capture_x_offset + local_capture_width_actual > attributes.width) {
local_capture_x_offset = attributes.width - local_capture_width_actual;
}
if (local_capture_y_offset + local_capture_height_actual > attributes.height) {
local_capture_y_offset = attributes.height - local_capture_height_actual;
}
if (local_capture_x_offset < 0) local_capture_x_offset = 0;
if (local_capture_y_offset < 0) local_capture_y_offset = 0;
}
if (!XShmQueryExtension(display)) {
std::cerr << "Error: X Shared Memory Extension not available!" << std::endl;
XCloseDisplay(display);
@ -2108,7 +2325,8 @@ void ScreenCaptureModule::capture_loop() {
settings_ss << " | Mode: H264 (" << encoder_type << ")";
settings_ss << (local_current_h264_fullframe ? " FullFrame" : " Striped");
if (local_current_h264_streaming_mode) settings_ss << " Streaming";
settings_ss << " | CRF: " << local_current_h264_crf;
if (!local_current_h264_cbr_mode) settings_ss << " | CRF: " << local_current_h264_crf;
else settings_ss << " | CBR: " << local_current_h264_bitrate_kbps;
if (local_current_use_paint_over_quality) {
settings_ss << " | PaintOver CRF: " << local_current_h264_paintover_crf
<< " (Burst: " << local_current_h264_paintover_burst_frames << "f)";
@ -2191,6 +2409,10 @@ void ScreenCaptureModule::capture_loop() {
local_watermark_location_setting = watermark_location_internal;
local_use_cpu = use_cpu;
local_debug_logging = debug_logging;
local_current_h264_cbr_mode = h264_cbr_mode;
local_current_h264_bitrate_kbps = h264_bitrate_kbps;
local_current_h264_vbv_buffer_size_kb = h264_vbv_buffer_size_kb;
local_current_auto_adjust_screen_capture_size = auto_adjust_screen_capture_size;
}
bool current_watermark_is_actually_loaded_in_loop;
@ -2231,6 +2453,32 @@ void ScreenCaptureModule::capture_loop() {
continue;
}
if (local_current_auto_adjust_screen_capture_size) {
XWindowAttributes attributes;
if (XGetWindowAttributes(display, root_window, &attributes)) {
if (local_capture_width_actual != attributes.width ||
local_capture_height_actual != attributes.height) {
if (debug_logging) {
std::cout << "[pixelflux] Auto-adjusting capture size from "
<< local_capture_width_actual << "x"
<< local_capture_height_actual << " to "
<< attributes.width << "x"
<< attributes.height << std::endl;
}
local_capture_width_actual = attributes.width;
local_capture_height_actual = attributes.height;
local_capture_x_offset = 0;
local_capture_y_offset = 0;
std::lock_guard<std::mutex> lock(settings_mutex);
capture_width = local_capture_width_actual;
capture_height = local_capture_height_actual;
capture_x = 0;
capture_y = 0;
}
}
}
if (old_w != local_capture_width_actual || old_h != local_capture_height_actual ||
yuv_config_changed) {
std::cout << "Capture parameters changed. Re-initializing XShm and YUV planes."
@ -2780,7 +3028,14 @@ void ScreenCaptureModule::capture_loop() {
if (local_current_use_paint_over_quality && this->nvenc_force_next_idr_) {
target_qp_for_frame = local_current_h264_paintover_crf;
}
if (!this->initialize_nvenc_encoder(local_capture_width_actual, local_capture_height_actual, target_qp_for_frame, local_current_target_fps, local_current_h264_fullcolor)) {
if (!this->initialize_nvenc_encoder(local_capture_width_actual,
local_capture_height_actual,
target_qp_for_frame,
local_current_target_fps,
local_current_h264_fullcolor,
local_current_h264_cbr_mode,
local_current_h264_bitrate_kbps,
local_current_h264_vbv_buffer_size_kb)) {
std::cerr << "NVENC: Re-initialization for QP change failed. Disabling NVENC." << std::endl;
this->nvenc_operational = false;
this->reset_nvenc_encoder();
@ -2820,8 +3075,8 @@ void ScreenCaptureModule::capture_loop() {
bool force_idr = this->force_next_idr_.exchange(false);
std::packaged_task<StripeEncodeResult(
MinimalEncoderStore&, int, int, int, int, const uint8_t*, int,
const uint8_t*, int, const uint8_t*, int, bool, int, int, int, bool,
bool, bool)>
const uint8_t*, int, const uint8_t*, int, bool, int, int, int,
bool, int, bool, bool, int, int)>
task(encode_stripe_h264);
futures.push_back(task.get_future());
threads.push_back(std::thread(
@ -2836,7 +3091,10 @@ void ScreenCaptureModule::capture_loop() {
derived_h264_colorspace_setting,
derived_h264_use_full_range,
local_current_h264_streaming_mode,
force_idr
force_idr,
local_current_h264_cbr_mode,
local_current_h264_bitrate_kbps,
local_current_h264_vbv_buffer_size_kb
));
}
}
@ -2927,9 +3185,11 @@ void ScreenCaptureModule::capture_loop() {
(local_current_h264_fullframe ? " FF" : " Striped"))
: std::string(""))
<< " Stripes: " << N_processing_stripes
<< (local_current_output_mode == OutputMode::H264
? " CRF:" + std::to_string(local_current_h264_crf)
: " Q:" + std::to_string(local_current_jpeg_quality))
<< (local_current_output_mode == OutputMode::H264 && local_current_h264_cbr_mode ?
" CBR:" + std::to_string(local_current_h264_bitrate_kbps):
(local_current_output_mode == OutputMode::H264
? " CRF" + std::to_string(local_current_h264_crf)
: " Q:" + std::to_string(local_current_jpeg_quality)))
<< " EncFPS: " << std::fixed << std::setprecision(2) << actual_fps_val
<< " EncStripes/s: " << std::fixed << std::setprecision(2)
<< total_stripes_per_second_val
@ -3092,7 +3352,7 @@ StripeEncodeResult encode_stripe_jpeg(
*
* Manages a thread-specific x264 encoder instance from the provided encoder store.
* The encoder is re-initialized if input parameters
* such as resolution or colorspace change. The CRF can be reconfigured
* such as resolution or colorspace change. The CRF or bitrate can be reconfigured
* between frames without a full re-initialization.
*
* The output NAL units are packaged into a StripeEncodeResult with a custom
@ -3112,9 +3372,14 @@ StripeEncodeResult encode_stripe_jpeg(
* @param v_stride Stride in bytes for the V plane.
* @param is_i444_input `true` for I444 colorspace, `false` for I420.
* @param frame_counter The frame number, used to set the picture's PTS.
* @param current_crf_setting The target Constant Rate Factor (CRF).
* @param current_crf_setting The target Constant Rate Factor (CRF) for CRF mode.
* @param colorspace_setting Integer representing the colorspace (444 or 420).
* @param use_full_range If `true`, signals full-range color in the VUI.
* @param h264_streaming_mode If `true`, enables streaming mode optimizations.
* @param force_idr If `true`, forces the encoder to generate an IDR frame.
* @param is_cbr If `true`, enables Constant Bitrate (CBR) mode.
* @param h264_bitrate_kbps Target bitrate in kbps for CBR mode.
* @param vbv_buffer_size_kb VBV buffer size in kb for CBR mode (0 for auto/default).
* @return A `StripeEncodeResult` containing the encoded bitstream.
* The `data` buffer is dynamically allocated and must be
* freed by the caller.
@ -3134,7 +3399,10 @@ StripeEncodeResult encode_stripe_h264(
int colorspace_setting,
bool use_full_range,
bool h264_streaming_mode,
bool force_idr) {
bool force_idr,
bool is_cbr,
int h264_bitrate_kbps,
int vbv_buffer_size_kb) {
StripeEncodeResult result;
result.type = StripeDataType::H264;
@ -3193,14 +3461,21 @@ StripeEncodeResult encode_stripe_h264(
colorspace_setting ||
h264_minimal_store.initialized_full_range_flags[thread_id] !=
use_full_range);
bool rc_changed = !is_first_init && h264_minimal_store.initialized_cbr_flags[thread_id] != is_cbr;
bool needs_crf_reinit = false;
if (!is_first_init &&
h264_minimal_store.initialized_crfs[thread_id] != current_crf_setting) {
needs_crf_reinit = true;
}
bool needs_cbr_reinit = false;
if (!is_first_init && is_cbr && h264_minimal_store.initialized_bitrates[thread_id] != h264_bitrate_kbps) {
needs_cbr_reinit = true;
}
bool perform_full_reinit = is_first_init || dims_changed || cs_or_fr_changed;
bool perform_full_reinit = is_first_init || dims_changed || cs_or_fr_changed || rc_changed;
if (perform_full_reinit) {
if (h264_minimal_store.encoders[thread_id]) {
@ -3221,9 +3496,6 @@ StripeEncodeResult encode_stripe_h264(
param.i_fps_num = 60;
param.i_fps_den = 1;
param.i_keyint_max = X264_KEYINT_MAX_INFINITE;
param.rc.f_rf_constant =
static_cast<float>(std::max(0, std::min(51, current_crf_setting)));
param.rc.i_rc_method = X264_RC_CRF;
param.b_repeat_headers = 1;
param.b_annexb = 1;
param.i_sync_lookahead = 0;
@ -3233,6 +3505,21 @@ StripeEncodeResult encode_stripe_h264(
param.vui.b_fullrange = 0;
param.vui.i_sar_width = 1;
param.vui.i_sar_height = 1;
if (is_cbr) {
param.rc.i_rc_method = X264_RC_ABR;
int abs_bitrate = static_cast<int>(std::abs(h264_bitrate_kbps));
param.rc.i_bitrate = abs_bitrate;
param.rc.i_vbv_max_bitrate = abs_bitrate;
if (vbv_buffer_size_kb > 0) {
param.rc.i_vbv_buffer_size = vbv_buffer_size_kb;
} else {
param.rc.i_vbv_buffer_size = (abs_bitrate + 9) / 10;
}
param.rc.b_filler = 0;
} else {
param.rc.i_rc_method = X264_RC_CRF;
param.rc.f_rf_constant = static_cast<float>(std::max(0, std::min(51, current_crf_setting)));
}
if (param.i_csp == X264_CSP_I444) {
param.vui.i_colorprim = 1;
param.vui.i_transfer = 1;
@ -3259,6 +3546,8 @@ StripeEncodeResult encode_stripe_h264(
h264_minimal_store.initialized_colorspaces[thread_id] = colorspace_setting;
h264_minimal_store.initialized_full_range_flags[thread_id] = use_full_range;
h264_minimal_store.force_idr_flags[thread_id] = true;
h264_minimal_store.initialized_cbr_flags[thread_id] = is_cbr;
h264_minimal_store.initialized_bitrates[thread_id] = static_cast<int>(std::abs(h264_bitrate_kbps));
}
}
} else if (needs_crf_reinit) {
@ -3277,6 +3566,28 @@ StripeEncodeResult encode_stripe_h264(
<< " may persist." << std::endl;
}
}
} else if (needs_cbr_reinit) {
x264_t* encoder_to_reconfig = h264_minimal_store.encoders[thread_id];
if (encoder_to_reconfig) {
x264_param_t params_for_reconfig;
x264_encoder_parameters(encoder_to_reconfig, &params_for_reconfig);
int abs_bitrate = static_cast<int>(std::abs(h264_bitrate_kbps));
params_for_reconfig.rc.i_bitrate = abs_bitrate;
params_for_reconfig.rc.i_vbv_max_bitrate = abs_bitrate;
if (vbv_buffer_size_kb > 0) {
params_for_reconfig.rc.i_vbv_buffer_size = vbv_buffer_size_kb;
} else {
params_for_reconfig.rc.i_vbv_buffer_size = static_cast<int>(abs_bitrate * 0.1);
}
if (x264_encoder_reconfig(encoder_to_reconfig, &params_for_reconfig) == 0) {
h264_minimal_store.initialized_bitrates[thread_id] = abs_bitrate;
} else {
std::cerr << "H264 T" << thread_id
<< ": x264_encoder_reconfig for CBR FAILED. Old CBR "
<< h264_minimal_store.initialized_bitrates[thread_id]
<< " may persist." << std::endl;
}
}
}
if (h264_minimal_store.initialized_flags[thread_id]) {
@ -3405,6 +3716,8 @@ StripeEncodeResult encode_stripe_h264(
* @param is_i420 True if the YUV format is I420 (chroma planes are half width and
* half height of the luma plane). False if I444 (chroma planes have
* the same dimensions as the luma plane).
* @param use_fullframe_hashing True to use full-frame hashing (samples every 12th row),
* false to hash every row.
* @return A 64-bit hash value representing the content of the YUV stripe.
* Returns 0 if input parameters are invalid (e.g., null pointers,
* non-positive dimensions).
@ -3541,8 +3854,11 @@ extern "C" {
}
}
// Expose the IDR request logic to the public C API
void screen_capture_request_idr(ScreenCaptureModuleHandle module_handle) {
/**
* @brief Requests an IDR frame from an encoder
* @param moduel_handle Handle to the ScreenCaptureModule instance.
*/
void request_idr(ScreenCaptureModuleHandle module_handle) {
if (module_handle) {
ScreenCaptureModule* module = static_cast<ScreenCaptureModule*>(module_handle);
if (module) {
@ -3551,6 +3867,42 @@ extern "C" {
}
}
/**
* @brief Update video bitrate of an encoder
* @param moduel_handle Handle to the ScreenCaptureModule instance.
* @param bitrate video bitrate to set
*/
void update_video_bitrate(ScreenCaptureModuleHandle module_handle, int bitrate) {
if (module_handle) {
ScreenCaptureModule* module = static_cast<ScreenCaptureModule*>(module_handle);
if (module) module->update_video_bitrate(bitrate);
}
}
/**
* @brief Updates the framerate of the screen capture process.
* @param module_handle Handle to the ScreenCaptureModule instance.
* @param fps The new framerate to set.
*/
void update_framerate(ScreenCaptureModuleHandle module_handle, double fps) {
if (module_handle) {
ScreenCaptureModule* module = static_cast<ScreenCaptureModule*>(module_handle);
if (module) module->update_framerate(fps);
}
}
/**
* @brief Updates the VBV buffer size for H.264 CBR mode.
* @param module_handle Handle to the ScreenCaptureModule instance.
* @param vbv_buffer_size_kbps The new VBV buffer size in kb
*/
void update_vbv_buffer_size(ScreenCaptureModuleHandle module_handle, int vbv_buffer_size_kb) {
if (module_handle) {
ScreenCaptureModule* module = static_cast<ScreenCaptureModule*>(module_handle);
if (module) module->update_vbv_buffer_size(vbv_buffer_size_kb);
}
}
/**
* @brief Stops the screen capture process.
* @param module_handle Handle to the ScreenCaptureModule instance.