You forgot to use backticks - or better: $( )
subshell - in the seq
invocation. This works:
for i in $( seq 50 );
do ffmpeg -i input.mpg -sameq -ss 00:`expr $i \* 2 - 2`:00 -t 00:02:00 output.mpg; done
Another thing is that you probably don't want output.mpg
to be overwritten in each run, do you? :) Use $i
in the output filename as well.
Apart from that: In bash, you can just use $(( ))
or $[ ]
instead of expr
- it also looks more clear (in my opinion). Also, there is no need for seq
- brace expansion is all you need to get a sequence. Here's an example:
for i in {1..50}
do ffmpeg -i input.mpg -sameq -ss 00:$[ i* 2 - 2 ]:00 -t 00:02:00 output_$i.mpg
done
Another good thing about braces is that you can have leading zeros in the names (very useful for file sorting in the future):
for i in {01..50}
do ffmpeg -i input.mpg -sameq -ss 00:$[ i* 2 - 2 ]:00 -t 00:02:00 output_$i.mpg
done
Notice as well, that i*2 - 2
can be easily simplified to i*2
if you just change the range:
for i in {00..49}
do ffmpeg -i input.mpg -sameq -ss 00:$[ i*2 ]:00 -t 00:02:00 output_$i.mpg
done
I found the answer. It turns out I was having problems because I didn't have the proper FFmpeg installed, but a fork of ffmpeg.
This code works for me:
ffmpeg -i fff.avi -acodec copy -f segment -segment_time 10 -vcodec copy -reset_timestamps 1 -map 0 fff%d.avi
fff.avi
is the name of the source clip. Change -segment_time 10
to the general duration you want for each segment. If you want each clip to be about 40 seconds, then use -segment_time 40
. Use -an
after -map 0
if you don't want the resulting clips to have sound.
Best Answer
Wrapping this up into a script to do it in a loop wouldn't be hard.
Beware that if you try to calculate the number of iterations based on the duration output from an
ffprobe
call that this is estimated from the average bit rate at the start of the clip and the clip's file size unless you give the-count_frames
argument, which slows its operation considerably.Another thing to be aware of is that the position of the
-ss
option on the command line matters. Where I have it now is slow but accurate. The first version of this answer gave the fast but inaccurate alternative. The linked article also describes a mostly-fast-but-still-accurate alternative, which you pay for with a bit of complexity.All that aside, I don't think you really want to be cutting at exactly 10 minutes for each clip. That will put cuts right in the middle of sentences, even words. I think you should be using a video editor or player to find natural cut points just shy of 10 minutes apart.
Assuming your file is in a format that YouTube can accept directly, you don't have to reencode to get segments. Just pass the natural cut point offsets to
ffmpeg
, telling it to pass the encoded A/V through untouched by using the "copy" codec:The
-c copy
argument tells it to copy all input streams (audio, video, and potentially others, such as subtitles) into the output as-is. For simple A/V programs, it is equivalent to the more verbose flags-c:v copy -c:a copy
or the old-style flags-vcodec copy -acodec copy
. You would use the more verbose style when you want to copy only one of the streams, but re-encode the other. For example, many years ago there was a common practice with QuickTime files to compress the video with H.264 video but leave the audio as uncompressed PCM; if you ran across such a file today, you could modernize it with-c:v copy -c:a aac
to reprocess just the audio stream, leaving the video untouched.The start point for every command above after the first is the previous command's start point plus the previous command's duration.