Error when cropping video using FFMPEG - ffmpeg

Question: Is there a single FFMPEG crop filter that will work for all videos, including (but not limited to): 856x480, 640x480, and 1280x720?
We have a video processing system (uses DirectShow), and all videos fed into this system must be 16:9 using the MJPEG codec. We use the following ffmpeg command to convert the source videos into MJPEG, scale the pixels to make them square, and then crop them to a 16:9 aspect ratio. This works great for most input videos, and the output is exactly what we want.
ffmpeg -i "1280x720input.mp4" -filter:v "scale=iw*sar:ih,crop=iw:iw/16*9" -codec:v mjpeg -q:v 2 -codec:a pcm_s16le -r 30 -y "output.avi"
However, when we use an input video with a resolution of 856x480, we get the following error:
[Parsed_crop_1 # 0000000004615720] Invalid too big or non positive
size for width '852' or height '480'
I tried a different crop filter that uses the input height in the calculation instead of input width, and it works with 856x480
ffmpeg -i "856x480input.mp4" -filter:v "scale=iw*sar:ih,crop=ih*16/9:ih" -codec:v mjpeg -q:v 2 -codec:a pcm_s16le -r 30 -y "output.avi"
However this does not work with other source videos in 16:9 (1280x720) or 4:3 (640x480) format. Is there a single crop command that will work on all videos?

You need conditional expressions:
crop='if(gte(dar,16/9),ih*16/9,iw)':'if(gte(dar,16/9),ih,iw*9/16)'
dar is the display aspect ratio i.e. iw * sar / ih

Related

ffmpeg: filter_complex issue: 25fps vs. 60fps (with transparent VP9 webm)

I have a lot of created png images with transparency / alpha channel.
These need to be converted to a webm file with the VP9 codec to have a video with transparent areas.
The output framerate should be 60fps. But filter_complex (which i need to select the yuva420p format) apparently has an issue with that.
I found an answer how to fix that; tried to adapt it to my case but it did not work.
The 1055 source png files should create a video with 60fps and a duration of 17.6 sec. But the result is a file with 42.2 sec (1055 frames at 25fps); according to ffmpeg 2530 frames were used.
So the video file itself has 60fps but the source frames are "stretched" to 25fps. Kind of a slow-mo video.
What do i need to change?
Or would it be possible to put the pngs into a VP9 webm with 60fps without using filter_complex at all?
The command i'm using by adapting another found answer is:
ffmpeg -hide_banner -i srcImgMasked_%05d.png -filter_complex "nullsrc=size=1920x1080:rate=60 [base]; [0]format=yuva420p [bla]; [base] [bla] overlay=shortest=0" -c:v libvpx-vp9 -crf 20 -b:v 0 -threads 0 result.webm -y

How to resize h264 video with ffmpeg?

I have a raw .h264 video with resolution 1920x1080 and I want to resize to 1280x720 in the same format (h264) using ffmpeg. I found examples on doing that but to mp4 (https://askubuntu.com/questions/690015/how-can-i-convert-264-file-to-mp4) but I actually want to resize to .h264 format (no container)
Use the scale filter:
ffmpeg -i input.h264 -vf scale=1280:720 output.h264
If ffmpeg assumes an incorrect frame rate (refer to console output) add the -framerate input option:
ffmpeg -framerate 24 -i input.h264 -vf scale=1280:720 output.h264

ffmpeg convert one png to DNxHD

I'm trying to create a DNxHD file from one PNG file. The output should be "24000/1001" fps, 1920x1080, using the dnxhd codec. Every frame should be the same. The outputted stream must be 20 seconds in length.
I have a solution which uses filter_complex to loop the PNG for each frame, however this results in extremely large files. Given that I will be combining possibly multiple hundred DNxHD files into one AAF file, the output file size is too large.
Is there any improvement I can make on the command below which would achieve this file size reduction?
ffmpeg -i INFILE.png -y -nostdin -an -vcodec dnxhd -pix_fmt yuv422p -vb 36M -framerate 24000/1001 -filter_complex loop=479:1:0 OUFILE.dnxhd
I do not know ffmpeg all that well, this command has been constructed by copying parts of commands I have found online.
DNxHD is an intra-coded codec i.e. each frame is encoded (and thus decodable) independently of each other. So, the size can't be decreased without changing ratecontrol parameters like bitrate.
BTW, your command can be simplified to
ffmpeg -framerate 24000/1001 -loop 1 -t 20 -i IN.png -c:v dnxhd -pix_fmt yuv422p -b:v 36M OUT.dnxhd
framerate doesn't have any relevance when saving to a raw stream (.dnxhd); only when saving to containers like .mov. It is possible technically to construct a MOV file with only two frames, with the 2nd frame, having a timestamp 20 seconds forward, but not sure if your workflow will handle such files as desired.

avconv / ffmpeg webcam capture while using minimum CPU processing

I have a question about avconv (or ffmpeg) usage.
My goal is to capture video from a webcam and saving it to a file.
Also, I don't want to use too much CPU processing. (I don't want avconv to scale or re-encode the stream)
So, I was thinking to use the compressed mjpeg video stream from the webcam and directly saving it to a file.
My webcam is a Microsoft LifeCam HD 3000 and its capabilities are:
ffmpeg -f v4l2 -list_formats all -i /dev/video0
Raw: yuyv422 : YUV 4:2:2 (YUYV) : 640x480 1280x720 960x544 800x448 640x360 424x240 352x288 320x240 800x600 176x144 160x120 1280x800
Compressed: mjpeg : MJPEG : 640x480 1280x720 960x544 800x448 640x360 800x600 416x240 352x288 176x144 320x240 160x120
What would be the avconv command to save the Compressed stream directly without having avconv doing scaling or re-encoding.
For now, I am using this command:
avconv -f video4linux2 -r 30 -s 320x240 -i /dev/video0 test.avi
I'm not sure that this command is CPU efficient since I don't tell anywhere to use the mjpeg Compressed capability of the webcam.
Is avconv taking care of the configuration of the webcam setting before starting to record the file ? Is it always working of raw stream and doing scaling and enconding on the raw stream ?
Thanks for your answer
Reading the actual documentation™ is the closest thing to magic you'll get in real life:
video4linux2, v4l2
input_format
Set the preferred pixel format (for raw video) or a codec name. This option allows one to select the input format, when several are available.
video_size
Set the video frame size. The argument must be a string in the form WIDTHxHEIGHT or a valid size abbreviation.
The command uses -c:v copy to just copy the received encoding without touching it therefore achieving the lowest resource use:
ffmpeg -f video4linux2 -input_format mjpeg -video_size 640x480 -i /dev/video0 -c:v copy <output>

How can I place a still image before the first frame of a video?

When I encode videos by FFMpeg I would like to put a jpg image before the very first video frame, because when I embed the video on a webpage with "video" html5 tag, it shows the very first picture as a splash image. Alternatively I want to encode an image to an 1 frame video and concatenate it to my encoded video. I don't want to use the "poster" property of the "video" html5 element.
You can use the concat filter to do that. The exact command depends on how long you want your splash screen to be. I am pretty sure you don't want an 1-frame splash screen, which is about 1/25 to 1/30 seconds, depending on the video ;)
The Answer
First, you need to get the frame rate of the video. Try ffmpeg -i INPUT and find the tbr value. E.g.
$ ffmpeg -i a.mkv
ffmpeg version N-62860-g9173602 Copyright (c) 2000-2014 the FFmpeg developers
built on Apr 30 2014 21:42:15 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
[...]
Input #0, matroska,webm, from 'a.mkv':
Metadata:
ENCODER : Lavf55.37.101
Duration: 00:00:10.08, start: 0.080000, bitrate: 23 kb/s
Stream #0:0: Video: h264 (High 4:4:4 Predictive), yuv444p, 320x240 [SAR 1:1 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
At least one output file must be specified
In the above example, it shows 25 tbr. Remember this number.
Second, you need to concatenate the image with the video. Try this command:
ffmpeg -loop 1 -framerate FPS -t SECONDS -i IMAGE \
-t SECONDS -f lavfi -i aevalsrc=0 \
-i INPUTVIDEO \
-filter_complex '[0:0] [1:0] [2:0] [2:1] concat=n=2:v=1:a=1' \
[OPTIONS] OUTPUT
If your video doesn't have audio, try this:
ffmpeg -loop 1 -framerate FPS -t SECONDS -i IMAGE \
-i INPUTVIDEO \
-filter_complex '[0:0] [1:0] concat=n=2:v=1:a=0' \
[OPTIONS] OUTPUT
FPS = tbr value got from step 1
SECONDS = duration you want the image to be shown.
IMAGE = the image name
INPUTVIDEO = the original video name
[OPTIONS] = optional encoding parameters (such as -vcodec libx264 or -b:a 160k)
OUTPUT = the output video file name
How Does This Work?
Let's split the command line I used:
-loop 1 -framerate FPS -t SECONDS -i IMAGE: this basically means: open the image, and loop over it to make it a video with SECONDS seconds with FPS frames per second. The reason you need it to have the same FPS as the input video is because the concat filter we will use later has a restriction on it.
-t SECONDS -f lavfi -i aevalsrc=0: this means: generate silence for SECONDS (0 means silence). You need silence to fill up the time for the splash image. This isn't needed if the original video doesn't have audio.
-i INPUTVIDEO: open the video itself.
-filter_complex '[0:0] [1:0] [2:0] [2:1] concat=n=2:v=1:a=1': this is the best part. You open file 0 stream 0 (the image-video), file 1 stream 0 (the silence audio), file 2 streams 0 and 1 (the real input audio and video), and concatenate them together. The options n, v, and a mean that there are 2 segments, 1 output video, and 1 output audio.
[OPTIONS] OUTPUT: this just means to encode the video to the output file name. If you are using HTML5 streaming, you'd probably want to use -c:v libx264 -crf 23 -c:a libfdk_aac (or -c:a libfaac) -b:a 128k for H.264 video and AAC audio.
Further information
You can check out the documentation for the image2 demuxer which is the core of the magic behind -loop 1.
Documentation for concat filter is also helpful.
Another good source of information is the FFmpeg wiki on concatenation.
The answer above works for me but in my case it took too much time to execute (perhaps because it re-encodes the entire video). I found another solution that's much faster. The basic idea is:
Create a "video" that only has the image.
Concatenate the above video with the original one, without re-encoding.
Create a video that only has the image:
ffmpeg -loop 1 -framerate 30 -i image.jpg -c:v libx264 -t 3 -pix_fmt yuv420p image.mp4
Note the -framerate 30 option. It has to be the same with the main video. Also, the image should have the same dimension with the main video. The -t 3 specifies the length of the video in seconds.
Convert the videos to MPEG-2 transport stream
According to the ffmpeg official documentation, only certain files can be concatenated using the concat protocal, this includes the MPEG-2 transport streams. And since we have 2 MP4 videos, they can be losslessly converted to MPEG-2 TS:
ffmpeg -i image.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts image.ts
and for the main video:
ffmpeg -i video.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts video.ts
Concatenate the MPEG-2 TS files
Now use the following command to concatenate the above intermediate files:
ffmpeg -i "concat:image.ts|video.ts" -c copy -bsf:a aac_adtstoasc output.mp4
Although there are 4 commands to run, combined they're still much faster then re-encoding the entire video.
My solution. It sets an image with duration of 5 sec before the video along with aligning video to be 1280x720. Image should have 16/9 aspect ratio.
ffmpeg -i video.mp4 -i image.png -filter_complex '
color=c=black:size=1280x720 [temp]; \
[temp][1:v] overlay=x=0:y=0:enable='between(t,0,5)' [temp]; \
[0:v] setpts=PTS+5/TB, scale=1280x720:force_original_aspect_ratio=decrease, pad=1280:720:-1:-1:color=black [v:0]; \
[temp][v:0] overlay=x=0:y=0:shortest=1:enable='gt(t,5)' [v]; \
[0:a] asetpts=PTS+5/TB [a]'
-map [v] -map [a] -preset veryfast output.mp4

Resources