Output image with correct aspect with ffmpeg - ffmpeg

I have a mkv video with the following properties (obtained with mediainfo):
Width : 718 pixels
Height : 432 pixels
Display aspect ratio : 2.35:1
Original display aspect ratio : 2.35:1
I'd like to take screenshots of it at certain times:
ffmpeg -ss 4212 -i filename.mkv -frames:v 1 -q:v 2 out.jpg
This will produce a 718x432 jpg image, but the aspect ratio is wrong (the image is "squeezed" horizontally). AFAIK, the output image should be 1015*432 (with width=height * DAR). Is this calculation correct?
Is there a way to have ffmpeg output images with the correct size/AR for all videos (i.e. no "hardcoded" values)? I tried playing with the setdar/setsar filters without success.
Also, out of curiosity, trying to obtain SAR and DAR with ffmpeg produces:
Stream #0:0(eng): Video: h264 (High), yuv420p(tv, smpte170m/smpte170m/bt709, progressive),
718x432 [SAR 64:45 DAR 2872:1215], SAR 155:109 DAR 55645:23544, 24.99 fps, 24.99 tbr, 1k tbn, 49.98 tbc (default)
2872/1215 is 2.363, so a slightly different value than what mediainfo reported. Anyone knows why?

Without looking at the file, can't diagnose the reason for the distinct readings, but the generic method to get a square pixel result is
ffmpeg -ss 4212 -i filename.mkv -vf scale=iw*sar:ih -frames:v 1 -q:v 2 out.jpg

Accordin to the doc of FFmpeg
ffmpeg -ss 4212 -i filename.mkv -vf scale='trunc(ih*dar):ih',setsar=1/1 \
-frames:v 1 -q:v 2 out.jpg
making sure the resulting resolution is even (required by some codecs)

Related

What is the default FFMPEG codec for outputting images and what are alternatives?

I took a look at Default ffmpeg codec when nothing is specified. I see that the default codec is likely libx264 for video. Is this the same answer for using an FFMPEG command to make a JPG?
If it's libx264, what would be a good alternative codec to generate an image? I took a look at https://johnvansickle.com/ffmpeg/git-readme.txt, and I can't seem to find anything on what those codecs do. For example, searching libzimg just comes up with pages of people enabling it in a long list of options. https://ffmpeg.org/ffmpeg-codecs.html has no mention of zimg.
My current command is ffmpeg -loglevel quiet -report -timelimit 9 -timeout 9 -i www.example.com/manifest -vframes 1 output.jpg -y. I want to make sure I know what codec is being used when doing this.
I'm using https://johnvansickle.com/ffmpeg/git-readme.txt, which has mjpeg listed. I just want to be sure that this would be the default, when nothing is specified. (I can specify things going forward, but want to know what would have been used before).
Default JPG encoder is mjpeg. Default encoders are not really documented as it depends on your ffmpeg configuration, but you can refer to the log from your ffmpeg command.
Example:
ffmpeg -i input.mp4 -frames:v 1 output.jpg
Log excerpt:
Output #0, image2, to 'output.jpg':
Metadata:
encoder : Lavf58.42.102
Stream #0:0: Video: mjpeg, yuvj444p(pc), 320x240 [SAR 1:1 DAR 4:3], q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc
So in this example the default muxer is image2 and the default encoder is mjpeg.
Get more info on these with ffmpeg -h muxer=image2 and ffmpeg -h encoder=mjpeg as shown in What are all codecs and formats supported by FFmpeg?

FFMPEG converting HEVC to VP9 large file size

