tensorcodec 0.1.1__tar.gz → 0.1.3__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 (51) hide show
  1. tensorcodec-0.1.3/PKG-INFO +205 -0
  2. tensorcodec-0.1.3/README.md +179 -0
  3. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/docs/compatibility.md +32 -7
  4. tensorcodec-0.1.3/docs/package_size.md +73 -0
  5. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/docs/playback_semantics.md +1 -1
  6. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/docs/releasing.md +37 -20
  7. tensorcodec-0.1.3/docs/system_ffmpeg.md +85 -0
  8. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/licenses/README.md +4 -2
  9. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/native/Cargo.lock +2 -1
  10. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/native/Cargo.toml +2 -1
  11. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/native/src/ffmpeg.rs +209 -115
  12. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/native/src/lib.rs +26 -5
  13. tensorcodec-0.1.3/packaging/size-baseline.json +82 -0
  14. tensorcodec-0.1.3/packaging/size-policy.json +23 -0
  15. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/pyproject.toml +5 -1
  16. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/scripts/build_ffmpeg.sh +1 -1
  17. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/scripts/build_linux_wheel.sh +1 -0
  18. tensorcodec-0.1.3/scripts/build_macos_wheel.sh +16 -0
  19. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/scripts/check_wheel_runtime.py +38 -0
  20. tensorcodec-0.1.3/scripts/check_wheel_size.py +84 -0
  21. tensorcodec-0.1.3/scripts/update_size_comparison.py +143 -0
  22. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/src/tensorcodec/__init__.py +1 -1
  23. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/src/tensorcodec/_frame.py +5 -1
  24. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/src/tensorcodec/_metadata.py +2 -0
  25. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/src/tensorcodec/decoders/_decoder.py +59 -15
  26. tensorcodec-0.1.3/tests/__init__.py +1 -0
  27. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/tests/conftest.py +83 -31
  28. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/tests/test_audio_contract.py +1 -1
  29. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/tests/test_differential.py +28 -1
  30. tensorcodec-0.1.3/tests/test_native_output.py +296 -0
  31. tensorcodec-0.1.3/tests/test_open_cost.py +93 -0
  32. tensorcodec-0.1.3/tests/test_size_comparison.py +131 -0
  33. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/tests/test_video_contract.py +1 -1
  34. tensorcodec-0.1.3/tests/test_video_fidelity.py +82 -0
  35. tensorcodec-0.1.3/tests/test_wheel_size.py +101 -0
  36. tensorcodec-0.1.3/tests/utils.py +70 -0
  37. tensorcodec-0.1.1/PKG-INFO +0 -167
  38. tensorcodec-0.1.1/README.md +0 -142
  39. tensorcodec-0.1.1/tests/__init__.py +0 -1
  40. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/LICENSE +0 -0
  41. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/licenses/FFmpeg-GPL-3.0.txt +0 -0
  42. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/licenses/FFmpeg-LGPL-3.0.txt +0 -0
  43. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/licenses/FFmpeg-NOTICE.md +0 -0
  44. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/licenses/OpenSSL.txt +0 -0
  45. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/licenses/Zstandard.txt +0 -0
  46. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/scripts/build_nasm.sh +0 -0
  47. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/scripts/build_openssl.sh +0 -0
  48. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/scripts/configure_oracle_ffmpeg.py +0 -0
  49. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/src/tensorcodec/decoders/__init__.py +0 -0
  50. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/src/tensorcodec/py.typed +0 -0
  51. {tensorcodec-0.1.1 → tensorcodec-0.1.3}/tests/test_runtime.py +0 -0
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: tensorcodec
3
+ Version: 0.1.3
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Operating System :: POSIX :: Linux
6
+ Classifier: Operating System :: MacOS :: MacOS X
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Rust
9
+ Classifier: Topic :: Multimedia :: Video
10
+ Requires-Dist: numpy>=1.26
11
+ License-File: LICENSE
12
+ License-File: licenses/FFmpeg-GPL-3.0.txt
13
+ License-File: licenses/FFmpeg-LGPL-3.0.txt
14
+ License-File: licenses/FFmpeg-NOTICE.md
15
+ License-File: licenses/OpenSSL.txt
16
+ License-File: licenses/README.md
17
+ License-File: licenses/Zstandard.txt
18
+ Summary: NumPy audio/video decoding with TorchCodec-compatible playback semantics
19
+ Author-email: Suhwan Choi <milkclouds00@gmail.com>
20
+ License-Expression: MIT
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
23
+ Project-URL: Issues, https://github.com/MilkClouds/tensorcodec/issues
24
+ Project-URL: Repository, https://github.com/MilkClouds/tensorcodec
25
+
26
+ <div align="center">
27
+
28
+ # TensorCodec
29
+
30
+ CPU video/audio decoding with TorchCodec-style APIs and NumPy output.
31
+
32
+ <p align="center">
33
+ <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>
34
+ <a href="https://pypi.org/project/tensorcodec/"><img src="https://img.shields.io/pypi/v/tensorcodec" alt="PyPI"></a>
35
+ <a href="https://pypi.org/project/tensorcodec/"><img src="https://img.shields.io/badge/Python-3.10%2B-blue" alt="Python"></a>
36
+ <!-- wheel-size-badge:start -->
37
+ <a href="#package-size"><img src="https://img.shields.io/badge/wheel-10.4%20MiB-blue" alt="Wheel download"></a>
38
+ <!-- wheel-size-badge:end -->
39
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue" alt="License: MIT"></a>
40
+ </p>
41
+
42
+ [Quick start](#quick-start) · [Features](#features) · [Package size](#package-size) · [Compatibility](docs/compatibility.md)
43
+
44
+ </div>
45
+
46
+ - **TorchCodec API without PyTorch.** CPU video/audio decoder interfaces follow
47
+ TorchCodec and return NumPy arrays.
48
+ - **Validated playback semantics.** Frame selection, ordering, timestamps and audio
49
+ ranges are checked against TorchCodec 0.17.0 and independently generated media.
50
+ - **Efficient batch decoding.** Rust/PyO3 bindings to FFmpeg process frame batches
51
+ in a single native call, avoiding per-frame Python calls.
52
+ - **Lightweight installation.** Linux wheels are 10.2–10.4 MiB (v0.1.2), including
53
+ FFmpeg shared libraries. NumPy is the only Python dependency.
54
+
55
+ ## Quick start
56
+
57
+ ```sh
58
+ uv pip install tensorcodec
59
+ ```
60
+
61
+ Use an existing virtual environment, or create one with `uv venv` first.
62
+ No separate FFmpeg installation is needed for the published Linux wheels.
63
+
64
+ ```python
65
+ from tensorcodec.decoders import VideoDecoder, AudioDecoder
66
+
67
+ with VideoDecoder("video.mp4") as video:
68
+ frame = video[0] # RGB array: (C, H, W)
69
+ batch = video.get_frames_at([4, 0, 4]) # requested order, including duplicates
70
+ clip = video.get_frames_played_in_range(0, 1, fps=8)
71
+
72
+ with AudioDecoder("audio.wav", sample_rate=16000, num_channels=1) as audio:
73
+ samples = audio.get_samples_played_in_range(0, 1)
74
+ waveform = samples.data # float32: (channels, samples)
75
+ ```
76
+
77
+ Arrays keep their storage after the decoder closes. Paths, URLs, encoded bytes,
78
+ 1-D uint8 arrays and seekable file objects are supported.
79
+
80
+ ## Features
81
+
82
+ TensorCodec 0.1.3 relative to TorchCodec 0.17.0.
83
+ ✓ supported · △ partial support · — not implemented.
84
+
85
+ | Component | TensorCodec | TorchCodec 0.17.0 |
86
+ | --- | --- | --- |
87
+ | Video decoder | △ CPU, SDR/HDR RGB | ✓ CPU / CUDA |
88
+ | Audio decoder | ✓ CPU | ✓ CPU |
89
+ | Image decoders | — | ✓ |
90
+ | Video / audio / image encoders | — | ✓ |
91
+ | Clip samplers | — | ✓ |
92
+ | Decoder transforms | — | ✓ |
93
+
94
+ FPS-based frame queries are supported; clip samplers are a separate API.
95
+
96
+ ### Decoder compatibility
97
+
98
+ | Capability | TensorCodec | TorchCodec 0.17.0 |
99
+ | --- | --- | --- |
100
+ | Index / slice / batch selection | ✓ | ✓ |
101
+ | Playback timestamp / range queries | ✓ | ✓ |
102
+ | Request order and duplicate frames | Preserved | Preserved |
103
+ | Exact / approximate seeking | ✓ Default: exact | ✓ |
104
+ | FPS queries / custom frame mappings | ✓ | ✓ |
105
+ | CFR / VFR / offset PTS / B-frames | ✓ Tested | ✓ |
106
+ | NCHW / NHWC RGB output | ✓ | ✓ |
107
+ | uint8 / float32 / automatic dtype | ✓ SDR and high-bit-depth video | ✓ |
108
+ | uint16 RGB output | ✓ Full-range RGB48 | — |
109
+ | Native grayscale/depth and packed RGB(A) | ✓ Values preserved | — |
110
+ | PQ / HLG decoding | ✓ Transfer-encoded RGB | ✓ |
111
+ | Right-angle display rotation | ✓ | ✓ |
112
+ | Audio ranges / resampling / channel mixing | ✓ float32 | ✓ |
113
+ | Paths / URLs / bytes / seekable file objects | ✓ | ✓ |
114
+ | Encoded array input | 1-D uint8 NumPy array | PyTorch tensor |
115
+ | Decoded output | NumPy array; array interface / DLPack | PyTorch tensor |
116
+ | CUDA decoding | — | ✓ |
117
+
118
+ For high-bit-depth video, use `VideoDecoder(path, output_dtype="auto")` to select
119
+ float32 above 8 bits, or `output_dtype="uint16"` for full-range 16-bit RGB.
120
+ HDR output retains PQ/HLG encoding without SDR tone mapping. Rotation is applied
121
+ automatically, and metadata dimensions match the output.
122
+
123
+ For unmodified samples, use `VideoDecoder(path, output_format="native")`.
124
+ Supported formats: `gray`, `gray12le`, `gray16le/be`, `rgb24`, `rgba`.
125
+ Native output preserves channel count, integer values and pixel coordinates;
126
+ `expected_pixel_format` optionally asserts the source format.
127
+
128
+ ## Package size
129
+
130
+ <!-- wheel-size:start -->
131
+ Linux CPU wheels, Python 3.12. Download / unpacked size in MiB.
132
+
133
+ | Package | x86_64 | ARM64 |
134
+ | --- | ---: | ---: |
135
+ | TensorCodec | 10.2 / 24.7 | 10.4 / 22.9 |
136
+ | PyAV | 33.4 / 125.5 | 31.2 / 90.4 |
137
+ | TorchCodec + PyTorch (CPU) | 196.7 / 704.7 | 160.3 / 585.7 |
138
+ <!-- wheel-size:end -->
139
+
140
+ TensorCodec and PyAV bundle FFmpeg; TorchCodec needs it separately.
141
+ Other dependencies are excluded. [Measurements](docs/package_size.md).
142
+
143
+ ## Scope and compatibility
144
+
145
+ The supported CPU API is checked for frame selection, ordering, timestamps,
146
+ durations, stream selection and metadata, both against TorchCodec 0.17.0 and
147
+ independently generated media.
148
+
149
+ - Pixel comparisons allow color-conversion rounding of at most 1 uint8 unit or
150
+ 1/65535 for float32 in the tested cases.
151
+ - Empty index lists are supported, including the case affected by the reference's
152
+ empty-list dtype inference bug.
153
+ - NumPy output preserves the decoder API structure; callers expecting
154
+ `torch.Tensor` must adapt their array handling.
155
+
156
+ See the [compatibility contract](docs/compatibility.md) and
157
+ [playback rules](docs/playback_semantics.md) for the tested behavior.
158
+
159
+ ### Current limits
160
+
161
+ - **Wheels:** Linux x86_64 and ARM64 (aarch64), glibc 2.17+, CPython 3.10+.
162
+ NumPy must also provide a compatible wheel; newer Python versions may require
163
+ a newer glibc. macOS 14+ wheels support ARM64 and x86_64. Windows, musl/Alpine
164
+ and free-threaded Python wheels are not provided.
165
+ - **Exact seeking:** scans packet timestamps when opening the decoder. Incorrect
166
+ container keyframe flags can produce corrupt frames; repaired input or corrected
167
+ frame mappings are needed in that case.
168
+ - **Audio ranges:** decode from the beginning, so late ranges can be expensive.
169
+ - **Video conversion:** no HDR-to-SDR tone mapping or native YUV-plane output.
170
+ Reflected and non-right-angle display matrices are unsupported.
171
+
172
+ See [container behavior](docs/container_robustness.md) for seek limitations and
173
+ [benchmark tools](benchmarks/README.md) for workload measurements.
174
+
175
+ ## Development and verification
176
+
177
+ <details>
178
+ <summary>Build from source and run tests</summary>
179
+
180
+ Source builds require Rust, Clang/libclang, pkg-config and FFmpeg 7 development
181
+ headers/libraries. Python handles API and playback selection; Rust + PyO3 handles
182
+ FFmpeg. Native decoding releases the GIL, allowing separate decoder instances to
183
+ run concurrently across Python threads. Calls on the same instance are serialized.
184
+ The default is one FFmpeg thread per decoder; use independent workers for concurrent
185
+ windows and tune the total thread count to avoid oversubscription.
186
+
187
+ ```sh
188
+ uv sync --group dev --group oracle
189
+
190
+ uv run --group oracle pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
191
+ uv run --group oracle pytest --compare
192
+
193
+ # Rebuild after changing Rust code.
194
+ uv run --group oracle maturin develop --locked --uv
195
+ ```
196
+
197
+ Tests generate media with FFmpeg/ffprobe and Python's `wave` module.
198
+ `--compare` requires the pinned oracle; differential tests otherwise skip.
199
+
200
+ [Playback rules](docs/playback_semantics.md) · [Release guide](docs/releasing.md) · [Dependency licenses](licenses/README.md)
201
+
202
+ </details>
203
+
204
+ TensorCodec's own code is [MIT licensed](LICENSE).
205
+
@@ -0,0 +1,179 @@
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.2), 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.3 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
+ | Native grayscale/depth and packed RGB(A) | ✓ Values preserved | — |
85
+ | PQ / HLG decoding | ✓ Transfer-encoded RGB | ✓ |
86
+ | Right-angle display rotation | ✓ | ✓ |
87
+ | Audio ranges / resampling / channel mixing | ✓ float32 | ✓ |
88
+ | Paths / URLs / bytes / seekable file objects | ✓ | ✓ |
89
+ | Encoded array input | 1-D uint8 NumPy array | PyTorch tensor |
90
+ | Decoded output | NumPy array; array interface / DLPack | PyTorch tensor |
91
+ | CUDA decoding | — | ✓ |
92
+
93
+ For high-bit-depth video, use `VideoDecoder(path, output_dtype="auto")` to select
94
+ float32 above 8 bits, or `output_dtype="uint16"` for full-range 16-bit RGB.
95
+ HDR output retains PQ/HLG encoding without SDR tone mapping. Rotation is applied
96
+ automatically, and metadata dimensions match the output.
97
+
98
+ For unmodified samples, use `VideoDecoder(path, output_format="native")`.
99
+ Supported formats: `gray`, `gray12le`, `gray16le/be`, `rgb24`, `rgba`.
100
+ Native output preserves channel count, integer values and pixel coordinates;
101
+ `expected_pixel_format` optionally asserts the source format.
102
+
103
+ ## Package size
104
+
105
+ <!-- wheel-size:start -->
106
+ Linux CPU wheels, Python 3.12. Download / unpacked size in MiB.
107
+
108
+ | Package | x86_64 | ARM64 |
109
+ | --- | ---: | ---: |
110
+ | TensorCodec | 10.2 / 24.7 | 10.4 / 22.9 |
111
+ | PyAV | 33.4 / 125.5 | 31.2 / 90.4 |
112
+ | TorchCodec + PyTorch (CPU) | 196.7 / 704.7 | 160.3 / 585.7 |
113
+ <!-- wheel-size:end -->
114
+
115
+ TensorCodec and PyAV bundle FFmpeg; TorchCodec needs it separately.
116
+ Other dependencies are excluded. [Measurements](docs/package_size.md).
117
+
118
+ ## Scope and compatibility
119
+
120
+ The supported CPU API is checked for frame selection, ordering, timestamps,
121
+ durations, stream selection and metadata, both against TorchCodec 0.17.0 and
122
+ independently generated media.
123
+
124
+ - Pixel comparisons allow color-conversion rounding of at most 1 uint8 unit or
125
+ 1/65535 for float32 in the tested cases.
126
+ - Empty index lists are supported, including the case affected by the reference's
127
+ empty-list dtype inference bug.
128
+ - NumPy output preserves the decoder API structure; callers expecting
129
+ `torch.Tensor` must adapt their array handling.
130
+
131
+ See the [compatibility contract](docs/compatibility.md) and
132
+ [playback rules](docs/playback_semantics.md) for the tested behavior.
133
+
134
+ ### Current limits
135
+
136
+ - **Wheels:** Linux x86_64 and ARM64 (aarch64), glibc 2.17+, CPython 3.10+.
137
+ NumPy must also provide a compatible wheel; newer Python versions may require
138
+ a newer glibc. macOS 14+ wheels support ARM64 and x86_64. Windows, musl/Alpine
139
+ and free-threaded Python wheels are not provided.
140
+ - **Exact seeking:** scans packet timestamps when opening the decoder. Incorrect
141
+ container keyframe flags can produce corrupt frames; repaired input or corrected
142
+ frame mappings are needed in that case.
143
+ - **Audio ranges:** decode from the beginning, so late ranges can be expensive.
144
+ - **Video conversion:** no HDR-to-SDR tone mapping or native YUV-plane output.
145
+ Reflected and non-right-angle display matrices are unsupported.
146
+
147
+ See [container behavior](docs/container_robustness.md) for seek limitations and
148
+ [benchmark tools](benchmarks/README.md) for workload measurements.
149
+
150
+ ## Development and verification
151
+
152
+ <details>
153
+ <summary>Build from source and run tests</summary>
154
+
155
+ Source builds require Rust, Clang/libclang, pkg-config and FFmpeg 7 development
156
+ headers/libraries. Python handles API and playback selection; Rust + PyO3 handles
157
+ FFmpeg. Native decoding releases the GIL, allowing separate decoder instances to
158
+ run concurrently across Python threads. Calls on the same instance are serialized.
159
+ The default is one FFmpeg thread per decoder; use independent workers for concurrent
160
+ windows and tune the total thread count to avoid oversubscription.
161
+
162
+ ```sh
163
+ uv sync --group dev --group oracle
164
+
165
+ uv run --group oracle pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
166
+ uv run --group oracle pytest --compare
167
+
168
+ # Rebuild after changing Rust code.
169
+ uv run --group oracle maturin develop --locked --uv
170
+ ```
171
+
172
+ Tests generate media with FFmpeg/ffprobe and Python's `wave` module.
173
+ `--compare` requires the pinned oracle; differential tests otherwise skip.
174
+
175
+ [Playback rules](docs/playback_semantics.md) · [Release guide](docs/releasing.md) · [Dependency licenses](licenses/README.md)
176
+
177
+ </details>
178
+
179
+ 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,29 @@ 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.
69
+
70
+ ## Native video output
71
+
72
+ `output_format="native"` bypasses color conversion and display transforms for
73
+ `gray`, `gray12le`, `gray16le`, `gray16be`, `rgb24` and `rgba`. NCHW/NHWC keeps
74
+ 1, 3 or 4 channels. Output uses host-endian uint8/uint16 without range scaling;
75
+ `output_dtype` may be omitted, `"auto"`, or the matching integer dtype.
76
+ `expected_pixel_format` asserts the source layout. Unsupported formats, dtype
77
+ conversions and changes of pixel format or dimensions fail explicitly.
78
+
79
+ Frame/FrameBatch `pixel_format` records the native source format and survives
80
+ indexing and FPS resampling. RGB results retain their existing behavior. Native
81
+ mode preserves encoded pixel coordinates, including inputs with display matrices.
82
+ Playback selection follows the same TorchCodec contract as RGB, including the
83
+ frame overlapping a range's start; it does not copy PyAV's legacy PTS-only range rule.
@@ -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.3
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,33 +1,31 @@
1
1
  # Publishing TensorCodec
