Skip to main content

openh264/
encoder.rs

1//! Converts YUV / RGB images to NAL packets.
2
3use 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/// Convenience wrapper with guaranteed function pointers for easy access.
16///
17/// This struct automatically handles `WelsCreateSVCEncoder` and `WelsDestroySVCEncoder`.
18#[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    // Exposing these will probably do more harm than good.
67    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        // Safe because when we drop the pointer must have been initialized, and we aren't clone.
82        unsafe {
83            self.api.WelsDestroySVCEncoder(self.encoder_ptr);
84        }
85    }
86}
87
88unsafe impl Send for EncoderRawAPI {}
89unsafe impl Sync for EncoderRawAPI {}
90
91/// Specifies the mode used by the encoder to control the rate.
92#[derive(Copy, Clone, Debug, Default)]
93pub enum RateControlMode {
94    /// Quality mode.
95    #[default]
96    Quality,
97    /// Bitrate mode.
98    Bitrate,
99    /// No bitrate control, only using buffer status, adjust the video quality.
100    Bufferbased,
101    /// Rate control based timestamp.
102    Timestamp,
103    /// This is in-building RC MODE, WILL BE DELETED after algorithm tuning!
104    BitrateModePostSkip,
105    /// Rate control off mode.
106    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/// Sets the behavior for generating SPS/PPS.
123#[derive(Copy, Clone, Debug, Default)]
124pub enum SpsPpsStrategy {
125    /// Use a constant SPS/PPS ID. The ID will not change across encoded video frames.
126    ///
127    /// This is the default value.
128    #[default]
129    ConstantId,
130
131    /// Increment the SPS/PPS ID with each IDR frame.
132    ///
133    /// This allows decoders to detect missing frames.
134    IncreasingId,
135
136    /// Use SPS in the existing list if possible.
137    SpsListing,
138
139    /// _find doc for this_
140    SpsListingAndPpsIncreasing,
141
142    /// _find doc for this_
143    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/// The intended usage scenario for the encoder.
159///
160/// Note, this documen
161#[derive(Copy, Clone, Debug, Default)]
162pub enum UsageType {
163    /// Camera video for real-time communication.
164    #[default]
165    CameraVideoRealTime,
166    /// Used for real-time screen sharing.
167    ScreenContentRealTime,
168    /// Camera video for non-real-time communication.
169    CameraVideoNonRealTime,
170    /// Used for non-real-time screen recordings.
171    ScreenContentNonRealTime,
172    /// It's unclear what this does, PRs adding documentation welcome.
173    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/// Bitrate of the encoder.
189#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
190pub struct BitRate(u32);
191
192impl BitRate {
193    /// Creates a new bitrate with the given bits per second.
194    #[must_use]
195    pub const fn from_bps(bps: u32) -> Self {
196        Self(bps)
197    }
198}
199
200/// Frame rate of the encoder.
201#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
202pub struct FrameRate(f32);
203
204impl FrameRate {
205    /// Creates a new framerate with the given Hertz.
206    #[must_use]
207    pub const fn from_hz(hz: f32) -> Self {
208        Self(hz)
209    }
210}
211
212/// The H.264 encoding profile
213#[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/// H.264 encoding levels with their corresponding capabilities.
246///
247/// | Level   | Max Resolution (Pixels) | Max Frame Rate (fps) | Max Bitrate (Main Profile) | Max Bitrate (High Profile) |
248/// |---------|--------------------------|-----------------------|-----------------------------|-----------------------------|
249/// | 1.0     | 176x144 (QCIF)          | 15                   | 64 kbps                    | 80 kbps                    |
250/// | 1.1     | 176x144 (QCIF)          | 30                   | 192 kbps                   | 240 kbps                   |
251/// | 1.2     | 320x240 (QVGA)          | 30                   | 384 kbps                   | 480 kbps                   |
252/// | 2.0     | 352x288 (CIF)           | 30                   | 2 Mbps                     | 2.5 Mbps                   |
253/// | 3.0     | 720x576 (SD)            | 30                   | 10 Mbps                    | 12.5 Mbps                  |
254/// | 3.1     | 1280x720 (HD)           | 30                   | 14 Mbps                    | 17.5 Mbps                  |
255/// | 4.0     | 1920x1080 (Full HD)     | 30                   | 20 Mbps                    | 25 Mbps                    |
256/// | 4.1     | 1920x1080 (Full HD)     | 60                   | 50 Mbps                    | 62.5 Mbps                  |
257/// | 5.0     | 3840x2160 (4K)          | 30                   | 135 Mbps                   | 168.75 Mbps                |
258/// | 5.1     | 3840x2160 (4K)          | 60                   | 240 Mbps                   | 300 Mbps                   |
259/// | 5.2     | 4096x2160 (4K Cinema)   | 60                   | 480 Mbps                   | 600 Mbps                   |
260#[derive(Copy, Clone, Debug)]
261#[allow(missing_docs, non_camel_case_types)]
262pub enum Level {
263    /// Level 1.0: Max resolution 176x144 (QCIF), 15 fps, 64 kbps (Main), 80 kbps (High)
264    Level_1_0,
265    /// Level 1.B: Specialized low-complexity baseline level.
266    Level_1_B,
267    /// Level 1.1: Max resolution 176x144 (QCIF), 30 fps, 192 kbps (Main), 240 kbps (High)
268    Level_1_1,
269    /// Level 1.2: Max resolution 320x240 (QVGA), 30 fps, 384 kbps (Main), 480 kbps (High)
270    Level_1_2,
271    /// Level 1.3: Reserved in standard, similar to Level 2.0.
272    Level_1_3,
273    /// Level 2.0: Max resolution 352x288 (CIF), 30 fps, 2 Mbps (Main), 2.5 Mbps (High)
274    Level_2_0,
275    /// Level 2.1: Max resolution 352x288 (CIF), 30 fps, 4 Mbps (Main), 5 Mbps (High)
276    Level_2_1,
277    /// Level 2.2: Max resolution 352x288 (CIF), 30 fps, 10 Mbps (Main), 12.5 Mbps (High)
278    Level_2_2,
279    /// Level 3.0: Max resolution 720x576 (SD), 30 fps, 10 Mbps (Main), 12.5 Mbps (High)
280    Level_3_0,
281    /// Level 3.1: Max resolution 1280x720 (HD), 30 fps, 14 Mbps (Main), 17.5 Mbps (High)
282    Level_3_1,
283    /// Level 3.2: Max resolution 1280x720 (HD), 60 fps, 20 Mbps (Main), 25 Mbps (High)
284    Level_3_2,
285    /// Level 4.0: Max resolution 1920x1080 (Full HD), 30 fps, 20 Mbps (Main), 25 Mbps (High)
286    Level_4_0,
287    /// Level 4.1: Max resolution 1920x1080 (Full HD), 60 fps, 50 Mbps (Main), 62.5 Mbps (High)
288    Level_4_1,
289    /// Level 4.2: Max resolution 1920x1080 (Full HD), 120 fps, 100 Mbps (Main), 125 Mbps (High)
290    Level_4_2,
291    /// Level 5.0: Max resolution 3840x2160 (4K), 30 fps, 135 Mbps (Main), 168.75 Mbps (High)
292    Level_5_0,
293    /// Level 5.1: Max resolution 3840x2160 (4K), 60 fps, 240 Mbps (Main), 300 Mbps (High)
294    Level_5_1,
295    /// Level 5.2: Max resolution 4096x2160 (4K Cinema), 60 fps, 480 Mbps (Main), 600 Mbps (High)
296    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/// Complexity of the encoder (speed vs. quality).
324#[derive(Debug, Default, Clone, Copy)]
325#[allow(missing_docs)]
326pub enum Complexity {
327    /// The lowest complexity, the fastest speed.
328    Low,
329    /// Medium complexity, medium speed, medium quality.
330    #[default]
331    Medium,
332    /// High complexity, lowest speed, high quality.
333    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/// Quantization parameter range to control the degree of compression.
347///
348/// This can be used to control the balance between size and video quality.
349#[derive(Debug, Clone, Copy)]
350pub struct QpRange {
351    min: u8,
352    max: u8,
353}
354
355impl QpRange {
356    /// Limit the quantization of the encoder to the given range.
357    ///
358    /// Valid values for `min` and `max` are between 0 and 51, where 0
359    /// represents highest quality and 51 the strongest compression.
360    ///
361    /// # Panics
362    ///
363    /// Panics if `max > 51` or if `min > max`.
364    #[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/// A period in frames after which a new I-Frame is generated.
380#[derive(Debug, Clone, Copy, Default)]
381pub struct IntraFramePeriod(u32);
382
383impl IntraFramePeriod {
384    /// Creates a period in which I-Frames (group of pictures, "GOP size") are generated.
385    ///
386    /// Using lower values improves error resilience and allows for faster seeking within the video,
387    /// but increases the overall required bitrate.
388    ///
389    /// Setting the value to zero is equal to calling [`IntraFramePeriod::auto()`].
390    #[must_use]
391    pub const fn from_num_frames(frames: u32) -> Self {
392        Self(frames)
393    }
394
395    /// Lets the encoder create I-frames as desired(?).
396    #[must_use]
397    pub const fn auto() -> Self {
398        Self(0)
399    }
400}
401
402// =============================================================================
403// VUI (Video Usability Information) Parameters
404// =============================================================================
405// These parameters are embedded in the H.264 SPS to signal color space
406// information to decoders. See ITU-T H.264 Annex E for details.
407
408/// H.264 colour_primaries values (ITU-T H.264 Table E-3).
409///
410/// Specifies the chromaticity coordinates of the source primaries.
411#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
412#[repr(u8)]
413pub enum ColorPrimaries {
414    /// ITU-R BT.709-6 / sRGB / IEC 61966-2-1 (HD television, sRGB displays)
415    #[default]
416    Bt709 = 1,
417    /// Unspecified - decoder determines based on context
418    Unspecified = 2,
419    /// ITU-R BT.470-6 System M (historical NTSC)
420    Bt470M = 4,
421    /// ITU-R BT.470-6 System B, G / ITU-R BT.601-7 625 (PAL)
422    Bt470BG = 5,
423    /// SMPTE 170M / ITU-R BT.601-7 525 (NTSC)
424    Smpte170M = 6,
425    /// SMPTE 240M (historical)
426    Smpte240M = 7,
427    /// Generic film (C illuminant)
428    Film = 8,
429    /// ITU-R BT.2020-2 / ITU-R BT.2100-2 (UHD/HDR)
430    Bt2020 = 9,
431}
432
433impl ColorPrimaries {
434    /// Get the raw u8 value for the VUI colour_primaries field.
435    #[must_use]
436    pub const fn as_u8(self) -> u8 {
437        self as u8
438    }
439}
440
441/// H.264 transfer_characteristics values (ITU-T H.264 Table E-4).
442///
443/// Specifies the opto-electronic transfer characteristic (gamma).
444#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
445#[repr(u8)]
446pub enum TransferCharacteristics {
447    /// ITU-R BT.709-6 / ITU-R BT.1361 (HD television)
448    #[default]
449    Bt709 = 1,
450    /// Unspecified
451    Unspecified = 2,
452    /// ITU-R BT.470-6 System M (2.2 gamma)
453    Bt470M = 4,
454    /// ITU-R BT.470-6 System B, G (2.8 gamma)
455    Bt470Bg = 5,
456    /// SMPTE 170M / BT.601 (same curve as BT.709)
457    Smpte170M = 6,
458    /// SMPTE 240M
459    Smpte240M = 7,
460    /// Linear transfer (gamma 1.0)
461    Linear = 8,
462    /// IEC 61966-2-1 (sRGB) - recommended for computer graphics
463    Srgb = 13,
464    /// ITU-R BT.2020 10-bit (same curve as BT.709)
465    Bt2020_10 = 14,
466    /// ITU-R BT.2020 12-bit (same curve as BT.709)
467    Bt2020_12 = 15,
468    /// SMPTE ST 2084 (PQ / HDR10)
469    Smpte2084 = 16,
470    /// ARIB STD-B67 (HLG)
471    Hlg = 18,
472}
473
474impl TransferCharacteristics {
475    /// Get the raw u8 value for the VUI transfer_characteristics field.
476    #[must_use]
477    pub const fn as_u8(self) -> u8 {
478        self as u8
479    }
480}
481
482/// H.264 matrix_coefficients values (ITU-T H.264 Table E-5).
483///
484/// Specifies the matrix coefficients for deriving luma and chroma from RGB.
485#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
486#[repr(u8)]
487pub enum MatrixCoefficients {
488    /// Identity (RGB, no matrix transformation)
489    Identity = 0,
490    /// ITU-R BT.709-6 (Kr=0.2126, Kb=0.0722) - HD television
491    #[default]
492    Bt709 = 1,
493    /// Unspecified
494    Unspecified = 2,
495    /// FCC 73.682 (historical)
496    Fcc = 4,
497    /// ITU-R BT.470-6 System B, G (same as BT.601-7 625)
498    Bt470Bg = 5,
499    /// SMPTE 170M / ITU-R BT.601-7 525 (Kr=0.299, Kb=0.114) - SD television
500    Smpte170M = 6,
501    /// SMPTE 240M
502    Smpte240M = 7,
503    /// YCgCo (lossless)
504    Ycgco = 8,
505    /// ITU-R BT.2020 non-constant luminance
506    Bt2020Ncl = 9,
507    /// ITU-R BT.2020 constant luminance
508    Bt2020Cl = 10,
509}
510
511impl MatrixCoefficients {
512    /// Get the raw u8 value for the VUI matrix_coefficients field.
513    #[must_use]
514    pub const fn as_u8(self) -> u8 {
515        self as u8
516    }
517}
518
519/// H.264 VUI configuration for signaling color space to decoders.
520///
521/// This struct groups all VUI color-related fields together for convenience.
522/// Use [`VuiConfig::bt709()`] or similar constructors for common presets.
523///
524/// # Example
525///
526/// ```
527/// use openh264::encoder::{EncoderConfig, VuiConfig};
528///
529/// let config = EncoderConfig::new()
530///     .vui(VuiConfig::bt709().full_range(true));  // HD BT.709 with full range
531/// ```
532#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
533#[must_use]
534pub struct VuiConfig {
535    /// Chromaticity coordinates of source primaries
536    color_primaries: ColorPrimaries,
537    /// Transfer characteristics (gamma)
538    transfer_characteristics: TransferCharacteristics,
539    /// Matrix coefficients for RGB↔YCbCr conversion
540    matrix_coefficients: MatrixCoefficients,
541    /// True for full range (0-255), false for limited range (16-235)
542    full_range: bool,
543}
544
545impl VuiConfig {
546    /// Create a new VuiConfig with default values (BT.709 limited range).
547    pub const fn new() -> Self {
548        Self::bt709()
549    }
550
551    /// BT.709 with limited range (default for HD content).
552    ///
553    /// This is the standard for HD television and most video content.
554    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    /// BT.709 with full range (for PC/computer graphics content).
564    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    /// BT.601 (SMPTE 170M) with limited range (for SD content).
574    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    /// sRGB with full range (ideal for desktop/web content).
584    ///
585    /// Uses BT.709 primaries but with sRGB transfer function.
586    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    /// BT.2020 with limited range (for UHD/HDR content).
596    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    /// Set the color primaries.
606    pub const fn color_primaries(mut self, value: ColorPrimaries) -> Self {
607        self.color_primaries = value;
608        self
609    }
610
611    /// Set the transfer characteristics.
612    pub const fn transfer_characteristics(mut self, value: TransferCharacteristics) -> Self {
613        self.transfer_characteristics = value;
614        self
615    }
616
617    /// Set the matrix coefficients.
618    pub const fn matrix_coefficients(mut self, value: MatrixCoefficients) -> Self {
619        self.matrix_coefficients = value;
620        self
621    }
622
623    /// Set full range mode.
624    pub const fn full_range(mut self, value: bool) -> Self {
625        self.full_range = value;
626        self
627    }
628}
629
630/// Configuration for the [`Encoder`].
631///
632/// Setting missing? Please file a PR!
633#[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    /// Creates a new default encoder config.
662    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    /// Sets the requested bit rate in bits per second.
689    pub const fn bitrate(mut self, bps: BitRate) -> Self {
690        self.target_bitrate = bps;
691        self
692    }
693
694    /// Enables detailed console logging inside OpenH264.
695    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    /// Set whether frames can be skipped to meet desired rate control target.
701    pub const fn skip_frames(mut self, value: bool) -> Self {
702        self.enable_skip_frame = value;
703        self
704    }
705
706    /// Sets the requested maximum frame rate in Hz.
707    pub const fn max_frame_rate(mut self, value: FrameRate) -> Self {
708        self.max_frame_rate = value;
709        self
710    }
711
712    /// Sets the usage type (e.g, screen vs. camera recording).
713    pub const fn usage_type(mut self, value: UsageType) -> Self {
714        self.usage_type = value;
715        self
716    }
717
718    /// Sets the requested rate control mode.
719    pub const fn rate_control_mode(mut self, value: RateControlMode) -> Self {
720        self.rate_control_mode = value;
721        self
722    }
723
724    /// Set the SPS/PPS behavior.
725    pub const fn sps_pps_strategy(mut self, value: SpsPpsStrategy) -> Self {
726        self.sps_pps_strategy = value;
727        self
728    }
729
730    /// Set the maximum slice length
731    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    /// Set the encoding profile
737    pub const fn profile(mut self, profile: Profile) -> Self {
738        self.profile = Some(profile);
739        self
740    }
741
742    /// Set the encoding profile level
743    pub const fn level(mut self, level: Level) -> Self {
744        self.level = Some(level);
745        self
746    }
747
748    /// Set the complexity
749    pub const fn complexity(mut self, complexity: Complexity) -> Self {
750        self.complexity = complexity;
751        self
752    }
753
754    /// Set the balance between compression and size
755    pub const fn qp(mut self, value: QpRange) -> Self {
756        self.qp = value;
757        self
758    }
759
760    /// Set scene change detect (on by default)
761    pub const fn scene_change_detect(mut self, value: bool) -> Self {
762        self.scene_change_detect = value;
763        self
764    }
765
766    /// Set adaptive quantization control (on by default)
767    pub const fn adaptive_quantization(mut self, value: bool) -> Self {
768        self.adaptive_quantization = value;
769        self
770    }
771
772    /// Set background detection (on by default)
773    pub const fn background_detection(mut self, value: bool) -> Self {
774        self.background_detection = value;
775        self
776    }
777
778    /// Set use of long term reference (off by default)
779    pub const fn long_term_reference(mut self, value: bool) -> Self {
780        self.long_term_reference = value;
781        self
782    }
783
784    /// Set the interval of intra frames (0 by default, disabling periodic intra frames)
785    pub const fn intra_frame_period(mut self, value: IntraFramePeriod) -> Self {
786        self.intra_frame_period = value;
787        self
788    }
789
790    /// Sets the number of internal encoder threads.
791    ///
792    /// * 0 - auto mode
793    /// * 1 - single threaded operation
794    /// * &gt;1 - fixed number of threads
795    ///
796    /// Defaults to 0 (auto mode).
797    pub const fn num_threads(mut self, threads: u16) -> Self {
798        self.multiple_thread_idc = threads;
799        self
800    }
801
802    /// Sets the VUI (Video Usability Information) parameters.
803    ///
804    /// VUI parameters are written into the H.264 SPS NAL unit and tell decoders
805    /// how to interpret the color space of the video data. This is essential for
806    /// correct color reproduction.
807    ///
808    /// # Example
809    ///
810    /// ```rust
811    /// use openh264::encoder::{EncoderConfig, VuiConfig};
812    ///
813    /// let config = EncoderConfig::new()
814    ///     .vui(VuiConfig::bt709());  // HD content with BT.709 color space
815    /// ```
816    ///
817    /// See [`VuiConfig`] for common presets like `bt709()`, `srgb()`, and `bt601()`.
818    pub const fn vui(mut self, config: VuiConfig) -> Self {
819        self.vui = Some(config);
820        self
821    }
822}
823
824/// An [OpenH264](https://github.com/cisco/openh264) encoder.
825pub 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    /// Create an encoder with default settings.
837    ///
838    /// The width and height will be taken from the [`YUVSource`] when calling [`Encoder::encode()`].
839    ///
840    /// This method is only available when compiling with the `source` feature.
841    ///
842    /// # Errors
843    ///
844    /// This should never error, but the underlying OpenH264 encoder has an error indication and
845    /// since we don't know their code that well we just can't guarantee it.
846    #[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    /// Create an encoder with the provided [API](OpenH264API) and [configuration](EncoderConfig).
860    ///
861    /// The width and height will be taken from the [`YUVSource`] when calling [`Encoder::encode()`].
862    ///
863    /// # Errors
864    ///
865    /// Might fail if the provided encoder parameters had issues.
866    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    /// Encodes a YUV source and returns the encoded bitstream.
878    ///
879    /// The returned bitstream consists of one or more NAL units or packets. The first packets contain
880    /// initialization information. Subsequent packages then contain, amongst others, keyframes
881    /// ("I frames") or delta frames. The interval at which they are produced depends on the encoder settings.
882    ///
883    /// The resolution of the encoded frame is allowed to change. Each time it changes, the
884    /// encoder is re-initialized with the new values.
885    ///
886    /// # Errors
887    ///
888    /// This might error for various reasons, many of which aren't clearly documented in OpenH264.
889    pub fn encode<T: YUVSource>(&mut self, yuv_source: &T) -> Result<EncodedBitStream<'_>, Error> {
890        self.encode_at(yuv_source, Timestamp::ZERO)
891    }
892
893    /// Encodes a YUV source and returns the encoded bitstream.
894    ///
895    /// The returned bitstream consists of one or more NAL units or packets. The first packets contain
896    /// initialization information. Subsequent packages then contain, amongst others, keyframes
897    /// ("I frames") or delta frames. The interval at which they are produced depends on the encoder settings.
898    ///
899    /// The resolution of the encoded frame is allowed to change. Each time it changes, the
900    /// encoder is re-initialized with the new values.
901    ///
902    /// # Panics
903    ///
904    /// Panics if the provided timestamp as milliseconds is out of range of i64.
905    ///
906    /// # Errors
907    ///
908    /// This might error for various reasons, many of which aren't clearly documented in OpenH264.
909    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        // Converting *const u8 to *mut u8 should be fine because the encoder _should_
920        // only read these arrays (TODO: needs verification).
921        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        // https://github.com/cisco/openh264/blob/master/README.md
952        // > Encoder errors when resolution exceeds 3840x2160 or 2160x3840
953        //
954        // Some more detail here:
955        // https://github.com/cisco/openh264/issues/3553
956        // > Currently the encoder/decoder could only support up to level 5.2,
957        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; // If we do .into() instead, could this fail to compile on some platforms?
969        params.iPicHeight = height as c_int; // If we do .into() instead, could this fail to compile on some platforms?
970        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        // Apply VUI (Video Usability Information) parameters for color space signaling
998        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            // Limit the slice length by setting both MaxNalSize and uiSliceSizeConstraint
1018            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            // No size limit, explicitly use defaults
1024            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                // First time we call initialize_ext
1031                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                // Subsequent times we call SetOption
1036                self.raw_api.set_option(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, addr_of_mut!(params).cast()).ok()?;
1037
1038                // Start with a new keyframe after dimensions changed.
1039                self.force_intra_frame();
1040            }
1041        }
1042
1043        Ok(())
1044    }
1045
1046    /// Forces the encoder to emit an intra frame (I-frame, "keyframe") for the next encoded frame.
1047    pub fn force_intra_frame(&mut self) {
1048        // SAFETY: This should be safe, simply as there is no indication why it shouldn't be. We are
1049        // initialized at this point, and forcing an IDR should be straightforward.
1050        unsafe {
1051            self.raw_api.force_intra_frame(true);
1052        }
1053    }
1054
1055    /// Obtain the raw API for advanced use cases.
1056    ///
1057    /// When resorting to this call, please consider filing an issue / PR.
1058    ///
1059    /// # Safety
1060    ///
1061    /// You must not set parameters the encoder relies on, we recommend checking the source.
1062    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        // Safe because when we drop the pointer must have been initialized.
1070        unsafe {
1071            self.raw_api.uninitialize();
1072        }
1073    }
1074}
1075
1076/// Bitstream output resulting from an [`encode()`](Encoder::encode) operation.
1077pub struct EncodedBitStream<'a> {
1078    /// Holds the bitstream info just encoded.
1079    bit_stream_info: &'a SFrameBSInfo,
1080}
1081
1082impl<'a> EncodedBitStream<'a> {
1083    /// Raw bitstream info returned by the encoder.
1084    #[must_use]
1085    pub const fn raw_info(&self) -> &'a SFrameBSInfo {
1086        self.bit_stream_info
1087    }
1088
1089    /// Frame type of the encoded packet.
1090    #[must_use]
1091    pub const fn frame_type(&self) -> FrameType {
1092        FrameType::from_c_int(self.bit_stream_info.eFrameType)
1093    }
1094
1095    /// Number of layers in the encoded packet.
1096    #[must_use]
1097    pub const fn num_layers(&self) -> usize {
1098        self.bit_stream_info.iLayerNum as usize
1099    }
1100
1101    /// Returns ith layer of this bitstream.
1102    #[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    /// Writes the current bitstream into the given Vec.
1114    #[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    /// Writes the current bitstream into the given Writer.
1128    ///
1129    /// # Errors
1130    ///
1131    /// Can error when bytes could not be written.
1132    #[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    /// Convenience method returning a Vec containing the encoded bitstream.
1155    #[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/// An encoded layer, contains the Network Abstraction Layer inputs.
1164#[derive(Debug)]
1165pub struct Layer<'a> {
1166    /// Native layer info.
1167    layer_info: &'a SLayerBSInfo,
1168}
1169
1170impl<'a> Layer<'a> {
1171    /// Raw layer info contained in a bitstream.
1172    #[must_use]
1173    pub const fn raw_info(&self) -> &'a SLayerBSInfo {
1174        self.layer_info
1175    }
1176
1177    /// NAL count of this layer.
1178    #[must_use]
1179    pub const fn nal_count(&self) -> usize {
1180        self.layer_info.iNalCount as usize
1181    }
1182
1183    /// Returns NAL unit data for the ith element.
1184    #[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                // Fast forward through all NALs we didn't request
1191                // TODO: We can probably do this math a bit more efficiently, not counting up all the time.
1192                // pNalLengthInByte is a c_int C array containing the nal unit sizes
1193                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    /// If this is a video layer or not.
1209    #[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/// Frame type returned by the encoder.
1216///
1217/// The variant documentation was directly taken from OpenH264 project.
1218#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
1219pub enum FrameType {
1220    /// Encoder not ready or parameters are invalidate.
1221    Invalid,
1222    /// IDR frame in H.264
1223    IDR,
1224    /// I frame type
1225    I,
1226    /// P frame type
1227    P,
1228    /// Skip the frame based encoder kernel"
1229    Skip,
1230    /// A frame where I and P slices are mixing, not supported yet.
1231    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}