use crate::error::NativeErrorExt;
use crate::formats::YUVSource;
use crate::{Error, Timestamp};
use openh264_sys2::{
videoFormatI420, EVideoFormatType, ISVCEncoder, ISVCEncoderVtbl, SEncParamBase, SEncParamExt, SFrameBSInfo, SLayerBSInfo,
SSourcePicture, WelsCreateSVCEncoder, WelsDestroySVCEncoder, ENCODER_OPTION, ENCODER_OPTION_DATAFORMAT,
ENCODER_OPTION_TRACE_LEVEL, RC_MODES, 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, null, null_mut};
#[rustfmt::skip]
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct EncoderRawAPI {
encoder_ptr: *mut *const ISVCEncoderVtbl,
initialize: unsafe extern "C" fn(arg1: *mut ISVCEncoder, pParam: *const SEncParamBase) -> c_int,
initialize_ext: unsafe extern "C" fn(arg1: *mut ISVCEncoder, pParam: *const SEncParamExt) -> c_int,
get_default_params: unsafe extern "C" fn(arg1: *mut ISVCEncoder, pParam: *mut SEncParamExt) -> c_int,
uninitialize: unsafe extern "C" fn(arg1: *mut ISVCEncoder) -> c_int,
encode_frame: unsafe extern "C" fn(arg1: *mut ISVCEncoder, kpSrcPic: *const SSourcePicture, pBsInfo: *mut SFrameBSInfo) -> c_int,
encode_parameter_sets: unsafe extern "C" fn(arg1: *mut ISVCEncoder, pBsInfo: *mut SFrameBSInfo) -> c_int,
force_intra_frame: unsafe extern "C" fn(arg1: *mut ISVCEncoder, bIDR: bool) -> c_int,
set_option: unsafe extern "C" fn(arg1: *mut ISVCEncoder, eOptionId: ENCODER_OPTION, pOption: *mut c_void) -> c_int,
get_option: unsafe extern "C" fn(arg1: *mut ISVCEncoder, eOptionId: ENCODER_OPTION, pOption: *mut c_void) -> c_int,
}
#[rustfmt::skip]
#[allow(clippy::too_many_arguments)]
#[allow(clippy::missing_safety_doc)]
#[allow(non_snake_case)]
#[allow(unused)]
impl EncoderRawAPI {
fn new() -> Result<Self, Error> {
unsafe {
let mut encoder_ptr = null::<ISVCEncoderVtbl>() as *mut *const ISVCEncoderVtbl;
WelsCreateSVCEncoder(&mut encoder_ptr as *mut *mut *const ISVCEncoderVtbl).ok()?;
let e = || {
Error::msg("VTable missing function.")
};
Ok(Self {
encoder_ptr,
initialize: (*(*encoder_ptr)).Initialize.ok_or_else(e)?,
initialize_ext: (*(*encoder_ptr)).InitializeExt.ok_or_else(e)?,
get_default_params: (*(*encoder_ptr)).GetDefaultParams.ok_or_else(e)?,
uninitialize: (*(*encoder_ptr)).Uninitialize.ok_or_else(e)?,
encode_frame: (*(*encoder_ptr)).EncodeFrame.ok_or_else(e)?,
encode_parameter_sets: (*(*encoder_ptr)).EncodeParameterSets.ok_or_else(e)?,
force_intra_frame: (*(*encoder_ptr)).ForceIntraFrame.ok_or_else(e)?,
set_option: (*(*encoder_ptr)).SetOption.ok_or_else(e)?,
get_option: (*(*encoder_ptr)).GetOption.ok_or_else(e)?,
})
}
}
unsafe fn uninitialize(&self) -> c_int { (self.uninitialize)(self.encoder_ptr) }
unsafe fn initialize(&self, pParam: *const SEncParamBase) -> c_int { (self.initialize)(self.encoder_ptr, pParam) }
unsafe fn initialize_ext(&self, pParam: *const SEncParamExt) -> c_int { (self.initialize_ext)(self.encoder_ptr, pParam) }
pub unsafe fn get_default_params(&self, pParam: *mut SEncParamExt) -> c_int { (self.get_default_params)(self.encoder_ptr, pParam) }
pub unsafe fn encode_frame(&self, kpSrcPic: *const SSourcePicture, pBsInfo: *mut SFrameBSInfo) -> c_int { (self.encode_frame)(self.encoder_ptr, kpSrcPic, pBsInfo) }
pub unsafe fn encode_parameter_sets(&self, pBsInfo: *mut SFrameBSInfo) -> c_int { (self.encode_parameter_sets)(self.encoder_ptr, pBsInfo) }
pub unsafe fn force_intra_frame(&self, bIDR: bool) -> c_int { (self.force_intra_frame)(self.encoder_ptr, bIDR) }
pub unsafe fn set_option(&self, eOptionId: ENCODER_OPTION, pOption: *mut c_void) -> c_int { (self.set_option)(self.encoder_ptr, eOptionId, pOption) }
pub unsafe fn get_option(&self, eOptionId: ENCODER_OPTION, pOption: *mut c_void) -> c_int { (self.get_option)(self.encoder_ptr, eOptionId, pOption) }
}
impl Drop for EncoderRawAPI {
fn drop(&mut self) {
unsafe {
WelsDestroySVCEncoder(self.encoder_ptr);
}
}
}
unsafe impl Send for EncoderRawAPI {}
unsafe impl Sync for EncoderRawAPI {}
#[derive(Copy, Clone, Debug)]
pub enum RateControlMode {
Quality,
Bitrate,
Bufferbased,
Timestamp,
BitrateModePostSkip,
Off,
}
impl Default for RateControlMode {
fn default() -> Self {
Self::Quality
}
}
impl RateControlMode {
fn to_c(self) -> RC_MODES {
match self {
RateControlMode::Quality => openh264_sys2::RC_QUALITY_MODE,
RateControlMode::Bitrate => openh264_sys2::RC_BITRATE_MODE,
RateControlMode::Bufferbased => openh264_sys2::RC_BUFFERBASED_MODE,
RateControlMode::Timestamp => openh264_sys2::RC_TIMESTAMP_MODE,
RateControlMode::BitrateModePostSkip => openh264_sys2::RC_BITRATE_MODE_POST_SKIP,
RateControlMode::Off => openh264_sys2::RC_OFF_MODE,
}
}
}
#[derive(Default, Copy, Clone, Debug)]
pub struct EncoderConfig {
width: u32,
height: u32,
enable_skip_frame: bool,
target_bitrate: u32,
enable_denoise: bool,
debug: i32,
data_format: EVideoFormatType,
max_frame_rate: f32,
rate_control_mode: RateControlMode,
}
impl EncoderConfig {
pub fn new(width: u32, height: u32) -> Self {
Self {
width,
height,
enable_skip_frame: true,
target_bitrate: 120_000,
enable_denoise: false,
debug: 0,
data_format: videoFormatI420,
max_frame_rate: 0.0,
rate_control_mode: Default::default(),
}
}
pub fn set_bitrate_bps(mut self, bps: u32) -> Self {
self.target_bitrate = bps;
self
}
pub fn debug(mut self, value: bool) -> Self {
self.debug = if value { WELS_LOG_DETAIL } else { WELS_LOG_QUIET };
self
}
pub fn enable_skip_frame(mut self, value: bool) -> Self {
self.enable_skip_frame = value;
self
}
pub fn max_frame_rate(mut self, value: f32) -> Self {
self.max_frame_rate = value;
self
}
pub fn rate_control_mode(mut self, value: RateControlMode) -> Self {
self.rate_control_mode = value;
self
}
}
pub struct Encoder {
params: SEncParamExt,
raw_api: EncoderRawAPI,
bit_stream_info: SFrameBSInfo,
}
unsafe impl Send for Encoder {}
unsafe impl Sync for Encoder {}
impl Encoder {
pub fn with_config(mut config: EncoderConfig) -> Result<Self, Error> {
let raw_api = EncoderRawAPI::new()?;
let mut params = SEncParamExt::default();
#[rustfmt::skip]
unsafe {
raw_api.get_default_params(&mut params).ok()?;
params.iPicWidth = config.width as c_int;
params.iPicHeight = config.height as c_int;
params.iRCMode = config.rate_control_mode.to_c();
params.bEnableFrameSkip = config.enable_skip_frame;
params.iTargetBitrate = config.target_bitrate as c_int;
params.bEnableDenoise = config.enable_denoise;
params.fMaxFrameRate = config.max_frame_rate;
raw_api.initialize_ext(¶ms).ok()?;
raw_api.set_option(ENCODER_OPTION_TRACE_LEVEL, addr_of_mut!(config.debug).cast()).ok()?;
raw_api.set_option(ENCODER_OPTION_DATAFORMAT, addr_of_mut!(config.data_format).cast()).ok()?;
};
Ok(Self {
params,
raw_api,
bit_stream_info: Default::default(),
})
}
pub fn encode<T: YUVSource>(&mut self, yuv_source: &T) -> Result<EncodedBitStream<'_>, Error> {
self.encode_at(yuv_source, Timestamp::ZERO)
}
pub fn encode_at<T: YUVSource>(&mut self, yuv_source: &T, timestamp: Timestamp) -> Result<EncodedBitStream<'_>, Error> {
assert_eq!(yuv_source.width(), self.params.iPicWidth);
assert_eq!(yuv_source.height(), self.params.iPicHeight);
let source = SSourcePicture {
iColorFormat: videoFormatI420,
iStride: [yuv_source.y_stride(), yuv_source.u_stride(), yuv_source.v_stride(), 0],
pData: [
yuv_source.y().as_ptr() as *mut c_uchar,
yuv_source.u().as_ptr() as *mut c_uchar,
yuv_source.v().as_ptr() as *mut c_uchar,
null_mut(),
],
iPicWidth: self.params.iPicWidth,
iPicHeight: self.params.iPicHeight,
uiTimeStamp: timestamp.as_native(),
};
unsafe {
self.raw_api.encode_frame(&source, &mut self.bit_stream_info).ok()?;
Ok(EncodedBitStream {
bit_stream_info: &self.bit_stream_info,
})
}
}
pub unsafe fn raw_api(&mut self) -> &mut EncoderRawAPI {
&mut self.raw_api
}
}
impl Drop for Encoder {
fn drop(&mut self) {
unsafe {
self.raw_api.uninitialize();
}
}
}
pub struct EncodedBitStream<'a> {
bit_stream_info: &'a SFrameBSInfo,
}
impl<'a> EncodedBitStream<'a> {
pub fn raw_info(&self) -> &'a SFrameBSInfo {
self.bit_stream_info
}
pub fn frame_type(&self) -> FrameType {
FrameType::from_c_int(self.bit_stream_info.eFrameType)
}
pub fn num_layers(&self) -> usize {
self.bit_stream_info.iLayerNum as usize
}
pub fn layer(&self, i: usize) -> Option<Layer<'a>> {
if i < self.num_layers() {
Some(Layer {
layer_info: &self.bit_stream_info.sLayerInfo[i],
})
} else {
None
}
}
pub fn write_vec(&self, dst: &mut Vec<u8>) {
for l in 0..self.num_layers() {
let layer = self.layer(l).unwrap();
for n in 0..layer.nal_count() {
let nal = layer.nal_unit(n).unwrap();
dst.extend_from_slice(nal)
}
}
}
pub fn write<T: std::io::Write>(&self, writer: &mut T) -> Result<(), Error> {
for l in 0..self.num_layers() {
let layer = self.layer(l).unwrap();
for n in 0..layer.nal_count() {
let nal = layer.nal_unit(n).unwrap();
match writer.write(nal) {
Ok(num) if num < nal.len() => {
return Err(Error::msg(&format!("only wrote {} out of {} bytes", num, nal.len())));
}
Err(e) => {
return Err(Error::msg(&format!("failed to write: {}", e)));
}
_ => {}
};
}
}
Ok(())
}
pub fn to_vec(&self) -> Vec<u8> {
let mut rval = Vec::new();
self.write_vec(&mut rval);
rval
}
}
#[derive(Debug)]
pub struct Layer<'a> {
layer_info: &'a SLayerBSInfo,
}
impl<'a> Layer<'a> {
pub fn raw_info(&self) -> &'a SLayerBSInfo {
self.layer_info
}
pub fn nal_count(&self) -> usize {
self.layer_info.iNalCount as usize
}
pub fn nal_unit(&self, i: usize) -> Option<&[u8]> {
if i < self.nal_count() {
let mut offset = 0;
let slice = unsafe {
for nal_idx in 0..i {
let size = *self.layer_info.pNalLengthInByte.add(nal_idx) as usize;
offset += size;
}
let size = *self.layer_info.pNalLengthInByte.add(i) as usize;
std::slice::from_raw_parts(self.layer_info.pBsBuf.add(offset), size)
};
Some(slice)
} else {
None
}
}
pub fn is_video(&self) -> bool {
self.layer_info.uiLayerType == VIDEO_CODING_LAYER as c_uchar
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
pub enum FrameType {
Invalid,
IDR,
I,
P,
Skip,
IPMixed,
}
impl FrameType {
fn from_c_int(native: std::os::raw::c_int) -> Self {
use openh264_sys2::{videoFrameTypeI, videoFrameTypeIDR, videoFrameTypeIPMixed, videoFrameTypeP, videoFrameTypeSkip};
#[allow(non_upper_case_globals)]
match native {
videoFrameTypeIDR => Self::IDR,
videoFrameTypeI => Self::I,
videoFrameTypeP => Self::P,
videoFrameTypeSkip => Self::Skip,
videoFrameTypeIPMixed => Self::IPMixed,
_ => Self::Invalid,
}
}
}