2
2
 
3
- Release version: `0.1.1`. Distribution and import name: `tensorcodec`.
3
+ Release version: `0.1.3`. 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
- runtime dependency is NumPy. macOS/Windows wheels are not yet provided.
7
+ runtime dependency is NumPy. macOS 14+ ARM64/x86_64 wheels bundle the same minimal
8
+ runtime. Windows wheels are not provided.
8
9
 
9
- ## One-time account setup
10
+ ## Trusted publisher configuration
10
11
 
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:
12
+ The PyPI project is already registered. Its GitHub Trusted Publisher uses:
15
13
 
16
- | Field | Value |
17
- | --- | --- |
18
- | PyPI project name | `tensorcodec` |
19
- | GitHub owner | `MilkClouds` |
20
- | Repository | `tensorcodec` |
21
- | Workflow filename | `publish.yml` |
22
- | Environment | `pypi` |
14
+ | Field | Value |
15
+ | --- | --- |
16
+ | PyPI project name | `tensorcodec` |
17
+ | GitHub owner | `MilkClouds` |
18
+ | Repository | `tensorcodec` |
19
+ | Workflow filename | `publish.yml` |
20
+ | Environment | `pypi` |
23
21
 
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.
22
+ Manage this configuration in the project's PyPI Publishing settings when moving
23
+ or renaming the repository or workflow. Publishing uses GitHub OIDC; no API token
24
+ is needed. Repository visibility does not need to change for a release.
27
25
 
