ffmpeg - Dynamic letters and random position watermark to video? - ffmpeg

I am making an online course, and to avoid piracy distribution I thought to put watermarks on the videos (including personal user information) so it cannot upload to sharing websites. Now the hard part: I would move the watermark during the video, in 3/4 random positions, every 30 seconds.
It is possibile with ffmpeg?

Edit: this is an adaptation of the answer in LN's link, which will randomize the position every 30 seconds with no repeats:
ffmpeg -i input.mp4 \
-vf \
"drawtext=fontfile=font.ttf:fontsize=80:fontcolor=yellow#0.5:text='studentname': \
x=if(eq(mod(t\,30)\,0)\,rand(0\,(W-tw))\,x): \
y=if(eq(mod(t\,30)\,0)\,rand(0\,(H-th))\,y)" \
-c:v libx264 -crf 23 -c:a copy output.mp4
Older answer
You can use a command like the one below:
ffmpeg -i input.mp4 \
-vf \
"drawtext=fontfile=font.ttf:fontsize=80:fontcolor=yellow#0.5: \
text='studentname':x=200:y=350:enable='between(mod(t\,30*3),0,30)', \
drawtext=fontfile=font.ttf:fontsize=80:fontcolor=yellow#0.5: \
text='studentname':x=1000:y=600:enable='between(mod(t\,30*3),31,60)', \
drawtext=fontfile=font.ttf:fontsize=80:fontcolor=yellow#0.5: \
text='studentname':x=450:y=50:enable='between(mod(t\,30*3),61,90)'" \
-c:v libx264 -crf 23 -c:a copy output.mp4
Here, three positions are rotated with a change occurring every 30 seconds. Each x:y parameter is manually set. If you're calling the command from a shell script, you can use a random number generator and feed that into the command. There is a random function included in the drawtext filter, but it is evaluated each frame, so that will result in a pseudo ping pong game with the text.

Related

FFMPEG changing output framerate issues

I want to take the input, blend N frames, decimate the other frames and use those for the output with the fps of my choice.
I used this line:
ffmpeg -y -i input.mp4 -vf tmix=frames=15:weights="1",select='not(mod(n\,15))' -vsync vfr frames/output-%05d.tif
That generated images, which I combined into the video. So far, so good.
But I'd like to skip the image output and go straight to video, so I tried this:
ffmpeg -y -i input.mp4 -vf tmix=frames=15:weights="1",select='not(mod(n\,15))' -vsync vfr -r 30 -c:v prores_ks -profile:v 3 -vendor apl0 -bits_per_mb 8000 -pix_fmt yuv422p10le output.mov
That produces 1.62 fps video, instead of 30 fps.
I'm at a loss on how to get it to output 30fps without the intermediate step of outputting images.
Thanks
I think the simplest way to achieve this is to feed the input at the 15-times the desired rate and drop all intermediate frames with -r 30:
ffmpeg -y -r 450 -i input.mp4 \
-vf tmix=frames=15:weights="1" \
-r 30 sandbox/out.mp4
However, a tmix solution is somewhat inefficient for your use case because it's mixing for all frames, including those dropped. If you don't mind a longer expression, you can try:
ffmpeg -i in.mp4 \
-vf
setpts=\'floor(N/15)/(30*TB)\',select=\'mod(n,15)+1\':n=15[v0][v1][v2][v3][v4][v5][v6][v7][v8][v9][v10][v11][v12][v13][v14];\
[v0][v1][v2][v3][v4][v5][v6][v7][v8][v9][v10][v11][v12][v13][v14]mix=inputs=15:weights=1 \
-r 30 sandbox/out.mp4
[edit] setpts expression should be floor(N/15)/(30*TB) not mod(n,15)+1 for 15 successive frames to have the same pts.

FFMPEG trimming video (using select between) doesn't affect overall duration

