1

I have a number of videos that overlap in time that were downloaded from an online video stream: 0m00s - 15m10s 14m50s - 30m20s ... The first few seconds (usually around 20 seconds) of a video segment duplicate last few seconds of the previous segment.

I'd like to concat the video segments and form a single video file without duplicate parts. I can do it programmatically using ffmpeg libraries to find the cut points by minimizing error between a frame of the current segment and the last frame of the previous segment.

Is there a way to achieve this with an ffmpeg command?

  • If the files have absolute timestamps, it may be possible. But if no, it's not possible in a single command. You can run one command to find join point betweeen two consecutive segments using blend filter/blackframe and then write a concat demuxer text file with the required inpoint/outpoint – Gyan Aug 20 '18 at 10:37
  • I just checked with -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. – Vadim Kantorov Aug 20 '18 at 10:43
  • You need to add -copyts else ffmpeg will remove any starting offset before it gets to the filter. – Gyan Aug 20 '18 at 10:46
  • It seems framemd5 matching is enough for my videos. I can make framemd5 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? – Vadim Kantorov Aug 20 '18 at 12:56
  • No, time has to be in seconds. – Gyan Aug 20 '18 at 14:23
0

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!). I also created a gist with a more complete version that supports exact PTS inpoints using concat video filter; computing inpoints using explicit frame differences.

# 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))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.