I'd like to make a batch operation for all files in a folder.
For every file I need to remove the first and last second of the clip, and save it to a destination folder.
This is the command I used:
for %f in (â*.*â) do c:\ffmpeg\bin\ffmpeg -i "%f" -ss 00:00:01.000 -sseof 00:00:01.000 -c:v copy -c:a copy âu:\footage\%fâ
That command does not work. It cuts the first second but leave the rest of the file as is.
Maybe there is something I need to change in the syntax regarding -sseof.
ss and sseof both tell ffmpeg where to start from, so your command has two start times specified and no end time. Your command is preferring ss (possibly since it is closest to the input) and ignoring sseof, then working to the end of the file.
You can use to (which takes a position in the file) or t (which takes a duration) to tell it where to end, but if your videos are not all of exactly the same length, you will need to calculate this value for each file. You can get the length of a video using ffprobe like this:
ffprobe -i <file> -show_entries format=duration -v quiet -of csv="p=0"
This will give you the length of the file in seconds. You will then need to subtract the desired amount (in your case, 1 second if you use to or 2 seconds if you use t) and feed the result into the appropriate flag in your ffmpeg command.
Related
Is it possible to remove the end of a video that has no sound?
: = Has Sound
. = No Sound
00:00 [::::::::::::::::::::::::::::........] 01:24
____________________^^^^^ Cut this
Thanks
Note time when sound ends, such as 00:01:23, then use:
ffmpeg -i input -c copy -t 00:01:23 output
You can use seconds if you prefer, such as -t 83.
Exact time is not guaranteed with stream copy mode (-c copy). If exactness is a requirement then remove -c copy.
If you want to automate it (you didn't specify) then integrate the silencedetect filter. It will output timestamps of detected silence, then use the supplied timestamps to create your ffmpeg command. See ffprobe/ffmpeg silence detection command for an example showing how to get the timestamps.
How do I cut a section out of a video with ffmpeg?
Imagine I have a 60 second mp4 A.
I want to remove all the stuff from 0:15 to 0:45.
The result should be a 30-second mp4, which is composed of the first 15 seconds of A directly followed by the last 15 seconds of A.
How can I do this without using concat?
I know how I could do it by creating two intermediary files and then using ffmpeg to concat them. I don't want to have to perform so much manual work for this (simple?) operation.
I have also seen the trim filder used for removing multiple parts from a video. All the usages I've found show that it seems to be very verbose, and I haven't found an example for a case as simple as I would like (just a single section removed).
Do I have to use trim for this operation? Or are there other less verbose solutions?
The ideal would of course be something at least simple as -ss 0:15 -to 0:45 which removes the ends of a video (-cut 0:15-0:45 for example).
I started from
https://stackoverflow.com/a/54192662/3499840 (currently the only answer to "FFmpeg remove 2 sec from middle of video and concat the parts. Single line solution").
Working from that example, the following works for me:
# In order to keep <start-15s> and <45s-end>, you need to
# keep all the frames which are "not between 15s and 45s":
ffmpeg -i input.mp4 \
-vf "select='not(between(t,15,45))', setpts=N/FRAME_RATE/TB" \
-af "aselect='not(between(t,15,45))', asetpts=N/SR/TB" \
output.mp4
This is a one-line linux command, but I've used the bash line-continuation character ('\') so that I can vertically align the equals-signs as this helps me to understand what is going on.
I had never seen ffmpeg's not and between operators before, but I found their documentation here.
Regarding the usual ffmpeg "copy vs re-encode" dichotomy, I was hoping to be able to use ffmpeg's "copy" "codec" (yeah, I know that it's not really a codec) so that ffmpeg would not re-encode my video, but if I specify "copy", then ffmpeg starts and stops at the nearest keyframes which are not sufficiently close to my desired start and stop points. (I want to remove a piece of video that is approximately 20 seconds long, but my source video only has one keyframe every 45 seconds!). Hence I am obliged to re-encode. See https://trac.ffmpeg.org/wiki/Seeking#Seekingwhiledoingacodeccopy for more info.
The setpts/asetpts filters set the timestamps on each frame to the correct values so that your media player will play each frame at the correct time.
HTH.
If you want to use the copy "codec", consider the following approach:
ffmpeg -i input.mp4 -t "$start_cut_section" -c copy part1.mp4&
ffmpeg -i input.mp4 -ss "$end_cut_section" -c copy part2.mp4&
echo "file 'part1.mp4'" > filelist;
echo "file 'part2.mp4'" >> filelist;
wait;
ffmpeg -f concat -i filelist -c copy output.mp4;
rm filelist;
This creates two files from before and after the cut, then combines them into a new trimmed final video. Obviously, this can be used to create as many cuts as you like. It may seem like a longer approach than the accepted answer, but it likely will execute much faster because of the use of the copy codec.
In Bash,
I am trying to match an image to a frame in ffmpeg. I also want to exit the ffmpeg process when the match is found. Here is a (simplified version) of the code currently:
ffmpeg --hide_banner -ss 0 -to 60 \
-i "video.mp4" -i "image.jpg" -filter_complex \
"blend=difference, blackframe" -f null - </dev/null 2>log.txt &
pid=$!
trap "kill $pid 2>/dev/null" EXIT
while kill -0 $pid 2>/dev/null; do
# (grep command to monitor log file)
# if grep finds blackframe match, return blackframe time
done
To my understanding, if the video actually contains a blackframe I will get a false-positive. How can I effectively mitigate this?
While this is unnecessary to answer the question, I would like to exit the ffmpeg process without having to use grep to constantly monitor the log file, instead using pure ffmpeg
Edit: I say this because while I understand the blend filter is computing the difference, I am getting a false positive on a blackframe in my video and I don't know why.
Edit: A possible solution to this issue is to not use blackframe at all, but psnr (Peak Signal to Noise Ratio) but normal usage is by comparing two videos frame by frame, and I don't know how to effectively use it with an image as input.
Use
ffmpeg -ss 0 -t 60 -copyts -i video.mp4 -i image.jpg -filter_complex "[0]extractplanes=y[v];[1]extractplanes=y[i];[v][i]blend=difference,blackframe=0,metadata=select:key=lavfi.blackframe.pblack:value=100:function=equal,trim=duration=0.0001,metadata=print:file=-" -an -v 0 -vsync 0 -f null -
If a match is found, it will print to stdout a line of the form,
frame:179 pts:2316800 pts_time:6.03333
lavfi.blackframe.pblack=100
else no lines will be printed. It will exit after the first match, if found, or till whole input is processed.
Since blackframe only looks at luma, I use extractplanes both to speed up blend and also avoid any unexpected format conversions blend may request.
blackframe threshold is set to 0, so all frames have the blackframe value metadata tagged. False positives are not possible since blend computes the difference. The difference between a black input frame and the reference frame is equal to the reference frame, unless the reference is a black frame, in which case, it is not a false positive.
The first metadata filter only passes through frames with blackframe value of 100. The trim filter stops a 2nd frame from passing through (except if your video's fps is greater than 10000). The 2nd metadata filter prints the selected frame's metadata.
I got pictures named as
pic_0_new.jpg
pic_10_new.jpg
pic_20_new.jpg
...
pic_1050_new.jpg
which I want to turn into a video (Ubuntu ffmpeg). I tried the following
ffmpeg -start_number 0 -i pic_%d_new.jpg -vcodec mpeg4 test.avi
but I don't know how to set the step size and the end number. How to do this?
Thanks for help :)
If your files are named with leading zeroes then you can use the built-in globbing functionality. If not (like your's), you can create a file list and supply that as the input, just like explained here.
The other thing you need to set is the input framerate that tells FFmpeg what framerate should assume for the images (note that the option is ahead of the -i input option).
So the command should look like this:
ffmpeg -framerate 25 -i pic_%04d_new.jpg <encoding_settings> test.avi
Also note that you can use the filename expansion on files with or without leading zeroes (Thanks for #Gyan to pointing it out):
match regularly numbered files: %d (this is what you're using)
img_%d.png // This will match files with any number as a postfix starting from img_0.png
match leading zeroes: %0<number_of_digits>d
img_%03d.png // This will match files ranging from img_000.png to img_999.png
In addition, mpeg4/avi is not the most convenient encoder/container to use...
I want to create a bunch of videos consisting of an single image which is shown throughout the whole video but each video has a different audio file. I can do it manually with various tools but the problem is that I have a lot of audio files and I can't optimize the frame rate (more on that later) and it takes a lot of time to do it that way but ffmpeg offers everything I need but the problem is that I don't know how to batch process everything.
The basic code:
ffmpeg -i song-name.mp3 -loop 1 -i cover.jpg -r frame-rate -t song-length -acodec copy output.mp4
What I want to achieve:
Let's say that I have a folder which consists of several audio files: song-name-1.mp3, song-name-2.mp3, ..., song-name-n.mp3 and cover.jpg.
I need a batch file which takes the name of every mp3 file in a folder (a FOR loop I suppose) and processes it with the same command:
ffmpeg -i song-name.mp3 -loop 1 -i cover.jpg -r frame-rate -t song-length -acodec copy output.mp4
So the image is always the same for every video. The song length can be taken with the tool mp3info and the corresponding command:
mp3info.exe -p %S song-name.mp3
Since I only have one image throughout the whole video, the optimal frame rate would be the inverse of the video length which is 1/length (where length is a variable in seconds which we get from mp3info).
So the final code should look something like this:
ffmpeg -i song-name.mp3 -loop 1 -i cover.jpg -r 1/length -t length -acodec copy song-name.mp4
Where "song-name" is a variable which changes for every iteration of the FOR loop (i.e. for every audio file in the folder) and length is a variable whose value we get with the command:
mp3info.exe -p %S song-name.mp3
I found examples of a FOR loop to fetch all file names of all mp3's in a specific folder but I do not know how to integrate mp3info. I hope that somebody can help me and I have some knowledge of the C programming language if that can be used in any way.
Here's the edited simplified version without the VBS math.
The reason %%S is used is that % is a special batch character used for %environment% variables and in forINdo loops and to get a single one in a forINdo command it has to be doubled. Similarly echo %% will echo a single percent sign.
#echo off
for %%a in (*.mp3) do (
for /f "delims=" %%b in ('mp3info.exe -p %%S "%%a"') do (
ffmpeg -i "%%a" -loop 1 -i "cover.jpg" -r 1 -t %%b -acodec copy "%%~na.mp4"
)
)