28
26
  ## Release
29
27
 
30
- Run the **Publish to PyPI** workflow on `main`. It builds the portable Linux wheels
28
+ Run the **Publish to PyPI** workflow on `main`. It builds the portable Linux/macOS wheels
31
29
  and source distribution, checks package metadata, validates the pinned oracle
32
30
  and compares playback before uploading through PyPI Trusted Publishing. It uses
33
31
  the existing GitHub `pypi` environment. Publication fails if authorization is
@@ -39,8 +37,8 @@ gh workflow run publish.yml --repo MilkClouds/tensorcodec --ref main
39
37
 
40
38
  For a build and full validation without uploading, pass `--field publish=false`.
41
39
 
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
40
+ Check the workflow and https://pypi.org/project/tensorcodec/0.1.3/ before reporting
41
+ success. Verify a fresh `uv pip install tensorcodec==0.1.3` and a decode without
44
42
  Torch/PyAV on both architectures. Update the version before subsequent releases;
45
43
  PyPI versions cannot be overwritten.
46
44
 
@@ -64,3 +62,22 @@ and source links are recorded in `licenses/README.md`.
64
62
  x86_64 and ARM64 runners also run the full pinned playback oracle comparison.
65
63
  - Release validation still tests the installed repaired wheel. The fixture CLI
66
64
  can be FFmpeg 6 or 7; fixtures explicitly remove auxiliary sentinel packets.
65
+
66
+ ## Size checks and published comparison
67
+
68
+ Final repaired wheels must stay within 15 MiB download and 35 MiB unpacked per
69
+ architecture. The build job checks `packaging/size-policy.json` and uploads a
70
+ separate `wheel-size-*` report, including differences from the last published
71
+ baseline. Size reports must not be placed in `dist/`.
72
+
73
+ After a successful publication, the `update-size-docs` job measures hash-verified
74
+ PyPI wheels for the release and pinned TorchCodec 0.17.0. It commits the published
75
+ snapshot and README table/badge with a normal push to `main`. Only this documentation
76
+ job has `contents: write`; the PyPI publisher retains OIDC plus read access. The
77
+ repository must permit the Actions bot to push these documentation updates.
78
+
79
+ If the documentation job fails after a successful publication, recover by running
80
+ `scripts/update_size_comparison.py --version <published-version>` and committing
81
+ its two outputs. Never retry publication of an already uploaded version. See the
82
+ [package size policy](package_size.md) for measurement definitions and the
83
+ [external FFmpeg guide](system_ffmpeg.md) for the optional source-build path.