tensorcodec 0.1.0__tar.gz → 0.1.1__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.0 → tensorcodec-0.1.1}/PKG-INFO +64 -46
- tensorcodec-0.1.1/README.md +142 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/docs/releasing.md +16 -10
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/native/Cargo.lock +1 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/native/Cargo.toml +1 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/native/src/ffmpeg.rs +1 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/pyproject.toml +2 -2
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/scripts/build_linux_wheel.sh +3 -2
- tensorcodec-0.1.1/scripts/build_nasm.sh +13 -0
- tensorcodec-0.1.1/scripts/check_wheel_runtime.py +75 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/src/tensorcodec/__init__.py +1 -1
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/src/tensorcodec/decoders/_decoder.py +5 -14
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/tests/test_differential.py +3 -9
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/tests/test_video_contract.py +1 -3
- tensorcodec-0.1.0/README.md +0 -124
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/LICENSE +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/docs/compatibility.md +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/docs/playback_semantics.md +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/licenses/FFmpeg-GPL-3.0.txt +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/licenses/FFmpeg-LGPL-3.0.txt +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/licenses/FFmpeg-NOTICE.md +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/licenses/OpenSSL.txt +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/licenses/README.md +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/licenses/Zstandard.txt +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/native/src/lib.rs +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/scripts/build_ffmpeg.sh +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/scripts/build_openssl.sh +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/scripts/configure_oracle_ffmpeg.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/src/tensorcodec/_frame.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/src/tensorcodec/_metadata.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/src/tensorcodec/decoders/__init__.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/src/tensorcodec/py.typed +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/tests/__init__.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/tests/conftest.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/tests/test_audio_contract.py +0 -0
- {tensorcodec-0.1.0 → tensorcodec-0.1.1}/tests/test_runtime.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tensorcodec
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Operating System :: POSIX :: Linux
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -24,42 +24,63 @@ Project-URL: Repository, https://github.com/MilkClouds/tensorcodec
|
|
|
24
24
|
|
|
25
25
|
# TensorCodec
|
|
26
26
|
|
|
27
|
+
[](https://github.com/MilkClouds/tensorcodec/actions/workflows/ci.yml)
|
|
28
|
+
[](https://pypi.org/project/tensorcodec/)
|
|
29
|
+
[](https://pypi.org/project/tensorcodec/)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
|
|
27
32
|
**TorchCodec-style video and audio decoding, without PyTorch.**
|
|
28
33
|
|
|
29
|
-
- Use the CPU decoder API and playback rules of
|
|
30
|
-
- Get
|
|
31
|
-
- Install
|
|
34
|
+
- Use the CPU decoder API and playback rules of TorchCodec 0.17.0.
|
|
35
|
+
- Get NumPy arrays instead of `torch.Tensor`.
|
|
36
|
+
- Install NumPy + TensorCodec. No Torch, PyAV, or FFmpeg CLI at runtime.
|
|
32
37
|
|
|
33
38
|
The goal is predictable frame selection, timestamps and audio ranges with a small
|
|
34
39
|
runtime dependency set. This is a **CPU decoding subset**, not the entire
|
|
35
40
|
TorchCodec package. It does not promise a speedup over PyAV or TorchCodec.
|
|
36
41
|
|
|
37
|
-
##
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
|
42
|
+
## Scope compared with TorchCodec
|
|
43
|
+
|
|
44
|
+
TorchCodec includes decoders, encoders, sampling and transforms.
|
|
45
|
+
|
|
46
|
+
Legend for both tables: ✓ supported · △ limited support · — unavailable.
|
|
47
|
+
|
|
48
|
+
| Module family | Component | TorchCodec 0.17.0 | TensorCodec 0.1.1 |
|
|
49
|
+
| --- | --- | :---: | --- |
|
|
50
|
+
| Decoders | Video · `VideoDecoder` | ✓ | △ CPU, SDR |
|
|
51
|
+
| | Audio · `AudioDecoder` | ✓ | △ CPU |
|
|
52
|
+
| | Images | ✓ | — |
|
|
53
|
+
| Encoders | Video / audio / JPEG / PNG | ✓ | — |
|
|
54
|
+
| Samplers | Clip sampling | ✓ | — |
|
|
55
|
+
| Transforms | Decoder transforms | ✓ | — |
|
|
56
|
+
|
|
57
|
+
FPS-based decoder queries are available; clip samplers are not implemented.
|
|
58
|
+
|
|
59
|
+
### Decoder compatibility
|
|
60
|
+
|
|
61
|
+
| Area | Capability | TorchCodec 0.17.0 | TensorCodec 0.1.1 |
|
|
62
|
+
| --- | --- | :---: | --- |
|
|
63
|
+
| Video · selection | Index / slice / batch | ✓ | ✓ |
|
|
64
|
+
| | Playback time / range | ✓ | ✓ |
|
|
65
|
+
| | Order / duplicates preserved | ✓ | ✓ |
|
|
66
|
+
| | Exact / approximate seek | ✓ | ✓ Default: exact |
|
|
67
|
+
| | FPS queries / custom frame mappings | ✓ | ✓ |
|
|
68
|
+
| Video · formats | CFR / VFR / offset PTS / B-frames | ✓ | ✓ Tested |
|
|
69
|
+
| | NCHW / NHWC RGB | ✓ | ✓ |
|
|
70
|
+
| | uint8 / float32 | ✓ | △ SDR |
|
|
71
|
+
| | HDR transfer / display rotation | ✓ | — Explicit rejection |
|
|
72
|
+
| Audio | Ranges / resampling / channel mixing | ✓ | ✓ float32 |
|
|
73
|
+
| Input / output | Paths / URLs / bytes / seekable files | ✓ | ✓ |
|
|
74
|
+
| | Encoded array input | `torch.Tensor` | 1-D uint8 NumPy arrays |
|
|
75
|
+
| | Decoded arrays | `torch.Tensor` | `numpy.ndarray` + array interface / DLPack |
|
|
76
|
+
| Execution | CPU | ✓ | ✓ |
|
|
77
|
+
| | CUDA | ✓ | — |
|
|
78
|
+
| | Python runtime dependency | PyTorch | NumPy |
|
|
58
79
|
|
|
59
80
|
### Compatibility means
|
|
60
81
|
|
|
61
82
|
- Match the supported CPU API's frame selection, ordering, timing and metadata.
|
|
62
|
-
- Check behavior independently
|
|
83
|
+
- Check behavior independently and against pinned TorchCodec 0.17.0.
|
|
63
84
|
- Allow color-conversion rounding: at most 1 uint8 unit or 1/65535 for float32
|
|
64
85
|
in the tested cases. Do not claim identical pixels across every FFmpeg build.
|
|
65
86
|
- Accept empty index lists, including the case affected by the reference's
|
|
@@ -69,9 +90,13 @@ Details and the tested scope: [compatibility contract](docs/compatibility.md).
|
|
|
69
90
|
|
|
70
91
|
### Current limits
|
|
71
92
|
|
|
72
|
-
- Binary wheels: **Linux x86_64, glibc 2.
|
|
73
|
-
-
|
|
93
|
+
- Binary wheels: **Linux x86_64 / ARM64 (aarch64), glibc 2.17+, CPython 3.10+**.
|
|
94
|
+
- A compatible NumPy wheel is also required. On older glibc, the installer may
|
|
95
|
+
select an older NumPy; newer Python versions may require a newer glibc.
|
|
96
|
+
- No macOS, Windows or musl/Alpine wheels yet; free-threaded Python is not a release target.
|
|
74
97
|
- Exact video seeking scans packet timestamps when opening the decoder.
|
|
98
|
+
- Seeking trusts container keyframe flags; incorrect flags can corrupt decoded
|
|
99
|
+
frames. Corrected frame mappings or a repaired input are needed in that case.
|
|
75
100
|
- Audio range queries currently decode from the beginning; late ranges can be
|
|
76
101
|
expensive.
|
|
77
102
|
- NumPy return types require caller changes where code expects Torch tensors.
|
|
@@ -80,18 +105,13 @@ Details and the tested scope: [compatibility contract](docs/compatibility.md).
|
|
|
80
105
|
## Install
|
|
81
106
|
|
|
82
107
|
```sh
|
|
83
|
-
|
|
108
|
+
uv pip install tensorcodec
|
|
84
109
|
```
|
|
85
110
|
|
|
111
|
+
Use an existing virtual environment, or create one with `uv venv` first.
|
|
86
112
|
Linux wheels bundle shared FFmpeg libraries. Source builds need Rust, libclang
|
|
87
113
|
and FFmpeg 7 development headers/libraries.
|
|
88
114
|
|
|
89
|
-
Before the first PyPI upload, install a local wheel:
|
|
90
|
-
|
|
91
|
-
```sh
|
|
92
|
-
python -m pip install dist/tensorcodec-*.whl
|
|
93
|
-
```
|
|
94
|
-
|
|
95
115
|
## Use
|
|
96
116
|
|
|
97
117
|
```python
|
|
@@ -124,17 +144,15 @@ A batch crosses the Python/Rust boundary once. Native decoding releases the GIL.
|
|
|
124
144
|
## Development and verification
|
|
125
145
|
|
|
126
146
|
```sh
|
|
127
|
-
# Requires Rust, Clang/libclang, pkg-config and FFmpeg 7 development libraries.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
|
|
137
|
-
pytest --compare
|
|
147
|
+
# Requires uv, Rust, Clang/libclang, pkg-config and FFmpeg 7 development libraries.
|
|
148
|
+
# Build the editable package and install development + pinned CPU oracle groups.
|
|
149
|
+
uv sync --group dev --group oracle
|
|
150
|
+
|
|
151
|
+
uv run --group oracle pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
|
|
152
|
+
uv run --group oracle pytest --compare
|
|
153
|
+
|
|
154
|
+
# Rebuild after changing Rust code.
|
|
155
|
+
uv run --group oracle maturin develop --locked --uv
|
|
138
156
|
```
|
|
139
157
|
|
|
140
158
|
Tests generate fixtures with FFmpeg/ffprobe and Python's `wave` module.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# TensorCodec
|
|
2
|
+
|
|
3
|
+
[](https://github.com/MilkClouds/tensorcodec/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/tensorcodec/)
|
|
5
|
+
[](https://pypi.org/project/tensorcodec/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
**TorchCodec-style video and audio decoding, without PyTorch.**
|
|
9
|
+
|
|
10
|
+
- Use the CPU decoder API and playback rules of TorchCodec 0.17.0.
|
|
11
|
+
- Get NumPy arrays instead of `torch.Tensor`.
|
|
12
|
+
- Install NumPy + TensorCodec. No Torch, PyAV, or FFmpeg CLI at runtime.
|
|
13
|
+
|
|
14
|
+
The goal is predictable frame selection, timestamps and audio ranges with a small
|
|
15
|
+
runtime dependency set. This is a **CPU decoding subset**, not the entire
|
|
16
|
+
TorchCodec package. It does not promise a speedup over PyAV or TorchCodec.
|
|
17
|
+
|
|
18
|
+
## Scope compared with TorchCodec
|
|
19
|
+
|
|
20
|
+
TorchCodec includes decoders, encoders, sampling and transforms.
|
|
21
|
+
|
|
22
|
+
Legend for both tables: ✓ supported · △ limited support · — unavailable.
|
|
23
|
+
|
|
24
|
+
| Module family | Component | TorchCodec 0.17.0 | TensorCodec 0.1.1 |
|
|
25
|
+
| --- | --- | :---: | --- |
|
|
26
|
+
| Decoders | Video · `VideoDecoder` | ✓ | △ CPU, SDR |
|
|
27
|
+
| | Audio · `AudioDecoder` | ✓ | △ CPU |
|
|
28
|
+
| | Images | ✓ | — |
|
|
29
|
+
| Encoders | Video / audio / JPEG / PNG | ✓ | — |
|
|
30
|
+
| Samplers | Clip sampling | ✓ | — |
|
|
31
|
+
| Transforms | Decoder transforms | ✓ | — |
|
|
32
|
+
|
|
33
|
+
FPS-based decoder queries are available; clip samplers are not implemented.
|
|
34
|
+
|
|
35
|
+
### Decoder compatibility
|
|
36
|
+
|
|
37
|
+
| Area | Capability | TorchCodec 0.17.0 | TensorCodec 0.1.1 |
|
|
38
|
+
| --- | --- | :---: | --- |
|
|
39
|
+
| Video · selection | Index / slice / batch | ✓ | ✓ |
|
|
40
|
+
| | Playback time / range | ✓ | ✓ |
|
|
41
|
+
| | Order / duplicates preserved | ✓ | ✓ |
|
|
42
|
+
| | Exact / approximate seek | ✓ | ✓ Default: exact |
|
|
43
|
+
| | FPS queries / custom frame mappings | ✓ | ✓ |
|
|
44
|
+
| Video · formats | CFR / VFR / offset PTS / B-frames | ✓ | ✓ Tested |
|
|
45
|
+
| | NCHW / NHWC RGB | ✓ | ✓ |
|
|
46
|
+
| | uint8 / float32 | ✓ | △ SDR |
|
|
47
|
+
| | HDR transfer / display rotation | ✓ | — Explicit rejection |
|
|
48
|
+
| Audio | Ranges / resampling / channel mixing | ✓ | ✓ float32 |
|
|
49
|
+
| Input / output | Paths / URLs / bytes / seekable files | ✓ | ✓ |
|
|
50
|
+
| | Encoded array input | `torch.Tensor` | 1-D uint8 NumPy arrays |
|
|
51
|
+
| | Decoded arrays | `torch.Tensor` | `numpy.ndarray` + array interface / DLPack |
|
|
52
|
+
| Execution | CPU | ✓ | ✓ |
|
|
53
|
+
| | CUDA | ✓ | — |
|
|
54
|
+
| | Python runtime dependency | PyTorch | NumPy |
|
|
55
|
+
|
|
56
|
+
### Compatibility means
|
|
57
|
+
|
|
58
|
+
- Match the supported CPU API's frame selection, ordering, timing and metadata.
|
|
59
|
+
- Check behavior independently and against pinned TorchCodec 0.17.0.
|
|
60
|
+
- Allow color-conversion rounding: at most 1 uint8 unit or 1/65535 for float32
|
|
61
|
+
in the tested cases. Do not claim identical pixels across every FFmpeg build.
|
|
62
|
+
- Accept empty index lists, including the case affected by the reference's
|
|
63
|
+
empty-list dtype inference bug.
|
|
64
|
+
|
|
65
|
+
Details and the tested scope: [compatibility contract](docs/compatibility.md).
|
|
66
|
+
|
|
67
|
+
### Current limits
|
|
68
|
+
|
|
69
|
+
- Binary wheels: **Linux x86_64 / ARM64 (aarch64), glibc 2.17+, CPython 3.10+**.
|
|
70
|
+
- A compatible NumPy wheel is also required. On older glibc, the installer may
|
|
71
|
+
select an older NumPy; newer Python versions may require a newer glibc.
|
|
72
|
+
- No macOS, Windows or musl/Alpine wheels yet; free-threaded Python is not a release target.
|
|
73
|
+
- Exact video seeking scans packet timestamps when opening the decoder.
|
|
74
|
+
- Seeking trusts container keyframe flags; incorrect flags can corrupt decoded
|
|
75
|
+
frames. Corrected frame mappings or a repaired input are needed in that case.
|
|
76
|
+
- Audio range queries currently decode from the beginning; late ranges can be
|
|
77
|
+
expensive.
|
|
78
|
+
- NumPy return types require caller changes where code expects Torch tensors.
|
|
79
|
+
- Historical avdec benchmarks are not TensorCodec performance results.
|
|
80
|
+
|
|
81
|
+
## Install
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
uv pip install tensorcodec
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Use an existing virtual environment, or create one with `uv venv` first.
|
|
88
|
+
Linux wheels bundle shared FFmpeg libraries. Source builds need Rust, libclang
|
|
89
|
+
and FFmpeg 7 development headers/libraries.
|
|
90
|
+
|
|
91
|
+
## Use
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from tensorcodec.decoders import VideoDecoder, AudioDecoder
|
|
95
|
+
|
|
96
|
+
with VideoDecoder("video.mp4") as video:
|
|
97
|
+
frame = video.get_frame_played_at(1.25) # frame playing at this time
|
|
98
|
+
print(frame.data.shape) # CHW NumPy array
|
|
99
|
+
batch = video.get_frames_at([4, 0, 4]) # order and duplicates preserved
|
|
100
|
+
clip = video.get_frames_played_in_range(0, 1, fps=8)
|
|
101
|
+
|
|
102
|
+
with AudioDecoder("audio.wav", sample_rate=16000, num_channels=1) as audio:
|
|
103
|
+
samples = audio.get_samples_played_in_range(0, 1)
|
|
104
|
+
print(samples.data.shape) # channels × samples, float32
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Decoded arrays keep their storage after the decoder closes. Input file objects
|
|
108
|
+
remain caller-owned.
|
|
109
|
+
|
|
110
|
+
## Implementation
|
|
111
|
+
|
|
112
|
+
| Layer | Responsibility |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| Python | Public API, frame/time selection, validation, result objects |
|
|
115
|
+
| Rust + PyO3 | FFmpeg handles, seeking/decoding, color conversion, resampling |
|
|
116
|
+
| FFmpeg | Codec and container implementations |
|
|
117
|
+
|
|
118
|
+
A batch crosses the Python/Rust boundary once. Native decoding releases the GIL.
|
|
119
|
+
|
|
120
|
+
## Development and verification
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
# Requires uv, Rust, Clang/libclang, pkg-config and FFmpeg 7 development libraries.
|
|
124
|
+
# Build the editable package and install development + pinned CPU oracle groups.
|
|
125
|
+
uv sync --group dev --group oracle
|
|
126
|
+
|
|
127
|
+
uv run --group oracle pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
|
|
128
|
+
uv run --group oracle pytest --compare
|
|
129
|
+
|
|
130
|
+
# Rebuild after changing Rust code.
|
|
131
|
+
uv run --group oracle maturin develop --locked --uv
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Tests generate fixtures with FFmpeg/ffprobe and Python's `wave` module.
|
|
135
|
+
`--compare` requires the exact oracle version; otherwise differential tests skip.
|
|
136
|
+
The old avdec decoder and tests are never executed.
|
|
137
|
+
|
|
138
|
+
- [Playback rules](docs/playback_semantics.md)
|
|
139
|
+
- [Release builds and PyPI publishing](docs/releasing.md)
|
|
140
|
+
- [Native dependency licenses and source/build notices](licenses/README.md)
|
|
141
|
+
|
|
142
|
+
TensorCodec's own code is MIT licensed.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Publishing TensorCodec
|
|
2
2
|
|
|
3
|
-
Release version: `0.1.
|
|
4
|
-
|
|
3
|
+
Release version: `0.1.1`. 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.
|
|
5
6
|
The wheel bundles shared FFmpeg 7.1.5 and OpenSSL 3.5.9 LTS; its only Python
|
|
6
7
|
runtime dependency is NumPy. macOS/Windows wheels are not yet provided.
|
|
7
8
|
|
|
@@ -26,7 +27,7 @@ settings instead. Do not put an API token in the repository or chat.
|
|
|
26
27
|
|
|
27
28
|
## Release
|
|
28
29
|
|
|
29
|
-
Run the **Publish to PyPI** workflow on `main`. It builds the portable Linux
|
|
30
|
+
Run the **Publish to PyPI** workflow on `main`. It builds the portable Linux wheels
|
|
30
31
|
and source distribution, checks package metadata, validates the pinned oracle
|
|
31
32
|
and compares playback before uploading through PyPI Trusted Publishing. It uses
|
|
32
33
|
the existing GitHub `pypi` environment. Publication fails if authorization is
|
|
@@ -38,13 +39,14 @@ gh workflow run publish.yml --repo MilkClouds/tensorcodec --ref main
|
|
|
38
39
|
|
|
39
40
|
For a build and full validation without uploading, pass `--field publish=false`.
|
|
40
41
|
|
|
41
|
-
Check the workflow and https://pypi.org/project/tensorcodec/0.1.
|
|
42
|
-
success. Verify a fresh `pip install tensorcodec==0.1.
|
|
43
|
-
Torch/PyAV. Update the version before subsequent releases;
|
|
44
|
-
be overwritten.
|
|
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
|
|
44
|
+
Torch/PyAV on both architectures. Update the version before subsequent releases;
|
|
45
|
+
PyPI versions cannot be overwritten.
|
|
45
46
|
|
|
46
47
|
The local Linux build is reproducible using `scripts/build_linux_wheel.sh` inside
|
|
47
|
-
`quay.io/pypa/
|
|
48
|
+
`quay.io/pypa/manylinux2014_x86_64` or
|
|
49
|
+
`quay.io/pypa/manylinux2014_aarch64` with Rust, maturin, libclang, NASM and Perl.
|
|
48
50
|
Both native source archives are version- and checksum-pinned. Their licensing
|
|
49
51
|
and source links are recorded in `licenses/README.md`.
|
|
50
52
|
|
|
@@ -53,8 +55,12 @@ and source links are recorded in `licenses/README.md`.
|
|
|
53
55
|
- Ordinary CI uses prebuilt conda-forge FFmpeg 7.1.1 through Pixi, including its
|
|
54
56
|
headers and shared libraries. It builds only the TensorCodec extension.
|
|
55
57
|
- PyPI wheels use the smaller LGPL FFmpeg 7.1.5 build plus OpenSSL 3.5.9.
|
|
56
|
-
Their native prefix is cached by
|
|
57
|
-
wheel's codec set, dependency size and licensing rather than bundling the full
|
|
58
|
+
Their native prefix is cached by architecture, glibc baseline and build-script
|
|
59
|
+
checksums. This preserves the wheel's codec set, dependency size and licensing rather than bundling the full
|
|
58
60
|
conda-forge dependency graph.
|
|
61
|
+
- Release validation installs each repaired wheel on glibc 2.17 with Python 3.10
|
|
62
|
+
and 3.13 and decodes video/audio without Torch, PyAV or a system FFmpeg. Python
|
|
63
|
+
3.10 also checks the minimum NumPy line (1.26.4). Native
|
|
64
|
+
x86_64 and ARM64 runners also run the full pinned playback oracle comparison.
|
|
59
65
|
- Release validation still tests the installed repaired wheel. The fixture CLI
|
|
60
66
|
can be FFmpeg 6 or 7; fixtures explicitly remove auxiliary sentinel packets.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tensorcodec"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.1"
|
|
4
4
|
description = "NumPy audio/video decoding with TorchCodec-compatible playback semantics"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -46,7 +46,7 @@ include = [
|
|
|
46
46
|
testpaths = ["tests"]
|
|
47
47
|
|
|
48
48
|
[tool.ruff]
|
|
49
|
-
line-length =
|
|
49
|
+
line-length = 119
|
|
50
50
|
target-version = "py310"
|
|
51
51
|
|
|
52
52
|
[tool.uv.sources]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Run inside
|
|
2
|
+
# Run inside manylinux2014 with Rust, maturin, libclang, NASM and Perl installed.
|
|
3
3
|
set -euo pipefail
|
|
4
|
+
export BINDGEN_EXTRA_CLANG_ARGS="-I$(gcc -print-file-name=include)${BINDGEN_EXTRA_CLANG_ARGS:+ $BINDGEN_EXTRA_CLANG_ARGS}"
|
|
4
5
|
build_prefix="${TENSORCODEC_NATIVE_PREFIX:-/opt/tensorcodec}"
|
|
5
6
|
scripts/build_openssl.sh "$build_prefix/openssl"
|
|
6
7
|
export PKG_CONFIG_PATH="$build_prefix/openssl/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
|
|
@@ -8,4 +9,4 @@ export LD_LIBRARY_PATH="$build_prefix/openssl/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY
|
|
|
8
9
|
scripts/build_ffmpeg.sh "$build_prefix/ffmpeg"
|
|
9
10
|
export FFMPEG_DIR="$build_prefix/ffmpeg"
|
|
10
11
|
export LD_LIBRARY_PATH="$FFMPEG_DIR/lib:$LD_LIBRARY_PATH"
|
|
11
|
-
maturin build --release --locked --auditwheel repair --compatibility
|
|
12
|
+
maturin build --release --locked --auditwheel repair --compatibility manylinux2014 --out dist
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Build-only assembler; manylinux2014's system NASM is too old for FFmpeg 7.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
prefix="${1:?usage: build_nasm.sh ABSOLUTE_INSTALL_PREFIX}"
|
|
5
|
+
version=2.16.03
|
|
6
|
+
build_root="$(mktemp -d)"
|
|
7
|
+
curl -fsSL "https://www.nasm.us/pub/nasm/releasebuilds/${version}/nasm-${version}.tar.xz" -o "$build_root/nasm.tar.xz"
|
|
8
|
+
echo "1412a1c760bbd05db026b6c0d1657affd6631cd0a63cddb6f73cc6d4aa616148 $build_root/nasm.tar.xz" | sha256sum --check
|
|
9
|
+
tar -xf "$build_root/nasm.tar.xz" -C "$build_root"
|
|
10
|
+
cd "$build_root/nasm-${version}"
|
|
11
|
+
./configure --prefix="$prefix"
|
|
12
|
+
make -j "${TENSORCODEC_BUILD_JOBS:-4}"
|
|
13
|
+
make install
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Generate portable fixtures, then decode them in a clean wheel runtime."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import importlib.util
|
|
7
|
+
import platform
|
|
8
|
+
import subprocess
|
|
9
|
+
import wave
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def generate(root: Path):
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
17
|
+
subprocess.run(
|
|
18
|
+
[
|
|
19
|
+
"ffmpeg",
|
|
20
|
+
"-hide_banner",
|
|
21
|
+
"-loglevel",
|
|
22
|
+
"error",
|
|
23
|
+
"-y",
|
|
24
|
+
"-f",
|
|
25
|
+
"lavfi",
|
|
26
|
+
"-i",
|
|
27
|
+
"testsrc2=size=64x48:rate=10:duration=1",
|
|
28
|
+
"-c:v",
|
|
29
|
+
"libx264",
|
|
30
|
+
"-g",
|
|
31
|
+
"10",
|
|
32
|
+
"-bf",
|
|
33
|
+
"2",
|
|
34
|
+
str(root / "video.mp4"),
|
|
35
|
+
],
|
|
36
|
+
check=True,
|
|
37
|
+
)
|
|
38
|
+
samples = np.stack([np.arange(8000) % 1000, -(np.arange(8000) % 1000)], axis=1).astype("<i2")
|
|
39
|
+
with wave.open(str(root / "audio.wav"), "wb") as audio:
|
|
40
|
+
audio.setparams((2, 2, 8000, 8000, "NONE", "not compressed"))
|
|
41
|
+
audio.writeframes(samples.tobytes())
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def check(root: Path):
|
|
45
|
+
import numpy as np
|
|
46
|
+
|
|
47
|
+
from tensorcodec.decoders import AudioDecoder, VideoDecoder
|
|
48
|
+
|
|
49
|
+
assert all(importlib.util.find_spec(name) is None for name in ("torch", "torchcodec", "av"))
|
|
50
|
+
with VideoDecoder(root / "video.mp4") as decoder:
|
|
51
|
+
assert len(decoder) == 10
|
|
52
|
+
frames = decoder.get_frames_at([7, 0, 7])
|
|
53
|
+
assert frames.data.shape == (3, 3, 48, 64)
|
|
54
|
+
assert frames.data.dtype == np.uint8
|
|
55
|
+
np.testing.assert_allclose(frames.pts_seconds, [0.7, 0.0, 0.7])
|
|
56
|
+
np.testing.assert_array_equal(frames.data[0], frames.data[2])
|
|
57
|
+
played = decoder.get_frame_played_at(0.75)
|
|
58
|
+
np.testing.assert_array_equal(played.data, frames.data[0])
|
|
59
|
+
assert frames.data.max() > frames.data.min()
|
|
60
|
+
with AudioDecoder(root / "audio.wav") as decoder:
|
|
61
|
+
audio = decoder.get_samples_played_in_range(0.125, 0.25)
|
|
62
|
+
assert audio.data.shape == (2, 1000)
|
|
63
|
+
assert audio.data.dtype == np.float32
|
|
64
|
+
expected = np.arange(1000, dtype=np.float32) / 32768
|
|
65
|
+
np.testing.assert_allclose(audio.data[0], expected, atol=1e-7)
|
|
66
|
+
np.testing.assert_allclose(audio.data[1], -expected, atol=1e-7)
|
|
67
|
+
print(f"Wheel decoding passed: Python {platform.python_version()}, {platform.machine()}, {platform.libc_ver()}")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
72
|
+
parser.add_argument("command", choices=("generate", "check"))
|
|
73
|
+
parser.add_argument("fixtures", type=Path)
|
|
74
|
+
args = parser.parse_args()
|
|
75
|
+
{"generate": generate, "check": check}[args.command](args.fixtures)
|
|
@@ -241,9 +241,7 @@ class VideoDecoder(_Decoder):
|
|
|
241
241
|
)
|
|
242
242
|
if self._order == "NCHW":
|
|
243
243
|
data = data.transpose(0, 3, 1, 2)
|
|
244
|
-
return FrameBatch(
|
|
245
|
-
data, np.asarray(pts, dtype=np.float64), np.asarray(durations, dtype=np.float64)
|
|
246
|
-
)
|
|
244
|
+
return FrameBatch(data, np.asarray(pts, dtype=np.float64), np.asarray(durations, dtype=np.float64))
|
|
247
245
|
|
|
248
246
|
def get_frame_at(self, index):
|
|
249
247
|
batch = self.get_frames_at([index])
|
|
@@ -275,9 +273,7 @@ class VideoDecoder(_Decoder):
|
|
|
275
273
|
raise RuntimeError("timestamp is outside the stream")
|
|
276
274
|
if self._mappings is not None:
|
|
277
275
|
return np.searchsorted(self._pts, values, side="right") - 1
|
|
278
|
-
return np.floor((values - self.metadata.begin_stream_seconds) * self.metadata.average_fps).astype(
|
|
279
|
-
np.int64
|
|
280
|
-
)
|
|
276
|
+
return np.floor((values - self.metadata.begin_stream_seconds) * self.metadata.average_fps).astype(np.int64)
|
|
281
277
|
|
|
282
278
|
def get_frames_played_at(self, seconds):
|
|
283
279
|
with self._lock:
|
|
@@ -312,12 +308,8 @@ class VideoDecoder(_Decoder):
|
|
|
312
308
|
start = int(np.searchsorted(self._pts, start_seconds, side="right") - 1)
|
|
313
309
|
stop = int(np.searchsorted(self._pts, stop_seconds, side="left"))
|
|
314
310
|
else:
|
|
315
|
-
start = math.floor(
|
|
316
|
-
|
|
317
|
-
)
|
|
318
|
-
stop = math.ceil(
|
|
319
|
-
(stop_seconds - self.metadata.begin_stream_seconds) * self.metadata.average_fps
|
|
320
|
-
)
|
|
311
|
+
start = math.floor((start_seconds - self.metadata.begin_stream_seconds) * self.metadata.average_fps)
|
|
312
|
+
stop = math.ceil((stop_seconds - self.metadata.begin_stream_seconds) * self.metadata.average_fps)
|
|
321
313
|
return self.get_frames_in_range(start, stop)
|
|
322
314
|
|
|
323
315
|
def get_all_frames(self, fps=None):
|
|
@@ -365,8 +357,7 @@ class AudioDecoder(_Decoder):
|
|
|
365
357
|
with self._lock:
|
|
366
358
|
self._check_open()
|
|
367
359
|
if not math.isfinite(start_seconds) or (
|
|
368
|
-
stop_seconds is not None
|
|
369
|
-
and (not math.isfinite(stop_seconds) or not start_seconds <= stop_seconds)
|
|
360
|
+
stop_seconds is not None and (not math.isfinite(stop_seconds) or not start_seconds <= stop_seconds)
|
|
370
361
|
):
|
|
371
362
|
raise ValueError("invalid audio time range")
|
|
372
363
|
data, first_pts = self._native.decode_audio(self._rate, self._channels, stop_seconds)
|
|
@@ -9,9 +9,7 @@ from tests.conftest import as_numpy, index_input, time_input
|
|
|
9
9
|
def compare_batch(actual, expected):
|
|
10
10
|
assert actual.data.shape == expected.data.shape
|
|
11
11
|
np.testing.assert_allclose(actual.pts_seconds, as_numpy(expected.pts_seconds), atol=1e-12, rtol=0)
|
|
12
|
-
np.testing.assert_allclose(
|
|
13
|
-
actual.duration_seconds, as_numpy(expected.duration_seconds), atol=1e-12, rtol=0
|
|
14
|
-
)
|
|
12
|
+
np.testing.assert_allclose(actual.duration_seconds, as_numpy(expected.duration_seconds), atol=1e-12, rtol=0)
|
|
15
13
|
# Identical FFmpeg conversion settings should match; allow one rounding unit, not 20.
|
|
16
14
|
np.testing.assert_allclose(actual.data, as_numpy(expected.data), atol=1, rtol=0)
|
|
17
15
|
|
|
@@ -40,9 +38,7 @@ def test_video_differential(backend, oracle, video):
|
|
|
40
38
|
for indices in [[11, 0, 5, 5, 2], [], list(range(12))]:
|
|
41
39
|
compare_batch(actual.get_frames_at(indices), expected.get_frames_at(index_input(oracle, indices)))
|
|
42
40
|
times = ((video.pts[:-1] + video.pts[1:]) / 2).tolist()[::-1]
|
|
43
|
-
compare_batch(
|
|
44
|
-
actual.get_frames_played_at(times), expected.get_frames_played_at(time_input(oracle, times))
|
|
45
|
-
)
|
|
41
|
+
compare_batch(actual.get_frames_played_at(times), expected.get_frames_played_at(time_input(oracle, times)))
|
|
46
42
|
compare_batch(
|
|
47
43
|
actual.get_frames_played_in_range(float(video.pts[1]), float(video.pts[8])),
|
|
48
44
|
expected.get_frames_played_in_range(float(video.pts[1]), float(video.pts[8])),
|
|
@@ -73,9 +69,7 @@ def test_color_conversion_differential(backend, oracle, color_video, dtype):
|
|
|
73
69
|
# Exact pixel conversion when both backends link the same FFmpeg version.
|
|
74
70
|
for indices in ([11, 3, 3, 0], [4, 1, 9], [0, 11]):
|
|
75
71
|
left, right = actual.get_frames_at(indices), expected.get_frames_at(indices)
|
|
76
|
-
np.testing.assert_allclose(
|
|
77
|
-
left.data, as_numpy(right.data), atol=1 if dtype == "uint8" else 1 / 65535, rtol=0
|
|
78
|
-
)
|
|
72
|
+
np.testing.assert_allclose(left.data, as_numpy(right.data), atol=1 if dtype == "uint8" else 1 / 65535, rtol=0)
|
|
79
73
|
np.testing.assert_allclose(left.pts_seconds, as_numpy(right.pts_seconds), atol=1e-12, rtol=0)
|
|
80
74
|
for field in ("color_space", "color_primaries", "color_transfer_characteristic", "pixel_format"):
|
|
81
75
|
assert getattr(actual.metadata, field) == getattr(expected.metadata, field)
|
|
@@ -115,9 +115,7 @@ def test_encoded_sources(backend, videos, kind):
|
|
|
115
115
|
assert_batch(backend.VideoDecoder(source).get_frames_at([11, 0, 4]), case, [11, 0, 4])
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
@pytest.mark.parametrize(
|
|
119
|
-
"kwargs", [{"dimension_order": "CHWN"}, {"seek_mode": "wrong"}, {"num_ffmpeg_threads": None}]
|
|
120
|
-
)
|
|
118
|
+
@pytest.mark.parametrize("kwargs", [{"dimension_order": "CHWN"}, {"seek_mode": "wrong"}, {"num_ffmpeg_threads": None}])
|
|
121
119
|
def test_invalid_constructor_arguments(backend, videos, kwargs):
|
|
122
120
|
with pytest.raises(ValueError):
|
|
123
121
|
backend.VideoDecoder(videos["cfr"].path, **kwargs)
|
tensorcodec-0.1.0/README.md
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# TensorCodec
|
|
2
|
-
|
|
3
|
-
**TorchCodec-style video and audio decoding, without PyTorch.**
|
|
4
|
-
|
|
5
|
-
- Use the CPU decoder API and playback rules of **TorchCodec 0.17.0**.
|
|
6
|
-
- Get **NumPy arrays** instead of `torch.Tensor`.
|
|
7
|
-
- Install **NumPy + TensorCodec**. No Torch, PyAV, or FFmpeg CLI at runtime.
|
|
8
|
-
|
|
9
|
-
The goal is predictable frame selection, timestamps and audio ranges with a small
|
|
10
|
-
runtime dependency set. This is a **CPU decoding subset**, not the entire
|
|
11
|
-
TorchCodec package. It does not promise a speedup over PyAV or TorchCodec.
|
|
12
|
-
|
|
13
|
-
## What is available?
|
|
14
|
-
|
|
15
|
-
| Capability | TorchCodec 0.17.0 | TensorCodec 0.1.0 |
|
|
16
|
-
| --- | --- | --- |
|
|
17
|
-
| Python runtime dependency | PyTorch | NumPy |
|
|
18
|
-
| Output arrays | `torch.Tensor` | `numpy.ndarray`; array interface + DLPack |
|
|
19
|
-
| Video index/slice/batch access | Supported | Supported |
|
|
20
|
-
| Playback time/range access | Supported | Supported |
|
|
21
|
-
| CFR, VFR, offset PTS, B-frames | Supported | Tested |
|
|
22
|
-
| Request ordering and duplicates | Preserved | Preserved |
|
|
23
|
-
| Exact / approximate seeking | Supported | Supported; exact is the default |
|
|
24
|
-
| NCHW / NHWC RGB | Supported | Supported |
|
|
25
|
-
| uint8 / float32 video | Supported | Supported for SDR |
|
|
26
|
-
| FPS sampling, custom frame mappings | Supported | Supported |
|
|
27
|
-
| Audio ranges, resampling, channel mixing | Supported | Supported; float32 output |
|
|
28
|
-
| Paths, URLs, bytes, seekable file objects | Supported | Supported |
|
|
29
|
-
| Encoded tensor input | `torch.Tensor` | 1-D uint8 NumPy arrays |
|
|
30
|
-
| CUDA decoding | Supported | **Not implemented** |
|
|
31
|
-
| Decoder transforms | Supported | **Not implemented** |
|
|
32
|
-
| HDR inputs / display rotation | Supported | **Rejected explicitly** |
|
|
33
|
-
| Other modules, including samplers/encoders | Available | **Outside the initial scope** |
|
|
34
|
-
|
|
35
|
-
### Compatibility means
|
|
36
|
-
|
|
37
|
-
- Match the supported CPU API's frame selection, ordering, timing and metadata.
|
|
38
|
-
- Check behavior independently **and** against pinned TorchCodec 0.17.0.
|
|
39
|
-
- Allow color-conversion rounding: at most 1 uint8 unit or 1/65535 for float32
|
|
40
|
-
in the tested cases. Do not claim identical pixels across every FFmpeg build.
|
|
41
|
-
- Accept empty index lists, including the case affected by the reference's
|
|
42
|
-
empty-list dtype inference bug.
|
|
43
|
-
|
|
44
|
-
Details and the tested scope: [compatibility contract](docs/compatibility.md).
|
|
45
|
-
|
|
46
|
-
### Current limits
|
|
47
|
-
|
|
48
|
-
- Binary wheels: **Linux x86_64, glibc 2.28+, CPython 3.10+**.
|
|
49
|
-
- No macOS or Windows wheels yet; free-threaded Python is not a release target.
|
|
50
|
-
- Exact video seeking scans packet timestamps when opening the decoder.
|
|
51
|
-
- Audio range queries currently decode from the beginning; late ranges can be
|
|
52
|
-
expensive.
|
|
53
|
-
- NumPy return types require caller changes where code expects Torch tensors.
|
|
54
|
-
- Historical avdec benchmarks are not TensorCodec performance results.
|
|
55
|
-
|
|
56
|
-
## Install
|
|
57
|
-
|
|
58
|
-
```sh
|
|
59
|
-
python -m pip install tensorcodec
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Linux wheels bundle shared FFmpeg libraries. Source builds need Rust, libclang
|
|
63
|
-
and FFmpeg 7 development headers/libraries.
|
|
64
|
-
|
|
65
|
-
Before the first PyPI upload, install a local wheel:
|
|
66
|
-
|
|
67
|
-
```sh
|
|
68
|
-
python -m pip install dist/tensorcodec-*.whl
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Use
|
|
72
|
-
|
|
73
|
-
```python
|
|
74
|
-
from tensorcodec.decoders import VideoDecoder, AudioDecoder
|
|
75
|
-
|
|
76
|
-
with VideoDecoder("video.mp4") as video:
|
|
77
|
-
frame = video.get_frame_played_at(1.25) # frame playing at this time
|
|
78
|
-
print(frame.data.shape) # CHW NumPy array
|
|
79
|
-
batch = video.get_frames_at([4, 0, 4]) # order and duplicates preserved
|
|
80
|
-
clip = video.get_frames_played_in_range(0, 1, fps=8)
|
|
81
|
-
|
|
82
|
-
with AudioDecoder("audio.wav", sample_rate=16000, num_channels=1) as audio:
|
|
83
|
-
samples = audio.get_samples_played_in_range(0, 1)
|
|
84
|
-
print(samples.data.shape) # channels × samples, float32
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Decoded arrays keep their storage after the decoder closes. Input file objects
|
|
88
|
-
remain caller-owned.
|
|
89
|
-
|
|
90
|
-
## Implementation
|
|
91
|
-
|
|
92
|
-
| Layer | Responsibility |
|
|
93
|
-
| --- | --- |
|
|
94
|
-
| Python | Public API, frame/time selection, validation, result objects |
|
|
95
|
-
| Rust + PyO3 | FFmpeg handles, seeking/decoding, color conversion, resampling |
|
|
96
|
-
| FFmpeg | Codec and container implementations |
|
|
97
|
-
|
|
98
|
-
A batch crosses the Python/Rust boundary once. Native decoding releases the GIL.
|
|
99
|
-
|
|
100
|
-
## Development and verification
|
|
101
|
-
|
|
102
|
-
```sh
|
|
103
|
-
# Requires Rust, Clang/libclang, pkg-config and FFmpeg 7 development libraries.
|
|
104
|
-
python -m venv .venv
|
|
105
|
-
. .venv/bin/activate
|
|
106
|
-
python -m pip install numpy pytest ruff 'maturin>=1.8,<2'
|
|
107
|
-
maturin develop --locked
|
|
108
|
-
|
|
109
|
-
# Reference dependencies are for tests only.
|
|
110
|
-
python -m pip install torch==2.14.1 torchcodec==0.17.0 \
|
|
111
|
-
--index-url https://download.pytorch.org/whl/cpu
|
|
112
|
-
pytest tests/test_video_contract.py tests/test_audio_contract.py --backend torchcodec
|
|
113
|
-
pytest --compare
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Tests generate fixtures with FFmpeg/ffprobe and Python's `wave` module.
|
|
117
|
-
`--compare` requires the exact oracle version; otherwise differential tests skip.
|
|
118
|
-
The old avdec decoder and tests are never executed.
|
|
119
|
-
|
|
120
|
-
- [Playback rules](docs/playback_semantics.md)
|
|
121
|
-
- [Release builds and PyPI publishing](docs/releasing.md)
|
|
122
|
-
- [Native dependency licenses and source/build notices](licenses/README.md)
|
|
123
|
-
|
|
124
|
-
TensorCodec's own code is MIT licensed.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|