Summary
Exporting a chained (multi-segment) recording on the GPU zero-copy decode path fails at the first segment boundary: the hardware decoder stops returning frames when the concat demuxer crosses from one segment to the next, aborting the export. CPU decode handles the boundary correctly.
Symptom
- Multi-file export aborts partway, around the first segment boundary.
- CUDA/NVDEC:
ERROR reco_io::zero_copy: <eye>: next_frame_gpu returned None at the boundary, then VideoEncoder dropped without calling finish() - output file may be corrupt and finalization failed.
- Surfaced via the community forum (multi-file export issues).
Root cause
open_chained() feeds all segments through FFmpeg's concat demuxer into a single hardware decoder. Hardware decoders do not survive the concat demuxer's segment transition: next_frame_gpu() returns Ok(None) at the boundary and the decode thread treats it as EOF. Software decode (next_frame) crosses the boundary fine - verified: --no-zero-copy completes a 2-segment export with continuous audio.
Scope
Affects all three GPU decode threads in crates/reco-io/src/zero_copy.rs:
spawn_single_decoder_gpu (CUDA/NVDEC) - reproduced on RTX 5070.
spawn_d3d11_decode_pair (Windows D3D11VA) - by inspection (same single-decoder-over-concat pattern).
spawn_vt_decode_pair (macOS VideoToolbox) - by inspection.
Fix
Decode each segment sequentially with a fresh hardware decoder - open the next segment on EOF and continue, mirroring the per-segment advance used for audio passthrough in #377 - instead of relying on the concat demuxer across one hardware decoder. Requires per-platform testing (CUDA / D3D11VA / VideoToolbox).
Workaround
Use CPU decode for multi-segment exports: --no-zero-copy (CLI). The CPU concat path is correct; only zero-copy throughput is lost.
Evidence
2-segment chained export on RTX 5070 (CUDA) aborts at the boundary with the error above; the same inputs with --no-zero-copy complete cleanly (audio fix: #377).
Summary
Exporting a chained (multi-segment) recording on the GPU zero-copy decode path fails at the first segment boundary: the hardware decoder stops returning frames when the concat demuxer crosses from one segment to the next, aborting the export. CPU decode handles the boundary correctly.
Symptom
ERROR reco_io::zero_copy: <eye>: next_frame_gpu returned Noneat the boundary, thenVideoEncoder dropped without calling finish() - output file may be corruptandfinalization failed.Root cause
open_chained()feeds all segments through FFmpeg's concat demuxer into a single hardware decoder. Hardware decoders do not survive the concat demuxer's segment transition:next_frame_gpu()returnsOk(None)at the boundary and the decode thread treats it as EOF. Software decode (next_frame) crosses the boundary fine - verified:--no-zero-copycompletes a 2-segment export with continuous audio.Scope
Affects all three GPU decode threads in
crates/reco-io/src/zero_copy.rs:spawn_single_decoder_gpu(CUDA/NVDEC) - reproduced on RTX 5070.spawn_d3d11_decode_pair(Windows D3D11VA) - by inspection (same single-decoder-over-concat pattern).spawn_vt_decode_pair(macOS VideoToolbox) - by inspection.Fix
Decode each segment sequentially with a fresh hardware decoder - open the next segment on EOF and continue, mirroring the per-segment advance used for audio passthrough in #377 - instead of relying on the concat demuxer across one hardware decoder. Requires per-platform testing (CUDA / D3D11VA / VideoToolbox).
Workaround
Use CPU decode for multi-segment exports:
--no-zero-copy(CLI). The CPU concat path is correct; only zero-copy throughput is lost.Evidence
2-segment chained export on RTX 5070 (CUDA) aborts at the boundary with the error above; the same inputs with
--no-zero-copycomplete cleanly (audio fix: #377).