@@ -104,12 +104,12 @@ bool VideoRecorder::initialize_video()
104104 codec_context->time_base = AVRational{1 , options.fps };
105105 codec_context->framerate = AVRational{options.fps , 1 };
106106
107- // MOBILE COMPATIBILITY: Set adaptive GOP size based on resolution
108107 // This will be used for keyframe forcing and must match the keyint setting
108+ bool isFullHD = (options.width >= 1920 || options.height >= 1080 );
109109 bool isHighRes = (options.width >= 1280 || options.height >= 720 );
110110 bool isLowRes = (options.width <= 640 && options.height <= 480 );
111- codec_context->gop_size = isHighRes ? 60 : isLowRes ? 15 : 30 ;
112-
111+ codec_context->gop_size = isFullHD ? 90 : isHighRes ? 60 : isLowRes ? 15 : 30 ;
112+
113113 codec_context->pix_fmt = AV_PIX_FMT_YUV420P;
114114 codec_context->bit_rate = options.bitrate ;
115115
@@ -121,71 +121,72 @@ bool VideoRecorder::initialize_video()
121121
122122 if (codec_context->codec_id == AV_CODEC_ID_H264)
123123 {
124+ bool isFullHD = (options.width >= 1920 || options.height >= 1080 );
125+ if (isFullHD) {
126+ av_opt_set (codec_context->priv_data , " profile" , " main" , 0 );
127+ av_opt_set (codec_context->priv_data , " level" , " 4.1" , 0 );
128+ av_opt_set_int (codec_context->priv_data , " refs" , 2 , 0 );
129+ av_opt_set_int (codec_context->priv_data , " b-frames" , 0 , 0 );
130+ } else {
131+ av_opt_set (codec_context->priv_data , " profile" , " baseline" , 0 );
132+ }
133+
124134 std::string preset = options.preset .empty () || options.preset == " realtime" ? " ultrafast" : options.preset ;
125135 av_opt_set (codec_context->priv_data , " preset" , preset.c_str (), 0 );
126136 av_opt_set (codec_context->priv_data , " tune" , " zerolatency" , 0 );
127- av_opt_set (codec_context->priv_data , " profile " , " baseline " , 0 );
128- av_opt_set_int (codec_context-> priv_data , " crf " , 23 , 0 );
137+ av_opt_set_int (codec_context->priv_data , " crf " , 20 , 0 );
138+
129139
130140 // CRITICAL: Configure for MP4 container - use AVCC format, not Annex B
131141 codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
132-
142+
133143 // Force AVCC format (length-prefixed NAL units) for MP4 compatibility
134144 av_opt_set_int (codec_context->priv_data , " annexb" , 0 , 0 );
135-
145+
136146 // MOBILE COMPATIBILITY: Adaptive settings based on resolution
137- DEBUG (" Configuring H.264 for " + std::to_string (options.width ) + " x" + std::to_string (options.height ) +
138- (isHighRes ? " (high resolution)" : isLowRes ? " (low resolution)" : " (standard resolution)" ));
139-
147+ DEBUG (" Configuring H.264 for " + std::to_string (options.width ) + " x" + std::to_string (options.height ) +
148+ (isFullHD ? " (high resolution)" : isLowRes ? " (low resolution)" : " (standard resolution)" ));
149+
140150 // CRITICAL: Force keyframe generation and SPS/PPS inclusion
141151 av_opt_set_int (codec_context->priv_data , " force-cfr" , 1 , 0 );
142-
143- // MOBILE COMPATIBILITY: Adaptive keyframe interval based on resolution
144- int keyframe_interval = isHighRes ? 60 : isLowRes ? 15 : 30 ;
152+ int keyframe_interval = isFullHD ? 90 : isHighRes ? 60 : isLowRes ? 15 : 30 ;
145153 av_opt_set_int (codec_context->priv_data , " keyint" , keyframe_interval, 0 );
146154 av_opt_set_int (codec_context->priv_data , " keyint_min" , 1 , 0 );
147155 av_opt_set_int (codec_context->priv_data , " scenecut" , 0 , 0 );
148156 av_opt_set_int (codec_context->priv_data , " intra-refresh" , 0 , 0 );
149-
157+
150158 DEBUG (" Using keyframe interval: " + std::to_string (keyframe_interval) + " frames for mobile compatibility" );
151-
152- // CRITICAL: Force full range encoding to match input
159+
153160 av_opt_set (codec_context->priv_data , " colorprim" , " bt709" , 0 );
154161 av_opt_set (codec_context->priv_data , " transfer" , " bt709" , 0 );
155162 av_opt_set (codec_context->priv_data , " colormatrix" , " bt709" , 0 );
156163 av_opt_set (codec_context->priv_data , " range" , " pc" , 0 ); // Full range
157-
158- // CRITICAL: Ensure SPS/PPS are written
164+
159165 av_opt_set_int (codec_context->priv_data , " repeat-headers" , 1 , 0 ); // Repeat SPS/PPS
160166 av_opt_set_int (codec_context->priv_data , " aud" , 1 , 0 ); // Add access unit delimiters
161-
162- // MOBILE COMPATIBILITY: Optimize encoding settings for different resolutions
167+
163168 if (isLowRes) {
164- // Low resolution: prioritize speed and compatibility
165- av_opt_set (codec_context->priv_data , " level" , " 3.0" , 0 ); // H.264 Level 3.0 for mobile
166- av_opt_set_int (codec_context->priv_data , " refs" , 1 , 0 ); // Single reference frame
167- av_opt_set_int (codec_context->priv_data , " b-frames" , 0 , 0 ); // No B-frames for simplicity
168- } else if (isHighRes) {
169- // High resolution: balance quality and compatibility
170- av_opt_set (codec_context->priv_data , " level" , " 4.0" , 0 ); // H.264 Level 4.0 for HD
171- av_opt_set_int (codec_context->priv_data , " refs" , 2 , 0 ); // Two reference frames
172- av_opt_set_int (codec_context->priv_data , " b-frames" , 1 , 0 ); // Minimal B-frames
169+ av_opt_set (codec_context->priv_data , " level" , " 3.0" , 0 );
170+ av_opt_set_int (codec_context->priv_data , " refs" , 1 , 0 );
171+ av_opt_set_int (codec_context->priv_data , " b-frames" , 0 , 0 );
172+ } else if (isFullHD) {
173+ av_opt_set (codec_context->priv_data , " level" , " 4.0" , 0 );
174+ av_opt_set_int (codec_context->priv_data , " refs" , 2 , 0 );
175+ av_opt_set_int (codec_context->priv_data , " b-frames" , 1 , 0 );
173176 } else {
174- // Standard resolution: balanced settings
175- av_opt_set (codec_context->priv_data , " level" , " 3.1" , 0 ); // H.264 Level 3.1
176- av_opt_set_int (codec_context->priv_data , " refs" , 1 , 0 ); // Single reference frame
177- av_opt_set_int (codec_context->priv_data , " b-frames" , 0 , 0 ); // No B-frames
177+ av_opt_set (codec_context->priv_data , " level" , " 3.1" , 0 );
178+ av_opt_set_int (codec_context->priv_data , " refs" , 1 , 0 );
179+ av_opt_set_int (codec_context->priv_data , " b-frames" , 0 , 0 );
178180 }
179181
180- // Use standard x264 options for MP4 compatibility
181182 av_opt_set (codec_context->priv_data , " x264opts" , " force-cfr=1:no-scenecut=1" , 0 );
182183
183184 codec_context->thread_count = 1 ;
184185 codec_context->thread_type = FF_THREAD_FRAME;
185186
186187 codec_context->rc_buffer_size = options.bitrate ;
187- codec_context->rc_max_rate = static_cast <int >(options.bitrate * 1.2 );
188- codec_context->rc_min_rate = static_cast <int >(options.bitrate * 0.5 );
188+ codec_context->rc_max_rate = static_cast <int >(options.bitrate * 1.5 );
189+ codec_context->rc_min_rate = static_cast <int >(options.bitrate * 0.8 );
189190
190191 av_opt_set_int (codec_context->priv_data , " fast-pskip" , 1 , 0 );
191192 av_opt_set_int (codec_context->priv_data , " no-dct-decimate" , 1 , 0 );
@@ -216,9 +217,9 @@ bool VideoRecorder::initialize_video()
216217 // VALIDATION: Verify SPS/PPS structure
217218 uint8_t * extradata = video_stream->codecpar ->extradata ;
218219 if (extradata[0 ] == 0x01 ) { // AVCC format marker
219- DEBUG (" ✓ AVCC format detected in extradata" );
220+ DEBUG (" AVCC format detected in extradata" );
220221 } else {
221- DEBUG (" ⚠️ WARNING: Unexpected extradata format - may cause playback issues" );
222+ DEBUG (" ⚠WARNING: Unexpected extradata format - may cause playback issues" );
222223 }
223224 } else {
224225 ERROR (" CRITICAL: No H.264 extradata found - SPS/PPS missing! Video will be unplayable." );
@@ -298,7 +299,6 @@ bool VideoRecorder::initialize_video()
298299 // Set MP4 specific options for proper MOOV atom handling
299300 AVDictionary *format_opts = nullptr ;
300301
301- // CRITICAL: Configure MP4 muxer for proper H.264 stream handling
302302 av_dict_set (&format_opts, " movflags" , " faststart+frag_keyframe+empty_moov+default_base_moof" , 0 );
303303
304304 // Force proper H.264 stream format in MP4 container
@@ -332,7 +332,7 @@ bool VideoRecorder::initialize_video()
332332
333333 // VALIDATION: Verify MP4 container state after header write
334334 if (format_context->nb_streams != 1 ) {
335- DEBUG (" ⚠️ WARNING: Expected 1 stream, got " + std::to_string (format_context->nb_streams ));
335+ DEBUG (" WARNING: Expected 1 stream, got " + std::to_string (format_context->nb_streams ));
336336 }
337337
338338 if (video_stream->codecpar ->codec_id != AV_CODEC_ID_H264) {
@@ -413,9 +413,9 @@ bool VideoRecorder::add_frame_rgba(const uint8_t* rgba_data, int width, int heig
413413
414414 bool needsScaling = (width != codec_context->width || height != codec_context->height );
415415 if (needsScaling) {
416- std::cout << " [VideoRecorder] ✓ Automatic scaling enabled for adaptive dimensions" << std::endl;
416+ std::cout << " [VideoRecorder] Automatic scaling enabled for adaptive dimensions" << std::endl;
417417 } else {
418- std::cout << " [VideoRecorder] ✓ Direct conversion - dimensions match" << std::endl;
418+ std::cout << " [VideoRecorder] Direct conversion - dimensions match" << std::endl;
419419 }
420420
421421 // Convert RGBA to YUV420P with scaling if needed
0 commit comments