Skip to content

libx265 stale frame returned after backward seek #1339

@CarolinePascal

Description

@CarolinePascal

🐛 Describe the bug

Hi !

I was benchmarking video decoding performance with torchcodec on HEVC-encoded datasets and noticed that VideoDecoder silently returns the wrong frame after seeking backward when the decoder had previously decoded a frame near the end of the video. Here is a minimal reproducible example:

import ctypes
import tempfile
from pathlib import Path

import av
from torchcodec.decoders import VideoDecoder

FPS = 10
NUM_FRAMES = 161   # Frames 0-160, 16.0s at 10 fps
PRIME_IDX = 159    # First seek near EOF to trigger the error
TARGET_IDX = 35    # Second backward seek earlier in the file


def make_hevc_video(path: Path) -> None:
    with av.open(str(path), "w") as container:
        stream = container.add_stream("hevc", FPS, options={"g": "3", "crf": "0"})
        stream.pix_fmt = "yuv420p"
        stream.width = stream.height = 64
        for i in range(NUM_FRAMES):
            frame = av.VideoFrame(64, 64, "yuv420p")
            for plane in frame.planes:
                ctypes.memset(plane.buffer_ptr, i % 256, plane.buffer_size)
            pkt = stream.encode(frame)
            if pkt:
                container.mux(pkt)
        pkt = stream.encode()
        if pkt:
            container.mux(pkt)


with tempfile.TemporaryDirectory() as tmp:
    path = Path(tmp) / "hevc.mp4"
    make_hevc_video(path)

    dec = VideoDecoder(str(path), seek_mode="approximate") # The issue also happens with seek_mode="exact" !

    dec.get_frames_at(indices=[PRIME_IDX]) # First seek - works.
    loaded = dec.get_frames_at(indices=[TARGET_IDX]).pts_seconds[0].item() # Second seek - wrong timestamp !

    expected = TARGET_IDX / FPS
    ok = abs(loaded - expected) < 0.5
    print(f"first={PRIME_IDX}  second={TARGET_IDX}  "
          f"expected={expected:.1f}s  loaded={loaded:.1f}s  "
          f"{'OK' if ok else 'BUG: stale frame returned'}")
    raise SystemExit(0 if ok else 1)

The script should print :

first=159  second=35  expected=3.5s  loaded=16.0s  BUG: stale frame returned

This error only happens when using libx265, other codecs (h264, av1) work seamlessly. First investigations pointed to an issue when draining the codec buffer as the decoder sends an EOF packet. I will dig in the C++ code and open a PR if I find the time 🦾

Versions

PyTorch version: 2.11.0
Is debug build: False
CUDA used to build PyTorch: None
ROCM used to build PyTorch: N/A

OS: macOS 15.7.4 (arm64)
GCC version: Could not collect
Clang version: 17.0.0 (clang-1700.6.4.2)
CMake version: version 4.1.3
Libc version: N/A

Python version: 3.12.13 | packaged by Anaconda, Inc. | (main, Mar 19 2026, 20:12:32) [Clang 20.1.8 ] (64-bit runtime)
Python platform: macOS-15.7.4-arm64-arm-64bit
Is CUDA available: False
CUDA runtime version: No CUDA
CUDA_MODULE_LOADING set to: N/A
GPU models and configuration: No CUDA
Nvidia driver version: No CUDA
cuDNN version: No CUDA
Is XPU available: False
HIP runtime version: N/A
MIOpen runtime version: N/A
Is XNNPACK available: True
Caching allocator config: N/A

CPU:
Apple M4 Pro

Versions of relevant libraries:
[pip3] mypy_extensions==1.1.0
[pip3] numpy==2.2.6
[pip3] torch==2.11.0
[pip3] torchcodec==0.11.1
[pip3] torchvision==0.25.0
[conda] numpy 2.2.6 pypi_0 pypi
[conda] torch 2.11.0 pypi_0 pypi
[conda] torchcodec 0.11.1 pypi_0 pypi
[conda] torchvision 0.25.0 pypi_0 pypi

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions