diff --git a/openh264/src/encoder.rs b/openh264/src/encoder.rs index 56537ae..615ec3c 100644 --- a/openh264/src/encoder.rs +++ b/openh264/src/encoder.rs @@ -5,9 +5,9 @@ use crate::formats::YUVSource; use crate::{Error, OpenH264API, Timestamp}; use openh264_sys2::{ videoFormatI420, ELevelIdc, EProfileIdc, EUsageType, EVideoFormatType, ISVCEncoder, ISVCEncoderVtbl, SEncParamBase, - SEncParamExt, SFrameBSInfo, SLayerBSInfo, SSourcePicture, API, DEBLOCKING_IDC_0, ENCODER_OPTION, ENCODER_OPTION_DATAFORMAT, - ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, ENCODER_OPTION_TRACE_LEVEL, RC_MODES, SM_SINGLE_SLICE, SM_SIZELIMITED_SLICE, - VIDEO_CODING_LAYER, WELS_LOG_DETAIL, WELS_LOG_QUIET, + SEncParamExt, SFrameBSInfo, SLayerBSInfo, SSourcePicture, API, DEBLOCKING_IDC_0, ENCODER_OPTION, ENCODER_OPTION_BITRATE, + ENCODER_OPTION_DATAFORMAT, ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, ENCODER_OPTION_TRACE_LEVEL, RC_MODES, SM_SINGLE_SLICE, + SM_SIZELIMITED_SLICE, VIDEO_CODING_LAYER, WELS_LOG_DETAIL, WELS_LOG_QUIET, }; use std::os::raw::{c_int, c_uchar, c_void}; use std::ptr::{addr_of_mut, from_mut, null, null_mut}; @@ -776,17 +776,17 @@ impl Encoder { } unsafe { - if self.previous_dimensions.is_none() { - // First time we call initialize_ext - self.raw_api.initialize_ext(&raw const params).ok()?; - self.raw_api.set_option(ENCODER_OPTION_TRACE_LEVEL, addr_of_mut!(self.config.debug).cast()).ok()?; - self.raw_api.set_option(ENCODER_OPTION_DATAFORMAT, addr_of_mut!(self.config.data_format).cast()).ok()?; - } else { + if self.is_initialized() { // Subsequent times we call SetOption self.raw_api.set_option(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, addr_of_mut!(params).cast()).ok()?; // Start with a new keyframe after dimensions changed. self.force_intra_frame(); + } else { + // First time we call initialize_ext + self.raw_api.initialize_ext(&raw const params).ok()?; + self.raw_api.set_option(ENCODER_OPTION_TRACE_LEVEL, addr_of_mut!(self.config.debug).cast()).ok()?; + self.raw_api.set_option(ENCODER_OPTION_DATAFORMAT, addr_of_mut!(self.config.data_format).cast()).ok()?; } } @@ -802,6 +802,39 @@ impl Encoder { } } + const fn is_initialized(&self) -> bool { + self.previous_dimensions.is_some() + } + + /// Sets the target bit rate "runtime". + /// + /// If the encoder is already running, this uses the openh264 API to change it without + /// reinitializing the encoder. + /// + /// # Errors + /// + /// This might error for various reasons, many of which aren't clearly documented in OpenH264. + pub fn set_target_bitrate(&mut self, bps: BitRate) -> Result<(), Error> { + if self.config.target_bitrate == bps { + return Ok(()); + } + + self.config.target_bitrate = bps; + + // If raw_api is already initialized, we do a runtime change, otherwise + // rely on the bitrate being set when calling initialize_ext() + if self.is_initialized() { + unsafe { + let mut bps = bps; + self.raw_api + .set_option(ENCODER_OPTION_BITRATE, from_mut(&mut bps.0).cast()) + .ok()?; + } + } + + Ok(()) + } + /// Obtain the raw API for advanced use cases. /// /// When resorting to this call, please consider filing an issue / PR.