Skip to content

Commit fd28720

Browse files
committed
webm video presets: migrate from VP8 to VP9 for smaller ZIMs
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)
1 parent f3d1b07 commit fd28720

File tree

3 files changed

+46
-27
lines changed

3 files changed

+46
-27
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

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

1316
### Fixed

src/zimscraperlib/video/presets.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,23 @@ class VideoWebmLow(Config):
3737
128k target video bitrate but stay within quality boundaries.
3838
48k audio bitrate"""
3939

40-
VERSION = 2
40+
VERSION = 3
4141

4242
ext = "webm"
4343
mimetype = f"{preset_type}/webm"
4444

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

5759

@@ -88,16 +90,22 @@ class VideoWebmHigh(Config):
8890
8991
25 constant quality"""
9092

91-
VERSION = 1
93+
VERSION = 2
9294

9395
ext = "webm"
9496
mimetype = f"{preset_type}/webm"
9597

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

103111

tests/video/test_video.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -151,19 +151,21 @@ def test_preset_has_mime_and_ext():
151151

152152
def test_preset_video_webm_low():
153153
config = VideoWebmLow()
154-
assert config.VERSION == 2
154+
assert config.VERSION == 3
155155
args = config.to_ffmpeg_args()
156-
assert len(args) == 20
156+
assert len(args) == 24
157157
options_map = [
158-
("codec:v", "libvpx"),
158+
("codec:v", "libvpx-vp9"),
159159
("codec:a", "libvorbis"),
160-
("b:v", "128k"),
160+
("b:v", "140k"),
161161
("ar", "44100"),
162162
("b:a", "48k"),
163-
("quality", "best"),
164-
("qmin", "18"),
163+
("quality", "good"),
164+
("qmin", "30"),
165165
("qmax", "40"),
166166
("vf", "scale='480:trunc(ow/a/2)*2'"),
167+
("g", "240"),
168+
("speed", "4"),
167169
]
168170
for option, val in options_map:
169171
idx = args.index(f"-{option}")
@@ -172,35 +174,41 @@ def test_preset_video_webm_low():
172174

173175
# test updating values
174176
config = VideoWebmLow(**{"-ar": "50000"})
175-
config["-bufsize"] = "900k"
177+
config["-qmin"] = "25"
176178
args = config.to_ffmpeg_args()
177179
idx = args.index("-ar")
178180
assert idx != -1 and args[idx + 1] == "50000"
179-
idx = args.index("-bufsize")
180-
assert idx != -1 and args[idx + 1] == "900k"
181+
idx = args.index("-qmin")
182+
assert idx != -1 and args[idx + 1] == "25"
181183

182184

183185
def test_preset_video_webm_high():
184186
config = VideoWebmHigh()
185-
assert config.VERSION == 1
187+
assert config.VERSION == 2
186188
args = config.to_ffmpeg_args()
187-
assert len(args) == 10
189+
assert len(args) == 22
188190
options_map = [
189-
("codec:v", "libvpx"),
191+
("codec:v", "libvpx-vp9"),
190192
("codec:a", "libvorbis"),
191-
("b:v", "0"),
192-
("crf", "25"),
193+
("b:v", "340k"),
194+
("ar", "44100"),
195+
("b:a", "48k"),
196+
("quality", "good"),
197+
("qmin", "26"),
198+
("qmax", "54"),
199+
("g", "240"),
200+
("speed", "1"),
193201
]
194202
for option, val in options_map:
195203
idx = args.index(f"-{option}")
196204
assert idx != -1
197205
assert args[idx + 1] == val
198206

199207
# test updating values
200-
config = VideoWebmHigh(**{"-crf": "51"})
208+
config = VideoWebmHigh(**{"-qmax": "51"})
201209
config["-b:v"] = "300k"
202210
args = config.to_ffmpeg_args()
203-
idx = args.index("-crf")
211+
idx = args.index("-qmax")
204212
assert idx != -1 and args[idx + 1] == "51"
205213
idx = args.index("-b:v")
206214
assert idx != -1 and args[idx + 1] == "300k"
@@ -314,7 +322,7 @@ def test_preset_voice_mp3_low():
314322
"video.mp4",
315323
"video.webm",
316324
VideoWebmLow().to_ffmpeg_args(),
317-
{"codecs": ["vp8", "vorbis"], "duration": 2},
325+
{"codecs": ["vp9", "vorbis"], "duration": 2},
318326
),
319327
(
320328
"video.webm",

0 commit comments

Comments
 (0)