For the first time I started encoding videos in WebM (replacing GIF), I had fun learning something new. The learning curve, at least to me, was not steep because I already had prior experience using the
ffmpeg to encode
H.264 videos. This entry on Haven.NightlyArt will serve as a note on what problems that challenged me throughout the process, and reminders for those who found this article. I assume my readers here already have basic experience with
ffmpeg, hence this will not serve as a tutorial for newcomers.
notes on browser compatibility
I should’ve checked this first.
I was compiling the videos with
libvpx-vp9 codec, which is not widely supported yet, for instance Apple mobile devices. I thought the Safari mobile on iOS 8 does not support the VP9 codec yet, but after a quick confirmation it is the WebM container itself that is not supported by the browser. So if you are targeting mobile users, it is a better preference to use the H.264/MP4 format.
The whole idea was to replace GIF, because GIF has a very low animation quality and it is an old format, belonging to the BBS era (that’s certainly an exaggeration). For your information, WebP is an image format (currently being developed by Google) to replace PNG, JPEG, and GIF. As you might guess, it hasn’t gained widespread adoption yet on the browser market, thus not a good choice to replace GIF either.
Most of the input videos that I used while conducting the experiment were H.264 1280x720 HD videos, all of them from anime (Food Wars, Attack on Titan, Sword Art Online 2, and Shakugan No Shana S3). How did I get those videos? Not telling hahaha.
To embed a full 1280x720 is not a good idea. 720x405 is good enough for a short animation, and let’s have mercy on mobile users with mobile broadband connection. That aim was to produce a small animated clip with lower file size while not compromising the quality. Ideally, the audio stream must not get included because (1) it is annoying to have an autoplay, on-loop video with audio stream, (2) a bit smaller file size.
A typical anime is about 24 minutes long, and of course we are not going to include the whole shebang. For trimming purpose, we will use
ffmpeg -i input_video.mp4 -ss hh:mm:ss -t hh:mm:ss -c copy output_video.mp4
-c copy option means that we want to retain the same format for all streams (video, audio, and subtitle if available). If the input is from
MP4 container, it is easy. But things get awry if the input is from
MKV container, because that’s where most problems creep in.
not in sync
I tried to trim episode 21 Attack on Titan. When the trim was done, the audio was not in-sync with the video but the subtitle was in sync. This situation didn’t really bother me that much because I didn’t need audio stream, but it called for a concern: how do we get the audio to be in-sync with the video?
When dealing with
MKV file, usually I will use the option
-c:s mov_text to change the subtitle format from ASS to MOV_TEXT. This is because I want the output video in MP4 container format, and MP4 container format only supports
mov_text subtitle format. For MKV, it supports
ffmpeg -i input_video.mkv -ss hh:mm:ss -t hh:mm:ss -c:a copy -c:v copy -c:s mov_text output_video.mp4
As mentioned before,
ffmpeg could not get the encode done right. The audio stream was off by few seconds delay, even after I changed the audio codec to something else like
libfdk_aac. I tried to encode the video with
mencoder, and this is what happened.
mencoder -ss hh:mm:ss -endpos hh:mm:ss -oac copy -ovc copy input_video.mkv -o output_video.mp4
-oac copy and
-ovc copy options tell the
mencoder to retain audio and video stream format. After running that
mencoder command above, it threw me an error (fully duplicated below).
MEncoder 1.1-4.2.1 (C) 2000-2012 MPlayer Team WARNING: OUTPUT FILE FORMAT IS _AVI_. See -of help. success: format: 0 data: 0x0 - 0x244b21e6 libavformat version 54.6.100 (internal) libavformat file format detected. [lavf] stream 0: video (h264), -vid 0 [lavf] stream 1: audio (aac), -aid 0, -alang jpn [lavf] stream 2: subtitle (movtext), -sid 0, -slang eng [lavf] stream 3: subtitle (movtext), -sid 1, -slang eng VIDEO: [H264] 1280x720 24bpp 23.976 fps 3164.1 kbps (386.2 kbyte/s) [V] filefmt:44 fourcc:0x34363248 size:1280x720 fps:23.976 ftime:=0.0417 ========================================================================== Opening audio decoder: [ffmpeg] FFmpeg/libavcodec audio decoders libavcodec version 54.23.100 (internal) AUDIO: 48000 Hz, 2 ch, s16le, 188.6 kbit/12.28% (ratio: 23578->192000) Selected audio codec: [ffaac] afm: ffmpeg (FFmpeg AAC (MPEG-2/MPEG-4 Audio)) ========================================================================== videocodec: framecopy (1280x720 24bpp fourcc=34363248) Audio format 0x4134504d is incompatible with '-oac copy', please try '-oac pcm' instead or use '-fafmttag' to override it. Exiting..
Bingo! We found the problem!
mencoder tells us here that
-oac copy isn’t compatible with the audio stream, suggesting us to use
-oac pcm instead. I ran the same command with new option
-oac pcm, and it worked! The audio stream was in-sync.
There was a but.
The subtitle stream wasn’t in there anymore. I tried using the
-s option to include the subtitle, but it didn’t work. To extract subtitle from a video container, use
mkvextract tracks input_video.mkv <track_number>:<output_file.ext> mkvextract tracks input_video.mkv 2:subtitle.ass
mencoder doesn’t understand
ass2srt package (via Gem), converted the
ASS subtitle to
SRT, and tried inserting it as a stream into the video container with
sudo gem install ass2srt ass2srt subtitle.ass mencoder -ss hh:mm:ss -endpos hh:mm:ss -oac copy -ovc copy input_video.mkv -s subtitle.srt -o output_video.mp4
It didn’t work either. The next viable option was to hardcode the subtitle into the video stream file with
ffmpeg -i input_video.mp4 -c:a libfdk_aac -vf subtitles=subtitle.srt output_video_hardcodedsub.mp4
The problem with hardcoding the subtitle into the video stream is that
ffmpeg will re-encode the whole video stream. The
-vf option is mutually exclusive with
-c:v option, thus it cannot be used together. Since we will re-encode the video stream, expect a very long time to complete the process.
My 2 cents: avoid re-encoding MKV to MP4 if possible.
encoding to WebM
ffmpeg -i input_trimmed.mp4 -c:v libvpx-vp9 -b:v 500k -threads 4 -speed 2 -tile-columns 6 -frame-parallel 1 -auto-alt-ref 1 -lag-in-frames 25 -s 720x405 -an final.webm
The command above is the modified version from this guide: VP9 Encoding Guide.
-s 720x405 scales the video from original input resolution down to 720x405 (maintained aspect ratio from 1280x720). The original guide instructs you to do a 2-pass, but ain’t nobody got time to do a 2-pass.
With H.264 video format
ffmpeg -i input_trimmed.mp4 -c:v libx264 -profile:v high -preset slow -s 720x406 -crf 30 -an final.mp4 ffmpeg -i input_trimmed.mp4 -c:v libx264 -profile:v high -preset slow -s 720x406 -an final.mp4
By manipulating the
-crf (constant rate factor, the lower the better), we can manipulate the output file size. Usually a value between 18 and 24 is chosen, but because we intend to produce a video file with a much less size,
-crf 30 is a good choice.
Important Note! There is a limitation regarding scaling with
libx264. We cannot down-scale the video resolution with odd number when using the
libx264 encode library, as explained here on Stack Overflow by Andy Hin.
The fun part!
WebM VP9 = 1.1 MB MP4 H.264 = 1.7 MB MP4 H.264 CRF 30 = 699 KB
Video with WebM container, VP9 video stream format.
Video with MP4 container, H264 video stream format, CRF 30.
Note You might notice the lag. I don’t know why, and too lazy to inspect.
notes on irssi
brew install irssi vim .irssi/config irssi /server rizon /msg NickServ HELP /window # IDENTIFY <password> /window # /join #channel /msg <dcc-bot> xdcc send <package ID> /dcc get <bot name> /leave /disconnect /quit
A brief overview of my
irssi workflow. The
irssi documentation has it all.