668

I tried to cut the video using the start and end time of the video by using the following command

ffmpeg -ss 00:00:03 -t 00:00:08 -i movie.mp4 -acodec copy -vcodec copy -async 1 cut.mp4

By using the above command i want to cut the video from 00:00:03 to 00:00:08. But it is not cutting the video between those times instead of that it is cutting the video with first 11 seconds. can anyone help me how resolve this?

Edit 1:

I have tried to cut by using the following command which is suggested by mark4o

ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 cut.mp4

But it was shown the following error.

the encoder 'aac' is experimental but experimental codecs are not enabled

so i added the -strict -2 into the command i.e.,

ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 -strict -2 cut.mp4

Now it is working fine.

Community
  • 1
  • 1
Kalai
  • 9,097
  • 8
  • 27
  • 47
  • 3
    Your full, uncut ffmpeg console output is missing. Please always include this when asking. Thanks. – slhck Aug 26 '13 at 12:55
  • 4
    Where did you get -strict 2 from? Just curious as it's not in the docs: https://ffmpeg.org/ffmpeg.html – StackOverflowed Apr 01 '14 at 14:25
  • Its for quality after installing ffmpeg, In ffmpeg help you can find this option – Kalai Apr 01 '14 at 15:46
  • 2
    You should either use `-t 00:00:05` or `-to 00:00:08` in order to cut the video from 00:00:03 to 00:00:08. Check out the documentation. – Louis55 Dec 29 '18 at 04:08
  • 1
    I think the problem here was actually just the fact that you put the from and to BEFORE the -i movie.mp4. Arguments for that stream go AFTER it, right? That's why the other way worked. =/ – Brent Rittenhouse Mar 19 '19 at 05:23
  • using `-t` or `-to` does not mean the cutoff time. It's the duration e.g 30 seconds from `-ss`. I've created a bash script to cut audio, but I'm sure with some modification you can use video as well — https://gist.github.com/r0lodex/5f0d8709629e45f975fdb351ec599f4f – rolodex Nov 18 '19 at 15:39

9 Answers9

718

You probably do not have a keyframe at the 3 second mark. Because non-keyframes encode differences from other frames, they require all of the data starting with the previous keyframe.

With the mp4 container it is possible to cut at a non-keyframe without re-encoding using an edit list. In other words, if the closest keyframe before 3s is at 0s then it will copy the video starting at 0s and use an edit list to tell the player to start playing 3 seconds in.

If you are using the latest ffmpeg from git master it will do this using an edit list when invoked using the command that you provided. If this is not working for you then you are probably either using an older version of ffmpeg, or your player does not support edit lists. Some players will ignore the edit list and always play all of the media in the file from beginning to end.

If you want to cut precisely starting at a non-keyframe and want it to play starting at the desired point on a player that does not support edit lists, or want to ensure that the cut portion is not actually in the output file (for example if it contains confidential information), then you can do that by re-encoding so that there will be a keyframe precisely at the desired start time. Re-encoding is the default if you do not specify copy. For example:

ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 cut.mp4

When re-encoding you may also wish to include additional quality-related options or a particular AAC encoder. For details, see ffmpeg's x264 Encoding Guide for video and AAC Encoding Guide for audio.

Also, the -t option specifies a duration, not an end time. The above command will encode 8s of video starting at 3s. To start at 3s and end at 8s use -t 5. If you are using a current version of ffmpeg you can also replace -t with -to in the above command to end at the specified time.

