Accurately cut video files from command line

24,289

Solution 1

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.

Solution 2

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...

Share:
24,289

Related videos on Youtube

curmil
Author by

curmil

Updated on September 18, 2022

Comments

  • curmil
    curmil almost 2 years

    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?

    • Julian
      Julian almost 12 years
      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.
    • slhck
      slhck almost 12 years
      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.
    • curmil
      curmil almost 12 years
      The FFmpeg trick worked! I didn't realize the order mattered so much. Is this the same for any of the other tools?
    • barlop
      barlop about 5 years
  • Baodad
    Baodad over 9 years
    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?
  • slhck
    slhck over 9 years
    @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.
  • Doug
    Doug about 9 years
    How do overcome the "Unknown encoder 'libfaac'" error?
  • slhck
    slhck about 9 years
    @Doug Choose a different encoder, for example -c:a aac -strict experimental. That's the simplest solution.
  • Andrea Lazzarotto
    Andrea Lazzarotto over 8 years
    This does not reencode and does not provide frame accuracy.
  • Gyan
    Gyan about 6 years
    melt is re-encoding the video, and it uses the FFmpeg libraries underneath. If you allow re-encoding, ffmpeg can produce the same output.
  • tetram
    tetram over 5 years
    Thanks @Gyan - was not aware of that (especially that melt uses FFmpeg libraries), good to know!
  • barlop
    barlop about 5 years
    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.
  • ScottN
    ScottN over 3 years
    @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?
  • slhck
    slhck over 3 years
    @ScottN It'd be good if you could ask a new question and include the complete command plus the whole command line output for us to diagnose the issue. Ideally attach a sample video to reproduce the issue.
  • ScottN
    ScottN over 3 years
    @slhck I could do that but wouldn't be at risk of being flagged as "duplicate question" and it going nowhere? What I'm trying to achieve exactly what this question was intended for and ultimately what your answer is addressing and is marked as the solution.
  • ScottN
    ScottN over 3 years
    @slhck I dug in deeper to some other questions on SU and came across an answer that was using more switches, after testing, I was able to get frame/time accuracy how I want. The switch is -fflags +genpts which in the docs says "Generate missing PTS if DTS is present". You may want to test on your end comparing your answer and these other flags and potentially update your answer if you find that it brings more value to this QA. here is the full command: ffmpeg -ss 00:00:01.100 -i "input.mp4" -fflags +genpts "output.mp4"
  • ScottN
    ScottN over 3 years
    Here is the link to the answer I got this from: superuser.com/a/1021065/14671
  • slhck
    slhck over 3 years
    @ScottN It wouldn't be marked as a duplicate if you said, "I followed this post, but it didn't work", because then it's a different issue. It shouldn't be needed to add -fflags +genpts but I added it to my answer in case it helps anyone.