Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions crates/reco-io/src/ffmpeg/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,23 @@ impl VideoDecoder {
for p in paths {
writeln!(manifest, "file '{}'", p.display())
.map_err(|e| DecodeError::Ffmpeg(format!("write manifest: {e}")))?;
// Without a `duration` hint, FFmpeg's concat demuxer can't build
// a seekable index across segments and reports the whole chain
// as non-seekable (`avformat_seek_file` -> AVERROR(ESPIPE)) for
// *any* target, even one within the first segment - confirmed
// directly against libavformat, not just inferred. One extra
// cheap format-only probe per segment (no decoder/hw setup) is
// worth it for seeking to actually work.
match probe_duration_secs(p) {
Some(secs) => {
writeln!(manifest, "duration {secs}")
.map_err(|e| DecodeError::Ffmpeg(format!("write manifest: {e}")))?;
}
None => log::warn!(
"Concat demuxer: could not probe duration of {} - seeking across this chain may fail",
p.display()
),
}
}
manifest
.flush()
Expand Down Expand Up @@ -1035,6 +1052,15 @@ fn is_hw_frame(frame: &VideoFrame) -> bool {
unsafe { !(*frame.as_ptr()).hw_frames_ctx.is_null() }
}

/// Probe a single segment's duration for the concat manifest's `duration`
/// hint (see `VideoDecoder::open_chained_impl`). Format-only open - no
/// decoder/hw device setup - since only the container-level duration is
/// needed.
fn probe_duration_secs(path: &Path) -> Option<f64> {
let dur = input(path).ok()?.duration();
(dur > 0).then(|| dur as f64 / f64::from(ffi::AV_TIME_BASE))
}

/// Pre-created hw device that can be shared across multiple decoders.
///
/// D3D11VA textures are device-bound: `CopySubresourceRegion` requires source
Expand Down
Loading