33

I'm having trouble finding a cli application that can take a video file (avi, mkv, and mp4 preferably) and cut out very short clips (2-6 seconds) with precision time accuracy. I've tried ffmpeg, mencoder, avidemux, and mp4box but they all cut on keyframes which creates 6+ second clips. Is there a tool that will re-encode the input file and cut the accurate time or cut inaccurately, re-encode, and then cut accurately?

4
  • You'll probably have to reencode before cutting to get it right. You could probably speed things up by first cutting out the surrounding keyframes and only reencode the snippets.
    – Nifle
    Aug 7, 2012 at 18:19
  • 4
    Which FFmpeg command have you tried, exactly? I believe if you decode the video before (i.e. place the -ss parameter after -i), it should be more accurate.
    – slhck
    Aug 7, 2012 at 18:19
  • 1
    The FFmpeg trick worked! I didn't realize the order mattered so much. Is this the same for any of the other tools?
    – curmil
    Aug 7, 2012 at 19:30
  • related- superuser.com/questions/1287650/…
    – barlop
    Apr 27, 2019 at 11:12

4 Answers 4

37

Cutting video with ffmpeg

You can accurately cut videos with FFmpeg. Since version 2.5 it's very easy.

This would for example cut 10 seconds, starting from 0 minutes, 3 seconds and 123 milliseconds.

ffmpeg -ss 00:00:03.123 -i input.mp4 -t 10 -c:v libx264 -c:a copy out.mp4

The position and the time may be either in seconds or in hh:mm:ss[.xxx] form.

Note that in these examples, video will be re-encoded using the x264 encoder; audio is copied over.

You can also use -to instead of -t to specify the end point instead of the duration. In this case, however, -to is equivalent to -t, since by putting the -ss in front of -i, ffmpeg will first seek to that point and then start outputting.

If the output does not appear to be cut correctly, adding -fflags +genpts to the command may help.

See also the Seeking wiki entry.

10
  • 1
    Instead of -c:v libx264 -c:a libfaac I think we can use -acodec copy -vcodec copy which tells ffmpeg just to detect and use the same codecs as the original file. Can anyone confirm?
    – Baodad
    Jan 23, 2015 at 4:31
  • 8
    @Baodad You can, but this will not accurately cut. When copying video/audio bitstreams, ffmpeg needs to start at a keyframe, which may just be placed at every second or even further apart.
    – slhck
    Jan 23, 2015 at 7:14
  • How do overcome the "Unknown encoder 'libfaac'" error?
    – Doug
    Apr 24, 2015 at 3:16
  • 1
    @Doug Choose a different encoder, for example -c:a aac -strict experimental. That's the simplest solution.
    – slhck
    Apr 24, 2015 at 6:34
  • @slhck did some testing on newer than 2.5 version with the re-encoding but I still could not get frame accuracy. I was attempting to trim off the first 00:00:01.96 off the original. Video+audio starts at 00:00:02 in original. After the command, I still had 1 second of black video before it started. It took off the first 01 second fine. I've imported the output video into a program that can do frame accuracy with no encoding (It's called tmpgenc mpeg smart renderer) and verified the frames are there in the beginning so ffmpeg still doing keyframes from the original, even after re-encode?
    – ScottN
    Feb 4, 2021 at 1:41
3

The only Linux command-line tool I've found so-far, that can cut at exact frame (or, with frame accuracy), is melt (sudo apt-get install melt).

Say you have an inputvid.mp4 - first check its encoding settings with say ffmpeg (here, I just say I want to encode it again to -f mp4, but as the file /dev/null so the output is discarded; I redirect stderr so I can grep through it - note in the middle, the command prompts, and you should answer y with ENTER, so the process proceeds and dumps the useful info; this is with ffmpeg 3.3.3 on Ubuntu 14):

ffmpeg -i inputvid.mp4 -f mp4 /dev/null 2>&1 | grep 'Stream\|encoder'
    Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, bt709), 640x360 [SAR 1:1 DAR 16:9], 389 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 95 kb/s (default)
