bfvt — black-frame video trimmer
Scan a video, find long stretches that are simultaneously black and silent, and remove all but the first moment of each — then write a re-encoded, trimmed file. Built for chopping the dead air out of recordings: the leader/trailer black, the gap between segments, the "I forgot to stop the screen recorder" tail.
Tiny stray bright pixels (codec ringing, a dead-pixel speck, a faint watermark)
are still treated as black, so a frame doesn't get disqualified just because a
handful of pixels aren't perfectly #000000.
How it decides what to cut
- Black detection (OpenCV). Frames are sampled (default 10 fps). A frame counts as black when the fraction of pixels brighter than a luma threshold is at or below a small ratio (default 0.2%) — that ratio is the "ignore stray pixels" knob.
- Silence detection (ffmpeg
silencedetect). Audio below a noise floor (default −30 dB) is silence. Videos with no audio track are treated as wholly silent, so black detection alone drives the cut. - Interval algebra (pure, deterministic). The black intervals are
intersected with the silent intervals. Each overlapping run that lasts at
least
--min-duration(default 5 s) is trimmed, keeping the first--keep-headseconds (default 0.25 s) so a hard chapter cut doesn't become a jarring jump. Everything else in the run is removed. - Re-encode (ffmpeg). The kept segments are concatenated via a
trim/atrim+concatfiltergraph and re-encoded (libx264 + AAC).
Given the same input and flags, the output is deterministic.
Install
pip install .
# or, for development + tests:
pip install -e ".[test]"
Requires ffmpeg and ffprobe on PATH. OpenCV is pulled in via
opencv-python-headless.
Usage
# Trim, writing alongside the input as <name>.trimmed.mp4
bfvt input.mp4
# Choose the output and the threshold
bfvt input.mp4 -o clean.mp4 --min-duration 5 --keep-head 0.25
# See what WOULD be cut without re-encoding
bfvt input.mp4 --dry-run
# Trim on darkness alone (ignore audio)
bfvt input.mp4 --ignore-audio
Key options
| Flag | Default | Meaning |
|---|---|---|
--min-duration SEC |
5.0 |
Minimum black+silent run before anything is trimmed |
--keep-head SEC |
0.25 |
Seconds at the start of each run to preserve |
--luma-threshold N |
32 |
0–255 luma above which a pixel is "bright" |
--bright-ratio R |
0.002 |
Max fraction of bright pixels for a frame to still be black |
--sample-fps FPS |
10 |
Frame sampling rate for black detection |
--noise-db DB |
-30 |
Audio below this dB is silence |
--ignore-audio |
off | Trim on darkness only |
--crf / --preset |
18 / medium |
libx264 quality/speed |
--dry-run |
off | Report cuts, write nothing |
Library
from bfvt import TrimConfig, analyze, run
cfg = TrimConfig(min_duration=5.0, keep_head=0.25)
res = analyze("input.mp4", cfg) # inspect res.black / res.silent / res.cuts
run("input.mp4", "out.mp4", cfg) # do the trim
The interval logic is exposed directly (merge_segments,
intersect_segments, black_silent_cuts, keep_segments_from_cuts) and is
pure/dependency-free.
Tests
pytest -q # all tests (integration generates a real clip)
pytest -q -m "not integration" # pure unit tests only, no ffmpeg/OpenCV needed
The unit tests cover the interval algebra, the black-frame pixel classifier
(including the stray-pixel tolerance), the silencedetect log parser, and the
ffmpeg filtergraph builder. The integration test synthesizes a
blue → black/silent → green clip with ffmpeg, runs the whole pipeline, and
asserts the black stretch is detected and removed.
License
MIT — see LICENSE.
Metadata
Release files for bfvt 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bfvt-0.1.0.tar.gz | 21.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bfvt-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.8 kB
Release files / bfvt-0.1.0.tar.gz
| Download URL | bfvt-0.1.0.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8b06cabdfa0949b98b46ab231f9d3b122e55824f8d1b8fac6fb600e78c3f3225
|
|
BLAKE2b-256 checksum How to use checksums |
37707e95a2dbbdcf17bb8c5e737691673c8f46f33fa96df1825e32537afdc510
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / bfvt-0.1.0-py3-none-any.whl
| Download URL | bfvt-0.1.0-py3-none-any.whl |
|---|---|
| Size | 16.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ad254b4f34c923237c5eaa3219402b8ebcdd6cb32fda6997847c64ed7d7b695c
|
|
BLAKE2b-256 checksum How to use checksums |
ad23c029f710dc65e205b906a5b59ed78f480b52425c9c521ac018493e552892
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|