How can I avoid an ffmpeg out of memory error - filter

I use ffmpeg with complex filtering. Input are different sets of FULLHD surveilance camera videos each 10 to 15 seconds long. Set size (number of videos per set) varies. To remove unchanged frames I apply mpdecimate. To avoid being triggered by moving bushes but still keep objects I want to remain, I apply a complex filter:
split the video (the original and a dummy to detect motion/stills)
scale the dummy down (so the 8x8-block-metric of mpdecimate matches the size of moving objects I want to keep)
add white boxes to dummy to mask unintendedly moving objects
apply mpdecimate to dummy to remove non-changing frames
scale dummy back to original size
overlay the remaining frames of dummy with matching frames of original
All this works fine if the number of input videos is small (less than 100). The memory consupmtion of the ffmpeg process varies somewhere between 2GiB and 5GiB.
If the number of input files gets larger (say 200), the memory consumption suddenly jumps to insane numbers until memory (32GiB plus 33GiB swap) runs out and ffmpeg gets killed. I can not predict if and why this happens. I have one example, where a set of 340 videos worked using 6GiB. Any other set above 100 videos I tried eats all RAM in under two minutes and dies.
There is no particular error message from ffmpeg.
dmesg says:
Out of memory: Kill process 29173 (ffmpeg)
Killed process 29173 (ffmpeg) total-vm:66707800kB
My ffmpeg command:
ffmpeg -f concat -safe 0 -i vidlist -vf 'split=2[full][masked];[masked]scale=w=iw/4:h=ih/4,drawbox=w=51:h=153:x=101:y=0:t=fill:c=white,drawbox=w=74:h=67:x=86:y=49:t=fill:c=white,drawbox=w=51:h=149:x=258:y=0:t=fill:c=white,drawbox=w=13:h=20:x=214:y=103:t=fill:c=white,drawbox=w=29:h=54:x=429:y=40:t=fill:c=white,drawbox=w=35:h=49:x=360:y=111:t=fill:c=white,drawbox=w=26:h=54:x=304:y=92:t=fill:c=white,drawbox=w=48:h=27:x=356:y=105:t=fill:c=white,drawbox=w=30:h=27:x=188:y=124:t=fill:c=white,drawbox=w=50:h=54:x=371:y=7:t=fill:c=white,drawbox=w=18:h=38:x=248:y=107:t=fill:c=white,drawbox=w=21:h=51:x=242:y=33:t=fill:c=white,mpdecimate=hi=64*80:lo=64*40:frac=0.001,scale=w=iw*4:h=ih*4[deduped];[deduped][full]overlay=shortest=1,setpts=N/(15*TB),mpdecimate=hi=64*80:lo=64*50:frac=0.001,setpts=N/(15*TB)' -r 15 -c:v libx265 -preset slower -crf 37 -pix_fmt yuv420p -an result.mkv
ffmpeg version 4.1.6
Debian 4.19.171-2
I hope that my filter can be tuned in some way that achieves the same result but doesn't eat RAM that much - but I have no clue how. Within reasonable limits, I wouldn't mind if processing time suffers. Any hints appreciated.

It seems the memory issue can be avoided by removing the split filter. Instead of spliting one input into two streams (that ffmpeg has to store in memory) the same input can be loaded twice.
So instead of using "full" and "dummy" as below
ffmpeg -i source -vf 'split=2[full][dummy];...;[dummy][full]overlay...
one would use "0:v" and "1:v" as in
ffmpeg -i source -i scource -filter_complex '.....;[0:v][1:v]overlay...
I get this to work with input videos, but so far I fail to do this with the concat demuxer as input.
Any hints very welcome.

Related

Increase bit rate while converting FFMPEG

I am trying to convert an .mp4 to .mpeg using:
ffmpeg -i "01_b.wmv" -an -c:v mpeg1video -b:v 20M "intro.mpeg"
My source video is 20MB/s but the converted .mpeg file doesn't ever go that high (usually ends up being around 12MB/s).
Is there something I am missing? I am relatively inexperienced with FFMPEG, so any help is appreciated.
The bit rate is calculated to an average at output versus being a simple duration/file size calculation. 100MB file, 10s = 10Mb/s, even though 40Mb/s could be the two seconds, and the rest is a lower bit rate.

ffmpeg to change video from 30 to 60fps to double speed without re-encoding

First, I have looked at the older questions asking the same, but the responses do not work.
Adding -r 30 or 60 to the input file does not impact the output, nor does setting it for the output, which remains unchanged.
I am handling a very large number of files from 1 to 22 gigs recorded (with audio) in 30fps that need to be re-posted as 60pfs, with the corresponding speed increase.
I toyed with ffmpeg a bit and came up with this..
-filter_complex "[0:v]setpts=0.50*PTS[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]" -vcodec:v libx264
It works fine, but to have to wait out a complete re-encoding of the video and audio to produce the same video with the fps changed seems like an insane waste of time.
Am I missing something simple? Is there not a way to -c copy with a new fps playback rate on the resulting file?
(if it still has to recode the audio to maintain sync that's fine, audio is quick enough it doesn't much matter)

