tensorcodec 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tensorcodec-0.1.2/PKG-INFO +196 -0
- tensorcodec-0.1.2/README.md +171 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/docs/compatibility.md +17 -7
- tensorcodec-0.1.2/docs/package_size.md +73 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/docs/playback_semantics.md +1 -1
- tensorcodec-0.1.2/docs/releasing.md +82 -0
- tensorcodec-0.1.2/docs/system_ffmpeg.md +77 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/native/Cargo.lock +1 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/native/Cargo.toml +1 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/native/src/ffmpeg.rs +56 -18
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/native/src/lib.rs +13 -2
- tensorcodec-0.1.2/packaging/size-baseline.json +82 -0
- tensorcodec-0.1.2/packaging/size-policy.json +23 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/pyproject.toml +5 -2
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/scripts/build_linux_wheel.sh +4 -2
- tensorcodec-0.1.2/scripts/build_nasm.sh +13 -0
- tensorcodec-0.1.2/scripts/check_wheel_runtime.py +75 -0
- tensorcodec-0.1.2/scripts/check_wheel_size.py +84 -0
- tensorcodec-0.1.2/scripts/update_size_comparison.py +143 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/src/tensorcodec/__init__.py +1 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/src/tensorcodec/_metadata.py +2 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/src/tensorcodec/decoders/_decoder.py +23 -23
- tensorcodec-0.1.2/tests/__init__.py +1 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/tests/conftest.py +83 -31
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/tests/test_audio_contract.py +1 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/tests/test_differential.py +31 -10
- tensorcodec-0.1.2/tests/test_size_comparison.py +131 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/tests/test_video_contract.py +2 -4
- tensorcodec-0.1.2/tests/test_video_fidelity.py +82 -0
- tensorcodec-0.1.2/tests/test_wheel_size.py +101 -0
- tensorcodec-0.1.2/tests/utils.py +70 -0
- tensorcodec-0.1.0/PKG-INFO +0 -149
- tensorcodec-0.1.0/README.md +0 -124
- tensorcodec-0.1.0/docs/releasing.md +0 -60
- tensorcodec-0.1.0/tests/__init__.py +0 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/LICENSE +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/licenses/FFmpeg-GPL-3.0.txt +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/licenses/FFmpeg-LGPL-3.0.txt +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/licenses/FFmpeg-NOTICE.md +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/licenses/OpenSSL.txt +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/licenses/README.md +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/licenses/Zstandard.txt +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/scripts/build_ffmpeg.sh +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/scripts/build_openssl.sh +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/scripts/configure_oracle_ffmpeg.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/src/tensorcodec/_frame.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/src/tensorcodec/decoders/__init__.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/src/tensorcodec/py.typed +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.2}/tests/test_runtime.py +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tensorcodec
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Rust
|
|
8
|
+
Classifier: Topic :: Multimedia :: Video
|
|
9
|
+
Requires-Dist: numpy>=1.26
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
License-File: licenses/FFmpeg-GPL-3.0.txt
|
|
12
|
+
License-File: licenses/FFmpeg-LGPL-3.0.txt
|
|
13
|
+
License-File: licenses/FFmpeg-NOTICE.md
|
|
14
|
+
License-File: licenses/OpenSSL.txt
|
|
15
|
+
License-File: licenses/README.md
|
|
16
|
+
License-File: licenses/Zstandard.txt
|
|
17
|
+
Summary: NumPy audio/video decoding with TorchCodec-compatible playback semantics
|
|
18
|
+
Author-email: Suhwan Choi <milkclouds00@gmail.com>
|
|
19
|
+
License-Expression: MIT
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
22
|
+
Project-URL: Issues, https://github.com/MilkClouds/tensorcodec/issues
|
|
23
|
+
Project-URL: Repository, https://github.com/MilkClouds/tensorcodec
|
|
24
|
+
|
|
25
|
+
<div align="center">
|
|
26
|
+
|
|
27
|
+
# TensorCodec
|
|
28
|
+
|
|
29
|
+
CPU video/audio decoding with TorchCodec-style APIs and NumPy output.
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<a href="https://github.com/MilkClouds/tensorcodec/actions/workflows/ci.yml"><img src="https://github.com/MilkClouds/tensorcodec/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
|
|
33
|
+
<a href="https://pypi.org/project/tensorcodec/"><img src="https://img.shields.io/pypi/v/tensorcodec" alt="PyPI"></a>
|
|
34
|
+
<a href="https://pypi.org/project/tensorcodec/"><img src="https://img.shields.io/badge/Python-3.10%2B-blue" alt="Python"></a>
|
|
35
|
+
<!-- wheel-size-badge:start -->
|
|
36
|
+
<a href="#package-size"><img src="https://img.shields.io/badge/wheel-10.4%20MiB-blue" alt="Wheel download"></a>
|
|
37
|
+
<!-- wheel-size-badge:end -->
|
|
38
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue" alt="License: MIT"></a>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
[Quick start](#quick-start) · [Features](#features) · [Package size](#package-size) · [Compatibility](docs/compatibility.md)
|
|
42
|
+
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
- **TorchCodec API without PyTorch.** CPU video/audio decoder interfaces follow
|
|
46
|
+
TorchCodec and return NumPy arrays.
|
|
47
|
+
- **Validated playback semantics.** Frame selection, ordering, timestamps and audio
|
|
48
|
+
ranges are checked against TorchCodec 0.17.0 and independently generated media.
|
|
49
|
+
- **Efficient batch decoding.** Rust/PyO3 bindings to FFmpeg process frame batches
|
|
50
|
+
in a single native call, avoiding per-frame Python calls.
|
|
51
|
+
- **Lightweight installation.** Linux wheels are 10.2–10.4 MiB (v0.1.1), including
|
|
52
|
+
FFmpeg shared libraries. NumPy is the only Python dependency.
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
uv pip install tensorcodec
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use an existing virtual environment, or create one with `uv venv` first.
|
|
61
|
+
No separate FFmpeg installation is needed for the published Linux wheels.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from tensorcodec.decoders import VideoDecoder, AudioDecoder
|
|
65
|
+
|
|
66
|
+
with VideoDecoder("video.mp4") as video:
|
|
67
|
+
frame = video[0] # RGB array: (C, H, W)
|
|
68
|
+
batch = video.get_frames_at([4, 0, 4]) # requested order, including duplicates
|
|
69
|
+
clip = video.get_frames_played_in_range(0, 1, fps=8)
|
|
70
|
+
|
|
71
|
+
with AudioDecoder("audio.wav", sample_rate=16000, num_channels=1) as audio:
|
|
72
|
+
samples = audio.get_samples_played_in_range(0, 1)
|
|
73
|
+
waveform = samples.data # float32: (channels, samples)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Arrays keep their storage after the decoder closes. Paths, URLs, encoded bytes,
|
|
77
|
+
1-D uint8 arrays and seekable file objects are supported.
|
|
78
|
+
|
|
79
|
+
## Features
|
|
80
|
+
|
|
81
|
+
TensorCodec 0.1.2 relative to TorchCodec 0.17.0.
|
|
82
|
+
✓ supported · △ partial support · — not implemented.
|
|
83
|
+
|
|
84
|
+
| Component | TensorCodec | TorchCodec 0.17.0 |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| Video decoder | △ CPU, SDR/HDR RGB | ✓ CPU / CUDA |
|
|
87
|
+
| Audio decoder | ✓ CPU | ✓ CPU |
|
|
88
|
+
| Image decoders | — | ✓ |
|
|
89
|
+
| Video / audio / image encoders | — | ✓ |
|
|
90
|
+
| Clip samplers | — | ✓ |
|
|
91
|
+
| Decoder transforms | — | ✓ |
|
|
92
|
+
|
|
93
|
+
FPS-based frame queries are supported; clip samplers are a separate API.
|
|
94
|
+
|
|
95
|
+
### Decoder compatibility
|
|
96
|
+
|
|
97
|
+
| Capability | TensorCodec | TorchCodec 0.17.0 |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| Index / slice / batch selection | ✓ | ✓ |
|
|
100
|
+
| Playback timestamp / range queries | ✓ | ✓ |
|
|
101
|
+
| Request order and duplicate frames | Preserved | Preserved |
|
|
102
|
+
| Exact / approximate seeking | ✓ Default: exact | ✓ |
|
|
103
|
+
| FPS queries / custom frame mappings | ✓ | ✓ |
|
|
104
|
+
| CFR / VFR / offset PTS / B-frames | ✓ Tested | ✓ |
|
|
105
|
+
| NCHW / NHWC RGB output | ✓ | ✓ |
|
|
106
|
+
| uint8 / float32 / automatic dtype | ✓ SDR and high-bit-depth video | ✓ |
|
|
107
|
+
| uint16 RGB output | ✓ Full-range RGB48 | — |
|
|
108
|
+
| PQ / HLG decoding | ✓ Transfer-encoded RGB | ✓ |
|
|
109
|
+
| Right-angle display rotation | ✓ | ✓ |
|
|
110
|
+
| Audio ranges / resampling / channel mixing | ✓ float32 | ✓ |
|
|
111
|
+
| Paths / URLs / bytes / seekable file objects | ✓ | ✓ |
|
|
112
|
+
| Encoded array input | 1-D uint8 NumPy array | PyTorch tensor |
|
|
113
|
+
| Decoded output | NumPy array; array interface / DLPack | PyTorch tensor |
|
|
114
|
+
| CUDA decoding | — | ✓ |
|
|
115
|
+
|
|
116
|
+
For high-bit-depth video, use `VideoDecoder(path, output_dtype="auto")` to select
|
|
117
|
+
float32 above 8 bits, or `output_dtype="uint16"` for full-range 16-bit RGB.
|
|
118
|
+
HDR output retains PQ/HLG encoding without SDR tone mapping. Rotation is applied
|
|
119
|
+
automatically, and metadata dimensions match the output.
|
|
120
|
+
|
|
121
|
+
## Package size
|
|
122
|
+
|
|
123
|
+
<!-- wheel-size:start -->
|
|
124
|
+
Linux CPU wheels, Python 3.12. Download / unpacked size in MiB.
|
|
125
|
+
|
|
126
|
+
| Package | x86_64 | ARM64 |
|
|
127
|
+
| --- | ---: | ---: |
|
|
128
|
+
| TensorCodec | 10.2 / 24.7 | 10.4 / 22.9 |
|
|
129
|
+
| PyAV | 33.4 / 125.5 | 31.2 / 90.4 |
|
|
130
|
+
| TorchCodec + PyTorch (CPU) | 196.7 / 704.7 | 160.3 / 585.7 |
|
|
131
|
+
<!-- wheel-size:end -->
|
|
132
|
+
|
|
133
|
+
TensorCodec and PyAV bundle FFmpeg; TorchCodec needs it separately.
|
|
134
|
+
Other dependencies are excluded. [Measurements](docs/package_size.md).
|
|
135
|
+
|
|
136
|
+
## Scope and compatibility
|
|
137
|
+
|
|
138
|
+
The supported CPU API is checked for frame selection, ordering, timestamps,
|
|
139
|
+
durations, stream selection and metadata, both against TorchCodec 0.17.0 and
|
|
140
|
+
independently generated media.
|
|
141
|
+
|
|
142
|
+
- Pixel comparisons allow color-conversion rounding of at most 1 uint8 unit or
|
|
143
|
+
1/65535 for float32 in the tested cases.
|
|
144
|
+
- Empty index lists are supported, including the case affected by the reference's
|
|
145
|
+
empty-list dtype inference bug.
|
|
146
|
+
- NumPy output preserves the decoder API structure; callers expecting
|
|
147
|
+
`torch.Tensor` must adapt their array handling.
|
|
148
|
+
|
|
149
|
+
See the [compatibility contract](docs/compatibility.md) and
|
|
150
|
+
[playback rules](docs/playback_semantics.md) for the tested behavior.
|
|
151
|
+
|
|
152
|
+
### Current limits
|
|
153
|
+
|
|
154
|
+
- **Wheels:** Linux x86_64 and ARM64 (aarch64), glibc 2.17+, CPython 3.10+.
|
|
155
|
+
NumPy must also provide a compatible wheel; newer Python versions may require
|
|
156
|
+
a newer glibc. macOS, Windows, musl/Alpine and free-threaded Python wheels are
|
|
157
|
+
not release targets yet.
|
|
158
|
+
- **Exact seeking:** scans packet timestamps when opening the decoder. Incorrect
|
|
159
|
+
container keyframe flags can produce corrupt frames; repaired input or corrected
|
|
160
|
+
frame mappings are needed in that case.
|
|
161
|
+
- **Audio ranges:** decode from the beginning, so late ranges can be expensive.
|
|
162
|
+
- **Video conversion:** no HDR-to-SDR tone mapping or native YUV-plane output.
|
|
163
|
+
Reflected and non-right-angle display matrices are unsupported.
|
|
164
|
+
|
|
165
|
+
See [container behavior](docs/container_robustness.md) for seek limitations and
|
|
166
|
+
[benchmark tools](benchmarks/README.md) for workload measurements.
|
|
167
|
+
|
|
168
|
+
## Development and verification
|
|
169
|
+
|
|
170
|
+
<details>
|
|
171
|
+
<summary>Build from source and run tests</summary>
|
|
172
|
+
|
|
173
|
+
Source builds require Rust, Clang/libclang, pkg-config and FFmpeg 7 development
|
|
174
|
+
headers/libraries. Python handles API and playback selection; Rust + PyO3 handles
|
|
175
|
+
FFmpeg. Native decoding releases the GIL, allowing separate decoder instances to
|
|
176
|
+
run concurrently across Python threads. Calls on the same instance are serialized.
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
uv sync --group dev --group oracle
|
|
180
|
+
|
|
181
|
+
uv run --group oracle pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
|
|
182
|
+
uv run --group oracle pytest --compare
|
|
183
|
+
|
|
184
|
+
# Rebuild after changing Rust code.
|
|
185
|
+
uv run --group oracle maturin develop --locked --uv
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Tests generate media with FFmpeg/ffprobe and Python's `wave` module.
|
|
189
|
+
`--compare` requires the pinned oracle; differential tests otherwise skip.
|
|
190
|
+
|
|
191
|
+
[Playback rules](docs/playback_semantics.md) · [Release guide](docs/releasing.md) · [Dependency licenses](licenses/README.md)
|
|
192
|
+
|
|
193
|
+
</details>
|
|
194
|
+
|
|
195
|
+
TensorCodec's own code is [MIT licensed](LICENSE).
|
|
196
|
+
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# TensorCodec
|
|
4
|
+
|
|
5
|
+
CPU video/audio decoding with TorchCodec-style APIs and NumPy output.
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/MilkClouds/tensorcodec/actions/workflows/ci.yml"><img src="https://github.com/MilkClouds/tensorcodec/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
|
|
9
|
+
<a href="https://pypi.org/project/tensorcodec/"><img src="https://img.shields.io/pypi/v/tensorcodec" alt="PyPI"></a>
|
|
10
|
+
<a href="https://pypi.org/project/tensorcodec/"><img src="https://img.shields.io/badge/Python-3.10%2B-blue" alt="Python"></a>
|
|
11
|
+
<!-- wheel-size-badge:start -->
|
|
12
|
+
<a href="#package-size"><img src="https://img.shields.io/badge/wheel-10.4%20MiB-blue" alt="Wheel download"></a>
|
|
13
|
+
<!-- wheel-size-badge:end -->
|
|
14
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue" alt="License: MIT"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
[Quick start](#quick-start) · [Features](#features) · [Package size](#package-size) · [Compatibility](docs/compatibility.md)
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
- **TorchCodec API without PyTorch.** CPU video/audio decoder interfaces follow
|
|
22
|
+
TorchCodec and return NumPy arrays.
|
|
23
|
+
- **Validated playback semantics.** Frame selection, ordering, timestamps and audio
|
|
24
|
+
ranges are checked against TorchCodec 0.17.0 and independently generated media.
|
|
25
|
+
- **Efficient batch decoding.** Rust/PyO3 bindings to FFmpeg process frame batches
|
|
26
|
+
in a single native call, avoiding per-frame Python calls.
|
|
27
|
+
- **Lightweight installation.** Linux wheels are 10.2–10.4 MiB (v0.1.1), including
|
|
28
|
+
FFmpeg shared libraries. NumPy is the only Python dependency.
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
uv pip install tensorcodec
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use an existing virtual environment, or create one with `uv venv` first.
|
|
37
|
+
No separate FFmpeg installation is needed for the published Linux wheels.
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from tensorcodec.decoders import VideoDecoder, AudioDecoder
|
|
41
|
+
|
|
42
|
+
with VideoDecoder("video.mp4") as video:
|
|
43
|
+
frame = video[0] # RGB array: (C, H, W)
|
|
44
|
+
batch = video.get_frames_at([4, 0, 4]) # requested order, including duplicates
|
|
45
|
+
clip = video.get_frames_played_in_range(0, 1, fps=8)
|
|
46
|
+
|
|
47
|
+
with AudioDecoder("audio.wav", sample_rate=16000, num_channels=1) as audio:
|
|
48
|
+
samples = audio.get_samples_played_in_range(0, 1)
|
|
49
|
+
waveform = samples.data # float32: (channels, samples)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Arrays keep their storage after the decoder closes. Paths, URLs, encoded bytes,
|
|
53
|
+
1-D uint8 arrays and seekable file objects are supported.
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
TensorCodec 0.1.2 relative to TorchCodec 0.17.0.
|
|
58
|
+
✓ supported · △ partial support · — not implemented.
|
|
59
|
+
|
|
60
|
+
| Component | TensorCodec | TorchCodec 0.17.0 |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| Video decoder | △ CPU, SDR/HDR RGB | ✓ CPU / CUDA |
|
|
63
|
+
| Audio decoder | ✓ CPU | ✓ CPU |
|
|
64
|
+
| Image decoders | — | ✓ |
|
|
65
|
+
| Video / audio / image encoders | — | ✓ |
|
|
66
|
+
| Clip samplers | — | ✓ |
|
|
67
|
+
| Decoder transforms | — | ✓ |
|
|
68
|
+
|
|
69
|
+
FPS-based frame queries are supported; clip samplers are a separate API.
|
|
70
|
+
|
|
71
|
+
### Decoder compatibility
|
|
72
|
+
|
|
73
|
+
| Capability | TensorCodec | TorchCodec 0.17.0 |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| Index / slice / batch selection | ✓ | ✓ |
|
|
76
|
+
| Playback timestamp / range queries | ✓ | ✓ |
|
|
77
|
+
| Request order and duplicate frames | Preserved | Preserved |
|
|
78
|
+
| Exact / approximate seeking | ✓ Default: exact | ✓ |
|
|
79
|
+
| FPS queries / custom frame mappings | ✓ | ✓ |
|
|
80
|
+
| CFR / VFR / offset PTS / B-frames | ✓ Tested | ✓ |
|
|
81
|
+
| NCHW / NHWC RGB output | ✓ | ✓ |
|
|
82
|
+
| uint8 / float32 / automatic dtype | ✓ SDR and high-bit-depth video | ✓ |
|
|
83
|
+
| uint16 RGB output | ✓ Full-range RGB48 | — |
|
|
84
|
+
| PQ / HLG decoding | ✓ Transfer-encoded RGB | ✓ |
|
|
85
|
+
| Right-angle display rotation | ✓ | ✓ |
|
|
86
|
+
| Audio ranges / resampling / channel mixing | ✓ float32 | ✓ |
|
|
87
|
+
| Paths / URLs / bytes / seekable file objects | ✓ | ✓ |
|
|
88
|
+
| Encoded array input | 1-D uint8 NumPy array | PyTorch tensor |
|
|
89
|
+
| Decoded output | NumPy array; array interface / DLPack | PyTorch tensor |
|
|
90
|
+
| CUDA decoding | — | ✓ |
|
|
91
|
+
|
|
92
|
+
For high-bit-depth video, use `VideoDecoder(path, output_dtype="auto")` to select
|
|
93
|
+
float32 above 8 bits, or `output_dtype="uint16"` for full-range 16-bit RGB.
|
|
94
|
+
HDR output retains PQ/HLG encoding without SDR tone mapping. Rotation is applied
|
|
95
|
+
automatically, and metadata dimensions match the output.
|
|
96
|
+
|
|
97
|
+
## Package size
|
|
98
|
+
|
|
99
|
+
<!-- wheel-size:start -->
|
|
100
|
+
Linux CPU wheels, Python 3.12. Download / unpacked size in MiB.
|
|
101
|
+
|
|
102
|
+
| Package | x86_64 | ARM64 |
|
|
103
|
+
| --- | ---: | ---: |
|
|
104
|
+
| TensorCodec | 10.2 / 24.7 | 10.4 / 22.9 |
|
|
105
|
+
| PyAV | 33.4 / 125.5 | 31.2 / 90.4 |
|
|
106
|
+
| TorchCodec + PyTorch (CPU) | 196.7 / 704.7 | 160.3 / 585.7 |
|
|
107
|
+
<!-- wheel-size:end -->
|
|
108
|
+
|
|
109
|
+
TensorCodec and PyAV bundle FFmpeg; TorchCodec needs it separately.
|
|
110
|
+
Other dependencies are excluded. [Measurements](docs/package_size.md).
|
|
111
|
+
|
|
112
|
+
## Scope and compatibility
|
|
113
|
+
|
|
114
|
+
The supported CPU API is checked for frame selection, ordering, timestamps,
|
|
115
|
+
durations, stream selection and metadata, both against TorchCodec 0.17.0 and
|
|
116
|
+
independently generated media.
|
|
117
|
+
|
|
118
|
+
- Pixel comparisons allow color-conversion rounding of at most 1 uint8 unit or
|
|
119
|
+
1/65535 for float32 in the tested cases.
|
|
120
|
+
- Empty index lists are supported, including the case affected by the reference's
|
|
121
|
+
empty-list dtype inference bug.
|
|
122
|
+
- NumPy output preserves the decoder API structure; callers expecting
|
|
123
|
+
`torch.Tensor` must adapt their array handling.
|
|
124
|
+
|
|
125
|
+
See the [compatibility contract](docs/compatibility.md) and
|
|
126
|
+
[playback rules](docs/playback_semantics.md) for the tested behavior.
|
|
127
|
+
|
|
128
|
+
### Current limits
|
|
129
|
+
|
|
130
|
+
- **Wheels:** Linux x86_64 and ARM64 (aarch64), glibc 2.17+, CPython 3.10+.
|
|
131
|
+
NumPy must also provide a compatible wheel; newer Python versions may require
|
|
132
|
+
a newer glibc. macOS, Windows, musl/Alpine and free-threaded Python wheels are
|
|
133
|
+
not release targets yet.
|
|
134
|
+
- **Exact seeking:** scans packet timestamps when opening the decoder. Incorrect
|
|
135
|
+
container keyframe flags can produce corrupt frames; repaired input or corrected
|
|
136
|
+
frame mappings are needed in that case.
|
|
137
|
+
- **Audio ranges:** decode from the beginning, so late ranges can be expensive.
|
|
138
|
+
- **Video conversion:** no HDR-to-SDR tone mapping or native YUV-plane output.
|
|
139
|
+
Reflected and non-right-angle display matrices are unsupported.
|
|
140
|
+
|
|
141
|
+
See [container behavior](docs/container_robustness.md) for seek limitations and
|
|
142
|
+
[benchmark tools](benchmarks/README.md) for workload measurements.
|
|
143
|
+
|
|
144
|
+
## Development and verification
|
|
145
|
+
|
|
146
|
+
<details>
|
|
147
|
+
<summary>Build from source and run tests</summary>
|
|
148
|
+
|
|
149
|
+
Source builds require Rust, Clang/libclang, pkg-config and FFmpeg 7 development
|
|
150
|
+
headers/libraries. Python handles API and playback selection; Rust + PyO3 handles
|
|
151
|
+
FFmpeg. Native decoding releases the GIL, allowing separate decoder instances to
|
|
152
|
+
run concurrently across Python threads. Calls on the same instance are serialized.
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
uv sync --group dev --group oracle
|
|
156
|
+
|
|
157
|
+
uv run --group oracle pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
|
|
158
|
+
uv run --group oracle pytest --compare
|
|
159
|
+
|
|
160
|
+
# Rebuild after changing Rust code.
|
|
161
|
+
uv run --group oracle maturin develop --locked --uv
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Tests generate media with FFmpeg/ffprobe and Python's `wave` module.
|
|
165
|
+
`--compare` requires the pinned oracle; differential tests otherwise skip.
|
|
166
|
+
|
|
167
|
+
[Playback rules](docs/playback_semantics.md) · [Release guide](docs/releasing.md) · [Dependency licenses](licenses/README.md)
|
|
168
|
+
|
|
169
|
+
</details>
|
|
170
|
+
|
|
171
|
+
TensorCodec's own code is [MIT licensed](LICENSE).
|
|
@@ -14,12 +14,12 @@ FFmpeg; arrays are returned as NumPy instead of torch.Tensor.
|
|
|
14
14
|
Also `get_all_frames`, FPS resampling and custom JSON frame mappings.
|
|
15
15
|
- `tensorcodec.decoders.AudioDecoder`: constructor, metadata, stream_index,
|
|
16
16
|
`get_all_samples`, `get_samples_played_in_range`, resampling and channel mixing.
|
|
17
|
-
- NumPy uint8 or float32 video and float32 audio; float64 batch timestamps/durations.
|
|
17
|
+
- NumPy uint8, uint16 or float32 video and float32 audio; float64 batch timestamps/durations.
|
|
18
18
|
Float32 RGB uses 16-bit color conversion rather than scaling uint8 output.
|
|
19
19
|
- NCHW/NHWC, paths/URLs, encoded bytes, uint8 arrays and seekable file-like input.
|
|
20
20
|
- Exact and approximate video seeking; exact is the default.
|
|
21
21
|
|
|
22
|
-
CUDA, torch inputs, torchvision transforms, HDR tone mapping,
|
|
22
|
+
CUDA, torch inputs, torchvision transforms, HDR tone mapping, arbitrary-angle rotation,
|
|
23
23
|
encoders and samplers are outside the
|
|
24
24
|
initial CPU decoding contract. Unsupported device/transform options fail explicitly.
|
|
25
25
|
Do not advertise full-package or torch.Tensor type compatibility.
|
|
@@ -39,13 +39,12 @@ exception classes follow the reference, including its restrictions on slice step
|
|
|
39
39
|
|
|
40
40
|
Fixtures are generated with FFmpeg from known grayscale frame identities and
|
|
41
41
|
explicit timestamp schedules, and with Python's wave module from known PCM.
|
|
42
|
-
ffprobe validates encoded packet metadata independently.
|
|
43
|
-
|
|
44
|
-
reference before adding implementation:
|
|
42
|
+
ffprobe validates encoded packet metadata independently. Run the same contract
|
|
43
|
+
tests against the reference before adding implementation:
|
|
45
44
|
|
|
46
45
|
```sh
|
|
47
|
-
pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
|
|
48
|
-
pytest --compare
|
|
46
|
+
uv run --group oracle pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
|
|
47
|
+
uv run --group oracle pytest --compare
|
|
49
48
|
```
|
|
50
49
|
|
|
51
50
|
Comparisons check timing separately from pixels. One uint8 RGB unit or 1/65535
|
|
@@ -56,3 +55,14 @@ when `--compare` is requested. Production installation does not require torch.
|
|
|
56
55
|
Intentional fix: TensorCodec accepts empty index lists. TorchCodec 0.17.0 infers
|
|
57
56
|
float for empty index lists; oracle tests use explicitly typed input tensors to
|
|
58
57
|
isolate playback semantics from that conversion bug.
|
|
58
|
+
|
|
59
|
+
## Video fidelity extensions
|
|
60
|
+
|
|
61
|
+
PQ/HLG inputs decode as transfer-encoded RGB without tone mapping. `auto` uses
|
|
62
|
+
float32 for source component depths above 8 bits, including high-depth SDR.
|
|
63
|
+
Explicit uint16 returns full-range RGB48 and is a TensorCodec extension; it is
|
|
64
|
+
not native YUV output. Source `bit_depth` and `color_range` metadata are also
|
|
65
|
+
TensorCodec extensions. Right-angle display rotations are applied automatically;
|
|
66
|
+
metadata dimensions describe the rotated output. Reflected and non-right-angle
|
|
67
|
+
display matrices remain unsupported. Color metadata and pixel aspect ratio
|
|
68
|
+
describe the source; HDR output is not linear light or sRGB.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Package size policy
|
|
2
|
+
|
|
3
|
+
TensorCodec keeps its NumPy-only Python dependency set and bundles a minimal
|
|
4
|
+
FFmpeg/OpenSSL runtime in its default Linux wheels. Size limits prevent additions
|
|
5
|
+
from silently increasing the distributed binary footprint.
|
|
6
|
+
|
|
7
|
+
## What is measured
|
|
8
|
+
|
|
9
|
+
| Metric | Definition | Per-wheel limit |
|
|
10
|
+
| --- | --- | ---: |
|
|
11
|
+
| Download | Final `.whl` file size, in bytes | 15 MiB |
|
|
12
|
+
| Unpacked | Sum of ZIP entry file sizes, including bundled libraries | 35 MiB |
|
|
13
|
+
|
|
14
|
+
The sole policy file is [`packaging/size-policy.json`](../packaging/size-policy.json).
|
|
15
|
+
The checker uses only Python's standard library and never extracts the archive.
|
|
16
|
+
Unpacked size excludes filesystem allocation overhead. Both metrics exclude NumPy,
|
|
17
|
+
Python, package caches and other external dependencies; they are not total
|
|
18
|
+
installation sizes. Reports and the README use MiB (2^20 bytes).
|
|
19
|
+
|
|
20
|
+
Check final, repaired wheels locally:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
uv run --no-project python scripts/check_wheel_size.py dist/*.whl --output reports/wheel-size.json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Each architecture is checked independently. Exactly reaching a limit passes;
|
|
27
|
+
exceeding either limit fails. The release workflow runs this after `auditwheel
|
|
28
|
+
repair`, before uploading distributions. JSON reports are separate artifacts, not
|
|
29
|
+
files in `dist/`. Actions summaries include changes from the committed published
|
|
30
|
+
baseline. Ordinary CI tests the checker without building FFmpeg from source.
|
|
31
|
+
|
|
32
|
+
Before changing a limit, explain the feature, the measured byte increase on both
|
|
33
|
+
architectures and why a smaller configuration would not provide the same behavior.
|
|
34
|
+
A limit change should be reviewed with the change that needs it.
|
|
35
|
+
|
|
36
|
+
## Decoder comparison and badge
|
|
37
|
+
|
|
38
|
+
[`packaging/size-baseline.json`](../packaging/size-baseline.json) records wheel URLs,
|
|
39
|
+
SHA-256 hashes, versions and sizes for TensorCodec, PyAV, TorchCodec and
|
|
40
|
+
PyTorch CPU. Artifacts come from PyPI except PyTorch, which uses the official
|
|
41
|
+
CPU index. All wheels support CPython 3.12 on Linux x86_64 or ARM64.
|
|
42
|
+
|
|
43
|
+
The README sums TorchCodec and PyTorch wheels. TensorCodec and PyAV include FFmpeg;
|
|
44
|
+
TorchCodec requires it separately. NumPy and other external dependencies are
|
|
45
|
+
excluded. These are package footprints, not complete environment sizes.
|
|
46
|
+
|
|
47
|
+
After publication, the update script verifies hashes, checks TensorCodec's limits,
|
|
48
|
+
and refreshes the table and badge. The badge shows the largest TensorCodec wheel
|
|
49
|
+
download; candidate builds never update it.
|
|
50
|
+
|
|
51
|
+
To reproduce or recover a documentation update after publication:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
uv run --no-project python scripts/update_size_comparison.py --version 0.1.2
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Review and commit `README.md` and `packaging/size-baseline.json` together. The script
|
|
58
|
+
requires both architectures to have been published and fails on ambiguous wheels,
|
|
59
|
+
yanked wheels, missing files or hash mismatches. If publication succeeded but the
|
|
60
|
+
documentation job failed, repair the documentation separately; do not republish
|
|
61
|
+
the same version. A concurrent change to `main` can reject the documentation push;
|
|
62
|
+
the workflow never force-pushes.
|
|
63
|
+
|
|
64
|
+
## Why FFmpeg stays bundled by default
|
|
65
|
+
|
|
66
|
+
Bundling the selected FFmpeg libraries provides one-step installation and fixes the
|
|
67
|
+
runtime ABI and codec configuration used by the release tests. A full conda-forge
|
|
68
|
+
FFmpeg environment can include many additional codec, graphics and system packages;
|
|
69
|
+
moving these outside the wheel does not necessarily reduce total installation size.
|
|
70
|
+
|
|
71
|
+
Reusing an existing shared FFmpeg 7 installation is an advanced source-build option:
|
|
72
|
+
[system FFmpeg guide](system_ffmpeg.md). FFmpeg CLI availability alone does not
|
|
73
|
+
satisfy the native library requirement.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The supported behavior is specified in [compatibility.md](compatibility.md) and
|
|
4
4
|
executable tests in `tests/test_video_contract.py` and `tests/test_audio_contract.py`.
|
|
5
|
-
The reference is TorchCodec 0.17.0.
|
|
5
|
+
The reference is TorchCodec 0.17.0.
|
|
6
6
|
|
|
7
7
|
Exact video seeking scans presentation timestamps and preceding key-frame PTS.
|
|
8
8
|
Index requests use that map; time requests select the frame playing at the requested
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Publishing TensorCodec
|
|
2
|
+
|
|
3
|
+
Release version: `0.1.2`. Distribution and import name: `tensorcodec`.
|
|
4
|
+
Binary wheels target Linux x86_64 and ARM64 (aarch64), glibc 2.17+, CPython 3.10+.
|
|
5
|
+
NumPy must also provide a compatible wheel for the selected Python/glibc pair.
|
|
6
|
+
The wheel bundles shared FFmpeg 7.1.5 and OpenSSL 3.5.9 LTS; its only Python
|
|
7
|
+
runtime dependency is NumPy. macOS/Windows wheels are not yet provided.
|
|
8
|
+
|
|
9
|
+
## Trusted publisher configuration
|
|
10
|
+
|
|
11
|
+
The PyPI project is already registered. Its GitHub Trusted Publisher uses:
|
|
12
|
+
|
|
13
|
+
| Field | Value |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| PyPI project name | `tensorcodec` |
|
|
16
|
+
| GitHub owner | `MilkClouds` |
|
|
17
|
+
| Repository | `tensorcodec` |
|
|
18
|
+
| Workflow filename | `publish.yml` |
|
|
19
|
+
| Environment | `pypi` |
|
|
20
|
+
|
|
21
|
+
Manage this configuration in the project's PyPI Publishing settings when moving
|
|
22
|
+
or renaming the repository or workflow. Publishing uses GitHub OIDC; no API token
|
|
23
|
+
is needed. Repository visibility does not need to change for a release.
|
|
24
|
+
|
|
25
|
+
## Release
|
|
26
|
+
|
|
27
|
+
Run the **Publish to PyPI** workflow on `main`. It builds the portable Linux wheels
|
|
28
|
+
and source distribution, checks package metadata, validates the pinned oracle
|
|
29
|
+
and compares playback before uploading through PyPI Trusted Publishing. It uses
|
|
30
|
+
the existing GitHub `pypi` environment. Publication fails if authorization is
|
|
31
|
+
missing, tests fail, or the version has already been uploaded.
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
gh workflow run publish.yml --repo MilkClouds/tensorcodec --ref main
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For a build and full validation without uploading, pass `--field publish=false`.
|
|
38
|
+
|
|
39
|
+
Check the workflow and https://pypi.org/project/tensorcodec/0.1.2/ before reporting
|
|
40
|
+
success. Verify a fresh `uv pip install tensorcodec==0.1.2` and a decode without
|
|
41
|
+
Torch/PyAV on both architectures. Update the version before subsequent releases;
|
|
42
|
+
PyPI versions cannot be overwritten.
|
|
43
|
+
|
|
44
|
+
The local Linux build is reproducible using `scripts/build_linux_wheel.sh` inside
|
|
45
|
+
`quay.io/pypa/manylinux2014_x86_64` or
|
|
46
|
+
`quay.io/pypa/manylinux2014_aarch64` with Rust, maturin, libclang, NASM and Perl.
|
|
47
|
+
Both native source archives are version- and checksum-pinned. Their licensing
|
|
48
|
+
and source links are recorded in `licenses/README.md`.
|
|
49
|
+
|
|
50
|
+
## CI versus release builds
|
|
51
|
+
|
|
52
|
+
- Ordinary CI uses prebuilt conda-forge FFmpeg 7.1.1 through Pixi, including its
|
|
53
|
+
headers and shared libraries. It builds only the TensorCodec extension.
|
|
54
|
+
- PyPI wheels use the smaller LGPL FFmpeg 7.1.5 build plus OpenSSL 3.5.9.
|
|
55
|
+
Their native prefix is cached by architecture, glibc baseline and build-script
|
|
56
|
+
checksums. This preserves the wheel's codec set, dependency size and licensing rather than bundling the full
|
|
57
|
+
conda-forge dependency graph.
|
|
58
|
+
- Release validation installs each repaired wheel on glibc 2.17 with Python 3.10
|
|
59
|
+
and 3.13 and decodes video/audio without Torch, PyAV or a system FFmpeg. Python
|
|
60
|
+
3.10 also checks the minimum NumPy line (1.26.4). Native
|
|
61
|
+
x86_64 and ARM64 runners also run the full pinned playback oracle comparison.
|
|
62
|
+
- Release validation still tests the installed repaired wheel. The fixture CLI
|
|
63
|
+
can be FFmpeg 6 or 7; fixtures explicitly remove auxiliary sentinel packets.
|
|
64
|
+
|
|
65
|
+
## Size checks and published comparison
|
|
66
|
+
|
|
67
|
+
Final repaired wheels must stay within 15 MiB download and 35 MiB unpacked per
|
|
68
|
+
architecture. The build job checks `packaging/size-policy.json` and uploads a
|
|
69
|
+
separate `wheel-size-*` report, including differences from the last published
|
|
70
|
+
baseline. Size reports must not be placed in `dist/`.
|
|
71
|
+
|
|
72
|
+
After a successful publication, the `update-size-docs` job measures hash-verified
|
|
73
|
+
PyPI wheels for the release and pinned TorchCodec 0.17.0. It commits the published
|
|
74
|
+
snapshot and README table/badge with a normal push to `main`. Only this documentation
|
|
75
|
+
job has `contents: write`; the PyPI publisher retains OIDC plus read access. The
|
|
76
|
+
repository must permit the Actions bot to push these documentation updates.
|
|
77
|
+
|
|
78
|
+
If the documentation job fails after a successful publication, recover by running
|
|
79
|
+
`scripts/update_size_comparison.py --version <published-version>` and committing
|
|
80
|
+
its two outputs. Never retry publication of an already uploaded version. See the
|
|
81
|
+
[package size policy](package_size.md) for measurement definitions and the
|
|
82
|
+
[external FFmpeg guide](system_ffmpeg.md) for the optional source-build path.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Reusing an existing FFmpeg installation
|
|
2
|
+
|
|
3
|
+
## Default install
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
uv venv
|
|
7
|
+
uv pip install tensorcodec
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Supported Linux wheels include minimal shared FFmpeg 7.1.5 and OpenSSL libraries.
|
|
11
|
+
No FFmpeg CLI, Pixi, Rust or libclang is required at runtime. This is the recommended
|
|
12
|
+
installation for a new environment.
|
|
13
|
+
|
|
14
|
+
## Source build with shared FFmpeg 7
|
|
15
|
+
|
|
16
|
+
Use this path when a server or container already provides compatible shared
|
|
17
|
+
FFmpeg libraries, or when you intentionally want the codec configuration of that
|
|
18
|
+
installation. The result depends on that external installation rather than the
|
|
19
|
+
bundled release libraries.
|
|
20
|
+
|
|
21
|
+
Requirements: supported Python, Rust/Cargo, a C toolchain, Clang/libclang,
|
|
22
|
+
`pkg-config`, and FFmpeg 7 headers and shared libraries. An executable-only or
|
|
23
|
+
static-only FFmpeg installation is insufficient. FFmpeg 8/9 is not a supported
|
|
24
|
+
replacement for the current native boundary. Do not point a repaired PyPI wheel
|
|
25
|
+
at another FFmpeg installation by removing its bundled libraries.
|
|
26
|
+
|
|
27
|
+
For a Linux Pixi example, the prebuilt FFmpeg configuration used by ordinary CI is:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pixi global install --environment tensorcodec-ffmpeg 'ffmpeg=7.1.1=gpl_*'
|
|
31
|
+
|
|
32
|
+
# Pixi's default global environment location; adjust if PIXI_HOME is configured.
|
|
33
|
+
export FFMPEG_DIR="${PIXI_HOME:-$HOME/.pixi}/envs/tensorcodec-ffmpeg"
|
|
34
|
+
export LD_LIBRARY_PATH="$FFMPEG_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
35
|
+
|
|
36
|
+
test -f "$FFMPEG_DIR/include/libavcodec/avcodec.h"
|
|
37
|
+
test -f "$FFMPEG_DIR/lib/libavcodec.so.61"
|
|
38
|
+
|
|
39
|
+
uv venv
|
|
40
|
+
uv pip install --no-binary tensorcodec 'tensorcodec==0.1.2'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The version/build constraint avoids silently selecting an incompatible FFmpeg
|
|
44
|
+
major. `FFMPEG_DIR` tells the source build where to find headers and libraries;
|
|
45
|
+
`LD_LIBRARY_PATH` tells the Linux loader where to find the libraries at runtime.
|
|
46
|
+
Keep the latter in your application/container environment. Adding the FFmpeg
|
|
47
|
+
executable to `PATH` is not enough. If libclang is outside the loader's search
|
|
48
|
+
paths, also set `LIBCLANG_PATH` to its library directory.
|
|
49
|
+
|
|
50
|
+
The example uses a GPL-enabled conda-forge build, unlike the minimal LGPL release
|
|
51
|
+
build. Its additional codecs, dependencies and licensing apply to your environment.
|
|
52
|
+
Review [`licenses/README.md`](../licenses/README.md) before redistributing a binary
|
|
53
|
+
built against a different FFmpeg configuration.
|
|
54
|
+
|
|
55
|
+
For development against the same prefix:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
uv sync --group dev
|
|
59
|
+
uv run maturin develop --locked --uv
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Do not reuse an existing `dist/` wheel while verifying this path: it may contain the
|
|
63
|
+
bundled release libraries. To verify library loading for a source-built install:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
uv run --no-sync python - <<'PY'
|
|
67
|
+
from tensorcodec.decoders import VideoDecoder
|
|
68
|
+
with VideoDecoder("video.mp4") as decoder:
|
|
69
|
+
print(decoder.get_frame_at(0).data.shape)
|
|
70
|
+
PY
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The native extension build and playback contracts run against prebuilt FFmpeg
|
|
74
|
+
7.1.1 in ordinary CI. The published-wheel tests separately verify decoding with
|
|
75
|
+
no external FFmpeg library installation. Configuration-specific outputs can differ
|
|
76
|
+
within the color-conversion tolerances described in the
|
|
77
|
+
[compatibility contract](compatibility.md).
|