@@ -19,6 +19,9 @@ max_attempts=${SE_VIDEO_WAIT_ATTEMPTS:-50}
1919file_ready_max_attempts=${SE_VIDEO_FILE_READY_WAIT_ATTEMPTS:- 5}
2020wait_uploader_shutdown_max_attempts=${SE_VIDEO_WAIT_UPLOADER_SHUTDOWN_ATTEMPTS:- 5}
2121graceful_stop_delay=${SE_VIDEO_GRACEFUL_STOP_DELAY:- 5}
22+ min_recording_duration=${SE_VIDEO_MIN_RECORDING_DURATION:- 5}
23+ video_validation_enabled=${SE_VIDEO_VALIDATION_ENABLED:- " true" }
24+ video_fix_corrupted=${SE_VIDEO_FIX_CORRUPTED:- " true" }
2225ts_format=${SE_LOG_TIMESTAMP_FORMAT:- " %Y-%m-%d %H:%M:%S,%3N" }
2326process_name=" video.recorder"
2427
@@ -187,12 +190,66 @@ function exit_on_max_session_reach() {
187190 fi
188191}
189192
193+ function validate_mp4_file() {
194+ local video_file=" $1 "
195+ local session_id_param=" $2 "
196+
197+ if [[ " ${video_validation_enabled} " != " true" ]]; then
198+ return 0
199+ fi
200+
201+ if [[ ! -f " ${video_file} " ]]; then
202+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] ERROR: Video file does not exist: ${video_file} "
203+ return 1
204+ fi
205+
206+ # Check if file has moov atom using ffmpeg probe
207+ if ffmpeg -v error -i " ${video_file} " -f null - 2>&1 | grep -q " moov atom not found" ; then
208+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] ERROR: Video file missing moov atom: ${video_file} "
209+ return 1
210+ fi
211+
212+ # Quick validation: check if ffmpeg can read the file
213+ if ! ffmpeg -v error -i " ${video_file} " -t 0.1 -f null - 2> /dev/null; then
214+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] ERROR: Video file validation failed: ${video_file} "
215+ return 1
216+ fi
217+
218+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Video file validation passed: ${video_file} "
219+ return 0
220+ }
221+
222+ function attempt_fix_mp4_file() {
223+ local video_file=" $1 "
224+ local session_id_param=" $2 "
225+
226+ if [[ " ${video_fix_corrupted} " != " true" ]]; then
227+ return 1
228+ fi
229+
230+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Attempting to fix corrupted MP4 file: ${video_file} "
231+
232+ local temp_file=" ${video_file} .temp.mp4"
233+
234+ # Try to recover the file by remuxing with faststart
235+ if ffmpeg -v warning -i " ${video_file} " -c copy -movflags +faststart " ${temp_file} " 2> /dev/null; then
236+ mv " ${temp_file} " " ${video_file} "
237+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Successfully fixed MP4 file: ${video_file} "
238+ return 0
239+ else
240+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Failed to fix MP4 file: ${video_file} "
241+ rm -f " ${temp_file} "
242+ return 1
243+ fi
244+ }
245+
190246function stop_ffmpeg() {
191247 while true ; do
192248 FFMPEG_PID=$( pgrep -f " ffmpeg -hide_banner" | tr ' \n' ' ' )
193249 if [ -n " $FFMPEG_PID " ]; then
194- kill -SIGINT $FFMPEG_PID
195- wait $FFMPEG_PID
250+ # Single SIGTERM for graceful shutdown - allows FFmpeg to write moov atom
251+ kill -SIGTERM $FFMPEG_PID
252+ wait $FFMPEG_PID 2> /dev/null
196253 fi
197254 if ! pgrep -f " ffmpeg -hide_banner" > /dev/null; then
198255 break
@@ -210,11 +267,12 @@ function stop_ffmpeg_graceful_async() {
210267 local session_id_param=" $6 "
211268
212269 (
213- # Send SIGINT to FFmpeg
270+ # Send single SIGTERM to FFmpeg for graceful shutdown
271+ # This allows FFmpeg to properly write the moov atom (MP4 metadata)
214272 FFMPEG_PID=$( pgrep -f " ffmpeg -hide_banner" | tr ' \n' ' ' )
215273 if [ -n " $FFMPEG_PID " ]; then
216- echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Sending SIGINT to FFmpeg PID: $FFMPEG_PID for file: ${video_file_name_param} "
217- kill -SIGINT $FFMPEG_PID
274+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Sending SIGTERM to FFmpeg PID: $FFMPEG_PID for file: ${video_file_name_param} "
275+ kill -SIGTERM $FFMPEG_PID
218276
219277 # Wait for FFmpeg to finish writing
220278 wait $FFMPEG_PID 2> /dev/null
@@ -223,15 +281,35 @@ function stop_ffmpeg_graceful_async() {
223281 echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Waiting ${graceful_stop_delay} seconds for video metadata finalization"
224282 sleep ${graceful_stop_delay}
225283
226- # Verify file integrity after grace period
284+ # Verify file exists
227285 if [[ -f " ${video_file_to_finalize} " ]]; then
228286 echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Video file finalized: ${video_file_to_finalize} "
229287
230- # Trigger upload AFTER file is finalized
231- if [[ " ${should_upload} " = " true" ]] && [[ -n " ${upload_dest_prefix} " ]] && [[ -n " ${upload_pipe_file} " ]]; then
232- upload_destination=" ${upload_dest_prefix} /${video_file_name_param} "
233- echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Add to pipe a signal Uploading video to ${upload_destination} "
234- echo " ${video_file_to_finalize} ${upload_dest_prefix} " >> " ${upload_pipe_file} "
288+ # Validate MP4 file integrity
289+ if validate_mp4_file " ${video_file_to_finalize} " " ${session_id_param} " ; then
290+ # File is valid, proceed with upload
291+ if [[ " ${should_upload} " = " true" ]] && [[ -n " ${upload_dest_prefix} " ]] && [[ -n " ${upload_pipe_file} " ]]; then
292+ upload_destination=" ${upload_dest_prefix} /${video_file_name_param} "
293+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Add to pipe a signal Uploading video to ${upload_destination} "
294+ echo " ${video_file_to_finalize} ${upload_dest_prefix} " >> " ${upload_pipe_file} "
295+ fi
296+ else
297+ # Validation failed, attempt to fix
298+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Video validation failed, attempting recovery"
299+ if attempt_fix_mp4_file " ${video_file_to_finalize} " " ${session_id_param} " ; then
300+ # Fixed successfully, re-validate and upload
301+ if validate_mp4_file " ${video_file_to_finalize} " " ${session_id_param} " ; then
302+ if [[ " ${should_upload} " = " true" ]] && [[ -n " ${upload_dest_prefix} " ]] && [[ -n " ${upload_pipe_file} " ]]; then
303+ upload_destination=" ${upload_dest_prefix} /${video_file_name_param} "
304+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] Add to pipe a signal Uploading recovered video to ${upload_destination} "
305+ echo " ${video_file_to_finalize} ${upload_dest_prefix} " >> " ${upload_pipe_file} "
306+ fi
307+ else
308+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] ERROR: Video file still corrupted after recovery attempt, skipping upload"
309+ fi
310+ else
311+ echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] ERROR: Video file recovery failed, skipping upload"
312+ fi
235313 fi
236314 else
237315 echo " $( date -u +" ${ts_format} " ) [${process_name} ] - [Session: ${session_id_param} ] WARNING: Video file not found after finalization: ${video_file_to_finalize} "
0 commit comments