I wrote the following script to solve my problem. It uses framemd5
checksums to find the inpoints in time and then uses the concat
demuxer (thanks @Gyan for the tip!).
# Usage: ffmpeg $(python3 script.py input1.mp4 input2.mp4) -y -an output.mp4
import os
import sys
import subprocess
ffmpeg_concat_echo, md5_last_frame = '', None
for video_path in map(os.path.abspath, sys.argv[1:]):
framemd5 = subprocess.check_output(['ffmpeg', '-nostats', '-hide_banner', '-i', video_path, '-an', '-f', 'framemd5', '-c', 'copy', '-'], stderr = open(os.devnull, 'w'))
tb_num, tb_den = 1, 1
inpoint = 0.0
for line in framemd5.split(b'\n'):
if line.startswith(b'#tb'):
tb_num, tb_den = list(map(int, line.split()[-1].split(b'/')))
elif line and not line.startswith(b'#'):
splitted = line.split(b',')
md5 = splitted[-1].strip()
pts_time = float(splitted[2]) * tb_num / tb_den
if md5_last_frame is not None and md5 == md5_last_frame:
inpoint = pts_time
md5_last_frame = md5
ffmpeg_concat_echo += 'echo "file {}"; echo "inpoint {}";'.format(video_path, inpoint);
print('-nostats -hide_banner -avoid_negative_ts make_zero -fflags +genpts -f concat -safe 0 -i <({}) -c copy'.format(ffmpeg_concat_echo))
Below is another version (source, archived) which supports exact PTS inpoints using concat
video filter; computing inpoints using explicit frame differences.
# Usage: ffmpeg $(( echo input1.mp4; echo input2.mp4 ) | python3 concat_overlapping_video_framemd5_demuxer.py concat_file.txt) -y -an output.mp4
import os
import sys
import subprocess
ffmpeg_concat_file, md5_last_frame = open(sys.argv[1], 'w'), None
for video_path in map(os.path.abspath, list(map(str.strip, sys.stdin))):
framemd5 = subprocess.check_output(['ffmpeg', '-nostats', '-hide_banner', '-i', video_path, '-an', '-f', 'framemd5', '-c', 'copy', '-'], stderr = open(os.devnull, 'w'))
inpoint, tb_num, tb_den = 0, 1, 1
for line in framemd5.split(b'\n'):
if line.startswith(b'#tb'):
tb_num, tb_den = list(map(int, line.split()[-1].split(b'/')))
elif not line.startswith(b'#') and len(line) > 0:
splitted = line.split(b',')
md5, pts_time = splitted[-1].strip(), float(splitted[2]) * tb_num / tb_den
if md5 == md5_last_frame:
inpoint = pts_time
md5_last_frame = md5
ffmpeg_concat_file.write('file {}\ninpoint {}\n'.format(video_path, inpoint))
print('-nostats -hide_banner -avoid_negative_ts make_zero -fflags +genpts -f concat -safe 0 -i {} -c copy'.format(sys.argv[1]))
-vf showinfo
, and it seems they do not have absolute timestamps, but they do have some checksums (are these useful for finding exact duplicates?) Thanks for the pointer on blend/blackframe - googling it.-copyts
else ffmpeg will remove any starting offset before it gets to the filter.framemd5
matching is enough for my videos. I can makeframemd5
files for every video segment and fetch out dts / pts of the starting frame in every segment. Is there a way to concat with dts / pts inpoints instead of timestamps?