tensorcodec 0.1.1__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.
Files changed (48) hide show
  1. tensorcodec-0.1.2/PKG-INFO +196 -0
  2. tensorcodec-0.1.2/README.md +171 -0
  3. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/docs/compatibility.md +17 -7
  4. tensorcodec-0.1.2/docs/package_size.md +73 -0
  5. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/docs/playback_semantics.md +1 -1
  6. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/docs/releasing.md +34 -18
  7. tensorcodec-0.1.2/docs/system_ffmpeg.md +77 -0
  8. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/native/Cargo.lock +1 -1
  9. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/native/Cargo.toml +1 -1
  10. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/native/src/ffmpeg.rs +55 -17
  11. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/native/src/lib.rs +13 -2
  12. tensorcodec-0.1.2/packaging/size-baseline.json +82 -0
  13. tensorcodec-0.1.2/packaging/size-policy.json +23 -0
  14. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/pyproject.toml +4 -1
  15. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/scripts/build_linux_wheel.sh +1 -0
  16. tensorcodec-0.1.2/scripts/check_wheel_size.py +84 -0
  17. tensorcodec-0.1.2/scripts/update_size_comparison.py +143 -0
  18. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/src/tensorcodec/__init__.py +1 -1
  19. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/src/tensorcodec/_metadata.py +2 -0
  20. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/src/tensorcodec/decoders/_decoder.py +18 -9
  21. tensorcodec-0.1.2/tests/__init__.py +1 -0
  22. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/tests/conftest.py +83 -31
  23. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/tests/test_audio_contract.py +1 -1
  24. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/tests/test_differential.py +28 -1
  25. tensorcodec-0.1.2/tests/test_size_comparison.py +131 -0
  26. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/tests/test_video_contract.py +1 -1
  27. tensorcodec-0.1.2/tests/test_video_fidelity.py +82 -0
  28. tensorcodec-0.1.2/tests/test_wheel_size.py +101 -0
  29. tensorcodec-0.1.2/tests/utils.py +70 -0
  30. tensorcodec-0.1.1/PKG-INFO +0 -167
  31. tensorcodec-0.1.1/README.md +0 -142
  32. tensorcodec-0.1.1/tests/__init__.py +0 -1
  33. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/LICENSE +0 -0
  34. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/licenses/FFmpeg-GPL-3.0.txt +0 -0
  35. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/licenses/FFmpeg-LGPL-3.0.txt +0 -0
  36. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/licenses/FFmpeg-NOTICE.md +0 -0
  37. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/licenses/OpenSSL.txt +0 -0
  38. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/licenses/README.md +0 -0
  39. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/licenses/Zstandard.txt +0 -0
  40. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/scripts/build_ffmpeg.sh +0 -0
  41. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/scripts/build_nasm.sh +0 -0
  42. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/scripts/build_openssl.sh +0 -0
  43. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/scripts/check_wheel_runtime.py +0 -0
  44. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/scripts/configure_oracle_ffmpeg.py +0 -0
  45. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/src/tensorcodec/_frame.py +0 -0
  46. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/src/tensorcodec/decoders/__init__.py +0 -0
  47. {tensorcodec-0.1.1 → tensorcodec-0.1.2}/src/tensorcodec/py.typed +0 -0
  48. {tensorcodec-0.1.1 → 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, rotated video,
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. The original avdec
43
- decoder and tests are not executed. Run the same contract tests against the
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. These replace the previous avdec/PyAV design.
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
@@ -1,29 +1,26 @@
1
1
  # Publishing TensorCodec
2
2
 
3
- Release version: `0.1.1`. Distribution and import name: `tensorcodec`.
3
+ Release version: `0.1.2`. Distribution and import name: `tensorcodec`.
4
4
  Binary wheels target Linux x86_64 and ARM64 (aarch64), glibc 2.17+, CPython 3.10+.
5
5
  NumPy must also provide a compatible wheel for the selected Python/glibc pair.
6
6
  The wheel bundles shared FFmpeg 7.1.5 and OpenSSL 3.5.9 LTS; its only Python
7
7
  runtime dependency is NumPy. macOS/Windows wheels are not yet provided.
8
8
 
9
- ## One-time account setup
9
+ ## Trusted publisher configuration
10
10
 
11
- 1. Rename `MilkClouds/avdec` to `tensorcodec` in GitHub repository Settings.
12
- Preserve the current visibility; publishing does not require making it public.
13
- 2. Open https://pypi.org/manage/account/publishing/ and add a **pending GitHub
14
- publisher** for a new project with these values:
11
+ The PyPI project is already registered. Its GitHub Trusted Publisher uses:
15
12
 
16
- | Field | Value |
17
- | --- | --- |
18
- | PyPI project name | `tensorcodec` |
19
- | GitHub owner | `MilkClouds` |
20
- | Repository | `tensorcodec` |
21
- | Workflow filename | `publish.yml` |
22
- | Environment | `pypi` |
13
+ | Field | Value |
14
+ | --- | --- |
15
+ | PyPI project name | `tensorcodec` |
16
+ | GitHub owner | `MilkClouds` |
17
+ | Repository | `tensorcodec` |
18
+ | Workflow filename | `publish.yml` |
19
+ | Environment | `pypi` |
23
20
 
24
- A pending publisher creates the project on its first successful upload. If the
25
- project already exists, register the publisher in that project's Publishing
26
- settings instead. Do not put an API token in the repository or chat.
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.
27
24
 
28
25
  ## Release
29
26
 
@@ -39,8 +36,8 @@ gh workflow run publish.yml --repo MilkClouds/tensorcodec --ref main
39
36
 
40
37
  For a build and full validation without uploading, pass `--field publish=false`.
41
38
 
42
- Check the workflow and https://pypi.org/project/tensorcodec/0.1.1/ before reporting
43
- success. Verify a fresh `uv pip install tensorcodec==0.1.1` and a decode without
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
44
41
  Torch/PyAV on both architectures. Update the version before subsequent releases;
45
42
  PyPI versions cannot be overwritten.
46
43
 
@@ -64,3 +61,22 @@ and source links are recorded in `licenses/README.md`.
64
61
  x86_64 and ARM64 runners also run the full pinned playback oracle comparison.
65
62
  - Release validation still tests the installed repaired wheel. The fixture CLI
66
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).
@@ -455,7 +455,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
455
455
 
456
456
  [[package]]
457
457
  name = "tensorcodec-native"
458
- version = "0.1.1"
458
+ version = "0.1.2"
459
459
  dependencies = [
460
460
  "ffmpeg-sys-next",
461
461
  "numpy",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "tensorcodec-native"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  edition = "2021"
5
5
  license = "MIT"
6
6