Potential Bug: moov atom missing in terminated RSTP feed recorded video files #1735
Unanswered
UltimateCodeWarrior
asked this question in
1. Help
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi All,
So there's a good chance when recording A/V from a RSTP stream that the stream will go down. Either the Network Connection, Router, Camera... Chain is only as strong as the weakest link. Problem is, the PyAV doesn't handle the error effectively, and just blows up somewhere deep in the bowels and doesn't allow for the finishing out of the output mp4 file gracefully and leaves it in an unplayable state. This corrupted mp4 file is missing something called a : moov atom. Would be nice if it would put one in there somewhere that could be moved to the beginning with a ffmpeg command such as:
ffmpeg -i corrupted_1738018839.561.mp4 -vcodec copy -acodec copy -movflags faststart fixed_video_file.mp4
There aren't many tools that can add the atom or fix the issue, or they are windows based tools. I need an automated way of fixing it or just fix the pyav so it never happens. It's a real hassle otherwise. Seems that for anything that needs to lock onto a RSTP feed, there should be some thought given to disaster recovery.
I've created a little python script that will alternate recording video with a slight overlap. I've also included a script that can stitch these sections back together with ffmpeg concat. It will calculate the overlap and shave off the front of the next file to try and keep the timeline correct. (Any improvements welcome, it's not perfect).
I did this method because trying to do a split recording with a/v with pyAV was a terrible experience. Fooling around with packet timings...really? I've included at the very end my version of some borrowed code to try and do this.
Smaller .mp4 files will result in more playable clips and better disaster recovery. Rather than recording to an hour long .mp4 file only to have it corrupted somewhere isn't a good idea. I mean if a vandal smashes your camera, good luck in trying to figure out what happened to it from the video logs. If you are recording in smaller chunks, you got a better chance of quickly identifying the perp from stored video. I'm actually locking on feed with opencv as well and saving the a bunch of jpeg frames to a disaster bin just in case of this.
So the code:
First Code writes out alternating video files with a slight overlap that gets cut out on ffmpeg concat code.
`import threading
import time
import av
rtsp_url = "rtsp://admin:admin@192.168.0.109:554/cam/realmonitor?channel=1&subtype=0"
def record_video(start_time, duration=60):
try:
source = av.open(rtsp_url, metadata_encoding='utf-8')
except OSError as e:
print(f"Error opening RTSP feed: {e}")
return
def schedule_recordings():
while True:
start_time = time.time()
threading.Thread(target=record_video, args=(start_time, 60)).start()
time.sleep(55) # Wait 55 seconds before starting the next recording
if name == "main":
schedule_recordings()`
FFMPEG MP4 Concat Code, will create a concatenated file ignoring overlaps from the files generated above.
`
import time
import os
import re
import subprocess
def get_timestamps(file):
# Extract the timestamp from the filename
match = re.search(r'output_(\d+.\d{3}).mp4', file)
if match:
file_timestamp = float(match.group(1))
else:
raise ValueError(f"Filename {file} does not contain a valid timestamp")
def merge():
# Get the current directory
current_directory = os.getcwd()
Call the merge function
ts = time.time()
merge()
print(time.time() - ts)
`
Here is some Code Tweaked from some other people on this forum, but enhanced with Audio Recording, works for first 10 seconds video clip, but audio fails for each successive. The first video has some sort of green screen artifact for 2 seconds, don't know what that's all about, but if you are feeling adventurous in adjusting packet timings....be my guest to work your magic.
`
import time
from os import path
import av
ts= time.time()
source = av.open('rtsp://admin:admin@192.168.0.109:554/cam/realmonitor?channel=1&subtype=0', metadata_encoding='utf-8')
v_stream = source.streams.video[0]
a_stream = source.streams.audio[0]
av_options = {'avoid_negative_ts': '1'}
packet = None
frag_offset = 0.0 # secs
frag_length = 10.0 # secs
frag_pts = 0
while True:
videoFilePath = f"out4_{frag_offset:.0f}.mp4"
with av.open(f"{videoFilePath}", "w") as mp4_output:
v_out_stream = mp4_output.add_stream_from_template(v_stream)
a_out_stream = mp4_output.add_stream_from_template(a_stream)
`
Beta Was this translation helpful? Give feedback.
All reactions