From 0694bd4abe604662ea17b43c9f25473716afde86 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Mon, 24 Nov 2025 14:55:13 +0800 Subject: [PATCH 01/29] add paddle wheel ci --- .editorconfig | 18 ++++++++++++++++ .github/workflows/paddle_wheel.yaml | 33 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/paddle_wheel.yaml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..ce67e34d5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig +# https://editorconfig.org/ + +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{py,sh,ipynb}] +indent_size = 4 + +[*{.md,rst}] +indent_size = 3 diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml new file mode 100644 index 000000000..402c7c651 --- /dev/null +++ b/.github/workflows/paddle_wheel.yaml @@ -0,0 +1,33 @@ +name: Build Wheels for Paddle + +on: + push: + branches: [main] + pull_request: + merge_group: + workflow_dispatch: + +jobs: + build-ffmpeg-LGPL-Linux-x86_64: + strategy: + fail-fast: false + matrix: + ffmpeg-version: ["4.4.4", "5.1.4", "6.1.1", "7.0.1", "8.0"] + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Build FFmpeg + run: | + export FFMPEG_VERSION="${{ matrix.ffmpeg-version }}" + export FFMPEG_ROOT="${PWD}/ffmpeg" + packaging/build_ffmpeg.sh + tar -cf ffmpeg.tar.gz ffmpeg/include ffmpeg/lib + - name: Upload FFmpeg artifact + uses: actions/upload-artifact@v3 + with: + name: ffmpeg-lgpl-linux_x86_64-${{ matrix.ffmpeg-version }} + path: ffmpeg.tar.gz From 0c54bc0a63d564a6d311b8bf796b9d2c21a52170 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Mon, 24 Nov 2025 14:55:50 +0800 Subject: [PATCH 02/29] upload-artifact v5 --- .github/workflows/paddle_wheel.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 402c7c651..ee2fac1c3 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -27,7 +27,7 @@ jobs: packaging/build_ffmpeg.sh tar -cf ffmpeg.tar.gz ffmpeg/include ffmpeg/lib - name: Upload FFmpeg artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v5 with: name: ffmpeg-lgpl-linux_x86_64-${{ matrix.ffmpeg-version }} path: ffmpeg.tar.gz From 3c691810c1275d645b0fd7643be0b8bde425a33c Mon Sep 17 00:00:00 2001 From: SigureMo Date: Mon, 24 Nov 2025 15:38:02 +0800 Subject: [PATCH 03/29] update script by copilot --- .github/workflows/paddle_wheel.yaml | 118 +++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 11 deletions(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index ee2fac1c3..fc2eeb901 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -8,26 +8,122 @@ on: workflow_dispatch: jobs: - build-ffmpeg-LGPL-Linux-x86_64: + build-paddlecodec-wheel: + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - ffmpeg-version: ["4.4.4", "5.1.4", "6.1.1", "7.0.1", "8.0"] - runs-on: ubuntu-latest + python-version: ["3.9", "3.10", "3.11", "3.12"] permissions: id-token: write contents: read steps: - name: Checkout repository uses: actions/checkout@v6 - - name: Build FFmpeg + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + pip install build wheel setuptools pybind11 + + - name: Install PaddlePaddle nightly + run: | + pip install --pre paddlepaddle -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/ + + - name: Run pre-build script + run: | + bash packaging/pre_build_script.sh + + - name: Build wheel run: | - export FFMPEG_VERSION="${{ matrix.ffmpeg-version }}" - export FFMPEG_ROOT="${PWD}/ffmpeg" - packaging/build_ffmpeg.sh - tar -cf ffmpeg.tar.gz ffmpeg/include ffmpeg/lib - - name: Upload FFmpeg artifact + # Use pre-built FFmpeg from PyTorch S3 + export BUILD_AGAINST_ALL_FFMPEG_FROM_S3=1 + python -m build --wheel -vvv --no-isolation + + - name: Run post-build script + run: | + bash packaging/post_build_script.sh + + - name: Upload wheel artifact uses: actions/upload-artifact@v5 with: - name: ffmpeg-lgpl-linux_x86_64-${{ matrix.ffmpeg-version }} - path: ffmpeg.tar.gz + name: paddlecodec-wheel-linux-py${{ matrix.python-version }} + path: dist/*.whl + + - name: List wheel contents + run: | + wheel_path=$(find dist -type f -name "*.whl") + echo "Wheel path: $wheel_path" + unzip -l $wheel_path + + test-paddlecodec-wheel: + needs: build-paddlecodec-wheel + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + ffmpeg-version: ["4.4.2", "5.1.2", "6.1.1", "7.0.1", "8.0"] + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Download wheel artifact + uses: actions/download-artifact@v4 + with: + name: paddlecodec-wheel-linux-py${{ matrix.python-version }} + path: dist/ + + - name: Install PaddlePaddle nightly + run: | + pip install --pre paddlepaddle -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/ + + - name: Install paddlecodec from wheel + run: | + wheel_path=$(find dist -type f -name "*.whl") + echo "Installing $wheel_path" + pip install $wheel_path -vvv + + - name: Install FFmpeg via conda + uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + miniforge-version: latest + activate-environment: test + python-version: ${{ matrix.python-version }} + + - name: Install FFmpeg from conda-forge + shell: bash -l {0} + run: | + source packaging/helpers.sh + # Check FFmpeg is not installed before + if command -v ffmpeg &> /dev/null; then + echo "Warning: FFmpeg is already installed" + fi + + conda install "ffmpeg=${{ matrix.ffmpeg-version }}" -c conda-forge -y + ffmpeg -version + + - name: Install test dependencies + run: | + pip install numpy pytest pillow + + - name: Delete src folder + run: | + # Delete src/ to ensure we're testing the installed wheel, not source code + rm -rf src/ + ls -la + + - name: Run tests + run: | + pytest --override-ini="addopts=-v" test From 1e3503fcd1351c79b0dd6ff390602301404ef05d Mon Sep 17 00:00:00 2001 From: SigureMo Date: Mon, 24 Nov 2025 15:43:42 +0800 Subject: [PATCH 04/29] update script by copilot --- .github/workflows/paddle_wheel.yaml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index fc2eeb901..5bf229a08 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -7,13 +7,25 @@ on: merge_group: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }} + cancel-in-progress: true + +permissions: + id-token: write + contents: write + +defaults: + run: + shell: bash -l -eo pipefail {0} + jobs: build-paddlecodec-wheel: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] permissions: id-token: write contents: read @@ -21,15 +33,18 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: Setup conda environment + uses: conda-incubator/setup-miniconda@v3 with: + auto-update-conda: true + miniforge-version: latest + activate-environment: build python-version: ${{ matrix.python-version }} - name: Install build dependencies run: | python -m pip install --upgrade pip - pip install build wheel setuptools pybind11 + pip install build wheel setuptools - name: Install PaddlePaddle nightly run: | @@ -67,7 +82,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] ffmpeg-version: ["4.4.2", "5.1.2", "6.1.1", "7.0.1", "8.0"] steps: - name: Checkout repository From e8f73c144f18ef31bc5f08abd1d3a61590d9ed34 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Mon, 24 Nov 2025 16:01:38 +0800 Subject: [PATCH 05/29] dont deps on libphi_gpu.so --- src/torchcodec/_core/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/torchcodec/_core/CMakeLists.txt b/src/torchcodec/_core/CMakeLists.txt index 4df8d1b6d..11fbcd82d 100644 --- a/src/torchcodec/_core/CMakeLists.txt +++ b/src/torchcodec/_core/CMakeLists.txt @@ -14,7 +14,6 @@ set( "${PADDLE_PATH}/libs/libcommon.so" "${PADDLE_PATH}/libs/libphi.so" "${PADDLE_PATH}/libs/libphi_core.so" - "${PADDLE_PATH}/libs/libphi_gpu.so" ) set( TORCH_INSTALL_PREFIX From bf61a123c3a584dfade9e9bed6badd99ee53c0f2 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Mon, 24 Nov 2025 19:27:58 +0800 Subject: [PATCH 06/29] upload-wheels-before-check --- .github/workflows/paddle_wheel.yaml | 8 ++++---- src/torchcodec/decoders/_video_decoder.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 5bf229a08..fa4369f29 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -60,16 +60,16 @@ jobs: export BUILD_AGAINST_ALL_FFMPEG_FROM_S3=1 python -m build --wheel -vvv --no-isolation - - name: Run post-build script - run: | - bash packaging/post_build_script.sh - - name: Upload wheel artifact uses: actions/upload-artifact@v5 with: name: paddlecodec-wheel-linux-py${{ matrix.python-version }} path: dist/*.whl + - name: Run post-build script + run: | + bash packaging/post_build_script.sh + - name: List wheel contents run: | wheel_path=$(find dist -type f -name "*.whl") diff --git a/src/torchcodec/decoders/_video_decoder.py b/src/torchcodec/decoders/_video_decoder.py index 730bfa0e5..6b524f119 100644 --- a/src/torchcodec/decoders/_video_decoder.py +++ b/src/torchcodec/decoders/_video_decoder.py @@ -102,7 +102,7 @@ def __init__( stream_index: Optional[int] = None, dimension_order: Literal["NCHW", "NHWC"] = "NCHW", num_ffmpeg_threads: int = 1, - device: Optional[Union[str, torch_device]] = "cpu", + device: Optional[Union[str, "torch_device"]] = "cpu", seek_mode: Literal["exact", "approximate"] = "exact", custom_frame_mappings: Optional[ Union[str, bytes, io.RawIOBase, io.BufferedReader] From 5dffb71d0b7e52679b2966677e39037c910a92b1 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Mon, 24 Nov 2025 21:09:11 +0800 Subject: [PATCH 07/29] use pytorch/manylinux2_28-builder:cpu --- .github/workflows/paddle_wheel.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index fa4369f29..2b6df2f7d 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -22,6 +22,8 @@ defaults: jobs: build-paddlecodec-wheel: runs-on: ubuntu-latest + container: + image: pytorch/manylinux2_28-builder:cpu strategy: fail-fast: false matrix: From b23501cf79953a1d989cb08bd60899e0aa3cdaad Mon Sep 17 00:00:00 2001 From: SigureMo Date: Tue, 25 Nov 2025 10:21:22 +0800 Subject: [PATCH 08/29] dont link libphi.so --- src/torchcodec/_core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/torchcodec/_core/CMakeLists.txt b/src/torchcodec/_core/CMakeLists.txt index 11fbcd82d..f6a02596a 100644 --- a/src/torchcodec/_core/CMakeLists.txt +++ b/src/torchcodec/_core/CMakeLists.txt @@ -12,7 +12,7 @@ set( TORCH_LIBRARIES "${PADDLE_PATH}/base/libpaddle.so" "${PADDLE_PATH}/libs/libcommon.so" - "${PADDLE_PATH}/libs/libphi.so" + # "${PADDLE_PATH}/libs/libphi.so" # currently libphi.so is static linked, we need remove it when it's shared linked "${PADDLE_PATH}/libs/libphi_core.so" ) set( From bbd3a7cc8a526fcf403f99f5df5f1176e29d815d Mon Sep 17 00:00:00 2001 From: SigureMo Date: Tue, 25 Nov 2025 11:17:04 +0800 Subject: [PATCH 09/29] add ut --- .github/workflows/paddle_wheel.yaml | 3 +- test_paddle/test_video_decode.py | 181 ++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 test_paddle/test_video_decode.py diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 2b6df2f7d..3e6ec8f72 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -120,7 +120,6 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install FFmpeg from conda-forge - shell: bash -l {0} run: | source packaging/helpers.sh # Check FFmpeg is not installed before @@ -143,4 +142,4 @@ jobs: - name: Run tests run: | - pytest --override-ini="addopts=-v" test + pytest --override-ini="addopts=-v" test_paddle diff --git a/test_paddle/test_video_decode.py b/test_paddle/test_video_decode.py new file mode 100644 index 000000000..21c697ca4 --- /dev/null +++ b/test_paddle/test_video_decode.py @@ -0,0 +1,181 @@ +import paddle +paddle.compat.enable_torch_proxy() + +import pytest +import torch +from dataclasses import dataclass, fields +from io import BytesIO +from typing import Callable, Mapping, Optional, Union + +import os +import httpx +import numpy as np + + +@dataclass +class VideoMetadata(Mapping): + total_num_frames: int + fps: Optional[float] = None + width: Optional[int] = None + height: Optional[int] = None + duration: Optional[float] = None + video_backend: Optional[str] = None + frames_indices: Optional[list[int]] = None + + def __iter__(self): + return (f.name for f in fields(self)) + + def __len__(self): + return len(fields(self)) + + def __getitem__(self, item): + return getattr(self, item) + + def __setitem__(self, key, value): + return setattr(self, key, value) + + @property + def timestamps(self) -> list[float]: + "Timestamps of the sampled frames in seconds." + if self.fps is None or self.frames_indices is None: + raise ValueError("Cannot infer video `timestamps` when `fps` or `frames_indices` is None.") + return [frame_idx / self.fps for frame_idx in self.frames_indices] + + def update(self, dictionary): + for key, value in dictionary.items(): + if hasattr(self, key): + setattr(self, key, value) + + +def default_sample_indices_fn(metadata: VideoMetadata, num_frames=None, fps=None, **kwargs): + total_num_frames = metadata.total_num_frames + video_fps = metadata.fps + + if num_frames is None and fps is not None: + num_frames = int(total_num_frames / video_fps * fps) + if num_frames > total_num_frames: + raise ValueError( + f"When loading the video with fps={fps}, we computed num_frames={num_frames} " + f"which exceeds total_num_frames={total_num_frames}. Check fps or video metadata." + ) + + if num_frames is not None: + indices = np.arange(0, total_num_frames, total_num_frames / num_frames, dtype=int) + else: + indices = np.arange(0, total_num_frames, dtype=int) + return indices + + +def read_video_decord( + video_path: Union["URL", "Path"], + sample_indices_fn: Callable, + **kwargs, +): + from decord import VideoReader, cpu + + vr = VideoReader(uri=video_path, ctx=cpu(0)) # decord has problems with gpu + video_fps = vr.get_avg_fps() + total_num_frames = len(vr) + duration = total_num_frames / video_fps if video_fps else 0 + metadata = VideoMetadata( + total_num_frames=int(total_num_frames), + fps=float(video_fps), + duration=float(duration), + video_backend="decord", + ) + + indices = sample_indices_fn(metadata=metadata, **kwargs) + video = vr.get_batch(indices).asnumpy() + + metadata.update( + { + "frames_indices": indices, + "height": video.shape[1], + "width": video.shape[2], + } + ) + return video, metadata + +def read_video_torchcodec( + video_path: Union["URL", "Path"], + sample_indices_fn: Callable, + **kwargs, +): + from torchcodec.decoders import VideoDecoder # import torchcodec + + decoder = VideoDecoder( + video_path, + seek_mode="exact", + num_ffmpeg_threads=0, + ) + metadata = VideoMetadata( + total_num_frames=decoder.metadata.num_frames, + fps=decoder.metadata.average_fps, + duration=decoder.metadata.duration_seconds, + video_backend="torchcodec", + height=decoder.metadata.height, + width=decoder.metadata.width, + ) + indices = sample_indices_fn(metadata=metadata, **kwargs) + + video = decoder.get_frames_at(indices=indices).data + video = video.cuda().contiguous().cpu() + metadata.frames_indices = indices + return video, metadata + + +VIDEO_DECODERS = { + "decord": read_video_decord, + "torchcodec": read_video_torchcodec, +} + + +def load_video( + video, + num_frames: Optional[int] = None, + fps: Optional[Union[int, float]] = None, + backend: str = "decord", + sample_indices_fn: Optional[Callable] = None, + **kwargs, +) -> np.ndarray: + + if fps is not None and num_frames is not None and sample_indices_fn is None: + raise ValueError( + "`num_frames`, `fps`, and `sample_indices_fn` are mutually exclusive arguments, please use only one!" + ) + + # If user didn't pass a sampling function, create one on the fly with default logic + if sample_indices_fn is None: + + def sample_indices_fn_func(metadata, **fn_kwargs): + return default_sample_indices_fn(metadata, num_frames=num_frames, fps=fps, **fn_kwargs) + + sample_indices_fn = sample_indices_fn_func + + # Early exit if provided an array or `PIL` frames + if not isinstance(video, str): + metadata = [None] * len(video) + return video, metadata + + if video.startswith("http://") or video.startswith("https://"): + file_obj = BytesIO(httpx.get(video, follow_redirects=True).content) + elif os.path.isfile(video): + file_obj = video + else: + raise TypeError("Incorrect format used for video. Should be an url linking to an video or a local path.") + + video_decoder = VIDEO_DECODERS[backend] + video, metadata = video_decoder(file_obj, sample_indices_fn, **kwargs) + return video, metadata + +def test_video_decode(): + url = "https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_video/example_video.mp4" + video, metadata = load_video(url, backend="torchcodec") + assert video.to(torch.int64).sum().item() == 247759890390 + assert metadata.total_num_frames == 263 + assert metadata.fps == pytest.approx(29.99418249715141) + assert metadata.width == 1920 + assert metadata.height == 1080 + assert metadata.duration == pytest.approx(8.768367) + for i, idx in enumerate(metadata.frames_indices): + assert idx == i From 7e066afd0f556bfd257bbb55520439009fe886a7 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Tue, 25 Nov 2025 12:48:20 +0800 Subject: [PATCH 10/29] restore `bash -l {0}` --- .github/workflows/paddle_wheel.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 3e6ec8f72..2ec247de6 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -120,6 +120,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install FFmpeg from conda-forge + shell: bash -l {0} run: | source packaging/helpers.sh # Check FFmpeg is not installed before From cf2d2e63a99a48ae2e6d8fe1581301de3b78e526 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Tue, 25 Nov 2025 13:14:55 +0800 Subject: [PATCH 11/29] create conda env before install packages --- .github/workflows/paddle_wheel.yaml | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 2ec247de6..6858faa38 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -101,16 +101,6 @@ jobs: name: paddlecodec-wheel-linux-py${{ matrix.python-version }} path: dist/ - - name: Install PaddlePaddle nightly - run: | - pip install --pre paddlepaddle -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/ - - - name: Install paddlecodec from wheel - run: | - wheel_path=$(find dist -type f -name "*.whl") - echo "Installing $wheel_path" - pip install $wheel_path -vvv - - name: Install FFmpeg via conda uses: conda-incubator/setup-miniconda@v3 with: @@ -120,17 +110,20 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install FFmpeg from conda-forge - shell: bash -l {0} run: | - source packaging/helpers.sh - # Check FFmpeg is not installed before - if command -v ffmpeg &> /dev/null; then - echo "Warning: FFmpeg is already installed" - fi - conda install "ffmpeg=${{ matrix.ffmpeg-version }}" -c conda-forge -y ffmpeg -version + - name: Install PaddlePaddle nightly in conda env + run: | + pip install --pre paddlepaddle -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/ + + - name: Install paddlecodec from wheel + run: | + wheel_path=$(find dist -type f -name "*.whl") + echo "Installing $wheel_path" + pip install $wheel_path -vvv + - name: Install test dependencies run: | pip install numpy pytest pillow From 101f109ff042716c71a5cef34a5ce87524e0cea2 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Tue, 25 Nov 2025 14:20:30 +0800 Subject: [PATCH 12/29] fix .cuda().cpu() --- test_paddle/test_video_decode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_paddle/test_video_decode.py b/test_paddle/test_video_decode.py index 21c697ca4..1ced5f335 100644 --- a/test_paddle/test_video_decode.py +++ b/test_paddle/test_video_decode.py @@ -119,7 +119,7 @@ def read_video_torchcodec( indices = sample_indices_fn(metadata=metadata, **kwargs) video = decoder.get_frames_at(indices=indices).data - video = video.cuda().contiguous().cpu() + video = video.contiguous() metadata.frames_indices = indices return video, metadata From a5337d1a59aa6f17b923cf696486f68923b1ea6c Mon Sep 17 00:00:00 2001 From: SigureMo Date: Tue, 25 Nov 2025 21:44:29 +0800 Subject: [PATCH 13/29] empty commit From e5a4c640a3c43e05d337a9a6e575e37ae1dd1bc0 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Wed, 26 Nov 2025 16:48:18 +0800 Subject: [PATCH 14/29] use self-hosted runner --- .github/workflows/paddle_wheel.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 6858faa38..0b3725865 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -80,7 +80,8 @@ jobs: test-paddlecodec-wheel: needs: build-paddlecodec-wheel - runs-on: ubuntu-latest + runs-on: + group: custom-ext-linux-cpu strategy: fail-fast: false matrix: From 03eef79c669d5cc76fb974b361ab9c53b5316a47 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Wed, 26 Nov 2025 21:52:39 +0800 Subject: [PATCH 15/29] add openvino so ver check --- .github/workflows/paddle_wheel.yaml | 3 +-- test_paddle/test_video_decode.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 0b3725865..6858faa38 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -80,8 +80,7 @@ jobs: test-paddlecodec-wheel: needs: build-paddlecodec-wheel - runs-on: - group: custom-ext-linux-cpu + runs-on: ubuntu-latest strategy: fail-fast: false matrix: diff --git a/test_paddle/test_video_decode.py b/test_paddle/test_video_decode.py index 1ced5f335..de3c987a8 100644 --- a/test_paddle/test_video_decode.py +++ b/test_paddle/test_video_decode.py @@ -168,6 +168,26 @@ def sample_indices_fn_func(metadata, **fn_kwargs): video, metadata = video_decoder(file_obj, sample_indices_fn, **kwargs) return video, metadata +def check_openvino(): + import ctypes + import os + mode = os.RTLD_NOW | os.RTLD_GLOBAL + try: + ctypes.CDLL("libopenvino.so.2500", mode) + except OSError: + print("Failed to load libopenvino.so.2500") + else: + print("Successfully loaded libopenvino.so.2500") + + try: + ctypes.CDLL("libopenvino.so.2520", mode) + except OSError: + print("Failed to load libopenvino.so.2520") + else: + print("Successfully loaded libopenvino.so.2520") + + + def test_video_decode(): url = "https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_video/example_video.mp4" video, metadata = load_video(url, backend="torchcodec") @@ -177,5 +197,6 @@ def test_video_decode(): assert metadata.width == 1920 assert metadata.height == 1080 assert metadata.duration == pytest.approx(8.768367) + check_openvino() for i, idx in enumerate(metadata.frames_indices): assert idx == i From ac87fd6089fea7535a55b1820f977571ed007a0c Mon Sep 17 00:00:00 2001 From: SigureMo Date: Wed, 26 Nov 2025 22:46:28 +0800 Subject: [PATCH 16/29] show openvino out --- .github/workflows/paddle_wheel.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 6858faa38..86d7129c0 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -136,4 +136,4 @@ jobs: - name: Run tests run: | - pytest --override-ini="addopts=-v" test_paddle + pytest --override-ini="addopts=-v" -s test_paddle From 747a25aa46a1eb993c434a7446f0f9cf48fdacb1 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Wed, 26 Nov 2025 23:40:58 +0800 Subject: [PATCH 17/29] empty commit From a1b0660743af3a3a6b9359778668d540b55bd103 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Thu, 27 Nov 2025 11:01:34 +0800 Subject: [PATCH 18/29] force to install libopenvino=2025.0.0 --- .github/workflows/paddle_wheel.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 86d7129c0..b1624517e 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -111,7 +111,9 @@ jobs: - name: Install FFmpeg from conda-forge run: | - conda install "ffmpeg=${{ matrix.ffmpeg-version }}" -c conda-forge -y + # Install libstdcxx-ng for CXXABI compatibility + # Attempt to pin libopenvino to 2025.0.0 to match Paddle's bundled version and avoid segfault + conda install "ffmpeg=${{ matrix.ffmpeg-version }}" libstdcxx-ng "libopenvino=2025.0.0" -c conda-forge -y ffmpeg -version - name: Install PaddlePaddle nightly in conda env From 08af0d272fa3c197c861e7a98b29058985627c34 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Thu, 27 Nov 2025 14:40:06 +0800 Subject: [PATCH 19/29] use gpu package to run ut --- .github/workflows/paddle_wheel.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index b1624517e..9bfa000ce 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -118,7 +118,7 @@ jobs: - name: Install PaddlePaddle nightly in conda env run: | - pip install --pre paddlepaddle -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/ + pip install --pre paddlepaddle-gpu -i https://www.paddlepaddle.org.cn/packages/nightly/cu126/ - name: Install paddlecodec from wheel run: | From 8c5daf25f9adeb128ed96cdb886dbc56ebfc02d5 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Thu, 27 Nov 2025 14:50:40 +0800 Subject: [PATCH 20/29] revert pin libopenvino version --- .github/workflows/paddle_wheel.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 9bfa000ce..c415d48cb 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -111,9 +111,7 @@ jobs: - name: Install FFmpeg from conda-forge run: | - # Install libstdcxx-ng for CXXABI compatibility - # Attempt to pin libopenvino to 2025.0.0 to match Paddle's bundled version and avoid segfault - conda install "ffmpeg=${{ matrix.ffmpeg-version }}" libstdcxx-ng "libopenvino=2025.0.0" -c conda-forge -y + conda install "ffmpeg=${{ matrix.ffmpeg-version }}" -c conda-forge -y ffmpeg -version - name: Install PaddlePaddle nightly in conda env From 1cefa7daadfa47f7d128464777aee5bcc62e5ea0 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 14:06:08 +0800 Subject: [PATCH 21/29] empty commit From 7ce892090658b82d2491e951d51eda984fb07ced Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 14:49:14 +0800 Subject: [PATCH 22/29] install cpu package --- .github/workflows/paddle_wheel.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index c415d48cb..86d7129c0 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -116,7 +116,7 @@ jobs: - name: Install PaddlePaddle nightly in conda env run: | - pip install --pre paddlepaddle-gpu -i https://www.paddlepaddle.org.cn/packages/nightly/cu126/ + pip install --pre paddlepaddle -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/ - name: Install paddlecodec from wheel run: | From 374204312c1f5cd1ab6d43bac20468ce9e98965f Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 15:56:17 +0800 Subject: [PATCH 23/29] add release part in workflow --- .github/workflows/paddle_wheel.yaml | 47 ++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 86d7129c0..4b06be1dd 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -3,6 +3,7 @@ name: Build Wheels for Paddle on: push: branches: [main] + tags: ["v*"] pull_request: merge_group: workflow_dispatch: @@ -85,7 +86,10 @@ jobs: fail-fast: false matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] - ffmpeg-version: ["4.4.2", "5.1.2", "6.1.1", "7.0.1", "8.0"] + # FFmpeg 8.0 depends on libopenvino.so.2520, PaddlePaddle CPU depends on libopenvino.so.2500 + # There has some conflict causing test failures, but it works with PaddlePaddle GPU. + # We skip FFmpeg 8.0 tests for PaddlePaddle CPU builds for now. + ffmpeg-version: ["4.4.2", "5.1.2", "6.1.1", "7.0.1"] steps: - name: Checkout repository uses: actions/checkout@v6 @@ -137,3 +141,44 @@ jobs: - name: Run tests run: | pytest --override-ini="addopts=-v" -s test_paddle + + publish-pypi: + runs-on: ubuntu-latest + name: Publish to PyPI + if: "startsWith(github.ref, 'refs/tags/')" + needs: + - test-paddlecodec-wheel + permissions: + id-token: write + + steps: + - name: Retrieve release distributions + uses: actions/download-artifact@v6 + with: + name: artifacts + path: dist/ + + - name: Publish release distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + publish-release: + runs-on: ubuntu-latest + name: Publish to GitHub + if: "startsWith(github.ref, 'refs/tags/')" + needs: + - list-artifacts + permissions: + contents: write + steps: + - uses: actions/download-artifact@v6 + with: + name: artifacts + path: dist/ + - name: Get tag name + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + - name: Publish to GitHub + uses: softprops/action-gh-release@v2 + with: + draft: true + files: dist/* + tag_name: ${{ env.RELEASE_VERSION }} From 1e5a37b84a650140c38644024a20cc6033a7da9c Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 15:57:57 +0800 Subject: [PATCH 24/29] limit proxy scope to torchcodec --- examples/decoding/sampling.py | 2 +- test_paddle/test_video_decode.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/decoding/sampling.py b/examples/decoding/sampling.py index 5b0f87819..f2a9c9a8b 100644 --- a/examples/decoding/sampling.py +++ b/examples/decoding/sampling.py @@ -20,7 +20,7 @@ # :ref:`sampling_tuto_start`. import paddle -paddle.compat.enable_torch_proxy() +paddle.compat.enable_torch_proxy(scope={"torchcodec"}) from typing import Optional import torch diff --git a/test_paddle/test_video_decode.py b/test_paddle/test_video_decode.py index de3c987a8..db6247086 100644 --- a/test_paddle/test_video_decode.py +++ b/test_paddle/test_video_decode.py @@ -1,5 +1,5 @@ import paddle -paddle.compat.enable_torch_proxy() +paddle.compat.enable_torch_proxy(scope={"torchcodec"}) import pytest import torch From a5165bb01d7104df54cc8a5a42456098af0bda4c Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 15:58:46 +0800 Subject: [PATCH 25/29] cleanup check_openvino --- test_paddle/test_video_decode.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/test_paddle/test_video_decode.py b/test_paddle/test_video_decode.py index db6247086..7def988bf 100644 --- a/test_paddle/test_video_decode.py +++ b/test_paddle/test_video_decode.py @@ -168,25 +168,6 @@ def sample_indices_fn_func(metadata, **fn_kwargs): video, metadata = video_decoder(file_obj, sample_indices_fn, **kwargs) return video, metadata -def check_openvino(): - import ctypes - import os - mode = os.RTLD_NOW | os.RTLD_GLOBAL - try: - ctypes.CDLL("libopenvino.so.2500", mode) - except OSError: - print("Failed to load libopenvino.so.2500") - else: - print("Successfully loaded libopenvino.so.2500") - - try: - ctypes.CDLL("libopenvino.so.2520", mode) - except OSError: - print("Failed to load libopenvino.so.2520") - else: - print("Successfully loaded libopenvino.so.2520") - - def test_video_decode(): url = "https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_video/example_video.mp4" @@ -197,6 +178,5 @@ def test_video_decode(): assert metadata.width == 1920 assert metadata.height == 1080 assert metadata.duration == pytest.approx(8.768367) - check_openvino() for i, idx in enumerate(metadata.frames_indices): assert idx == i From 32c5165747efcd43be1ffabe61659eae49c45a1d Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 16:00:15 +0800 Subject: [PATCH 26/29] fix workflow deps --- .github/workflows/paddle_wheel.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 4b06be1dd..2e5d0e21d 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -166,7 +166,7 @@ jobs: name: Publish to GitHub if: "startsWith(github.ref, 'refs/tags/')" needs: - - list-artifacts + - test-paddlecodec-wheel permissions: contents: write steps: From a79cbb5cbe91a42ad3c39f42c0b5c3816e641749 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 16:47:16 +0800 Subject: [PATCH 27/29] cleanup import torch --- test_paddle/test_video_decode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test_paddle/test_video_decode.py b/test_paddle/test_video_decode.py index 7def988bf..3ea7bbc9c 100644 --- a/test_paddle/test_video_decode.py +++ b/test_paddle/test_video_decode.py @@ -2,7 +2,6 @@ paddle.compat.enable_torch_proxy(scope={"torchcodec"}) import pytest -import torch from dataclasses import dataclass, fields from io import BytesIO from typing import Callable, Mapping, Optional, Union From 0c406b17a13edc2923bbd0af81096af47b8316df Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 16:52:23 +0800 Subject: [PATCH 28/29] fix scope usage --- src/torchcodec/_core/ops.py | 4 ++++ test_paddle/test_video_decode.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/torchcodec/_core/ops.py b/src/torchcodec/_core/ops.py index 804d536fd..9ea928890 100644 --- a/src/torchcodec/_core/ops.py +++ b/src/torchcodec/_core/ops.py @@ -91,6 +91,10 @@ def disallow_in_graph(self, fn): return fn torch._dynamo = FakeDynamo("torch._dynamo") torch._C._log_api_usage_once = lambda *args, **kwargs: None +# TODO: torch.__setattr__ should trigger paddle.__setattr__ +import paddle +paddle._dynamo = FakeDynamo("torch._dynamo") +paddle._C._log_api_usage_once = lambda *args, **kwargs: None # Note: We use disallow_in_graph because PyTorch does constant propagation of # factory functions. create_from_file = torch._dynamo.disallow_in_graph( diff --git a/test_paddle/test_video_decode.py b/test_paddle/test_video_decode.py index 3ea7bbc9c..dc9a125f7 100644 --- a/test_paddle/test_video_decode.py +++ b/test_paddle/test_video_decode.py @@ -171,7 +171,7 @@ def sample_indices_fn_func(metadata, **fn_kwargs): def test_video_decode(): url = "https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_video/example_video.mp4" video, metadata = load_video(url, backend="torchcodec") - assert video.to(torch.int64).sum().item() == 247759890390 + assert video.to(paddle.int64).sum().item() == 247759890390 assert metadata.total_num_frames == 263 assert metadata.fps == pytest.approx(29.99418249715141) assert metadata.width == 1920 From 67d168a91c29c7e38dd58c591f44ac5731969f83 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Fri, 28 Nov 2025 17:40:56 +0800 Subject: [PATCH 29/29] skip python3.13 ut --- .github/workflows/paddle_wheel.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/paddle_wheel.yaml b/.github/workflows/paddle_wheel.yaml index 2e5d0e21d..7cf150825 100644 --- a/.github/workflows/paddle_wheel.yaml +++ b/.github/workflows/paddle_wheel.yaml @@ -85,7 +85,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + # Python 3.13 needs waiting PaddlePaddle self-hosted index upload setuptools + python-version: ["3.9", "3.10", "3.11", "3.12"] # FFmpeg 8.0 depends on libopenvino.so.2520, PaddlePaddle CPU depends on libopenvino.so.2500 # There has some conflict causing test failures, but it works with PaddlePaddle GPU. # We skip FFmpeg 8.0 tests for PaddlePaddle CPU builds for now.