Skip to content

Commit 707ea13

Browse files
authored
Adding Mac to S3 non-GPL FFmpeg compile (#210)
1 parent 94c5114 commit 707ea13

File tree

6 files changed

+242
-37
lines changed

6 files changed

+242
-37
lines changed
File renamed without changes.

.github/workflows/macos_wheel.yaml

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Build and test MacOS
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- nightly
8+
- main
9+
- release/*
10+
tags:
11+
- v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+
12+
workflow_dispatch:
13+
14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}
16+
cancel-in-progress: true
17+
18+
permissions:
19+
id-token: write
20+
contents: write
21+
22+
defaults:
23+
run:
24+
shell: bash -l -eo pipefail {0}
25+
26+
jobs:
27+
install-and-test:
28+
runs-on: macos-m1-stable
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
python-version: ['3.9']
33+
ffmpeg-version-for-tests: ['4.4.2', '5.1.2', '6.1.1', '7.0.1']
34+
if: ${{ always() }}
35+
steps:
36+
- name: Setup conda env
37+
uses: conda-incubator/setup-miniconda@v3
38+
with:
39+
auto-update-conda: true
40+
miniconda-version: "latest"
41+
activate-environment: test
42+
python-version: ${{ matrix.python-version }}
43+
- name: Update pip
44+
run: python -m pip install --upgrade pip
45+
- name: Install PyTorch
46+
run: |
47+
python -m pip install --pre torch --index-url https://download.pytorch.org/whl/nightly/cpu
48+
- name: Check out repo
49+
uses: actions/checkout@v3
50+
- name: Install compile from source dependencies
51+
run: |
52+
conda install cmake pkg-config -c conda-forge
53+
- name: Install test dependencies
54+
run: |
55+
python -m pip install --pre torchvision --index-url https://download.pytorch.org/whl/nightly/cpu
56+
# Ideally we would find a way to get those dependencies from pyproject.toml
57+
python -m pip install numpy pytest pillow
58+
- name: Install torchcodec from source, building against non-GPL FFmpeg
59+
run: |
60+
BUILD_AGAINST_ALL_FFMPEG_FROM_S3=1 pip install -e ".[dev]" --no-build-isolation
61+
- name: Install ffmpeg, post build
62+
run: |
63+
# Ideally we would have checked for that before installing the wheel,
64+
# but we need to checkout the repo to access this file, and we don't
65+
# want to checkout the repo before installing the wheel to avoid any
66+
# side-effect. It's OK.
67+
source packaging/helpers.sh
68+
assert_ffmpeg_not_installed
69+
70+
conda install "ffmpeg=${{ matrix.ffmpeg-version-for-tests }}" -c conda-forge
71+
ffmpeg -version
72+
- name: Smoke test
73+
run: |
74+
python test/decoders/manual_smoke_test.py
75+
- name: Run Python tests
76+
run: |
77+
pytest test -vvv

src/torchcodec/decoders/_core/VideoDecoderOps.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <cstdint>
1010
#include <sstream>
1111
#include <string>
12+
#include "c10/util/Exception.h"
1213
#include "c10/core/SymIntArrayRef.h"
1314
#include "src/torchcodec/decoders/_core/VideoDecoder.h"
1415

@@ -144,7 +145,7 @@ OpsDecodedOutput get_next_frame(at::Tensor& decoder) {
144145
try {
145146
result = videoDecoder->getNextDecodedOutputNoDemux();
146147
} catch (const VideoDecoder::EndOfFileException& e) {
147-
throw pybind11::stop_iteration(e.what());
148+
C10_THROW_ERROR(IndexError, e.what());
148149
}
149150
if (result.frame.sizes().size() != 3) {
150151
throw std::runtime_error(

src/torchcodec/decoders/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake

+151-29
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,150 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
88
endif()
99

1010
include(FetchContent)
11+
1112
set(
1213
base_url
13-
https://pytorch.s3.amazonaws.com/torchcodec/ffmpeg/2024-06-11/linux_x86_64
14+
https://pytorch.s3.amazonaws.com/torchcodec/ffmpeg/2024-09-13
1415
)
16+
17+
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
18+
set(
19+
platform_url
20+
${base_url}/linux_x86_64
21+
)
22+
23+
set(
24+
f4_sha256
25+
07d3e33281f0dce04d3e987d20cce03b155b0c39965333960689c625f451f93a
26+
)
27+
set(
28+
f5_sha256
29+
1a2227445f513deb8f4f339050a160fa2419ca494a7f981df93e747d00eeaa69
30+
)
31+
set(
32+
f6_sha256
33+
63320ec05ae9341ba307ff0005ac853bcec0b9d2cb55a580d1a72731de2bb5d8
34+
)
35+
set(
36+
f7_sha256
37+
0b7c983b5d675441a1c1756eefa23cb24450af6bae5ae2011d9e5807a315d7df
38+
)
39+
40+
set(
41+
f4_library_file_names
42+
libavutil.so.56
43+
libavcodec.so.58
44+
libavformat.so.58
45+
libavdevice.so.58
46+
libavfilter.so.7
47+
)
48+
set(
49+
f5_library_file_names
50+
libavutil.so.57
51+
libavcodec.so.59
52+
libavformat.so.59
53+
libavdevice.so.59
54+
libavfilter.so.8
55+
)
56+
set(
57+
f6_library_file_names
58+
libavutil.so.58
59+
libavcodec.so.60
60+
libavformat.so.60
61+
libavdevice.so.60
62+
libavfilter.so.9
63+
)
64+
set(
65+
f7_library_file_names
66+
libavutil.so.59
67+
libavcodec.so.61
68+
libavformat.so.61
69+
libavdevice.so.61
70+
libavfilter.so.10
71+
)
72+
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
73+
set(
74+
platform_url
75+
${base_url}/macos_arm64
76+
)
77+
set(
78+
f4_sha256
79+
7839bebecb9a25f470405a745225d29a5a7f43f4e6d9a57868732aa897ce32be
80+
)
81+
set(
82+
f5_sha256
83+
df204c89ae52d3af16eb23604955e8cfbee649845d3ae737778a264346ab0063
84+
)
85+
set(
86+
f6_sha256
87+
8a82e9ae2eabb23ba546e2c96ba7f1bd656b4db38679876df936db7a92c15677
88+
)
89+
set(
90+
f7_sha256
91+
39d96d8191c58ff439d674701d83c775b2b57019a1c2436aa78e7bc9ab74445b
92+
)
93+
set(
94+
f4_library_file_names
95+
libavutil.56.dylib
96+
libavcodec.58.dylib
97+
libavformat.58.dylib
98+
libavdevice.58.dylib
99+
libavfilter.7.dylib
100+
)
101+
set(
102+
f5_library_file_names
103+
libavutil.57.dylib
104+
libavcodec.59.dylib
105+
libavformat.59.dylib
106+
libavdevice.59.dylib
107+
libavfilter.8.dylib
108+
)
109+
set(
110+
f6_library_file_names
111+
libavutil.58.dylib
112+
libavcodec.60.dylib
113+
libavformat.60.dylib
114+
libavdevice.60.dylib
115+
libavfilter.9.dylib
116+
)
117+
set(
118+
f7_library_file_names
119+
libavutil.59.dylib
120+
libavcodec.61.dylib
121+
libavformat.61.dylib
122+
libavdevice.61.dylib
123+
libavfilter.10.dylib
124+
)
125+
else()
126+
message(
127+
FATAL_ERROR
128+
"Unsupported operating system: ${CMAKE_SYSTEM_NAME}"
129+
)
130+
endif()
131+
15132
FetchContent_Declare(
16133
f4
17-
URL ${base_url}/ffmpeg_4.4.4.tar.gz
134+
URL ${platform_url}/4.4.4.tar.gz
18135
URL_HASH
19-
SHA256=a564721e51038d01ead4bbc7a482398929101ca4c80e5ce5c42042637235a297
136+
SHA256=${f4_sha256}
20137
)
21138
FetchContent_Declare(
22139
f5
23-
URL ${base_url}/ffmpeg_5.1.4.tar.gz
140+
URL ${platform_url}/5.1.4.tar.gz
24141
URL_HASH
25-
SHA256=d9c2d3a355c091ddc3205ae73426d0d6402ad8a31212dc920daabbaa5fdae944
142+
SHA256=${f5_sha256}
26143
)
27144
FetchContent_Declare(
28145
f6
29-
URL ${base_url}/ffmpeg_6.1.1.tar.gz
146+
URL ${platform_url}/6.1.1.tar.gz
30147
URL_HASH
31-
SHA256=7ee5830dc09fed7270aa575650474ab16e18477551e5511f256ce92daa30b136
148+
SHA256=${f6_sha256}
32149
)
33150
FetchContent_Declare(
34151
f7
35-
URL ${base_url}/ffmpeg_7.0.1.tar.gz
152+
URL ${platform_url}/7.0.1.tar.gz
36153
URL_HASH
37-
SHA256=fa4cda7aa67fcd58428017f7ebd2a981b0c6babba7ec89f71d6840877712ddcd
154+
SHA256=${f7_sha256}
38155
)
39156

40157
FetchContent_MakeAvailable(f4 f5 f6 f7)
@@ -50,39 +167,44 @@ target_include_directories(ffmpeg5 INTERFACE ${f5_SOURCE_DIR}/include)
50167
target_include_directories(ffmpeg6 INTERFACE ${f6_SOURCE_DIR}/include)
51168
target_include_directories(ffmpeg7 INTERFACE ${f7_SOURCE_DIR}/include)
52169

170+
list(
171+
TRANSFORM f4_library_file_names
172+
PREPEND ${f4_SOURCE_DIR}/lib/
173+
OUTPUT_VARIABLE f4_library_paths
174+
)
175+
list(
176+
TRANSFORM f5_library_file_names
177+
PREPEND ${f5_SOURCE_DIR}/lib/
178+
OUTPUT_VARIABLE f5_library_paths
179+
)
180+
list(
181+
TRANSFORM f6_library_file_names
182+
PREPEND ${f6_SOURCE_DIR}/lib/
183+
OUTPUT_VARIABLE f6_library_paths
184+
)
185+
list(
186+
TRANSFORM f7_library_file_names
187+
PREPEND ${f7_SOURCE_DIR}/lib/
188+
OUTPUT_VARIABLE f7_library_paths
189+
)
190+
53191
target_link_libraries(
54192
ffmpeg4
55193
INTERFACE
56-
${f4_SOURCE_DIR}/lib/libavutil.so.56
57-
${f4_SOURCE_DIR}/lib/libavcodec.so.58
58-
${f4_SOURCE_DIR}/lib/libavformat.so.58
59-
${f4_SOURCE_DIR}/lib/libavdevice.so.58
60-
${f4_SOURCE_DIR}/lib/libavfilter.so.7
194+
${f4_library_paths}
61195
)
62196
target_link_libraries(
63197
ffmpeg5
64198
INTERFACE
65-
${f5_SOURCE_DIR}/lib/libavutil.so.57
66-
${f5_SOURCE_DIR}/lib/libavcodec.so.59
67-
${f5_SOURCE_DIR}/lib/libavformat.so.59
68-
${f5_SOURCE_DIR}/lib/libavdevice.so.59
69-
${f5_SOURCE_DIR}/lib/libavfilter.so.8
199+
${f5_library_paths}
70200
)
71201
target_link_libraries(
72202
ffmpeg6
73203
INTERFACE
74-
${f6_SOURCE_DIR}/lib/libavutil.so.58
75-
${f6_SOURCE_DIR}/lib/libavcodec.so.60
76-
${f6_SOURCE_DIR}/lib/libavformat.so.60
77-
${f6_SOURCE_DIR}/lib/libavdevice.so.60
78-
${f6_SOURCE_DIR}/lib/libavfilter.so.9
204+
${f6_library_paths}
79205
)
80206
target_link_libraries(
81207
ffmpeg7
82208
INTERFACE
83-
${f7_SOURCE_DIR}/lib/libavutil.so.59
84-
${f7_SOURCE_DIR}/lib/libavcodec.so.61
85-
${f7_SOURCE_DIR}/lib/libavformat.so.61
86-
${f7_SOURCE_DIR}/lib/libavdevice.so.61
87-
${f7_SOURCE_DIR}/lib/libavfilter.so.10
209+
${f7_library_paths}
88210
)

test/decoders/test_video_decoder_ops.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,15 @@ def test_throws_exception_at_eof(self):
178178
last_frame, _, _ = get_next_frame(decoder)
179179
reference_last_frame = NASA_VIDEO.get_frame_by_name("time12.979633")
180180
assert_tensor_equal(last_frame, reference_last_frame)
181-
with pytest.raises(StopIteration, match="no more frames"):
181+
with pytest.raises(IndexError, match="no more frames"):
182182
get_next_frame(decoder)
183183

184184
def test_throws_exception_if_seek_too_far(self):
185185
decoder = create_from_file(str(NASA_VIDEO.path))
186186
add_video_stream(decoder)
187187
# pts=12.979633 is the last frame in the video.
188188
seek_to_pts(decoder, 12.979633 + 1.0e-4)
189-
with pytest.raises(StopIteration, match="no more frames"):
189+
with pytest.raises(IndexError, match="no more frames"):
190190
get_next_frame(decoder)
191191

192192
def test_compile_seek_and_next(self):

test/utils.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import importlib
22
import os
33
import pathlib
4+
import sys
45

56
from dataclasses import dataclass
67
from typing import Dict
@@ -10,12 +11,16 @@
1011
import torch
1112

1213

13-
# For use with decoded data frames, or in other instances were we are confident that
14-
# reference and test tensors should be exactly equal. This is true for decoded data
15-
# frames from media because we expect our decoding to exactly match what a user can
16-
# do on the command line with ffmpeg.
14+
# For use with decoded data frames. On Linux, we expect exact, bit-for-bit equality. On
15+
# all other platforms, we allow a small tolerance. FFmpeg does not guarantee bit-for-bit
16+
# equality across systems and architectures, so we also cannot. We currently use Linux
17+
# on x86_64 as our reference system.
1718
def assert_tensor_equal(*args, **kwargs):
18-
torch.testing.assert_close(*args, **kwargs, atol=0, rtol=0)
19+
if sys.platform == "linux":
20+
absolute_tolerance = 0
21+
else:
22+
absolute_tolerance = 3
23+
torch.testing.assert_close(*args, **kwargs, atol=absolute_tolerance, rtol=0)
1924

2025

2126
# For use with floating point metadata, or in other instances where we are not confident

0 commit comments

Comments
 (0)