mark4o
  • 52,963
  • 16
  • 81
  • 99
  • 6
    Is there a shortcut to specify till the end of the video? – Jikku Jose Feb 21 '15 at 09:35
  • 35
    @JikkuJose: Omit the `-t`/`-to` and its argument to continue to the end. – mark4o Feb 21 '15 at 18:39
  • 64
    Note: it is faster to supply `-ss` BEFORE the input file (before `-i`), because that way ffmpeg will skip directly to the location. However, that will also set the time "zero" to that location, meaning that `-to` will point to the wrong time. Source: https://trac.ffmpeg.org/wiki/Seeking – Denilson Sá Maia Jul 30 '15 at 19:41
  • 6
    I get audio and video sync issue after using this ```ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 cut.mp4``` , though the original file has no such issue. – user2002522 Aug 28 '15 at 03:26
  • 8
    @user2002522: Try without the `-async 1`. – mark4o Aug 28 '15 at 17:00
  • 3
    this answer, http://stackoverflow.com/a/14013439/1170023 is what you want for finding the keyframe times with -c copy to avoid audio sync – Kevin Apr 22 '17 at 20:37
  • Remember to put both `-ss` and `-t` before `-i` to avoid black screen for few seconds at the beginning and don't use `-to` because it will produce a file with incorrect duration. – Shayan May 08 '19 at 13:40
  • I also experienced sync issues. i tried the solution provided by George Chalhoub (below) and the sync issue was fixed – Yaniv Levi Jun 02 '20 at 11:53
  • i am getting quality issue. Is there anyone getting same issue ? – Muhammed Yalçın Kuru Sep 19 '20 at 23:17
  • @MuhammedYalçınKuru: To control the quality see https://trac.ffmpeg.org/wiki/Encode/H.264 – mark4o Sep 21 '20 at 04:07
  • @mark4o i have tried with my lack of info. Didn't find fit one. while cutting passed this one ` -c:v libx264 -preset ultrafast -crf 0 ` – Muhammed Yalçın Kuru Sep 21 '20 at 06:20
  • Is there any way to re-encode only the part up to next keyframe, then copy everything else over? – Ale Jan 20 '21 at 19:39
  • In layman's terms what does now deprecated `async 1` does? – Sudhir Singh Khanger Mar 07 '21 at 01:53
711

Try using this. It is the fastest and best ffmpeg-way I have figure it out:

 ffmpeg -ss 00:01:00 -i input.mp4 -to 00:02:00 -c copy output.mp4

This command trims your video in seconds!

Explanation of the command:

-i: This specifies the input file. In that case, it is (input.mp4).
-ss: Used with -i, this seeks in the input file (input.mp4) to position.
00:01:00: This is the time your trimmed video will start with.
-to: This specifies duration from start (00:01:40) to end (00:02:12).
00:02:00: This is the time your trimmed video will end with.
-c copy: This is an option to trim via stream copy. (NB: Very fast)

The timing format is: hh:mm:ss

Please note that the current highly upvoted answer is outdated and the trim would be extremely slow. For more information, look at this official ffmpeg article.

Display name
  • 9,021
  • 3
  • 29
  • 59
  • 29
    this processed my 45 min 720p file in 3s. This should be the correct answer. – Dheeraj Bhaskar Jun 10 '17 at 19:54
  • 13
    You may still suffer from a lack of keyframes with this though. I took your approach to cutting, then re-encoded as per the accepted answer to make sure I minimised the amount of time I needed to spend re-encoding. – pelson Sep 23 '17 at 10:33
  • 13
    As @pelson says, this will cut off key frames and leave the first few seconds blank (if the cutting time is between key frames). Interestingly, changing the parameter order solves the problem: `ffmpeg -ss 00:01:00 -to 00:02:00 -i input.mp4 -ss 00:01:00 -to 00:02:00 -c copy output.mp4`. Now, however, skipping is broken and the end time is not correct (in my case). – Raphael Nov 20 '17 at 08:37
  • 1
    According to [FFmpeg](https://trac.ffmpeg.org/wiki/Seeking), input seeking is now both fast and accurate, is there any specific reason you use output seeking instead of input seeking? – vxh.viet Nov 30 '17 at 04:49
  • 1
    Nice! What worked for me was `ffmpeg -i origin.mp4 -ss 02:51:14 -c copy destination.mp4` (I guess `-to` is optional and unnecessary if you want the end point to be the absolute end of the video.) – Ryan Jan 19 '18 at 15:57
  • @Ryan: Interesting, maybe we should add this to the answer. – Display name Jan 20 '18 at 17:43
  • 3
    For my Linux machine (Ubuntu 16.04, ffplay version 2.8.14), this command line corrupted the output video somehow. – Cloud Cho Jun 02 '18 at 04:04
  • 1
    For me also, the output was corrupted and with no sound. IINA player is crashing whenever I'm trying to open the video. I'm on macOS 10.13.5 and ffmpeg is 3.4 – duplex143 Jul 07 '18 at 13:22
  • 11
    You should use this answer if you don't need to trim at an exact milisecond, thus it will select the closest keyframe available. That's why it's so fast, it selects the closest keyframes and takes the snippet out without the need of reencoding. However, if you do need a precise time, taking the nearest keyframe isn't enough, because it certainly won't match exactly. The only solution available is to reencode the video, and that's why it's so slow. That's the only solution, though, if you do need this. That's why the other answer should be the accepted one, as that's what was OP's issue about. – Yamaneko Oct 03 '18 at 14:56
  • @Raphael - Your comment is what matters the MOST!. – cafebabe1991 Oct 19 '18 at 15:52
  • 7
    Not sure why but this doesn't seem to be more correct than the accepted answer because this leads to issues with missing key-frames. For short video clips the effect is unacceptable. Slow reencode seems to be the only solution in such cases. – bdutta74 Nov 19 '18 at 14:16
  • 3
    You probably want `-ss` *and* `-to` before `-i`, example: `ffmpeg -ss aa:bb:cc -to xx:yy:zz -i input.mp4 -c copy output.mp4`. Otherwise the `-to` value ends up being a duration instead of the end time from the original video. Omitting the `-c copy` will make it slower and more accurate by re-encoding, but still faster than if the `-ss` and `-to` are specified after `-i`, since that case means to trim after having to process the whole input file. – theferrit32 Dec 16 '18 at 20:18
  • @theferrit32 it gives `Option to (record or transcode stop time) cannot be applied to input url file.mp4 -- you are trying to apply an input option to an output file or vice versa. Move this option before the file it belongs to.` – techkuz Jun 22 '19 at 13:52
  • This is my winner option. The process takes just a few seconds. – Philippe Delteil Aug 05 '19 at 21:10
  • 1
    Note that if you specify `-ss` before `-i` only, the timestamps will be reset to zero, so `-t` and `-to` have not the same effect. If you want to keep the original timestamps, add the `-copyts` option. [Reference](https://trac.ffmpeg.org/wiki/Seeking#Cuttingsmallsections) – Bugs Happen Jan 06 '20 at 14:01
  • 1
    @GeorgeChalhoub `-to` is not the duration, `-t` is ! – vdegenne Feb 10 '20 at 18:25
  • this command is not working on android 10 correctly – gowtham6672 Sep 04 '20 at 07:25
  • @pelson can you explain how you achieve the exact time ? didn't get what you mean by re-encoding. – Muhammed Yalçın Kuru Sep 21 '20 at 18:21
  • I can confirm that with the -c copy the speed is more than 300 times of normal playing speed whereas without the -c copy the speed is only 4 times of the normal playing speed. As typing the comment, the "with c " command has finished, and the "without c " is still running.... – Franva Nov 03 '20 at 01:06
  • This one needs more upvotes. – droid_31 Dec 23 '20 at 05:05
  • This one is outdated, unless there are differences between the win and linux builds, or the answer was made with an old version of ffmpeg, but placing -to after the input file, for my recent win build, it creates an unwanted output, as in it resets timestamps with -ss to 0 and -to won't actually end with the desired timestamp from the input file. The -ss and -to options need to be placed before the input... for new versions of ffmpeg. Otherwise, -ss -i input should be followed by -t, i.e. total duration of the clip, to achieve the right duration for the cut. – JasonXA Jan 04 '21 at 07:37
106
ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 -c copy cut.mp4 

Use -c copy for make in instantly. In that case ffmpeg will not re-encode video, just will cut to according size.

Vlad Hudnitsky
  • 1,200
  • 2
  • 10
  • 11
34
    ffmpeg -i movie.mp4 -vf trim=3:8 cut.mp4

Drop everything except from second 3 to second 8.

JFMD
  • 349
  • 3
  • 2
  • 6
    I get the first 8 seconds with this, with the first 3 seconds frozen on the first frame. Fixed by chaining `setpts=PTS-STARTPTS` filter after the trim filter. – jiggunjer Dec 11 '15 at 08:04
  • This says "The encoder 'aac' is experimental but experimental codecs are not enabled, add '-strict -2' if you want to use it." Adding -strict -2 does not help. The result is a zero length file. – Ivan Dec 24 '15 at 01:44
  • 4
    @Ivan, did you try adding `-strict -2` before `cut.mp4` (as opposed to the end of the command line)? – Jan Gondol Jan 01 '16 at 13:08
  • Hey , Can you tell me if I only want video without 3 to 8 second? – Parth Solanki Jun 01 '16 at 09:19
  • 2
    THis is super slow – Trect Nov 26 '19 at 15:49
24

new answer (fast)

You can make bash do the math for you, and it works with milliseconds.

toSeconds() {
    awk -F: 'NF==3 { print ($1 * 3600) + ($2 * 60) + $3 } NF==2 { print ($1 * 60) + $2 } NF==1 { print 0 + $1 }' <<< $1
}

StartSeconds=$(toSeconds "45.5")
EndSeconds=$(toSeconds "1:00.5")
Duration=$(bc <<< "(${EndSeconds} + 0.01) - ${StartSeconds}" | awk '{ printf "%.4f", $0 }')
ffmpeg -ss $StartSeconds -i input.mpg -t $Duration output.mpg

This, like the old answer, will produce a 15 second clip. This method is ideal even when clipping from deep within a large file because seeking isn't disabled, unlike the old answer. And yes, I've verified it's frame perfect.

NOTE: The start-time is INCLUSIVE and the end-time is normally EXCLUSIVE, hence the +0.01, to make it inclusive.

If you use mpv you can enable millisecond timecodes in the OSD with --osd-fractions


old answer with explanation (slow)

To cut based on start and end time from the source video and avoid having to do math, specify the end time as the input option and the start time as the output option.

ffmpeg -t 1:00 -i input.mpg -ss 45 output.mpg

This will produce a 15 second cut from 0:45 to 1:00.

This is because when -ss is given as an output option, the discarded time is still included in the total time read from the input, which -t uses to know when to stop. Whereas if -ss is given as an input option, the start time is seeked and not counted, which is where the confusion comes from.

It's slower than seeking since the omitted segment is still processed before being discarded, but this is the only way to do it as far as I know. If you're clipping from deep within a large file, it's more prudent to just do the math and use -ss for the input.

y o
  • 1,024
  • 8
  • 14
21

Here's what I use and will only take a few seconds to run:

ffmpeg -i input.mp4 -ss 01:19:27 -to 02:18:51 -c:v copy -c:a copy output.mp4

Reference: https://www.arj.no/2018/05/18/trimvideo


Generated mp4 files could also be used in iMovie. More info related to get the full duration using get_duration(input_video) modele.


If you want to concatenate multiple cut scenes you can use following Python script:

#!/usr/bin/env python3

import subprocess


def get_duration(input_video):
    cmd = ["ffprobe", "-i", input_video, "-show_entries", "format=duration",
           "-v", "quiet", "-sexagesimal", "-of", "csv=p=0"]
    return subprocess.check_output(cmd).decode("utf-8").strip()


if __name__ == "__main__":
    name = "input.mkv"

    times = []
    times.append(["00:00:00", "00:00:10"])
    times.append(["00:06:00", "00:07:00"])

    # times = [["00:00:00", get_duration(name)]]

    if len(times) == 1:
        time = times[0]
        cmd = ["ffmpeg", "-i", name, "-ss", time[0], "-to", time[1], "-c:v", "copy", "-c:a", "copy", "output.mp4"]
        subprocess.check_output(cmd)
    else:
        open('concatenate.txt', 'w').close()
        for idx, time in enumerate(times):
            output_filename = f"output{idx}.mp4"
            cmd = ["ffmpeg", "-i", name, "-ss", time[0], "-to", time[1], "-c:v", "copy", "-c:a", "copy", output_filename]
            subprocess.check_output(cmd)

            with open("concatenate.txt", "a") as myfile:
                myfile.write(f"file {output_filename}\n")

        cmd = ["ffmpeg", "-f", "concat", "-i", "concatenate.txt", "-c", "copy", "output.mp4"]
        output = subprocess.check_output(cmd).decode("utf-8").strip()

Example script will cut and merge scenes in between 00:00:00 - 00:00:10 and 00:06:00 - 00:07:00.


If you want to cut the complete video (in case if you want to convert mkv format into mp4) just uncomment the following line:

# times = [["00:00:00", get_duration(name)]]
alper
  • 1,558
  • 2
  • 24
  • 54
  • 1
    My approach is different from the accepted answers. I have tried those which has longer time to construct the mp4 file. @Tessaracter – alper Nov 26 '19 at 16:55
  • 2
    This is the ONLY answer here that worked for me for cutting a small clip from a Large video. This works and FAST!!! Thank you – lacostenycoder Mar 15 '20 at 15:47
  • 2
    You are welcome, I am glad I can help. I was able to find this answer after trying many approaches. Most of them were pretty slow. @lacostenycoder – alper Mar 15 '20 at 15:51
  • Couldn't this just use "-c" with "copy" instead of splitting out -c:v and -c:a? – nstenz May 06 '20 at 07:32
  • I don't know why but I'm getting error with this code – Pablo Díaz Aug 12 '20 at 18:27
  • this is slow. the input file isn't being seeked, it's being processed and discarded. -ss as an output option = slow. see my updated answer. – y o Sep 21 '20 at 12:42
  • I'm getting the video cutted but for first seconds the frames just freeze – Pablo Díaz Sep 21 '20 at 21:29
  • @R Y : It shouldn't be slow. What do you mean by `input file isn't being seeked`? Instead of `input.mp4` provide the path of the movie you want to convert. I have tried to convert 6.7 GB BlueRay movie by the script I provided, which only took 3.56 seconds. – alper Sep 21 '20 at 21:33
  • @PabloDíaz The video you are using might be faulty. Can you give it a try with a different video uncommenting `# times = [["00:00:00", get_duration(name)]]` and see if the generated `output.mp4` file is fine – alper Sep 21 '20 at 21:40
6

Use -to instead of -t: -to specifies the end time, -t specifies the duration

dynamichael
  • 566
  • 7
  • 8
0

feel free to use this tool https://github.com/rooty0/ffmpeg_video_cutter I wrote awhile ago Pretty much that's cli front-end for ffmpeg... you just need to create a yaml what you want to cut out... something like this

cut_method: delete  # we're going to delete following video fragments from a video
timeframe:
  - from: start   # waiting for people to join the conference
    to: 4m
  - from: 10m11s  # awkward silence
    to: 15m50s
  - from: 30m5s   # Off-Topic Discussion
    to: end

and then just run a tool to get result

Stan
  • 34
  • 4
  • Going by the GitHub account profile, it looks like this might be a tool that you've created yourself. If this is the case, [you *must* disclose this in your answer](https://stackoverflow.com/help/promotion), otherwise this can be considered spam. – Hoppeduppeanut Sep 04 '20 at 01:29
-4

Even though I'm 6 years late, but I think all the answers above didn't properly address the question @kalai is asking. The bash script below will process a text file in the following format:

URL | start_time | end_time | filename

for example

https://www.youtube.com/watch?v=iUDURCrvrMI|00:02:02|00:03:41|1

and loop through the file, downloads the file that youtube-dl supports, calculating duration between start_time and end_time and passing it to ffmpeg, since -t is actually the duration, not the real end_time

Hit me up if you have any question.

    for i in $(<video.txt);
    do
        URL=`echo $i | cut -d "|" -f 1`;
        START=`echo $i | cut -d "|" -f 2`;
        END=`echo $i | cut -d "|" -f 3`;
        FILE=`echo $i | cut -d "|" -f 4`;

        SEC1=`echo $START | sed 's/^/((/; s/:/)*60+/g' | bc`
        SEC2=`echo $END | sed 's/^/((/; s/:/)*60+/g' | bc`

        DIFFSEC=`expr ${SEC2} - ${SEC1}`

        ffmpeg $(youtube-dl -g $URL | sed "s/.*/-ss $START -i &/") -t $DIFFSEC -c copy $FILE".mkv";
        ffmpeg -i $FILE".mkv" -f mp3 -ab 192000 -vn $FILE".mp3";
        rm $FILE".mkv";
    done;
rolodex
  • 520
  • 1
  • 6
  • 18