Skip to content

Commit

Permalink
Merge pull request #170 from openzim/videos_vp9
Browse files Browse the repository at this point in the history
webm video presets: migrate from VP8 to VP9 for smaller ZIMs
  • Loading branch information
benoit74 authored Jun 20, 2024
2 parents f3d1b07 + fd28720 commit baec9c1
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 baec9c1

Please sign in to comment.