Skip to content

Commit 4d40573

Browse files
committed
Merge branch 'main' of github.com:pytorch/torchcodec into aefljnaef
2 parents 28bc995 + 2c07f58 commit 4d40573

File tree

9 files changed

+122
-67
lines changed

9 files changed

+122
-67
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Start by installing the **nightly** build of PyTorch following the
3030
Then, the easiest way to install the rest of the dependencies is to run:
3131

3232
```bash
33-
conda install cmake pkg-config pybind11 "ffmpeg<8" -c conda-forge
33+
conda install cmake pkg-config pybind11 "ffmpeg" -c conda-forge
3434
```
3535

3636
### Clone and build

README.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,16 @@ ffmpeg -f lavfi -i \
107107
`torch` and `torchcodec`.
108108

109109
2. Install FFmpeg, if it's not already installed. Linux distributions usually
110-
come with FFmpeg pre-installed. TorchCodec supports major FFmpeg versions
111-
in [4, 7] on all platforms, and FFmpeg version 8 is supported on Mac and Linux.
110+
come with FFmpeg pre-installed. TorchCodec supports supports all major FFmpeg versions
111+
in [4, 8].
112112

113113
If FFmpeg is not already installed, or you need a more recent version, an
114114
easy way to install it is to use `conda`:
115115

116116
```bash
117-
conda install "ffmpeg<8"
117+
conda install "ffmpeg"
118118
# or
119-
conda install "ffmpeg<8" -c conda-forge
119+
conda install "ffmpeg" -c conda-forge
120120
```
121121

122122
3. Install TorchCodec:
@@ -148,16 +148,15 @@ format you want. Refer to Nvidia's GPU support matrix for more details
148148
[here](https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new).
149149

150150
1. Install FFmpeg with NVDEC support.
151-
TorchCodec with CUDA should work with FFmpeg versions in [4, 7] on all platforms,
152-
and FFmpeg version 8 is supported on Linux.
151+
TorchCodec with CUDA should work with FFmpeg versions in [4, 8].
153152

154153
If FFmpeg is not already installed, or you need a more recent version, an
155154
easy way to install it is to use `conda`:
156155

157156
```bash
158-
conda install "ffmpeg<8"
157+
conda install "ffmpeg"
159158
# or
160-
conda install "ffmpeg<8" -c conda-forge
159+
conda install "ffmpeg" -c conda-forge
161160
```
162161

163162
After installing FFmpeg make sure it has NVDEC support when you list the supported

src/torchcodec/_core/Metadata.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ enum class SeekMode { exact, approximate, custom_frame_mappings };
2222