I'm trying to convert HEVC videos to VP9 so they can be played in a web browser while keeping file size roughly the same.
I am struggling to create a video with similar quality/file size.
Here's the stream info for one of the HEVC videos the video is is 22:49 and 168.7mb:
Stream #0:0(und): Video: hevc (Main) (hev1 / 0x31766568), yuv420p(tv, bt709), 1920x1080, 900 kb/s, 23.98 fps, 23.98 tbr, 90k tbn, 23.98 tbc (default)
The bitrate is 900K so I had thought that the following command would use the same bitrate and give a roughly similar image quality:
ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -vaapi_device /dev/dri/renderD128 -i "$infile" -vf 'format=nv12,hwupload' -c:v vp9_vaapi -b:v 900K -bf 2 -bsf:v vp9_raw_reorder,vp9_superframe -c:a libvorbis "$outfile"
Using this, the quality is noticeably much, much worse and busy scenes look incredibly blocky though the file size is roughly equivalent to the HEVC source.
If I omit the bitrate and let VP9 work it out
ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -vaapi_device /dev/dri/renderD128 -i "$infile" -vf 'format=nv12,hwupload' -c:v vp9_vaapi -b:v 0 -bf 2 -bsf:v vp9_raw_reorder,vp9_superframe -c:a libvorbis "$outfile"
The quality is visually indistinguishable but the file size of the VP9 converted video reaches 401mb, up from 168mb of the HEVC file and during encoding the bitrate is over 3m for most of the video.
I tried going up to 1.2M (33% higher than the source video) and VP9 still gave a very blocky looking video.
Is VP9 really that much worse than HEVC or is there an option I am missing? or is it because I am converting from HEVC?

Trying to tonemap 14-bit grayscale video

I'm trying to generate h.264 video from raw 2-byte gray video (14-bit range encoded in 16-bit values). I can do something like:
ffmpeg -f rawvideo -pix_fmt gray16le -s:v 1280x720 -r 60 -i input.raw -c:v libx264 output.mp4
And I get video but it's pretty dark, not sure if it's clipping, doing a linear remap, or storing the 16-bit data and VLC is doing the remap. ffprobe is reporting Video: h264 (High 4:4:4 Predictive) (avc1 / 0x31637661), yuvj444p(pc), 1280x720, 108 kb/s, 60 fps, 60 tbr, 15360 tbn, 120 tbc
I was figuring I'd use the tonemap filter to make a better mapping. I added a filter before the output file with -vf.
tonemap=hable errors Impossible to convert between the formats supported by the filter 'graph 0 input from stream 0:0' and the filter 'auto_scaler_0'
zscale=transfer=linear,tonemap=hable errors Impossible to convert between the formats supported by the filter 'Parsed_tonemap_1' and the filter 'auto_scaler_1'
zscale=transfer=linear,tonemap=hable,zscale=transfer=bt709,format=yuvj444p errors code 3074: no path between colorspaces
I'm not sure where to proceed from here...

Re-encoding vlc-created mpeg2 .ts file results in 20 second file; AKA: multi-stream file with hidden streams

I was recording something with vlc off v4l2 (in case that makes a difference), and I just selected the first format that worked, being mpeg2 using TS container. File resulted in .ts extension, as automatically selected by vlc. When I then tried to put the video file in my video editor, it said the video was 19,884 hours long, when it should be about 6 minutes (it is ~80mb in size). When I try to play it in xine, it correctly shows the duration (vlc doesn't), and when I use ffprobe:
[mpegts # 0x9b2c0a0] max_analyze_duration 5000000 reached at 5000000
Input #0, mpegts, from 'loopbacktestcap.ts': Duration: N/A, start:
17978.139456, bitrate: N/A Program 1
Stream #0:0[0x44](): Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p, 640x480 [SAR 1:1 DAR 4:3], 104857 kb/s, 30 fps, 30
tbr, 90k tbn, 60 tbc
Stream #0:1[0x46](): Video: mpeg2video ([2][0][0][0] / 0x0002), 90k tbn
Notice especially this line:
Duration: N/A, start: 17978.139456, bitrate: N/A Program 1
I looked it up and it seems the lack of duration has to do with the container. But I have tried a few things to reencode (I tried -vcodec copy, mpeg2, libx264...) and all I can get is 20 second files of 1.1mb - 1.8mb.
So how can I reencode this file so the duration appears, and I get the full 6 minutes, instead of just the first 20 seconds?
First milestone was reached when I found this link: Map - ffmpeg -- Example #8 which led me to try:
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts
Yielding the following:
[mpegts # 0x9980f40] max_analyze_duration 90000000 reached at 90000000
Input #0, mpegts, from 'loopbacktestcap.ts':
Duration: 00:16:00.96, start: 17978.139456, bitrate: 695 kb/s
Program 1
Stream #0:0[0x44](): Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p, 640x480 [SAR 1:1 DAR 4:3], 104857 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc
Stream #0:1[0x45](): Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p, 640x480 [SAR 1:1 DAR 4:3], 104857 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc
Stream #0:2[0x46](): Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p, 640x480 [SAR 1:1 DAR 4:3], 104857 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc
Notice that it now gives me the correct duration as well as an extra stream and more info on the second one (which is technically the third one). So then I ran:
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0 -c copy map0.mp4
and that now produced a 79451050 byte file map0.mp4, where my_mpeg2_file.ts is 83499636. I try to play this in vlc, and it shows the duration as 9:12, but if I jog the play position, two more vlc windows are opened and vlc begins to act strange; the video display area is hung, though the playback position indicator continues to progress. This might be just a buggy vlc, but I am stuck at this point. Let me know if anyone sees anything I am missing here.
xine however cannot play back the new file (where it plays the original and shows that it is 6 minutes long -- actually, the duration counter goes a little nutty as I play the file and changes constantly, so I don't know).
So, next command I tried was:
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0 -c libx264 map0x264.mp4
Trying to play the resulting file (43652975 bytes, for the curious) led to the same strange behavior in vlc, where now I can see that it is opening a new window for each stream, and playing the stream when it is its "turn" in its respective window, freezing the other display areas of the other windows. Trying to close them stops playback for all and closes the two extra windows. I am guessing keeping all 3 streams in the same file is nonsense.
SUCCESS!
Next attempt was:
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0:0 -c copy map0_0.ts
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0:1 -c copy map0_1.ts
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0:2 -c copy map0_2.ts
which resulted in files of size:
4912 map0_0.ts
5372 map0_1.ts
74728 map0_2.ts
map0_0.ts is 20 seconds long, map0_1.ts is a still image 1:12 long, and map0_2.ts is 9:12. Exactly what I wanted! And my video editor accepts them no problem. SOLVED!
I had a similar challenge of supporting transport stream (ts) video files in our video processing infrastructure. I used ffmpeg to convert ts into mp4 files with two-pass encoding.
The command(s) in its full length:
Pass 1:
ffmpeg -i camera.ts -filter:v scale=-1:480,setsar=1/1 -pix_fmt yuv420p -threads 0 -r 25/1 -force_fps -c:v libx264 -profile:v baseline -preset slow -x264opts level=3.0:ref=1 -b:v 1000k -maxrate 1000k -bufsize 2000k -s hd480 -c:a libfaac -ar 16000 -ac 2 -ab 128000 -pass 1 -movflags faststart -y video.mp4
Pass 2:
ffmpeg -i camera.ts -filter:v scale=-1:480,setsar=1/1 -pix_fmt yuv420p -threads 0 -r 25/1 -force_fps -c:v libx264 -profile:v baseline -preset slow -x264opts level=3.0:ref=1 -b:v 1000k -maxrate 1000k -bufsize 2000k -s hd480 -c:a libfaac -ar 16000 -ac 2 -ab 128000 -pass 2 -movflags faststart -y video.mp4 >>& ffmpeg.log
I downscale the video to hd480 (852x480) reencoding the video stream with libx264 baseline level 3 with a relatively low bitrate as well as audio with libfaac and moving the moov atom to the beginning of the file for web compatibility (-movflags faststart)
See the FFMpeg documentation for all details on the flags.
Note that I just dug up this command which works for our setup: processing transport stream recordings from IP cameras. You would probably need to specifically adapt resolution and bitrates to your needs. Hope this points you in the right direction.

Use ffmpeg to watermark and scale an image on video

I want to be able to watermark videos with a logo image, which contains a website url.
The videos can be of different formats and dimension.
I'm trying to figure out a generic ffmpeg command to achieve it, so that i don't have to tweak the command depending on the video i have to process.
So far i got:
ffmpeg -i sample.mov -sameq -acodec copy -vf 'movie=logo.png [watermark]; [in][watermark] overlay=main_w-overlay_w-10:main_h-overlay_h-10 [out]' sample2.mov
In this way though the logo will look too big or too small with video of different size.
I've seen there is a scale option for avfilter, but I haven't figure out whether it's possible to resize the image logo based on the dimension of the input video, so that I can say to scale the logo to 1/3 of the video length for example, and keep the image ratio.
Any idea? doesn't need to be done in a single command, could even be a script.
thanks in advance.
In the meantime i came up with this script that does the job:
#!/bin/bash
VIDEO=$1
LOGO=$2
VIDEO_WATERMARKED=w_${VIDEO}
VIDEO_WIDTH=`ffprobe -show_streams $VIDEO 2>&1 | grep ^width | sed s/width=//`
echo The video width is $VIDEO_WIDTH
cp $LOGO logo.png
IMAGE_WIDTH=$((VIDEO_WIDTH/3))
echo The image width will be $IMAGE_WIDTH
mogrify -resize $IMAGE_WIDTH logo.png
echo logo.png resized
echo Starting watermarking
ffmpeg -i $VIDEO -sameq -acodec copy -vf 'movie=logo.png [watermark]; [in][watermark] overlay=main_w-overlay_w-10:main_h-overlay_h-10 [out]' $VIDEO_WATERMARKED
echo Video watermarked
The only thing i'm not certain about is how to keep the same video quality. I thought that "-sameq" would keep the same video quality, but the resulting video size is smaller.
I've noticed this:
INPUT
Duration: 00:01:25.53, start: 0.000000, bitrate: 307 kb/s
Stream #0:0(eng): Video: mpeg4 (Simple Profile) (mp4v / 0x7634706D),
yuv420p, 640x480 [SAR 1:1 DAR 4:3], 261 kb/s, 10 fps, 10 tbr, 3k tbn, 25 tbc
OUTPUT
encoder : Lavf53.20.0
Stream #0:0(eng): Video: h264 (avc1 / 0x31637661), yuv420p, 640x480 [SAR 1:
1 DAR 4:3], q=-1--1, 10 tbn, 10 tbc
whereas the audio information are identical.
Any advice on how to keep the original video quality?
thanks
Thanks for idea, Ae.!
Same thing using powershell:
$videoFilename = "..."
$logoFilename = "..."
$videoInfo = (& "$($ffmpeg)ffprobe.exe" -show_streams -of xml -loglevel quiet $videoFilename) | Out-String
$videoStreamInfo = Select-Xml -Content $videoInfo -XPath "/ffprobe/streams/stream[#codec_type='video' and #width and #height][1]"
$videoWidth = $videoStreamInfo.Node.width
$videoHeight = $videoStreamInfo.Node.height
# logo will be 10% orginal video width
$logoWidth = $videoWidth/10
# preparing arguments
$a = "-i", $videoFilename, "-i", $logoFilename, "-filter_complex", "[1]scale=$($logoWidth):$($logoWidth)/a [logo]; [0][logo]overlay=main_w-overlay_w-10:10", "-ss", "-y", "-loglevel", "error", $node.output
# logo actual height is cumputed by ffdshow`s scale filter at "$($logoWidth)/a". a - original video aspect ratio
# clear error stream for clear error handling
$error.Clear()
# execute ffmpeg
(& "$($ffmpeg)ffmpeg.exe" $a)
if($error.Count -gt 0){
Write-Output "error! $error"
}
here a can go without using 'mogrify' tool, only ffmpeg distribution.

Resources