Completely new to working with FFMPEG, what I'm trying to achieve is applying overlaying graphics at certain positions and times, and cutting out sections of a single input video.
I've worked out the overlaying graphics, so this code is working:
ffmpeg -i /Users/username/projectdir/static/video.mp4 \
-i overlay.png -i overlay2.png \
-filter_complex "[0:v][1:v] overlay=192:108:enable='between(t, 0, 5)'[ov0];
[ov0] overlay=192:108:enable='between(t, 5, 10)'" \
-pix_fmt yuv420p output_overlayed.mp4
But when I try to cut out sections using this code:
ffmpeg -i /Users/username/projectdir/static/video.mp4 \
-i overlay.png -i overlay2.png \
-filter_complex "[0:v][1:v] overlay=192:108:enable='between(t, 0, 5)'[ov0]; \
[ov0] overlay=192:108:enable='between(t, 5, 10)', \
select='between(t,0,5)+between(t,10,15)', \
setpts='N/FRAME_RATE/TB'" \
-pix_fmt yuv420p output_overlayed_trimmed.mp4
It seems to cut correctly, so the original video starts playing from 0 seconds until 5 seconds and then plays from 10 seconds in until 15 seconds and cuts out. But after the point where the video cuts out it's just a black screen for the duration of the video. I can't seem to get it to work so it affects the overall duration of the video.
(The values being passed in are just examples by the way, eg. I've got it to start an overlay 5 seconds in but also cut 5 seconds in)
I have the timestamps for when the overlays should appear on the non-trimmed video, so the overlaying should happen first and then the trimming. If the video is trimmed first then the overlays will appear at the wrong times.
An alternative way of achieving this that is currently working is by performing the first line of code (which just produces a new video file with the overlay) and then separately take this new file and perform the trimming independently:
ffmpeg -ss 0 -to 5 -i /Users/username/projectdir/static/output_overlayed.mp4 \
-ss 15 -to 20 -i /Users/username/projectdir/static/output_overlayed.mp4 \
-filter_complex "[0][1]concat=n=2:v=1:a=1" output_trimmed.mp4
But this means working with 2 separate files and then having to remove the first after the 2nd execution is complete. Ideally I'd combine them into one command which doesn't produce multiple files.
Would appreciate any help - thanks!
How about get the input twice with different trims (so both video & audio are cut in sync) then concatenate after overlaying? Like this:
ffmpeg -t 5 -i /Users/username/projectdir/static/video.mp4 \
-ss 10 -to 15 -i /Users/username/projectdir/static/video.mp4 \
-i overlay.png -i overlay2.png \
-filter_complex "[0:v][2:v] overlay=192:108[ov0]; \
[1:v][3:v] overlay=192:108[ov1]; \
[ov0][0:a][ov1][1:a] concat=n=2:v=2:a=2[vout][aout] \
-map [vout] -map[aout] -pix_fmt yuv420p output_overlayed_trimmed.mp4

ffmpeg, add static image to beginning and end with transitions

ffmpeg noob here, trying to help my mother with some videos for real estate walkthroughs. I'd like to set up a simple pipeline that I can run videos through and have outputted as such:
5 second (silent) title card ->
xfade transition ->
property walk through ->
xfade transition ->
5 second (silent) title card
Considerations:
The intro / outro card will be the same content.
The input walkthrough videos will be of variable length so, if possible, a dynamic solution accounting for this would be ideal. If this requires me to script something using ffprobe, I can do that - just need to gain an understanding of the syntax and order of operations.
The video clip will come in with some audio already overlaid. I would like for the title cards to be silent, and have the video/audio clip fade in/out together.
I have gotten a sample working without the transitions:
ffmpeg -loop 1 -t 5 -i title_card.jpg \
-i walkthrough.MOV \
-f lavfi -t 0.1 -i anullsrc \
-filter_complex "[0][2][1:v][1:a][0][2]concat=n=3:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
-vcodec libx265 \
-crf 18 \
-vsync 2 \
output_without_transitions.mp4
I have been unable to get it to work with transitions. See below for the latest iteration:
ffmpeg -loop 1 -t 5 -r 60 -i title_card.jpg \
-r 60 -i walkthrough.MOV \
-f lavfi -t 0.1 -i anullsrc \
-filter_complex \
"[0][1:v]xfade=transition=fade:duration=0.5:offset=4.5[v01]; \
[v01][0]xfade=transition=fade:duration=0.5:offset=12.8[v]" \
-map "[v]" \
-vcodec libx265 \
-crf 18 \
-vsync 2 \
output_with_transitions.mp4
This half-works, resulting in the initial title card, fading into the video, but the second title card never occurs. Note, I also removed any references to audio, in an effort to get the transitions alone to work.
I have been beating my head against the wall on this, so help would be appreciated :)
Assuming walkthrough.MOV is 10 seconds long:
ffmpeg -loop 1 -t 5 -framerate 30 -i title_card.jpg -i walkthrough.MOV -filter_complex "[0]settb=AVTB,split[begin][end];[1:v]settb=AVTB[main];[begin][main]xfade=transition=fade:duration=1:offset=4[xf];[xf][end]xfade=transition=fade:duration=1:offset=13,format=yuv420p[v];[1:a]adelay=4s:all=1,afade=t=in:start_time=4:duration=1,afade=t=out:start_time=13:duration=1,apad=pad_dur=4[a]" -map "[v]" -map "[a]" -c:v libx265 -crf 18 -movflags +faststart output.mp4
You will need to upgrade your ffmpeg for this to work. The current release version (4.3 as of this answer) is too old, so get a build from the git master branch. See FFmpeg Download for links to builds for your OS, or see FFmpeg Wiki: Compile Guide.
title_card.jpg frame rate, width, and height must match walkthrough.MOV.
See Merging multiple video files with ffmpeg and xfade filter to see how to calculate xfade and afade offsets.
See FFmpeg Filter documentation for details on each filter.
See How to get video duration in seconds? which can help you automate this via scripting.
apad is supposed to automatically work with -shortest, but it doesn't with -filter_complex. So pad_dur is used to add the additional silence to the last title image, but whole_dur can be used instead if that is easier for you. Another method is to use anullsrc as in your question, then concatenate audio only with the concat filter, but I wanted to show adelay+apad as a viable alternative.