What codec/format to use for fastest possible decoding?

I'm using an ffmpeg script (in Windows) that extracts all the keyframes from a video and pastes them into a folder. I've made sure that my drive speed, CPU, and RAM are not causing a bottleneck.
The quality of the video is actually not important at all in this case. I need to encode the video that the script extracts frames from so that it has the fastest possible decoding speed. File size and quality are not important, only read speed. The video does not have audio. What would work best for me?
If it matters, here's the script I'm working with:
ffmpeg -i input.mp4 -vf "select=eq(pict_type\,I)" -vsync 1 %%3d.bmp
Sorry if sound like I don't know what I'm talking about, this is not a topic I am super familiar with. I appreciate your help!
The below will be much faster for a codec like H.264
ffmpeg -discard nokey -i input.mp4 -vsync 0 %%3d.bmp
Your present approach decodes all frames and then the select filter gets to do gating, but the above command discards everything but keyframes at the demuxer level. It only works with MP4/MOV like containers.

ffmpeg drop frames on purpose to lower filesize

Our security system records and archives our IP cameras streams with ffmpeg -use_wallclock_as_timestamps 1 -i rtsp://192.168.x.x:554/mpeg4 -c copy -t 60 my_input_video.avi
I run it with crontab every minute so it creates videos of 60 seconds (~15Mb) for each camera every minute. When an intrusion occurs, the camera sends a picture through FTP and a script called by incrontab:
1- forwards immediately the picture by email
2- selects the video covering the minute the intrusion occured, compress it with h264 (to ~2,6Mb) and sends it by email
It is working really well but if a thief crosses the path of various cameras, the connection to the SMTP server is not fast enough so video emails are delayed. I'd like to compress the videos even more to avoid that. I could lower the resolution (640x480 to 320x240 for example) but sometimes 640x480 is handy to zoom on something which looks to be moving...
So my idea is to drop frames in the video in order to lower the filesize. I don't care if the thief is walking like a "stop motion Lego" on the video, the most important is I know there is someone so I can act.
mediainfo my_input_video.avi says Frame rate = 600.000 fps but it is of course wrong. FPS sent by IP cameras are always false because it varies with the network quality; this is why i use "-use_wallclock_as_timestamps 1" in my command to record the streams.
with ffmpeg -i my_input_video.avi -vcodec h264 -preset ultrafast -crf 28 -acodec mp3 -q:a 5 -r 8 output.avi the video is OK but filesize is higher (3Mb)
with ffmpeg -i my_input_video.avi -vcodec h264 -preset ultrafast -crf 28 -acodec mp3 -q:a 5 -r 2 output.avi the filesize is lower (2,2Mb) but the video doesn't work (it is blocked at the first frame).
Creating a mjpeg video (mjpeg = not interlaced frames) in the middle of the process (first exporting to mjpeg with less frames and then exporting to h264) creates same results.
Do you know how I can get my thief to walk like a "stop motion Lego" to lower the filesize to a minimum?
Thanks for any help
What are your constraints file size wise? 2.6MB for 60 seconds of video seems pretty reasonable to me, thats about 350kbps, which is pretty low for video quality.
You need to specify the video bitrate -b:v 125000 (125kbps, should drop you to about 900kb) to control the bitrate/s you want the video encoded at. Your not giving FFMpeg enough hints as to how you want the video handled, so its picking arbitrary values you don't like. As you drop the frame rate, its just using up the buffers allocating more bits to each frame. (one big thing you need to keep in mind with this is, as you stretch the video out over a longer time period the more likely the scene will change significantly require an I frame (full encoded frame vs frame based on previous frame) so reducing the frame rate will help some, but may not help as much as you'd think).
Your "(it is blocked at the first frame)." is most likely an issue with you trying to start decoding a stream when it is not at an I frame and not an issue with your settings.

ffmpeg segment to devide video into small chunks

hello everyone I want to split my video into small chunks.I am using ffmpeg segment to achieve this and I want to split the video in same video lenght chunks.I am using ffmpeg segment to split the video.I have written the command
**ffmpeg -i first.mp4 -codec copy -map 0 -f segment -segment_time 10 -segment_list out.list SplitVideo/out%04d.mp4**
When I am executing this command it splits the video in to small chunks but it is not accurate.The chunks are not of same length
From personal experience I realized the following:
Depending on the video format, there are usually dependencies between different frames. It means that some frames are depended on some other frames to be decoded. Therefore the splitting MAYBE cannot be done at any time you want because some of these dependencies might get lost their connections and hence cannot be decoded later. So what ffmpeg does is actually to find the nearest point that is independent from previous frames so all dependencies are satisfied. This will cause different lengths.

Resources