11use super :: EncodedFrame ;
22use crate :: { RecorderError , VideoEncoder , VideoEncoderConfig , recorder:: ResizedImageBuffer } ;
3+ use image:: { ImageBuffer , Rgb } ;
34use openh264:: {
45 OpenH264API ,
5- encoder:: { Complexity , Encoder , EncoderConfig , FrameRate , Profile , UsageType } ,
6+ encoder:: { Complexity , Encoder , EncoderConfig , FrameRate , Profile , RateControlMode , UsageType } ,
67 formats:: { RgbSliceU8 , YUVBuffer } ,
78} ;
89
@@ -11,6 +12,8 @@ pub struct OpenH264VideoEncoder {
1112 height : u32 ,
1213 frame_index : u64 ,
1314 encoder : Encoder ,
15+ headers_cache : Option < Vec < u8 > > ,
16+ first_frame_encoded : bool ,
1417}
1518
1619impl OpenH264VideoEncoder {
@@ -20,9 +23,12 @@ impl OpenH264VideoEncoder {
2023 let encoder_config = EncoderConfig :: new ( )
2124 . max_frame_rate ( FrameRate :: from_hz ( config. fps . to_u32 ( ) as f32 ) )
2225 . skip_frames ( false )
23- . usage_type ( UsageType :: ScreenContentRealTime )
2426 . complexity ( Complexity :: High )
25- . profile ( Profile :: Baseline ) ;
27+ . background_detection ( false )
28+ . adaptive_quantization ( false )
29+ . profile ( Profile :: Baseline )
30+ . rate_control_mode ( RateControlMode :: Off )
31+ . usage_type ( UsageType :: ScreenContentRealTime ) ;
2632
2733 let encoder = Encoder :: with_api_config ( OpenH264API :: from_source ( ) , encoder_config)
2834 . map_err ( |e| {
@@ -36,8 +42,137 @@ impl OpenH264VideoEncoder {
3642 height : config. height ,
3743 encoder,
3844 frame_index : 0 ,
45+ headers_cache : None ,
46+ first_frame_encoded : false ,
3947 } )
4048 }
49+
50+ fn convert_annex_b_to_length_prefixed ( & self , annex_b_data : & [ u8 ] ) -> Vec < u8 > {
51+ let mut result = Vec :: new ( ) ;
52+ let mut i = 0 ;
53+
54+ // Parse Annex B NAL units and convert to length-prefixed format
55+ while i < annex_b_data. len ( ) {
56+ // Look for NAL start codes (00 00 00 01 or 00 00 01)
57+ let start_code_len = if i + 4 <= annex_b_data. len ( )
58+ && annex_b_data[ i] == 0
59+ && annex_b_data[ i + 1 ] == 0
60+ && annex_b_data[ i + 2 ] == 0
61+ && annex_b_data[ i + 3 ] == 1
62+ {
63+ 4
64+ } else if i + 3 <= annex_b_data. len ( )
65+ && annex_b_data[ i] == 0
66+ && annex_b_data[ i + 1 ] == 0
67+ && annex_b_data[ i + 2 ] == 1
68+ {
69+ 3
70+ } else {
71+ i += 1 ;
72+ continue ;
73+ } ;
74+
75+ // Skip the start code
76+ let nal_start = i + start_code_len;
77+ if nal_start >= annex_b_data. len ( ) {
78+ break ;
79+ }
80+
81+ // Find the end of this NAL unit (next start code or end of data)
82+ let mut nal_end = nal_start;
83+ while nal_end + 3 <= annex_b_data. len ( ) {
84+ if ( annex_b_data[ nal_end] == 0
85+ && annex_b_data[ nal_end + 1 ] == 0
86+ && annex_b_data[ nal_end + 2 ] == 0
87+ && annex_b_data[ nal_end + 3 ] == 1 )
88+ || ( annex_b_data[ nal_end] == 0
89+ && annex_b_data[ nal_end + 1 ] == 0
90+ && annex_b_data[ nal_end + 2 ] == 1 )
91+ {
92+ break ;
93+ }
94+ nal_end += 1 ;
95+ }
96+
97+ // If we reached the end without finding another start code, go to the actual end
98+ if nal_end + 3 > annex_b_data. len ( ) {
99+ nal_end = annex_b_data. len ( ) ;
100+ }
101+
102+ let nal_data = & annex_b_data[ nal_start..nal_end] ;
103+ if !nal_data. is_empty ( ) {
104+ // Add length prefix (4 bytes, big-endian)
105+ result. extend_from_slice ( & ( nal_data. len ( ) as u32 ) . to_be_bytes ( ) ) ;
106+ result. extend_from_slice ( nal_data) ;
107+ }
108+
109+ i = nal_end;
110+ }
111+
112+ result
113+ }
114+
115+ fn extract_sps_pps_from_bitstream ( & self , bitstream : & [ u8 ] ) -> Option < Vec < u8 > > {
116+ let mut sps_data = None ;
117+ let mut pps_data = None ;
118+ let mut result = Vec :: new ( ) ;
119+ let mut i = 0 ;
120+
121+ // Parse length-prefixed NAL units (this should be the converted format)
122+ while i + 4 <= bitstream. len ( ) {
123+ // Read NAL unit length (big-endian)
124+ let nal_length = ( ( bitstream[ i] as u32 ) << 24 )
125+ | ( ( bitstream[ i + 1 ] as u32 ) << 16 )
126+ | ( ( bitstream[ i + 2 ] as u32 ) << 8 )
127+ | ( bitstream[ i + 3 ] as u32 ) ;
128+
129+ if i + 4 + nal_length as usize > bitstream. len ( ) {
130+ log:: warn!( "Invalid NAL length {} at position {}" , nal_length, i) ;
131+ break ;
132+ }
133+
134+ let nal_start = i + 4 ;
135+ let nal_end = nal_start + nal_length as usize ;
136+ let nal_data = & bitstream[ nal_start..nal_end] ;
137+
138+ if !nal_data. is_empty ( ) {
139+ let nal_type = nal_data[ 0 ] & 0x1F ;
140+ match nal_type {
141+ 7 => {
142+ sps_data = Some ( nal_data) ;
143+ log:: debug!( "Found SPS: {} bytes" , nal_data. len( ) ) ;
144+ }
145+ 8 => {
146+ pps_data = Some ( nal_data) ;
147+ log:: debug!( "Found PPS: {} bytes" , nal_data. len( ) ) ;
148+ }
149+ _ => { }
150+ }
151+ }
152+
153+ i = nal_end;
154+ }
155+
156+ // If we found both SPS and PPS, create length-prefixed header data
157+ if let ( Some ( sps) , Some ( pps) ) = ( sps_data, pps_data) {
158+ // Add length prefix (4 bytes, big-endian) for SPS
159+ result. extend_from_slice ( & ( sps. len ( ) as u32 ) . to_be_bytes ( ) ) ;
160+ result. extend_from_slice ( sps) ;
161+ // Add length prefix (4 bytes, big-endian) for PPS
162+ result. extend_from_slice ( & ( pps. len ( ) as u32 ) . to_be_bytes ( ) ) ;
163+ result. extend_from_slice ( pps) ;
164+
165+ log:: debug!(
166+ "Extracted SPS ({} bytes) and PPS ({} bytes) from OpenH264 bitstream" ,
167+ sps. len( ) ,
168+ pps. len( )
169+ ) ;
170+ return Some ( result) ;
171+ }
172+
173+ log:: warn!( "Could not find both SPS and PPS in bitstream" ) ;
174+ None
175+ }
41176}
42177
43178impl VideoEncoder for OpenH264VideoEncoder {
@@ -58,13 +193,72 @@ impl VideoEncoder for OpenH264VideoEncoder {
58193 RecorderError :: VideoEncodingFailed ( format ! ( "OpenH264 encoding failed: {:?}" , e) )
59194 } ) ?;
60195
61- let encoded_frame = EncodedFrame :: Frame ( ( self . frame_index , bitstream. to_vec ( ) ) ) ;
62- self . frame_index += 1 ;
196+ // Convert OpenH264 Annex B output to length-prefixed format
197+ let annex_b_data = bitstream. to_vec ( ) ;
198+ let converted_data = self . convert_annex_b_to_length_prefixed ( & annex_b_data) ;
63199
200+ // If this is the first frame and we haven't cached headers yet, try to extract SPS/PPS
201+ if !self . first_frame_encoded && self . headers_cache . is_none ( ) {
202+ if let Some ( headers) = self . extract_sps_pps_from_bitstream ( & converted_data) {
203+ self . headers_cache = Some ( headers) ;
204+ log:: info!( "Successfully extracted SPS/PPS headers from first OpenH264 frame" ) ;
205+ } else {
206+ log:: warn!( "Could not extract SPS/PPS from first frame" ) ;
207+ }
208+ self . first_frame_encoded = true ;
209+ }
210+
211+ let encoded_frame = EncodedFrame :: Frame ( ( self . frame_index , converted_data) ) ;
212+ self . frame_index += 1 ;
64213 Ok ( encoded_frame)
65214 }
66215
67216 fn headers ( & mut self ) -> Result < Vec < u8 > , RecorderError > {
217+ if let Some ( ref headers) = self . headers_cache {
218+ return Ok ( headers. clone ( ) ) ;
219+ }
220+
221+ log:: debug!( "Encoding test frame to extract SPS/PPS headers from OpenH264" ) ;
222+
223+ let test_frame_data = vec ! [ 0u8 ; ( self . width * self . height * 3 ) as usize ] ;
224+ let test_img =
225+ ImageBuffer :: < Rgb < u8 > , Vec < u8 > > :: from_raw ( self . width , self . height , test_frame_data)
226+ . ok_or_else ( || {
227+ RecorderError :: ImageProcessingFailed (
228+ "Failed to create test frame for SPS/PPS extraction" . to_string ( ) ,
229+ )
230+ } ) ?;
231+
232+ let rgb_source = RgbSliceU8 :: new (
233+ test_img. as_raw ( ) ,
234+ ( self . width as usize , self . height as usize ) ,
235+ ) ;
236+ let yuv_buffer = YUVBuffer :: from_rgb8_source ( rgb_source) ;
237+
238+ match self . encoder . encode ( & yuv_buffer) {
239+ Ok ( bitstream) => {
240+ // Convert Annex B format to length-prefixed format
241+ let annex_b_data = bitstream. to_vec ( ) ;
242+ let converted_data = self . convert_annex_b_to_length_prefixed ( & annex_b_data) ;
243+
244+ if let Some ( headers) = self . extract_sps_pps_from_bitstream ( & converted_data) {
245+ self . headers_cache = Some ( headers. clone ( ) ) ;
246+ log:: info!( "Successfully extracted SPS/PPS headers from OpenH264 test frame" ) ;
247+ return Ok ( headers) ;
248+ } else {
249+ log:: warn!(
250+ "Could not extract SPS/PPS from OpenH264 test frame, using empty headers"
251+ ) ;
252+ }
253+ }
254+ Err ( e) => {
255+ log:: warn!(
256+ "Failed to encode test frame for SPS/PPS extraction: {:?}" ,
257+ e
258+ ) ;
259+ }
260+ }
261+
68262 Ok ( vec ! [ ] )
69263 }
70264
0 commit comments