2323
struct StreamMetadata {
2424
// Common (video and audio) fields derived from the AVStream.
25-
int streamIndex;
25+
int streamIndex = -1;
2626

2727
// See this link for what various values are available:
2828
// https://ffmpeg.org/doxygen/trunk/group__lavu__misc.html#ga9a84bba4713dfced21a1a56163be1f48
29-
AVMediaType mediaType;
29+
AVMediaType mediaType = AVMEDIA_TYPE_UNKNOWN;
3030

3131
std::optional<AVCodecID> codecId;
3232
std::optional<std::string> codecName;

src/torchcodec/_core/ops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def load_torchcodec_shared_libraries():
7070
raise RuntimeError(
7171
f"""Could not load libtorchcodec. Likely causes:
7272
1. FFmpeg is not properly installed in your environment. We support
73-
versions 4, 5, 6, and 7 on all platforms, and 8 on Mac and Linux.
73+
versions 4, 5, 6, 7, and 8.
7474
2. The PyTorch version ({torch.__version__}) is not compatible with
7575
this version of TorchCodec. Refer to the version compatibility
7676
table:

test/conftest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44
import pytest
55
import torch
66

7+
from .utils import in_fbcode
8+
79

810
def pytest_configure(config):
911
# register an additional marker (see pytest_collection_modifyitems)
1012
config.addinivalue_line(
1113
"markers", "needs_cuda: mark for tests that rely on a CUDA device"
1214
)
15+
config.addinivalue_line(
16+
"markers", "needs_ffmpeg_cli: mark for tests that rely on ffmpeg"
17+
)
1318

1419

1520
def pytest_collection_modifyitems(items):
@@ -28,6 +33,15 @@ def pytest_collection_modifyitems(items):
2833
# 'needs_cuda' mark, and the ones with device == 'cpu' won't have the
2934
# mark.
3035
needs_cuda = item.get_closest_marker("needs_cuda") is not None
36+
needs_ffmpeg_cli = item.get_closest_marker("needs_ffmpeg_cli") is not None
37+
has_skip_marker = item.get_closest_marker("skip") is not None
38+
has_skipif_marker = item.get_closest_marker("skipif") is not None
39+
40+
if in_fbcode():
41+
# fbcode doesn't like skipping tests, so instead we just don't collect the test
42+
# so that they don't even "exist", hence the continue statements.
43+
if needs_ffmpeg_cli or has_skip_marker or has_skipif_marker:
44+
continue
3145

3246
if (
3347
needs_cuda

test/test_decoders.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
BT709_FULL_RANGE,
3030
cuda_version_used_for_building_torch,
3131
get_ffmpeg_major_version,
32+
get_python_version,
3233
H264_10BITS,
3334
H265_10BITS,
3435
H265_VIDEO,
@@ -39,6 +40,7 @@
3940
NASA_AUDIO_MP3_44100,
4041
NASA_VIDEO,
4142
needs_cuda,
43+
needs_ffmpeg_cli,
4244
psnr,
4345
SINE_MONO_S16,
4446
SINE_MONO_S32,
@@ -1146,6 +1148,10 @@ def test_get_key_frame_indices(self, device):
11461148

11471149
# TODO investigate why this fails internally.
11481150
@pytest.mark.skipif(in_fbcode(), reason="Compile test fails internally.")
1151+
@pytest.mark.skipif(
1152+
get_python_version() >= (3, 14),
1153+
reason="torch.compile is not supported on Python 3.14+",
1154+
)
11491155
@pytest.mark.parametrize("device", all_supported_devices())
11501156
def test_compile(self, device):
11511157
decoder, device = make_video_decoder(NASA_VIDEO.path, device=device)
@@ -1311,10 +1317,7 @@ def setup_frame_mappings(tmp_path, file, stream_index):
13111317
# Return the custom frame mappings as a JSON string
13121318
return custom_frame_mappings
13131319

1314-
@pytest.mark.skipif(
1315-
in_fbcode(),
1316-
reason="ffprobe not available internally",
1317-
)
1320+
@needs_ffmpeg_cli
13181321
@pytest.mark.parametrize("device", all_supported_devices())
13191322
@pytest.mark.parametrize("stream_index", [0, 3])
13201323
@pytest.mark.parametrize(
@@ -1361,10 +1364,7 @@ def test_custom_frame_mappings_json_and_bytes(
13611364
),
13621365
)
13631366

1364-
@pytest.mark.skipif(
1365-
in_fbcode(),
1366-
reason="ffprobe not available internally",
1367-
)
1367+
@needs_ffmpeg_cli
13681368
@pytest.mark.parametrize("device", all_supported_devices())
13691369
@pytest.mark.parametrize(
13701370
"custom_frame_mappings,expected_match",

test/test_encoders.py

Lines changed: 70 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
assert_tensor_close_on_at_least,
1818
get_ffmpeg_major_version,
1919
get_ffmpeg_minor_version,
20-
in_fbcode,
2120
IS_WINDOWS,
2221
NASA_AUDIO_MP3,
22+
needs_ffmpeg_cli,
2323
psnr,
2424
SINE_MONO_S32,
2525
TEST_SRC_2_720P,
@@ -217,13 +217,22 @@ def test_bad_input_parametrized(self, method, tmp_path):
217217
getattr(decoder, method)(**valid_params, num_channels=num_channels)
218218

219219
@pytest.mark.parametrize("method", ("to_file", "to_tensor", "to_file_like"))
220-
@pytest.mark.parametrize("format", ("wav", "flac"))
220+
@pytest.mark.parametrize(
221+
"format",
222+
[
223+
pytest.param(
224+
"wav",
225+
marks=pytest.mark.skipif(
226+
get_ffmpeg_major_version() == 4,
227+
reason="Swresample with FFmpeg 4 doesn't work on wav files",
228+
),
229+
),
230+
"flac",
231+
],
232+
)
221233
def test_round_trip(self, method, format, tmp_path):
222234
# Check that decode(encode(samples)) == samples on lossless formats
223235

224-
if get_ffmpeg_major_version() == 4 and format == "wav":
225-
pytest.skip("Swresample with FFmpeg 4 doesn't work on wav files")
226-
227236
asset = NASA_AUDIO_MP3
228237
source_samples = self.decode(asset).data
229238

@@ -249,7 +258,7 @@ def test_round_trip(self, method, format, tmp_path):
249258
self.decode(encoded_source).data, source_samples, rtol=rtol, atol=atol
250259
)
251260

252-
@pytest.mark.skipif(in_fbcode(), reason="TODO: enable ffmpeg CLI")
261+
@needs_ffmpeg_cli
253262
@pytest.mark.parametrize("asset", (NASA_AUDIO_MP3, SINE_MONO_S32))
254263
@pytest.mark.parametrize("bit_rate", (None, 0, 44_100, 999_999_999))
255264
@pytest.mark.parametrize("num_channels", (None, 1, 2))
@@ -356,17 +365,31 @@ def test_against_cli(
356365
@pytest.mark.parametrize("asset", (NASA_AUDIO_MP3, SINE_MONO_S32))
357366
@pytest.mark.parametrize("bit_rate", (None, 0, 44_100, 999_999_999))
358367
@pytest.mark.parametrize("num_channels", (None, 1, 2))
359-
@pytest.mark.parametrize("format", ("mp3", "wav", "flac"))
368+
@pytest.mark.parametrize(
369+
"format",
370+
[
371+
# TODO: https://github.com/pytorch/torchcodec/issues/837
372+
pytest.param(
373+
"mp3",
374+
marks=pytest.mark.skipif(
375+
IS_WINDOWS and get_ffmpeg_major_version() <= 5,
376+
reason="Encoding mp3 on Windows is weirdly buggy",
377+
),
378+
),
379+
pytest.param(
380+
"wav",
381+
marks=pytest.mark.skipif(
382+
get_ffmpeg_major_version() == 4,
383+
reason="Swresample with FFmpeg 4 doesn't work on wav files",
384+
),
385+
),
386+
"flac",
387+
],
388+
)
360389
@pytest.mark.parametrize("method", ("to_tensor", "to_file_like"))
361390
def test_against_to_file(
362391
self, asset, bit_rate, num_channels, format, tmp_path, method
363392
):
364-
if get_ffmpeg_major_version() == 4 and format == "wav":
365-
pytest.skip("Swresample with FFmpeg 4 doesn't work on wav files")
366-
if IS_WINDOWS and get_ffmpeg_major_version() <= 5 and format == "mp3":
367-
# TODO: https://github.com/pytorch/torchcodec/issues/837
368-
pytest.skip("Encoding mp3 on Windows is weirdly buggy")
369-
370393
encoder = AudioEncoder(self.decode(asset).data, sample_rate=asset.sample_rate)
371394

372395
params = dict(bit_rate=bit_rate, num_channels=num_channels)
@@ -847,16 +870,27 @@ def encode_to_tensor(frames):
847870
)
848871

849872
@pytest.mark.parametrize(
850-
"format", ("mov", "mp4", "mkv", pytest.param("webm", marks=pytest.mark.slow))
873+
"format",
874+
[
875+
"mov",
876+
"mp4",
877+
"mkv",
878+
pytest.param(
879+
"webm",
880+
marks=[
881+
pytest.mark.slow,
882+
pytest.mark.skipif(
883+
get_ffmpeg_major_version() == 4
884+
or (IS_WINDOWS and get_ffmpeg_major_version() in (6, 7)),
885+
reason="Codec for webm is not available in this FFmpeg installation.",
886+
),
887+
],
888+
),
889+
],
851890
)
852891
@pytest.mark.parametrize("method", ("to_file", "to_tensor", "to_file_like"))
853892
def test_round_trip(self, tmp_path, format, method):
854893
# Test that decode(encode(decode(frames))) == decode(frames)
855-
ffmpeg_version = get_ffmpeg_major_version()
856-
if format == "webm" and (
857-
ffmpeg_version == 4 or (IS_WINDOWS and ffmpeg_version in (6, 7))
858-
):
859-
pytest.skip("Codec for webm is not available in this FFmpeg installation.")
860894
source_frames, frame_rate = self.decode_and_get_frame_rate(TEST_SRC_2_720P.path)
861895

862896
encoder = VideoEncoder(frames=source_frames, frame_rate=frame_rate)
@@ -889,25 +923,29 @@ def test_round_trip(self, tmp_path, format, method):
889923

890924
@pytest.mark.parametrize(
891925
"format",
892-
(
926+
[
893927
"mov",
894928
"mp4",
895929
"avi",
896930
"mkv",
897931
"flv",
898932
"gif",
899-
pytest.param("webm", marks=pytest.mark.slow),
900-
),
933+
pytest.param(
934+
"webm",
935+
marks=[
936+
pytest.mark.slow,
937+
pytest.mark.skipif(
938+
get_ffmpeg_major_version() == 4
939+
or (IS_WINDOWS and get_ffmpeg_major_version() in (6, 7)),
940+
reason="Codec for webm is not available in this FFmpeg installation.",
941+
),
942+
],
943+
),
944+
],
901945
)
902946
@pytest.mark.parametrize("method", ("to_tensor", "to_file_like"))
903947
def test_against_to_file(self, tmp_path, format, method):
904948
# Test that to_file, to_tensor, and to_file_like produce the same results
905-
ffmpeg_version = get_ffmpeg_major_version()
906-
if format == "webm" and (
907-
ffmpeg_version == 4 or (IS_WINDOWS and ffmpeg_version in (6, 7))
908-
):
909-
pytest.skip("Codec for webm is not available in this FFmpeg installation.")
910-
911949
source_frames, frame_rate = self.decode_and_get_frame_rate(TEST_SRC_2_720P.path)
912950
encoder = VideoEncoder(frames=source_frames, frame_rate=frame_rate)
913951

@@ -928,7 +966,7 @@ def test_against_to_file(self, tmp_path, format, method):
928966
rtol=0,
929967
)
930968

931-
@pytest.mark.skipif(in_fbcode(), reason="ffmpeg CLI not available")
969+
@needs_ffmpeg_cli
932970
@pytest.mark.parametrize(
933971
"format",
934972
(
@@ -1150,10 +1188,7 @@ def write(self, data):
11501188
):
11511189
encoder.to_file_like(NoSeekMethod(), format="mp4")
11521190

1153-
@pytest.mark.skipif(
1154-
in_fbcode(),
1155-
reason="ffprobe not available internally",
1156-
)
1191+
@needs_ffmpeg_cli
11571192
@pytest.mark.parametrize(
11581193
"format,codec_spec",
11591194
[
@@ -1181,10 +1216,7 @@ def test_codec_parameter_utilized(self, tmp_path, format, codec_spec):
11811216
]
11821217
assert actual_codec_spec == codec_spec
11831218

1184-
@pytest.mark.skipif(
1185-
in_fbcode(),
1186-
reason="ffprobe not available internally",
1187-
)
1219+
@needs_ffmpeg_cli
11881220
@pytest.mark.parametrize(
11891221
"codec_spec,codec_impl",
11901222
[
@@ -1227,7 +1259,7 @@ def test_codec_spec_vs_impl_equivalence(self, tmp_path, codec_spec, codec_impl):
12271259
frames_impl = self.decode(impl_output)
12281260
torch.testing.assert_close(frames_spec, frames_impl, rtol=0, atol=0)
12291261

1230-
@pytest.mark.skipif(in_fbcode(), reason="ffprobe not available")
1262+
@needs_ffmpeg_cli
12311263
@pytest.mark.parametrize(
12321264
"profile,colorspace,color_range",
12331265
[

0 commit comments

Comments
 (0)