Splitting video in short clip result in some being empty?

I'm trying to split a video (50-100Mo) into several small clips of a few seconds each. I don't need re-encoding, hence my use of codec copy.
However some of the resulting clips don't have any video.
Fast but no video in some files
ffmpeg \
-y \
-i ./data/partie-1:-Apprendre-300-mots-du-quotidien-en-LSF.jauvert-laura.hd.mkv \
-ss 0:00:07.00 \
-codec copy \
-loglevel error \
-to 0:00:10.36 \
'raw/0:00:07.00.au revoir.mkv'
I also tried -map 0 -c copy, -acodec copy -map 0:a -vcodec copy -map 0:v or no option related to codec.
Slow but complete
No argument related to audio/video encoding, it's working but pretty slow.
ffmpeg -y \
-i "$SOURCE_VIDEO_FILE" \
-ss 0:05:37.69 \
-to 0:05:40.64 \
-loglevel error
'raw/0:05:37.69.pas la peine.mkv'
Question
How do I split a video into small chunk ~2-4s when I have no need for re-encoding?
related: https://video.stackexchange.com/q/25365/23799
Your constraint can't be satisfied. Some video codecs appear to use chunks, where they start with a complete frame and then store "diffs", so in order to use -vcodec copy, ffmpeg has to honour the chunk boundaries.
Don't use -vcodec copy if you encounter this problem.

How to get the highest resolution, dividable by 2 that contains the video without black borders after rotation?

I rotate a video. Then my goal is to get the biggest resolution, dividable by 2 that contains the video without black borders.
to
My approach.
ffmpeg -ss 6 -i "t.MP4" -ss 0 -t 5 -vf "rotate='8*PI/180:ow=hypot(iw,ih):oh=ow', scale='1920:1920', crop='1920:1080:0:420'" -c:v libx264 -crf 28 -acodec copy "t2.MP4"
But that does not meet the criteria. Which command do I need?
Use
ffmpeg -i t.mp4 -vf "rotate=8*PI/180:ow=rotw(8*PI/180):oh=roth(8*PI/180), \
crop=iw-2*imgh*sin(8*PI/180):ih-2*imgw*sin(8*PI/180): \
imgh*sin(8*PI/180):imgw*sin(8*PI/180), \
scale=iw:-2"
-c:v libx264 -crf 28 -acodec copy "t2.MP4"
For anti-clockwise rotation, add a -ve sign to the angles in the rotate filter, but not in the crop filter.
In the commands above, imgw and imgh stand for the width and height of the source image and have to be explicitly supplied i.e. for a 1280x720 video, where it says imgw, you should supply 1280.

Resources