Video Formatting and Embedding

I’m standardizing my videos to keep them as small as possible while maintaining good quality. The goal is to make them easy to download or stream, without compromising readability or viewing experience. I may also include subtitles to make the content easier to understand and follow. As of now I want them to be at least 1080p. At some point I might do 2K videos, only for some videos.

  • 720p (HD) is a video with resolution of 1280 pixels horizontally by 720 pixels vertically (1280×720)
  • 1080p (Full HD or FHD) is a video with resolution of 1920 pixels horizontally by 1080 pixels vertically (1920×1080)
  • 2K (DCI 2K) is a video with resolution of 2048 pixels horizontal and 1080 vertical (2048×1080).

Standard Parameters

Container format : WebM

Video

  • Codec: AV1
  • FPS: 24 or max 30
  • Bitrate: Yet to experiment

Audio

  • Opus 64–96 kbps

Reference

Use CaseCRFPresetAvg Video MbpsAudio kbpsTotal Mbps1-Min Size (MB)
Screen cast (UI, code)34–408–100.6 – 1.564–96~1.07 – 11 MB
Lecture (talking head + slides)32–367–91.2 – 2.564–96~2.114 – 18 MB
Travel vlog28–326–83 – 696–128~4.630 – 40 MB
Cinematic / landscape / drone26–304–75 – 10128~8.155 – 70 MB
Fast motion (POV riding / sports)26–284–66 – 12128~1070 – 90 MB
Archival master22–263–510 – 20128–160~15105 – 120 MB

Mobile Screencasts

Compress the video

time ffmpeg -i osmand_route_using_gpx.mp4 \
-vf "fps=24" \
-c:v libsvtav1 \
-crf 38 -preset 10 \
-c:a libopus \
osmand_route_using_gpx.webm

Video with 800 pixel height

time ffmpeg -i osmand_route_using_gpx.mp4 \
-vf "fps=24,scale=-2:800" \
-c:v libsvtav1 \
-crf 38 -preset 10 \
-c:a libopus \
osmand_route_using_gpx.webm

Re-scale to 1080p with padding

# Rescale to 1080p landscape with blurred backhround
ffmpeg -i osmand_route_using_gpx.mp4 -filter_complex \
"[0:v]fps=24,scale=1920:1080:force_original_aspect_ratio=increase,\
crop=1920:1080,boxblur=30:5[bg]; \
 [0:v]fps=24,scale=1920:1080:force_original_aspect_ratio=decrease[fg]; \
 [bg][fg]overlay=(W-w)/2:(H-h)/2" \
-c:v libsvtav1 -crf 38 -preset 10 \
-pix_fmt yuv420p \
-c:a libopus -b:a 96k \
OsmAnd_route_using_gpx_1080p_24fps.webm

# Rescale with a specific color background
# I have used color=0x82b965

ffmpeg -i OsmAnd_route_using_gpx.mp4 -filter_complex \
"[0:v]fps=24,scale=1920:1080:force_original_aspect_ratio=decrease[fg]; \
 [fg]pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=0x82b965,format=yuv420p[v]" \
-map "[v]" -map 0:a? \
-c:v libsvtav1 -crf 38 -preset 12 \
-c:a libopus -b:a 96k \
OsmAnd_route_using_gpx_1080p_24fps_bgcolor_1.webm

Desktop Screencasts

TBD

Travel Videos

TBD

Examples

Mobile Screencast 800px height

Embedded here with width = 800px. Under 10MB for 2 Min video.

Mobile Screencast 720p

Embedded here with width = 100%. Try full screen for better idea. Under 10MB for 2 Min video.

Mobile Screencast 1080p

Embedded here with width = 100%. Try full screen for better idea. Under 15MB for 2 Min video.

AVIF

AV1 Image File Format (AVIF) is an open, royalty-free file format specification for storing images or image sequences compressed with AV1 in the HEIF container format. In simple words, its just a better GIF.

ffmpeg -i example.webm -crf 32 -r 8 -vf scale=1000:-1 example.avif
  • -r is frame rate
    -vf scale=1000:-1 is resize to 1000px width and -1 says keep the aspect ratio

Streaming

Progressive

WebM videos support progressive playback natively in the browser — playback begins before the full file downloads. This works because WebM (based on the Matroska container) stores codec headers early in the file along with a Cues element that acts as a seek table, mapping timestamps to byte offsets. Combined with HTTP range requests, the browser knows exactly which byte range to fetch for any given position, allowing it to buffer and seek without downloading the entire file. For small videos in particular, this makes progressive loading seamless without any additional streaming infrastructure.

Seeking depends on the Cues element. Without it; which can happen with poorly muxed files; seeking degrades to a linear scan or fails entirely.

Cues are sometimes written at the end of the file during recording, since the muxer doesn’t know byte offsets upfront. When this happens, seeking may be limited or unreliable until enough of the file has buffered, as the browser cannot resolve timestamp-to-byte mappings without first reaching the cues. Tools like mkvmerge, ffmpeg (with -cues_to_front) can relocate cues to the front of the file, which is recommended.

You can check where the cues are using ffprobe. For example

ffprobe -v trace -i screencast_linux_bash.webm 2>&1 | grep -i "cue\|seek"
[matroska,webm @ 0x59d40e52af80] Before avformat_find_stream_info() pos: 928 bytes read:32768 seeks:0 nb_streams:2
[matroska,webm @ 0x59d40e52af80] After avformat_find_stream_info() pos: 45464 bytes read:65536 seeks:0 frames:2
[AVIOContext @ 0x59d40e5337c0] Statistics: 65536 bytes read, 0 seeks

The command output shows -> Stream and codec information for both audio and video (nb_streams: 2) was found within the first 928 bytes (pos: 928). ffprobe only needed to read ~45KB (pos: 45464) into the file to gather everything it needed. Total read was 64KB across two sequential 32KB buffer loads — no seeking at all (seeks: 0), everything was found near the front of the file. It decoded 2 frames (frames: 2) to confirm codec parameters.

This is good for progressive load.

Dynamic Adaptive Streaming over HTTP (DASH)

NOTE: WIP

ffmpeg -i osmand_route_using_gpx_720p_24fps_bgcolor.webm \
-map 0:v:0 -map 0:a? \
-c copy \
-f dash \
-use_template 1 -use_timeline 1 \
-seg_duration 30 \
-init_seg_name 'osmand_route_using_gpx_720p_24fps_bgcolor-init-$RepresentationID$.webm' \
-media_seg_name 'osmand_route_using_gpx_720p_24fps_bgcolor-chunk-$RepresentationID$-$Number$.webm' \
manifest.mpd

Other Test Videos

These are test videos. The ones that interests me are the 10 seconds videos of 1MB and 2MB file size. They are FHD.

10 Seconds – 1 MB – FHD

Embedded here with width = 100%. Try full screen for better idea.

10 Seconds – 2MB – FHD

Embedded here with width = 100%. Try full screen for better idea.