FFmpeg concat after overlay's (append) - ffmpeg

I have some video to witch i need to concat a header and add some overlay's, the problem is that i need to do the overlay's first becouse otherwise the time woundn't be correct, and only afterwards concat the header to the begining of the video
ffmpeg -i talk.mp4 -i start_pancarte.png -i end_pancarte.png -i logo_alpha.png -i header.mp4-filter_complex\
" overlay= enable='between(t,0,3.04)' [x];\
[x] overlay = enable='between(t,495.64, 498.6)' [y];\
[y] overlay=15:15:enable='between(t,3.04,495.64)'[c];\
[c] concat " solution.mp4
This obviously adds the header to the end and I need it in the front, how do I do this (in 1 command)?

Use
ffmpeg -i talk.mp4 -i start_pancarte.png -i end_pancarte.png -i logo_alpha.png -i header.mp4
-filter_complex
"[0][1]overlay= enable='between(t,0,3.04)'[x];\
[x][2]overlay=enable='between(t,495.64, 498.6)'[y];\
[y][3] overlay=15:15:enable='between(t,3.04,495.64)'[c];\
[4][c]concat" solution.mp4
Relying on implicit filter pad connections can be sloppy, as you have discovered. I've made the inputs to each filter explicit and arranged the inputs to the concat in the required order.

Related

ffmpeg: How to save SSIM and PSNR to a file?

I'm a ffmpeg newbie.
I would like my script (on Windows) to output the average PSNR and average SSIM values to a file.
(but not the values for every frame)
I can output them to the standard output but not to a file.
I use this line:
ffmpeg -i ref.avi -i compressed.avi -lavfi "ssim;[0:v][1:v]psnr" -f null -
I understand I have to change something here: "-f null -" , but I cannot make it work.
Using ssim & psnr filters
On Linux and macOS you can use grep:
$ ffmpeg -i compressed.avi -i reference.avi -lavfi "[0][1]ssim;[0][1]psnr" -f null - |& grep Parsed_ > ff.log
Simple, but major downside is it won't show you the console output, so you may miss errors. To fix that add tee:
$ ffmpeg -i compressed.avi -i reference.avi -lavfi "[0][1]ssim;[0][1]psnr" -f null - |& tee >(grep Parsed_ > ff.log)
Example contents of ff.log from either command:
[Parsed_ssim_0 # 0x5579d7f17b40] SSIM Y:0.796135 (6.906565) U:0.843488 (8.054531) V:0.822424 (7.506157) All:0.820682 (7.463768)
[Parsed_psnr_1 # 0x5579d7f12b00] PSNR y:24.940925 u:23.938192 v:23.641771 average:24.138969 min:23.298059 max:26.880485
If you want to append to ff.log instead of overwrite use grep Parsed_ >> ff.log instead.
If |& does not work for you use 2>&1 instead.
Or use libvmaf
libvmaf filter is slower but will output a log file containing the VMAF, SSIM, and PSNR aggregate scores along with the per frame scores in XML or JSON. Your ffmpeg will need to be compiled with --enable-libvmaf to use this filter.
ffmpeg -i compressed.avi -i reference.avi -lavfi "[0][1]libvmaf=log_path=vmaf.xml:log_fmt=xml:ssim=1:psnr=1" -f null -

Remove a section from the middle of a video without concat

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.

Input parameters to FFMPEG

I am trying to make an FFMPEG script that relied on a glob input pattern from Linux to Windows. Unfortunately that is not supported so I am looking for an alternative. I do not want to have to rename or copy the files every time I run the script because the files are used elsewhere and I cannot rename them and I would like to avoid duplication or unnecessary temporary files.
Are globs numerically sequential named images my only option here? Ideally I would like to input a list of image paths to FFMPEG as a substitute for ffmpeg -i *.jpg
The workarounds are to prepare a text file with the names and use the concat demuxer.
Or you can use image2pipe
cat *.jpg | ffmpeg -f image2pipe -framerate 25 -i - out.mp4
The best solution I could find (that's Windows compatible) was to generate a line separated list of files in a text file and pass that through to FFMPEG. For example, to generate a stabilized MP4 from a bunch of JPEGs:
ffmpeg -f concat -safe 0 -i ./files.txt -vf deshake=rx=64:ry=64 ./stabilized.mp4
Where files.txt is a list of the files in the following format. The safe option toggles the ability to have absolute/relative file paths.
# this is a comment
file 'C:/path/to/file1.jpg'
file 'C:/path/to/file2.jpg'
file 'C:/path/to/file3.jpg'

How to make ffmpeg insert timestamp in filename

I'm trying to make a bash script that grabs a still shot from an IP camera and than emails it.
Using
ffmpeg -i http://admin:Stupidpassword1#10.12.10.40/Streaming/channels/1/picture \
-f image2 -updatefirst 1 doorbell.jpg
From what I have read this should work but the output file name is still doorbell.jpg How can I make the filename TIMESTAMPdoorbell.jpg?
Use the "strftime" feature:
ffmpeg -i http://admin:Stupidpassword1#10.12.10.40/Streaming/channels/1/picture -vframes 1 -f image2 -strftime 1 "%Y-%m-%d_%H-%M-%S_doorbell.jpg"
"-vframes 1" will cause it to only process the first frame that it receives.
You can change the date/time format using a strftime compatible string:
http://man7.org/linux/man-pages/man3/strftime.3.html
Further documentation/examples:
https://www.ffmpeg.org/ffmpeg-formats.html#image2-2

Need explanation of details of ffmpeg and pipes command

Got the following from FFmpeg FAQ:
mkfifo intermediate1.mpg
mkfifo intermediate2.mpg
ffmpeg -i input1.avi -sameq -y intermediate1.mpg < /dev/null &
ffmpeg -i input2.avi -sameq -y intermediate2.mpg < /dev/null &
cat intermediate1.mpg intermediate2.mpg |\
ffmpeg -f mpeg -i - -sameq -vcodec mpeg4 -acodec libmp3lame output.avi
Before i use or modify it I would like to understand it completely.
What does the < /dev/null & do?
I understand | is pipe but why |\ ?
What is the -f mpeg after ffmpeg (Seems, it tells ffmpeg to accept the piped in output from the cat(?) )
< /dev/null &
This is actually two parts:
< /dev/null
&
1 (< /dev/null) is just a simple way to pass no input/EOF to a program. I'm not sure it's needed but it may be because you are using named pipes.
2 (&) simply pushes the command to the background and allows you to do other things. This is necessary because otherwise, ffmpeg would just sit there waiting for the other end of the named pipe to "open".
Backslash after pipe
The backslash after the pipe is simply there to allow you to enter the long command on multiple lines. If you want to write it on a single line, you should omit the backslash. You'll notice that the prompt changes from your usual [user#machine directory]$ (or whatever) to something like > after you enter the first line (ending with a backslash). This signifies that your command is being continued from an earlier line.
ffmpeg -f switch
The man page for ffmpeg indicates that the -f switch allows you to force a file format. In the example in the FAQ, you want to force an input format (read: tell ffmpeg what input format to expect) since your using piped bits as input. Usually, it would try to guess the input format based on the file extension and/or "file magic".

Resources