y
File '/dev/null' already exists. Overwrite ? [y/N] Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
  Stream #0:1 -> #0:1 (aac (native) -> aac (native))
    encoder         : Lavf57.71.100
    Stream #0:0(und): Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv420p(progressive), 640x360 [SAR 1:1 DAR 16:9], q=-1--1, 23.98 fps, 24k tbn, 23.98 tbc (default)
      encoder         : Lavc57.89.100 libx264
    Stream #0:1(und): Audio: aac (LC) ([64][0][0][0] / 0x0040), 44100 Hz, stereo, fltp, 128 kb/s (default)
      encoder         : Lavc57.89.100 aac

Ok, so we can see ffmpeg chooses libx264 and aac encoders for this video; then we can enter this in for melt:

melt inputvid.mp4 in=7235 out=7349 -consumer avformat:cut.mp4 acodec=aac vcodec=libx264

.... and melt will cut with the piece between frames 7235 and 7349 into a new file, cut.mp4. Then to check if cut.mp4 loops correctly, use melt again to play it back twice - and play it to an SDL window:

melt cut.mp4 cut.mp4 -consumer sdl

... and here is what ffmpeg sees for this file:

ffmpeg -i cut.mp4 -f mp4 /dev/null 2>&1 | grep 'Stream\|encoder'    encoder         : Lavf54.20.4
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 640x360 [SAR 1:1 DAR 16:9], 526 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 182 kb/s (default)
y
File '/dev/null' already exists. Overwrite ? [y/N] Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
  Stream #0:1 -> #0:1 (aac (native) -> aac (native))
    encoder         : Lavf57.71.100
    Stream #0:0(und): Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv420p, 640x360 [SAR 1:1 DAR 16:9], q=-1--1, 23.98 fps, 24k tbn, 23.98 tbc (default)
      encoder         : Lavc57.89.100 libx264
    Stream #0:1(und): Audio: aac (LC) ([64][0][0][0] / 0x0040), 48000 Hz, stereo, fltp, 128 kb/s (default)
      encoder         : Lavc57.89.100 aac

The video encoding settings for cut.mp4 seem to be identical to inputvid.mp4 except video bitrate changed from 389 kb/s to 526 kb/s, and also the audio encoding settings are nearly the same, except the sampling rate changed from 44100 to 48000 Hz; though that can be regulated with:

melt inputvid.mp4 in=7235 out=7349 -consumer avformat:cut.mp4 acodec=aac ar=44100 ab=95k vcodec=libx264 vb=389k

... however, even with this, the final video bitrate for me ends up 337 kb/s. Still, the cuts loop fine (and that includes audio) when played in a loop, so I guess this is indeed frame-accurate...

3
  • 3
    melt is re-encoding the video, and it uses the FFmpeg libraries underneath. If you allow re-encoding, ffmpeg can produce the same output.
    – Gyan
    Jun 12, 2018 at 7:06
  • Thanks @Gyan - was not aware of that (especially that melt uses FFmpeg libraries), good to know!
    – sdaau
    Jan 18, 2019 at 11:15
  • melt looks easier to use than ffmpeg, all we need is a way to specify times , particularly times in hr,min,sec form, or hr,min,sec,ms, that converts them to the right frame.
    – barlop
    Apr 27, 2019 at 11:11
0

Like Baodad said in the comments (I post because it's not easy to find if you read quickly), the better approach is to detect the audio/video encoders automatically by ffmpeg, so :

ffmpeg -ss 00:05:17.18 -i in.mp4 -t 00:06:29.10 -acodec copy -vcodec copy out.mp4 
  • start @ 00:05:17.18
  • input = in.mp4
  • stop @ 00:06:29.10
  • output = out.mp4
1
  • 2
    This does not reencode and does not provide frame accuracy. Feb 17, 2016 at 1:15
0

In an answer to a similar question unreliable results with -ss in earlier versions are mentioned. But in new builds it appears the same. To recap, if you use ss/to/t before -i, it cuts at i-frames, after -i, it cuts at specified times and uses the next i-frame till this frame is reached, i.e. you get a still picture to that time. At the time for -to it may cut as requested, if it's no coincidence. I thought you could get an accurate cut by encoding the part before the i-frame and copying the part after, but apparently you can't join the resulting files. Can we safely conclude that an accurate cut off i-frames is impossible without encoding the whole video?

You must log in to answer this question.

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