Skip to content

Commit

Permalink
webm video presets: migrate from VP8 to VP9 for smaller ZIMs
Browse files Browse the repository at this point in the history
WebmLow and WebmHigh presets are migrated to use VP9 instead of VP8.

Presets are also enhanced to maximize video quality all while minimizing
final file size. A compromise with encoding duration (which is known to
be significantly higher with VP9 compared to VP8) is also taken into
account (hence the -speed setting values, 0 is optimal but takes too
long for minimal quality improvement given our computing power available)
  • Loading branch information
benoit74 committed Jun 18, 2024
1 parent f3d1b07 commit fd28720
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 27 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed

- Migrate the **VideoWebmLow** and **VideoWebmHigh** presets to VP9 for smaller file size #79
- New preset versions are v3 and v2 respectively
- Simplify type annotations by replacing Union and Optional with pipe character ("|") for improved readability and clarity

### Fixed
Expand Down
28 changes: 18 additions & 10 deletions src/zimscraperlib/video/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,23 @@ class VideoWebmLow(Config):
128k target video bitrate but stay within quality boundaries.
48k audio bitrate"""

VERSION = 2
VERSION = 3

ext = "webm"
mimetype = f"{preset_type}/webm"

options: ClassVar[dict[str, str | bool | int | None]] = {
"-codec:v": "libvpx", # video codec
"-quality": "best", # codec preset
"-b:v": "128k", # Adjust quantizer within min/max to target this bitrate
"-qmin": "18", # Reduce the bitrate on very still videos
"-codec:v": "libvpx-vp9", # video codec
"-b:v": "140k", # Adjust quantizer within min/max to target this bitrate
"-qmin": "30", # Reduce the bitrate on very still videos
"-qmax": "40", # Increase the bitrate on very busy videos
"-g": "240", # Number of frames allowed between keyframes
"-quality": "good", # codec preset
"-speed": "4", # Encoding speed (compromise between quality and encoding time)
"-vf": "scale='480:trunc(ow/a/2)*2'", # frame size
"-codec:a": "libvorbis", # audio codec
"-ar": "44100", # audio sampling rate
"-b:a": "48k", # target audio bitrate
"-ar": "44100", # audio sampling rate
}


Expand Down Expand Up @@ -88,16 +90,22 @@ class VideoWebmHigh(Config):
25 constant quality"""

VERSION = 1
VERSION = 2

ext = "webm"
mimetype = f"{preset_type}/webm"

options: ClassVar[dict[str, str | bool | int | None]] = {
"-codec:v": "libvpx", # video codec
"-codec:v": "libvpx-vp9", # video codec
"-b:v": "340k", # Adjust quantizer within min/max to target this bitrate
"-qmin": "26", # Reduce the bitrate on very still videos
"-qmax": "54", # Increase the bitrate on very busy videos
"-g": "240", # Number of frames allowed between keyframes
"-quality": "good", # codec preset
"-speed": "1", # Encoding speed (compromise between quality and encoding time)
"-codec:a": "libvorbis", # audio codec
"-crf": "25", # constant quality, lower value gives better qual and larger size
"-b:v": "0", # must be passed if using constant quality mode via -cbr for codec
"-b:a": "48k", # target audio bitrate
"-ar": "44100", # audio sampling rate
}


Expand Down
42 changes: 25 additions & 17 deletions tests/video/test_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,21 @@ def test_preset_has_mime_and_ext():

def test_preset_video_webm_low():
config = VideoWebmLow()
assert config.VERSION == 2
assert config.VERSION == 3
args = config.to_ffmpeg_args()
assert len(args) == 20
assert len(args) == 24
options_map = [
("codec:v", "libvpx"),
("codec:v", "libvpx-vp9"),
("codec:a", "libvorbis"),
("b:v", "128k"),
("b:v", "140k"),
("ar", "44100"),
("b:a", "48k"),
("quality", "best"),
("qmin", "18"),
("quality", "good"),
("qmin", "30"),
("qmax", "40"),
("vf", "scale='480:trunc(ow/a/2)*2'"),
("g", "240"),
("speed", "4"),
]
for option, val in options_map:
idx = args.index(f"-{option}")
Expand All @@ -172,35 +174,41 @@ def test_preset_video_webm_low():

# test updating values
config = VideoWebmLow(**{"-ar": "50000"})
config["-bufsize"] = "900k"
config["-qmin"] = "25"
args = config.to_ffmpeg_args()
idx = args.index("-ar")
assert idx != -1 and args[idx + 1] == "50000"
idx = args.index("-bufsize")
assert idx != -1 and args[idx + 1] == "900k"
idx = args.index("-qmin")
assert idx != -1 and args[idx + 1] == "25"


def test_preset_video_webm_high():
config = VideoWebmHigh()
assert config.VERSION == 1
assert config.VERSION == 2
args = config.to_ffmpeg_args()
assert len(args) == 10
assert len(args) == 22
options_map = [
("codec:v", "libvpx"),
("codec:v", "libvpx-vp9"),
("codec:a", "libvorbis"),
("b:v", "0"),
("crf", "25"),
("b:v", "340k"),
("ar", "44100"),
("b:a", "48k"),
("quality", "good"),
("qmin", "26"),
("qmax", "54"),
("g", "240"),
("speed", "1"),
]
for option, val in options_map:
idx = args.index(f"-{option}")
assert idx != -1
assert args[idx + 1] == val

# test updating values
config = VideoWebmHigh(**{"-crf": "51"})
config = VideoWebmHigh(**{"-qmax": "51"})
config["-b:v"] = "300k"
args = config.to_ffmpeg_args()
idx = args.index("-crf")
idx = args.index("-qmax")
assert idx != -1 and args[idx + 1] == "51"
idx = args.index("-b:v")
assert idx != -1 and args[idx + 1] == "300k"
Expand Down Expand Up @@ -314,7 +322,7 @@ def test_preset_voice_mp3_low():
"video.mp4",
"video.webm",
VideoWebmLow().to_ffmpeg_args(),
{"codecs": ["vp8", "vorbis"], "duration": 2},
{"codecs": ["vp9", "vorbis"], "duration": 2},
),
(
"video.webm",
Expand Down

0 comments on commit fd28720

Please sign in to comment.