1

I want to be able to trim a little bit off the beginning and end of a long video with FFmpeg. I can do this with a command like:

ffmpeg -i input.mp4 -ss 13 -to 59 output.mp4

FFmpeg will seek accurately to the requested times and do a full re-encode to the output file. This will take a relatively long time, but produce great results.

A faster way of trimming is to copy the video and audio data using -c copy, e.g:

ffmpeg -i input.mp4 -ss 13 -to 59 -c copy output.mp4

However, because the trims are not at I-Frames, the output video is often choppy: frozen until the first I-Frames. Or doesn't quite have the length requested.

I wondered if it was possible to combine these methods:

  1. Measure where the I-Frames are, e.g: ffprobe -select_streams v -skip_frame nokey -show_frames -show_entries frame=pts_time input.mp4
  2. Do a re-encode trim from the requested start up to the nearest I-Frame, e.g: ffmpeg -i input.mp4 -ss 13 -to 13.12345 output1.mp4
  3. Do a copy trim from I-Frame to I-Frame, e.g: ffmpeg -i input.mp4 -ss 13.12345 -to 58.12345 -c copy output2.mp4
  4. Do a re-encode trim from the last requested I-Frame up to the end, e.g: ffmpeg -i input.mp4 -ss 58.12345 -to 59 output3.mp4
  5. Concatenating the three files together, e.g:
echo "file 'output1.mp4'" > mylist.txt
echo "file 'output2.mp4'" >> mylist.txt
echo "file 'output3.mp4'" >> mylist.txt
ffmpeg -y -f concat -safe 0 -i mylist.txt -c copy output4.mp4

In my testing this produces video that doesn't play well. Frames that freeze, audio out of sync, etc. I imagine because the three files I am concatenating aren't exactly the same format (frame rate, position of key frames, something along those lines). However, it certainly is faster for trimming little bits off of long files, if I could get it to work.

Is this method likely to work? If so, how could I get FFmpeg to produce three intermediate files that will concatenate nicely?

2 Answers 2

0

The short answer is, you won't. The problem is - concatenating successfully requires files encoded with the same parameters. Or pretty close to the same.

You are encoding clips that are unlikely to match your original unencoded sections. There will usually be issues when you attempt to do this. It is one of the reasons so-called lossless cutting rarely works as it should.

0

it will works, but it can produce multiple warnings: Non-monotonous DTS in output stream...

#!/bin/bash
f="input.mp4"
IFS=, read VID TBN BRV <<< $(ffprobe -v 0 -select_streams v:0 -show_entries stream=codec_name,time_base,bit_rate -of csv=p=0 "$f")
TBN=${TBN#*/}
echo $VID $TBN $BRV
IFS=, read AUD BRA <<< $(ffprobe -v 0 -select_streams a:0 -show_entries stream=codec_name,bit_rate -of csv=p=0 "$f")
echo $AUD $BRA
PTS=($(ffprobe -v error -show_entries frame=pts_time -of csv=p=0 -select_streams v:0 -skip_frame nokey "$f"))
T01=9.1
T04=22.6
for T02 in ${PTS[@]}; do
  if (($(echo "$T01 < $T02" | bc))); then
    break
  fi
done
echo -ss $T01 -to $T02
for t in ${PTS[@]}; do
  if (($(echo "$T04 < $t" | bc))); then
    break
  fi
  T03=$t
done
echo -ss $T03 -to $T04
ffmpeg -ss $T01 -to $T02 -i "$f" -c:v $VID -b:v $BRV -c:a $AUD -b:a $BRA -video_track_timescale $TBN -y 1.mp4 -hide_banner
ffmpeg -ss $T02 -to $T03 -i "$f" -c copy -y 2.mp4 -hide_banner
ffmpeg -ss $T03 -to $T04 -i "$f" -c:v $VID -b:v $BRV -c:a $AUD -b:a $BRA -video_track_timescale $TBN -y 3.mp4 -hide_banner
echo "file '1.mp4'" > list.txt
echo "file '2.mp4'" >> list.txt
echo "file '3.mp4'" >> list.txt
ffmpeg -f concat -i list.txt -c copy -y output.mkv -hide_banner
mpv output.mkv

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .