hector-sab / hectorsabblog

1 stars 0 forks source link

Convert mp4 into webm vp8/vp9 and scale it #11

Closed hector-sab closed 1 year ago

hector-sab commented 1 year ago

I want to convert an mp4 into webm using vp8 or vp9 so that I can replace gifs with them. How can this be achieved with ffmpeg so that I can use it in both Linux and mac?

hector-sab commented 1 year ago

From here, we can use

fmpeg  -i input.mp4  -b:v 0  -crf 30  -pass 1  -an -f webm -y /dev/null
ffmpeg  -i input.mp4  -b:v 0  -crf 30  -pass 2  output.webm

Explanation: Setting the video bitrate to zero while also specifying the CRF (Constant Rate Factor) enables Constant Quality mode which targets a certain perceptual quality level. For VP9, the CRF can range from 0 (best quality) to 63 (smallest file size).

It is important to set the video bitrate to zero. If you do not specify a video bitrate, it defaults to something low which gets you Constrained Quality mode, where each frame has a maximum bitrate. While that can be useful for streaming, it reduces quality significantly as frames with a lot of motion will not have the bandwidth needed to look good.

Another part of the reason you’re losing quality is that webm (VP9) prefers to encode in two passes for best results. The first pass compiles statistics about the video which is then used in the second pass to make a smaller and higher quality video.

Please see the ffmpeg VP9 documentation if you’d like to learn more.

A word about transcoding By the way, in general, transcoding formats (taking a compressed video and recompressing it with a different codec) is a bad idea as you’ll not only add the normal errors from lossy video encoding, but you’ll waste bits in the new format trying to preserve the artifacts from the old.

If you mean “without quality loss” literally The -lossless 1 option of ffmpeg’s VP9 encoder enables lossless quality mode, but with a potentially enormous increase in filesize.

All you need is this:

ffmpeg -i input.mp4 -lossless 1 output.webm When I tried -lossless 1 on a low-quality mp4, the resulting webm was 100× larger, which was not acceptable to me. However, if your mp4 files are already very high quality, I expect the size difference will not be so great.

hector-sab commented 1 year ago

How do we manually set the codec we want to use? From the ffmpeg wiki, we use the flag -c:v libvpx-vp9

ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 0 -crf 30 -pass 1 -an -f null /dev/null && \
ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 0 -crf 30 -pass 2 -c:a libopus output.webm

Two-Pass Two-pass is the recommended encoding method for libvpx-vp9 as some quality-enhancing encoder features are only available in 2-pass mode.

There are two different 2-pass encoding methods available in libvpx-vp9: a conventional mode for targeting an average bitrate, and a two-pass constant quality mode that uses the more contemporary CRF-style approach for the final pass to achieve a certain perceptual quality level while still gaining the aforementioned compression benefits by also doing a first pass.

For two-pass, you need to run ffmpeg twice, with almost the same settings, except for:

In pass 1 and 2, use the -pass 1 and -pass 2 options, respectively. In pass 1, output to a null file descriptor, not an actual file. (This will generate a logfile that ffmpeg needs for the second pass.) In pass 1, you can leave audio out by specifying -an. For two-pass targeting an average bitrate, the target bitrate is specified with the -b:v switch:

ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 2M -pass 1 -an -f null /dev/null && \ ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 2M -pass 2 -c:a libopus output.webm Constant quality 2-pass is invoked by setting -b:v to zero and specifiying a quality level using the -crf switch:

ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 0 -crf 30 -pass 1 -an -f null /dev/null && \ ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 0 -crf 30 -pass 2 -c:a libopus output.webm Note: Windows users should use NUL instead of /dev/null and ^ instead of .

For libvpx-vp9, the traditional wisdom of speeding up the first pass by using a faster encoding speed setting does not apply; -speed values from 0 to 4 result in the same speed for the first pass and yield the exact same results in the final encode, whereas any speed setting above 4 results in the first pass utilising only a single core, slowing things down significantly. Therefore the -speed switch can be entirely omitted from the first pass, since the default value of 1 will result in fast speed.

If you want to constrain the bitrate, for example when streaming your video, use the options given in the Constrained Quality section.

Constant Quality In addition to the "default" two-pass modes, there's a constant quality (CQ) mode (similar to CRF in the x264 encoder) that targets a certain perceptual quality level while only using a single pass. While using this single-pass mode will result in less efficient compression due to libvpx's preference for 2-pass encoding, this mode may still be useful if the extra time required for the first pass and the additional CPU cycles used for better compression in 2-pass mode aren't worth it for your use case.

To trigger this mode, you must use a combination of -crf and -b:v 0. Note that -b:v MUST be 0. Setting it to anything higher or omitting it entirely will instead invoke the Constrained Quality mode.

ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 output.webm The CRF value can be from 0–63. Lower values mean better quality. Recommended values range from 15–35, with 31 being recommended for 1080p HD video. See ​this guide for more info.

hector-sab commented 1 year ago

How to change the FPS? From the ffmpeg wiki

ffmpeg -i <input> -filter:v fps=30 <output>
hector-sab commented 1 year ago

How to scale the video? From the ffmpeg wiki

If you need to simply resize your video to a specific size (e.g 320×240), you can use the scale filter in its most basic form:

ffmpeg -i input.avi -vf scale=320:240 output.avi

Same works for images too:

ffmpeg -i input.jpg -vf scale=320:240 output_320x240.png

If we'd like to keep the aspect ratio, we need to specify only one component, either width or height, and set the other component to -1. For example, this command line:

ffmpeg -i input.jpg -vf scale=320:-1 output_320.png

If you want to half the size of the picture, just multiply by .5 or divide by 2:

ffmpeg -i input.jpg -vf "scale=iw*.5:ih*.5" input_half_size.png
ffmpeg -i input.jpg -vf "scale=iw/2:ih/2" input_half_size.png
hector-sab commented 1 year ago

How to change the frame rate and the scale? From stackoverflow:

ffmpeg -i 240_video.mp4 -vf "fps=20,scale=1920:1080" 240_scaled/out%d.png
  • If your input has more than 20 fps, then ffmpeg will drop frames to convert to 20 fps. If your input has less than 20 fps, then ffmpeg will duplicate frames to convert to 20 fps. If you want all of the frames as is then omit the fps filter.
  • I used the fps filter first because in this case, assuming your input frame rate is higher than 20 fps, it will be slightly more efficient and faster than scaling first because frames will be dropped before the scale filter.
hector-sab commented 1 year ago

What browsers support vp9? https://caniuse.com/?search=vp9

hector-sab commented 1 year ago

How to find the file frame rate? https://askubuntu.com/questions/110264/how-to-find-frames-per-second-of-any-video-file

This will tell you the framerate if it's not variable framerate:

ffmpeg -i filename

Sample output with filename obscured:

Input #0, matroska,webm, from 'somerandom.mkv':
  Duration: 01:16:10.90, start: 0.000000, bitrate: N/A
    Stream #0.0: Video: h264 (High), yuv420p, 720x344 [PAR 1:1 DAR 90:43], 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
    Stream #0.1: Audio: aac, 48000 Hz, stereo, s16 (default)