|
|
||
|---|---|---|
| logs | ||
| opentranscode | ||
| tests | ||
| LICENSE | ||
| README.md | ||
| open-transcode.py | ||
| pyproject.toml | ||
| pytest.ini | ||
README.md
OpenTranscode v4.0.0 — Production Release
v4.0.0 resolves the "works up until near the end, never saves chunks into a full file" bug that affected phone-recorded MP4s with sparse keyframes.
The v4.0.0 fix (in detail)
The bug
When no VapourSynth source plugins are installed (the common case on most
distros — lsmash, ffms2, bestsource are all separate packages),
av1an auto-selects the Hybrid chunk method. Hybrid does:
ffmpeg -c copy -f segmentto split the source at scene boundaries- Re-decode each segment to y4m via a second ffmpeg invocation
- Pipe the y4m to the encoder (SvtAv1EncApp / vpxenc / x265)
Phone-recorded MP4s (the 20190707_112725.11b774bacde3.mp4 files in
the production log) only have I-frames every 5–10 seconds. Scene
boundaries detected by av1an's av_scenechange rarely align with those
sparse keyframes. The segment muxer can only split on keyframes, so the
segment for scene N actually starts somewhere inside scene N-1's GOP.
The result: the decoder has no I-frame reference → errors with
[h264 @ 0x...] error while decoding MB 35 25 → the y4m pipe breaks →
the encoder reads EOF mid-frame → Failed to read y4m frame delimiter. Read broken. EOF: 1 → every chunk fails after 3 retries → no chunks
to concat → no output file.
The previous ffmpeg fallback rescued the file, but it was slow (single-pass, no chunk-parallel) and the diagnostic was misleading ("concat failure" when it was actually a chunk-extraction failure).
The fix (three parts)
-
_encode_onenow accepts achunk_methodparameter. When av1an fails with the y4m break pattern, it recursively retries with--chunk-method select. Select uses VapourSynth'sselect()filter to extract frames one-by-one — slower than Hybrid but reliable for any file VapourSynth can open. This is faster than the ffmpeg fallback (chunk-parallel still works) and produces identical-quality output (same encoder, same params). -
The working chunk_method is cached in
env.av1an_flags["chunk_method_override"]so subsequent files skip the wasted first attempt. -
env_probenow probes for VapourSynth source plugins via_probe_vs_source_plugins(). When NONE are found, it pre-setschunk_method_override = "select"to avoid the wasted first attempt entirely. The probe checks~/.local/lib/vapoursynth/,/usr/lib/vapoursynth/,/usr/local/lib/vapoursynth/, and the Debian multiarch path.
Also fixed
The "SUMMARY block + non-zero exit" diagnostic previously misdiagnosed
y4m break failures as "concat failure" (because SVT-AV1 prints a
SUMMARY block per-chunk before the pipe breaks). The check is now
guarded by "Failed to read y4m frame delimiter" not in stderr_full so
it only fires for true concat failures.
New CLI flag
opentranscode --chunk-method {auto,select,hybrid,segment,ffms2,lsmash,bestsource,dgdecnv}
Lets the user force a specific chunk method. Useful for debugging or
for environments where the probe picks the wrong default. auto clears
any override the probe set.
Quick start
Option A: Install as a package (recommended)
cd /path/to/this/directory
pip install -e . # editable install (dev)
# OR
pip install -e ".[dev]" # with pytest + build tools
# Now you can run it three ways:
opentranscode # console entry point
python -m opentranscode # module entry point
python -m opentranscode --version # → opentranscode 4.0.0
Option B: Run the single-file script (backwards compat)
python open-transcode.py # the single-file version still works
Option C: Verify your environment without encoding
opentranscode --dry-run # probe env + smoke test, no GUI, no encode
opentranscode --dry-run --chunk-method select # preview a forced chunk method
opentranscode --verify-only /path/to/existing_output.mkv # re-verify an output
Files
| Path | Description |
|---|---|
opentranscode/ |
Package — 16 modules. Importable as import opentranscode. |
open-transcode.py |
Single-file script (~6,100 lines). Kept for backwards compat + as the test target for the mocked tests. Mirrors the package behavior. |
pyproject.toml |
PEP 621 build config. Entry point: opentranscode = opentranscode.__main__:main. Ready for pip install -e . and python -m build. |
tests/ |
103 tests across 12 files. All pass in ~10 seconds. |
pytest.ini |
pytest config (also in pyproject.toml). |
README.md |
This file. |
logs/av1an.log.2026-07-13 |
The production log that revealed the y4m break bug. Kept for reference. |
Test suite
cd /path/to/this/directory
python -m pytest tests/ -v
# 103 tests, ~10 seconds:
# 25 mocked unit tests (smoke test, encoder pipeline, audio loudnorm, etc.)
# 12 real-ffmpeg e2e tests — requires ffmpeg + ffprobe
# 36 package-structure tests
# 13 chunk-method retry tests
# 1 chunk-method e2e recovery test
The e2e tests generate a real 2-second test video with ffmpeg, run the full EncoderWorker pipeline on it (AV1→MKV, x265→MKV, VP9→WebM), and verify the output file exists, is non-empty, has the correct codec, and has the expected duration. The chunk-method e2e test additionally simulates the y4m break pattern and verifies the retry-with-select produces a valid output file.
Verification (run these to confirm v4.0.0 works)
# 1. Package imports cleanly
python -c "import opentranscode; print(opentranscode.__version__)" # → 4.0.0
# 2. CLI works
python -m opentranscode --version # → opentranscode 4.0.0
python -m opentranscode --help # → usage (includes --chunk-method)
python -m opentranscode --dry-run # → env probe report (shows VS plugins + chunk method)
# 3. All tests pass
python -m pytest tests/ -q # → 103 passed in ~10s
# 4. Install works
pip install -e . # → installs opentranscode + PySide6
opentranscode --version # → opentranscode 4.0.0
# 5. Single-file script still works (backwards compat)
python open-transcode.py # → launches GUI (if PySide6 + display)
Publishing to PyPI
v4.0.0 is the production release. To publish:
python -m build # produces dist/opentranscode-4.0.0.tar.gz + .whl
twine upload dist/* # publishes to PyPI
The production log that revealed the bug
The file logs/av1an.log.2026-07-13 is the actual av1an log from the
user's production run that revealed the y4m break bug. Key markers:
INFO encode_file: Input: 1920x1080 @ 29.763 fps, YUVJ420P, SDR
INFO encode_file: scenecut: found 8 scene(s) [with extra_splits: 16 scene(s)]
DEBUG encode_file: Segmenting video
DEBUG encode_file: Segment done
INFO encode_file:
Encoding Failed to read y4m frame delimiter. Read broken. EOF: 1
[h264 @ 0x55da365b30c0] error while decoding MB 35 25
WARN encode_chunk: Encoder failed (on chunk 11):
Encoding Failed to read y4m frame delimiter. Read broken. EOF: 1
SUMMARY -----------------------------------------------------------------
Average Speed: 2.501 fps
ERROR av1an_core::broker: [chunk 4] encoder failed 3 times, shutting down worker
The smoke test (line 1 of the log) succeeds because it uses
chunk_method: Select — but the real encode (line 4 onward) uses
chunk_method: Hybrid (av1an's auto-selection when no VS plugins are
installed) and fails on every chunk.