1use crate::error::NativeErrorExt;
4use crate::formats::YUVSource;
5use crate::{Error, OpenH264API, Timestamp};
6use openh264_sys2::{
7 API, DEBLOCKING_IDC_0, ELevelIdc, ENCODER_OPTION, ENCODER_OPTION_DATAFORMAT, ENCODER_OPTION_SVC_ENCODE_PARAM_EXT,
8 ENCODER_OPTION_TRACE_LEVEL, EProfileIdc, EUsageType, EVideoFormatType, ISVCEncoder, ISVCEncoderVtbl, RC_MODES, SEncParamBase,
9 SEncParamExt, SFrameBSInfo, SLayerBSInfo, SM_SINGLE_SLICE, SM_SIZELIMITED_SLICE, SSourcePicture, VIDEO_CODING_LAYER,
10 WELS_LOG_DETAIL, WELS_LOG_QUIET, videoFormatI420,
11};
12use std::os::raw::{c_int, c_uchar, c_void};
13use std::ptr::{addr_of_mut, from_mut, null, null_mut};
14
15#[rustfmt::skip]
19#[allow(non_snake_case)]
20pub struct EncoderRawAPI {
21 api: OpenH264API,
22 encoder_ptr: *mut *const ISVCEncoderVtbl,
23 initialize: unsafe extern "C" fn(arg1: *mut ISVCEncoder, pParam: *const SEncParamBase) -> c_int,
24 initialize_ext: unsafe extern "C" fn(arg1: *mut ISVCEncoder, pParam: *const SEncParamExt) -> c_int,
25 get_default_params: unsafe extern "C" fn(arg1: *mut ISVCEncoder, pParam: *mut SEncParamExt) -> c_int,
26 uninitialize: unsafe extern "C" fn(arg1: *mut ISVCEncoder) -> c_int,
27 encode_frame: unsafe extern "C" fn(arg1: *mut ISVCEncoder, kpSrcPic: *const SSourcePicture, pBsInfo: *mut SFrameBSInfo) -> c_int,
28 encode_parameter_sets: unsafe extern "C" fn(arg1: *mut ISVCEncoder, pBsInfo: *mut SFrameBSInfo) -> c_int,
29 force_intra_frame: unsafe extern "C" fn(arg1: *mut ISVCEncoder, bIDR: bool) -> c_int,
30 set_option: unsafe extern "C" fn(arg1: *mut ISVCEncoder, eOptionId: ENCODER_OPTION, pOption: *mut c_void) -> c_int,
31 get_option: unsafe extern "C" fn(arg1: *mut ISVCEncoder, eOptionId: ENCODER_OPTION, pOption: *mut c_void) -> c_int,
32}
33
34#[rustfmt::skip]
35#[allow(clippy::too_many_arguments)]
36#[allow(clippy::missing_safety_doc)]
37#[allow(clippy::must_use_candidate)]
38#[allow(non_snake_case, unused, missing_docs)]
39impl EncoderRawAPI {
40 fn new(api: OpenH264API) -> Result<Self, Error> {
41 unsafe {
42 let mut encoder_ptr = null::<ISVCEncoderVtbl>() as *mut *const ISVCEncoderVtbl;
43
44 api.WelsCreateSVCEncoder(from_mut(&mut encoder_ptr)).ok()?;
45
46 let e = || {
47 Error::msg("VTable missing function.")
48 };
49
50 Ok(Self {
51 api,
52 encoder_ptr,
53 initialize: (*(*encoder_ptr)).Initialize.ok_or_else(e)?,
54 initialize_ext: (*(*encoder_ptr)).InitializeExt.ok_or_else(e)?,
55 get_default_params: (*(*encoder_ptr)).GetDefaultParams.ok_or_else(e)?,
56 uninitialize: (*(*encoder_ptr)).Uninitialize.ok_or_else(e)?,
57 encode_frame: (*(*encoder_ptr)).EncodeFrame.ok_or_else(e)?,
58 encode_parameter_sets: (*(*encoder_ptr)).EncodeParameterSets.ok_or_else(e)?,
59 force_intra_frame: (*(*encoder_ptr)).ForceIntraFrame.ok_or_else(e)?,
60 set_option: (*(*encoder_ptr)).SetOption.ok_or_else(e)?,
61 get_option: (*(*encoder_ptr)).GetOption.ok_or_else(e)?,
62 })
63 }
64 }
65
66 unsafe fn uninitialize(&self) -> c_int { unsafe { (self.uninitialize)(self.encoder_ptr) }}
68 unsafe fn initialize(&self, pParam: *const SEncParamBase) -> c_int { unsafe { (self.initialize)(self.encoder_ptr, pParam) }}
69 unsafe fn initialize_ext(&self, pParam: *const SEncParamExt) -> c_int { unsafe { (self.initialize_ext)(self.encoder_ptr, pParam) }}
70
71 pub unsafe fn get_default_params(&self, pParam: *mut SEncParamExt) -> c_int { unsafe { (self.get_default_params)(self.encoder_ptr, pParam) }}
72 pub unsafe fn encode_frame(&self, kpSrcPic: *const SSourcePicture, pBsInfo: *mut SFrameBSInfo) -> c_int { unsafe { (self.encode_frame)(self.encoder_ptr, kpSrcPic, pBsInfo) }}
73 pub unsafe fn encode_parameter_sets(&self, pBsInfo: *mut SFrameBSInfo) -> c_int { unsafe { (self.encode_parameter_sets)(self.encoder_ptr, pBsInfo) }}
74 pub unsafe fn force_intra_frame(&self, bIDR: bool) -> c_int { unsafe { (self.force_intra_frame)(self.encoder_ptr, bIDR) }}
75 pub unsafe fn set_option(&self, eOptionId: ENCODER_OPTION, pOption: *mut c_void) -> c_int { unsafe { (self.set_option)(self.encoder_ptr, eOptionId, pOption) }}
76 pub unsafe fn get_option(&self, eOptionId: ENCODER_OPTION, pOption: *mut c_void) -> c_int { unsafe { (self.get_option)(self.encoder_ptr, eOptionId, pOption) }}
77}
78
79impl Drop for EncoderRawAPI {
80 fn drop(&mut self) {
81 unsafe {
83 self.api.WelsDestroySVCEncoder(self.encoder_ptr);
84 }
85 }
86}
87
88unsafe impl Send for EncoderRawAPI {}
89unsafe impl Sync for EncoderRawAPI {}
90
91#[derive(Copy, Clone, Debug, Default)]
93pub enum RateControlMode {
94 #[default]
96 Quality,
97 Bitrate,
99 Bufferbased,
101 Timestamp,
103 BitrateModePostSkip,
105 Off,
107}
108
109impl RateControlMode {
110 const fn to_c(self) -> RC_MODES {
111 match self {
112 Self::Quality => openh264_sys2::RC_QUALITY_MODE,
113 Self::Bitrate => openh264_sys2::RC_BITRATE_MODE,
114 Self::Bufferbased => openh264_sys2::RC_BUFFERBASED_MODE,
115 Self::Timestamp => openh264_sys2::RC_TIMESTAMP_MODE,
116 Self::BitrateModePostSkip => openh264_sys2::RC_BITRATE_MODE_POST_SKIP,
117 Self::Off => openh264_sys2::RC_OFF_MODE,
118 }
119 }
120}
121
122#[derive(Copy, Clone, Debug, Default)]
124pub enum SpsPpsStrategy {
125 #[default]
129 ConstantId,
130
131 IncreasingId,
135
136 SpsListing,
138
139 SpsListingAndPpsIncreasing,
141
142 SpsPpsListing,
144}
145
146impl SpsPpsStrategy {
147 const fn to_c(self) -> RC_MODES {
148 match self {
149 Self::ConstantId => openh264_sys2::CONSTANT_ID,
150 Self::IncreasingId => openh264_sys2::INCREASING_ID,
151 Self::SpsListing => openh264_sys2::SPS_LISTING,
152 Self::SpsListingAndPpsIncreasing => openh264_sys2::SPS_LISTING_AND_PPS_INCREASING,
153 Self::SpsPpsListing => openh264_sys2::SPS_PPS_LISTING,
154 }
155 }
156}
157
158#[derive(Copy, Clone, Debug, Default)]
162pub enum UsageType {
163 #[default]
165 CameraVideoRealTime,
166 ScreenContentRealTime,
168 CameraVideoNonRealTime,
170 ScreenContentNonRealTime,
172 InputContentTypeAll,
174}
175
176impl UsageType {
177 const fn to_c(self) -> EUsageType {
178 match self {
179 Self::CameraVideoRealTime => openh264_sys2::CAMERA_VIDEO_REAL_TIME,
180 Self::ScreenContentRealTime => openh264_sys2::SCREEN_CONTENT_REAL_TIME,
181 Self::CameraVideoNonRealTime => openh264_sys2::CAMERA_VIDEO_NON_REAL_TIME,
182 Self::ScreenContentNonRealTime => openh264_sys2::SCREEN_CONTENT_NON_REAL_TIME,
183 Self::InputContentTypeAll => openh264_sys2::INPUT_CONTENT_TYPE_ALL,
184 }
185 }
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
190pub struct BitRate(u32);
191
192impl BitRate {
193 #[must_use]
195 pub const fn from_bps(bps: u32) -> Self {
196 Self(bps)
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
202pub struct FrameRate(f32);
203
204impl FrameRate {
205 #[must_use]
207 pub const fn from_hz(hz: f32) -> Self {
208 Self(hz)
209 }
210}
211
212#[derive(Copy, Clone, Debug)]
214#[allow(missing_docs)]
215pub enum Profile {
216 Baseline,
217 Main,
218 Extended,
219 High,
220 High10,
221 High422,
222 High444,
223 CAVLC444,
224 ScalableBaseline,
225 ScalableHigh,
226}
227
228impl Profile {
229 const fn to_c(self) -> EProfileIdc {
230 match self {
231 Self::Baseline => openh264_sys2::PRO_BASELINE,
232 Self::Main => openh264_sys2::PRO_MAIN,
233 Self::Extended => openh264_sys2::PRO_EXTENDED,
234 Self::High => openh264_sys2::PRO_HIGH,
235 Self::High10 => openh264_sys2::PRO_HIGH10,
236 Self::High422 => openh264_sys2::PRO_HIGH422,
237 Self::High444 => openh264_sys2::PRO_HIGH444,
238 Self::CAVLC444 => openh264_sys2::PRO_CAVLC444,
239 Self::ScalableBaseline => openh264_sys2::PRO_SCALABLE_BASELINE,
240 Self::ScalableHigh => openh264_sys2::PRO_SCALABLE_HIGH,
241 }
242 }
243}
244
245#[derive(Copy, Clone, Debug)]
261#[allow(missing_docs, non_camel_case_types)]
262pub enum Level {
263 Level_1_0,
265 Level_1_B,
267 Level_1_1,
269 Level_1_2,
271 Level_1_3,
273 Level_2_0,
275 Level_2_1,
277 Level_2_2,
279 Level_3_0,
281 Level_3_1,
283 Level_3_2,
285 Level_4_0,
287 Level_4_1,
289 Level_4_2,
291 Level_5_0,
293 Level_5_1,
295 Level_5_2,
297}
298
299impl Level {
300 const fn to_c(self) -> ELevelIdc {
301 match self {
302 Self::Level_1_0 => openh264_sys2::LEVEL_1_0,
303 Self::Level_1_B => openh264_sys2::LEVEL_1_B,
304 Self::Level_1_1 => openh264_sys2::LEVEL_1_1,
305 Self::Level_1_2 => openh264_sys2::LEVEL_1_2,
306 Self::Level_1_3 => openh264_sys2::LEVEL_1_3,
307 Self::Level_2_0 => openh264_sys2::LEVEL_2_0,
308 Self::Level_2_1 => openh264_sys2::LEVEL_2_1,
309 Self::Level_2_2 => openh264_sys2::LEVEL_2_2,
310 Self::Level_3_0 => openh264_sys2::LEVEL_3_0,
311 Self::Level_3_1 => openh264_sys2::LEVEL_3_1,
312 Self::Level_3_2 => openh264_sys2::LEVEL_3_2,
313 Self::Level_4_0 => openh264_sys2::LEVEL_4_0,
314 Self::Level_4_1 => openh264_sys2::LEVEL_4_1,
315 Self::Level_4_2 => openh264_sys2::LEVEL_4_2,
316 Self::Level_5_0 => openh264_sys2::LEVEL_5_0,
317 Self::Level_5_1 => openh264_sys2::LEVEL_5_1,
318 Self::Level_5_2 => openh264_sys2::LEVEL_5_2,
319 }
320 }
321}
322
323#[derive(Debug, Default, Clone, Copy)]
325#[allow(missing_docs)]
326pub enum Complexity {
327 Low,
329 #[default]
331 Medium,
332 High,
334}
335
336impl Complexity {
337 const fn to_c(self) -> ELevelIdc {
338 match self {
339 Self::Low => openh264_sys2::LOW_COMPLEXITY,
340 Self::Medium => openh264_sys2::MEDIUM_COMPLEXITY,
341 Self::High => openh264_sys2::HIGH_COMPLEXITY,
342 }
343 }
344}
345
346#[derive(Debug, Clone, Copy)]
350pub struct QpRange {
351 min: u8,
352 max: u8,
353}
354
355impl QpRange {
356 #[must_use]
365 pub const fn new(min: u8, max: u8) -> Self {
366 assert!(max <= 51, "quantization value out of range (0..=51)");
367 assert!(min <= max, "quantization min value larger than max");
368
369 Self { min, max }
370 }
371}
372
373impl Default for QpRange {
374 fn default() -> Self {
375 Self { min: 0, max: 51 }
376 }
377}
378
379#[derive(Debug, Clone, Copy, Default)]
381pub struct IntraFramePeriod(u32);
382
383impl IntraFramePeriod {
384 #[must_use]
391 pub const fn from_num_frames(frames: u32) -> Self {
392 Self(frames)
393 }
394
395 #[must_use]
397 pub const fn auto() -> Self {
398 Self(0)
399 }
400}
401
402#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
412#[repr(u8)]
413pub enum ColorPrimaries {
414 #[default]
416 Bt709 = 1,
417 Unspecified = 2,
419 Bt470M = 4,
421 Bt470BG = 5,
423 Smpte170M = 6,
425 Smpte240M = 7,
427 Film = 8,
429 Bt2020 = 9,
431}
432
433impl ColorPrimaries {
434 #[must_use]
436 pub const fn as_u8(self) -> u8 {
437 self as u8
438 }
439}
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
445#[repr(u8)]
446pub enum TransferCharacteristics {
447 #[default]
449 Bt709 = 1,
450 Unspecified = 2,
452 Bt470M = 4,
454 Bt470Bg = 5,
456 Smpte170M = 6,
458 Smpte240M = 7,
460 Linear = 8,
462 Srgb = 13,
464 Bt2020_10 = 14,
466 Bt2020_12 = 15,
468 Smpte2084 = 16,
470 Hlg = 18,
472}
473
474impl TransferCharacteristics {
475 #[must_use]
477 pub const fn as_u8(self) -> u8 {
478 self as u8
479 }
480}
481
482#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
486#[repr(u8)]
487pub enum MatrixCoefficients {
488 Identity = 0,
490 #[default]
492 Bt709 = 1,
493 Unspecified = 2,
495 Fcc = 4,
497 Bt470Bg = 5,
499 Smpte170M = 6,
501 Smpte240M = 7,
503 Ycgco = 8,
505 Bt2020Ncl = 9,
507 Bt2020Cl = 10,
509}
510
511impl MatrixCoefficients {
512 #[must_use]
514 pub const fn as_u8(self) -> u8 {
515 self as u8
516 }
517}
518
519#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
533#[must_use]
534pub struct VuiConfig {
535 color_primaries: ColorPrimaries,
537 transfer_characteristics: TransferCharacteristics,
539 matrix_coefficients: MatrixCoefficients,
541 full_range: bool,
543}
544
545impl VuiConfig {
546 pub const fn new() -> Self {
548 Self::bt709()
549 }
550
551 pub const fn bt709() -> Self {
555 Self {
556 color_primaries: ColorPrimaries::Bt709,
557 transfer_characteristics: TransferCharacteristics::Bt709,
558 matrix_coefficients: MatrixCoefficients::Bt709,
559 full_range: false,
560 }
561 }
562
563 pub const fn bt709_full() -> Self {
565 Self {
566 color_primaries: ColorPrimaries::Bt709,
567 transfer_characteristics: TransferCharacteristics::Bt709,
568 matrix_coefficients: MatrixCoefficients::Bt709,
569 full_range: true,
570 }
571 }
572
573 pub const fn bt601() -> Self {
575 Self {
576 color_primaries: ColorPrimaries::Smpte170M,
577 transfer_characteristics: TransferCharacteristics::Smpte170M,
578 matrix_coefficients: MatrixCoefficients::Smpte170M,
579 full_range: false,
580 }
581 }
582
583 pub const fn srgb() -> Self {
587 Self {
588 color_primaries: ColorPrimaries::Bt709,
589 transfer_characteristics: TransferCharacteristics::Srgb,
590 matrix_coefficients: MatrixCoefficients::Bt709,
591 full_range: true,
592 }
593 }
594
595 pub const fn bt2020() -> Self {
597 Self {
598 color_primaries: ColorPrimaries::Bt2020,
599 transfer_characteristics: TransferCharacteristics::Bt2020_10,
600 matrix_coefficients: MatrixCoefficients::Bt2020Ncl,
601 full_range: false,
602 }
603 }
604
605 pub const fn color_primaries(mut self, value: ColorPrimaries) -> Self {
607 self.color_primaries = value;
608 self
609 }
610
611 pub const fn transfer_characteristics(mut self, value: TransferCharacteristics) -> Self {
613 self.transfer_characteristics = value;
614 self
615 }
616
617 pub const fn matrix_coefficients(mut self, value: MatrixCoefficients) -> Self {
619 self.matrix_coefficients = value;
620 self
621 }
622
623 pub const fn full_range(mut self, value: bool) -> Self {
625 self.full_range = value;
626 self
627 }
628}
629
630#[derive(Default, Copy, Clone, Debug)]
634#[must_use]
635#[allow(clippy::struct_excessive_bools)]
636pub struct EncoderConfig {
637 enable_skip_frame: bool,
638 target_bitrate: BitRate,
639 enable_denoise: bool,
640 debug: i32,
641 data_format: EVideoFormatType,
642 max_frame_rate: FrameRate,
643 rate_control_mode: RateControlMode,
644 sps_pps_strategy: SpsPpsStrategy,
645 multiple_thread_idc: u16,
646 usage_type: UsageType,
647 max_slice_len: Option<u32>,
648 profile: Option<Profile>,
649 level: Option<Level>,
650 complexity: Complexity,
651 qp: QpRange,
652 scene_change_detect: bool,
653 adaptive_quantization: bool,
654 background_detection: bool,
655 long_term_reference: bool,
656 intra_frame_period: IntraFramePeriod,
657 vui: Option<VuiConfig>,
658}
659
660impl EncoderConfig {
661 pub const fn new() -> Self {
663 Self {
664 enable_skip_frame: true,
665 target_bitrate: BitRate::from_bps(120_000),
666 enable_denoise: false,
667 debug: 0,
668 data_format: videoFormatI420,
669 max_frame_rate: FrameRate::from_hz(0.0),
670 rate_control_mode: RateControlMode::Quality,
671 sps_pps_strategy: SpsPpsStrategy::ConstantId,
672 multiple_thread_idc: 0,
673 usage_type: UsageType::CameraVideoRealTime,
674 max_slice_len: None,
675 profile: None,
676 level: None,
677 complexity: Complexity::Medium,
678 qp: QpRange::new(0, 51),
679 scene_change_detect: true,
680 adaptive_quantization: true,
681 background_detection: true,
682 long_term_reference: false,
683 intra_frame_period: IntraFramePeriod::from_num_frames(0),
684 vui: None,
685 }
686 }
687
688 pub const fn bitrate(mut self, bps: BitRate) -> Self {
690 self.target_bitrate = bps;
691 self
692 }
693
694 pub const fn debug(mut self, value: bool) -> Self {
696 self.debug = if value { WELS_LOG_DETAIL } else { WELS_LOG_QUIET };
697 self
698 }
699
700 pub const fn skip_frames(mut self, value: bool) -> Self {
702 self.enable_skip_frame = value;
703 self
704 }
705
706 pub const fn max_frame_rate(mut self, value: FrameRate) -> Self {
708 self.max_frame_rate = value;
709 self
710 }
711
712 pub const fn usage_type(mut self, value: UsageType) -> Self {
714 self.usage_type = value;
715 self
716 }
717
718 pub const fn rate_control_mode(mut self, value: RateControlMode) -> Self {
720 self.rate_control_mode = value;
721 self
722 }
723
724 pub const fn sps_pps_strategy(mut self, value: SpsPpsStrategy) -> Self {
726 self.sps_pps_strategy = value;
727 self
728 }
729
730 pub const fn max_slice_len(mut self, max_slice_len: u32) -> Self {
732 self.max_slice_len = Some(max_slice_len);
733 self
734 }
735
736 pub const fn profile(mut self, profile: Profile) -> Self {
738 self.profile = Some(profile);
739 self
740 }
741
742 pub const fn level(mut self, level: Level) -> Self {
744 self.level = Some(level);
745 self
746 }
747
748 pub const fn complexity(mut self, complexity: Complexity) -> Self {
750 self.complexity = complexity;
751 self
752 }
753
754 pub const fn qp(mut self, value: QpRange) -> Self {
756 self.qp = value;
757 self
758 }
759
760 pub const fn scene_change_detect(mut self, value: bool) -> Self {
762 self.scene_change_detect = value;
763 self
764 }
765
766 pub const fn adaptive_quantization(mut self, value: bool) -> Self {
768 self.adaptive_quantization = value;
769 self
770 }
771
772 pub const fn background_detection(mut self, value: bool) -> Self {
774 self.background_detection = value;
775 self
776 }
777
778 pub const fn long_term_reference(mut self, value: bool) -> Self {
780 self.long_term_reference = value;
781 self
782 }
783
784 pub const fn intra_frame_period(mut self, value: IntraFramePeriod) -> Self {
786 self.intra_frame_period = value;
787 self
788 }
789
790 pub const fn num_threads(mut self, threads: u16) -> Self {
798 self.multiple_thread_idc = threads;
799 self
800 }
801
802 pub const fn vui(mut self, config: VuiConfig) -> Self {
819 self.vui = Some(config);
820 self
821 }
822}
823
824pub struct Encoder {
826 config: EncoderConfig,
827 raw_api: EncoderRawAPI,
828 bit_stream_info: SFrameBSInfo,
829 previous_dimensions: Option<(i32, i32)>,
830}
831
832unsafe impl Send for Encoder {}
833unsafe impl Sync for Encoder {}
834
835impl Encoder {
836 #[cfg(feature = "source")]
847 pub fn new() -> Result<Self, Error> {
848 let api = OpenH264API::from_source();
849 let config = EncoderConfig::new();
850 let raw_api = EncoderRawAPI::new(api)?;
851
852 Ok(Self {
853 config,
854 raw_api,
855 bit_stream_info: SFrameBSInfo::default(),
856 previous_dimensions: None,
857 })
858 }
859 pub fn with_api_config(api: OpenH264API, config: EncoderConfig) -> Result<Self, Error> {
867 let raw_api = EncoderRawAPI::new(api)?;
868
869 Ok(Self {
870 config,
871 raw_api,
872 bit_stream_info: SFrameBSInfo::default(),
873 previous_dimensions: None,
874 })
875 }
876
877 pub fn encode<T: YUVSource>(&mut self, yuv_source: &T) -> Result<EncodedBitStream<'_>, Error> {
890 self.encode_at(yuv_source, Timestamp::ZERO)
891 }
892
893 pub fn encode_at<T: YUVSource>(&mut self, yuv_source: &T, timestamp: Timestamp) -> Result<EncodedBitStream<'_>, Error> {
910 let new_dimensions = yuv_source.dimensions_i32();
911
912 if self.previous_dimensions != Some(new_dimensions) {
913 self.reinit(new_dimensions.0, new_dimensions.1)?;
914 self.previous_dimensions = Some(new_dimensions);
915 }
916
917 let strides = yuv_source.strides_i32();
918
919 let source = SSourcePicture {
922 iColorFormat: videoFormatI420,
923 iStride: [strides.0, strides.1, strides.2, 0],
924 pData: [
925 yuv_source.y().as_ptr().cast_mut(),
926 yuv_source.u().as_ptr().cast_mut(),
927 yuv_source.v().as_ptr().cast_mut(),
928 null_mut(),
929 ],
930 iPicWidth: new_dimensions.0,
931 iPicHeight: new_dimensions.1,
932 uiTimeStamp: timestamp.as_native(),
933 bPsnrY: false,
934 bPsnrU: false,
935 bPsnrV: false,
936 };
937
938 unsafe {
939 self.raw_api
940 .encode_frame(&raw const source, &raw mut self.bit_stream_info)
941 .ok()?;
942 }
943
944 Ok(EncodedBitStream {
945 bit_stream_info: &self.bit_stream_info,
946 })
947 }
948
949 #[rustfmt::skip]
950 fn reinit(&mut self, width: i32, height: i32) -> Result<(), Error> {
951 let greater_dim = std::cmp::max(width, height);
958 let smaller_dim = std::cmp::min(width, height);
959
960 if greater_dim > 3840 || smaller_dim > 2160 {
961 return Err(Error::msg("Encoder max resolution 3840x2160 horizontal or 2160x3840 vertical"));
962 }
963
964 let mut params = SEncParamExt::default();
965
966 unsafe { self.raw_api.get_default_params(&raw mut params).ok()? };
967
968 params.iPicWidth = width as c_int; params.iPicHeight = height as c_int; params.iRCMode = self.config.rate_control_mode.to_c();
971 params.bEnableFrameSkip = self.config.enable_skip_frame;
972 params.iTargetBitrate = self.config.target_bitrate.0.try_into()?;
973 params.bEnableDenoise = self.config.enable_denoise;
974 params.fMaxFrameRate = self.config.max_frame_rate.0;
975 params.eSpsPpsIdStrategy = self.config.sps_pps_strategy.to_c();
976 params.iMultipleThreadIdc = self.config.multiple_thread_idc;
977 params.iUsageType = self.config.usage_type.to_c();
978
979 params.bEnableSceneChangeDetect = self.config.scene_change_detect;
980 params.bEnableAdaptiveQuant = self.config.adaptive_quantization;
981 params.bEnableBackgroundDetection = self.config.background_detection;
982 params.bEnableLongTermReference = self.config.long_term_reference;
983 params.iComplexityMode = self.config.complexity.to_c();
984 params.uiIntraPeriod = self.config.intra_frame_period.0;
985 params.iLoopFilterDisableIdc = DEBLOCKING_IDC_0;
986 params.iMinQp = self.config.qp.min.into();
987 params.iMaxQp = self.config.qp.max.into();
988
989 if let Some(profile) = self.config.profile {
990 params.sSpatialLayers[0].uiProfileIdc = profile.to_c();
991 }
992
993 if let Some(level) = self.config.level {
994 params.sSpatialLayers[0].uiLevelIdc = level.to_c();
995 }
996
997 if let Some(ref vui) = self.config.vui {
999 params.sSpatialLayers[0].bVideoSignalTypePresent = true;
1000 params.sSpatialLayers[0].bColorDescriptionPresent = true;
1001 params.sSpatialLayers[0].bFullRange = vui.full_range;
1002 params.sSpatialLayers[0].uiColorPrimaries = vui.color_primaries.as_u8();
1003 params.sSpatialLayers[0].uiTransferCharacteristics = vui.transfer_characteristics.as_u8();
1004 params.sSpatialLayers[0].uiColorMatrix = vui.matrix_coefficients.as_u8();
1005 }
1006
1007 params.iSpatialLayerNum = 1;
1008 params.iTemporalLayerNum = 1;
1009 params.iLtrMarkPeriod = 30;
1010 params.sSpatialLayers[0].iMaxSpatialBitrate = self.config.target_bitrate.0.try_into()?;
1011 params.sSpatialLayers[0].iSpatialBitrate = self.config.target_bitrate.0.try_into()?;
1012 params.sSpatialLayers[0].fFrameRate = self.config.max_frame_rate.0;
1013 params.sSpatialLayers[0].iVideoWidth = width;
1014 params.sSpatialLayers[0].iVideoHeight = height;
1015
1016 if let Some(max_slice_len) = self.config.max_slice_len {
1017 params.uiMaxNalSize = max_slice_len;
1019
1020 params.sSpatialLayers[0].sSliceArgument.uiSliceMode = SM_SIZELIMITED_SLICE;
1021 params.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = max_slice_len;
1022 } else {
1023 params.sSpatialLayers[0].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE;
1025 params.sSpatialLayers[0].sSliceArgument.uiSliceNum = 1;
1026 }
1027
1028 unsafe {
1029 if self.previous_dimensions.is_none() {
1030 self.raw_api.initialize_ext(&raw const params).ok()?;
1032 self.raw_api.set_option(ENCODER_OPTION_TRACE_LEVEL, addr_of_mut!(self.config.debug).cast()).ok()?;
1033 self.raw_api.set_option(ENCODER_OPTION_DATAFORMAT, addr_of_mut!(self.config.data_format).cast()).ok()?;
1034 } else {
1035 self.raw_api.set_option(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, addr_of_mut!(params).cast()).ok()?;
1037
1038 self.force_intra_frame();
1040 }
1041 }
1042
1043 Ok(())
1044 }
1045
1046 pub fn force_intra_frame(&mut self) {
1048 unsafe {
1051 self.raw_api.force_intra_frame(true);
1052 }
1053 }
1054
1055 pub const unsafe fn raw_api(&mut self) -> &mut EncoderRawAPI {
1063 &mut self.raw_api
1064 }
1065}
1066
1067impl Drop for Encoder {
1068 fn drop(&mut self) {
1069 unsafe {
1071 self.raw_api.uninitialize();
1072 }
1073 }
1074}
1075
1076pub struct EncodedBitStream<'a> {
1078 bit_stream_info: &'a SFrameBSInfo,
1080}
1081
1082impl<'a> EncodedBitStream<'a> {
1083 #[must_use]
1085 pub const fn raw_info(&self) -> &'a SFrameBSInfo {
1086 self.bit_stream_info
1087 }
1088
1089 #[must_use]
1091 pub const fn frame_type(&self) -> FrameType {
1092 FrameType::from_c_int(self.bit_stream_info.eFrameType)
1093 }
1094
1095 #[must_use]
1097 pub const fn num_layers(&self) -> usize {
1098 self.bit_stream_info.iLayerNum as usize
1099 }
1100
1101 #[must_use]
1103 pub const fn layer(&self, i: usize) -> Option<Layer<'a>> {
1104 if i < self.num_layers() {
1105 Some(Layer {
1106 layer_info: &self.bit_stream_info.sLayerInfo[i],
1107 })
1108 } else {
1109 None
1110 }
1111 }
1112
1113 #[allow(clippy::missing_panics_doc)]
1115 pub fn write_vec(&self, dst: &mut Vec<u8>) {
1116 for l in 0..self.num_layers() {
1117 let layer = self.layer(l).unwrap();
1118
1119 for n in 0..layer.nal_count() {
1120 let nal = layer.nal_unit(n).unwrap();
1121
1122 dst.extend_from_slice(nal);
1123 }
1124 }
1125 }
1126
1127 #[allow(clippy::missing_panics_doc)]
1133 pub fn write<T: std::io::Write>(&self, writer: &mut T) -> Result<(), Error> {
1134 for l in 0..self.num_layers() {
1135 let layer = self.layer(l).unwrap();
1136
1137 for n in 0..layer.nal_count() {
1138 let nal = layer.nal_unit(n).unwrap();
1139
1140 match writer.write(nal) {
1141 Ok(num) if num < nal.len() => {
1142 return Err(Error::msg(&format!("only wrote {} out of {} bytes", num, nal.len())));
1143 }
1144 Err(e) => {
1145 return Err(Error::msg(&format!("failed to write: {e}")));
1146 }
1147 _ => {}
1148 }
1149 }
1150 }
1151 Ok(())
1152 }
1153
1154 #[must_use]
1156 pub fn to_vec(&self) -> Vec<u8> {
1157 let mut rval = Vec::new();
1158 self.write_vec(&mut rval);
1159 rval
1160 }
1161}
1162
1163#[derive(Debug)]
1165pub struct Layer<'a> {
1166 layer_info: &'a SLayerBSInfo,
1168}
1169
1170impl<'a> Layer<'a> {
1171 #[must_use]
1173 pub const fn raw_info(&self) -> &'a SLayerBSInfo {
1174 self.layer_info
1175 }
1176
1177 #[must_use]
1179 pub const fn nal_count(&self) -> usize {
1180 self.layer_info.iNalCount as usize
1181 }
1182
1183 #[must_use]
1185 pub fn nal_unit(&self, i: usize) -> Option<&[u8]> {
1186 if i < self.nal_count() {
1187 let mut offset = 0;
1188
1189 let slice = unsafe {
1190 for nal_idx in 0..i {
1194 let size = *self.layer_info.pNalLengthInByte.add(nal_idx) as usize;
1195 offset += size;
1196 }
1197
1198 let size = *self.layer_info.pNalLengthInByte.add(i) as usize;
1199 std::slice::from_raw_parts(self.layer_info.pBsBuf.add(offset), size)
1200 };
1201
1202 Some(slice)
1203 } else {
1204 None
1205 }
1206 }
1207
1208 #[must_use]
1210 pub const fn is_video(&self) -> bool {
1211 self.layer_info.uiLayerType == VIDEO_CODING_LAYER as c_uchar
1212 }
1213}
1214
1215#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
1219pub enum FrameType {
1220 Invalid,
1222 IDR,
1224 I,
1226 P,
1228 Skip,
1230 IPMixed,
1232}
1233
1234impl FrameType {
1235 const fn from_c_int(native: std::os::raw::c_int) -> Self {
1236 use openh264_sys2::{videoFrameTypeI, videoFrameTypeIDR, videoFrameTypeIPMixed, videoFrameTypeP, videoFrameTypeSkip};
1237
1238 #[allow(non_upper_case_globals)]
1239 match native {
1240 videoFrameTypeIDR => Self::IDR,
1241 videoFrameTypeI => Self::I,
1242 videoFrameTypeP => Self::P,
1243 videoFrameTypeSkip => Self::Skip,
1244 videoFrameTypeIPMixed => Self::IPMixed,
1245 _ => Self::Invalid,
1246 }
1247 }
1248}