Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Mac to S3 non-GPL FFmpeg compile #210

Merged
merged 1 commit into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
77 changes: 77 additions & 0 deletions .github/workflows/macos_wheel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Build and test MacOS

on:
pull_request:
push:
branches:
- nightly
- main
- release/*
tags:
- v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}
cancel-in-progress: true

permissions:
id-token: write
contents: write

defaults:
run:
shell: bash -l -eo pipefail {0}

jobs:
install-and-test:
runs-on: macos-m1-stable
strategy:
fail-fast: false
matrix:
python-version: ['3.9']
ffmpeg-version-for-tests: ['4.4.2', '5.1.2', '6.1.1', '7.0.1']
if: ${{ always() }}
steps:
- name: Setup conda env
uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
miniconda-version: "latest"
activate-environment: test
python-version: ${{ matrix.python-version }}
- name: Update pip
run: python -m pip install --upgrade pip
- name: Install PyTorch
run: |
python -m pip install --pre torch --index-url https://download.pytorch.org/whl/nightly/cpu
- name: Check out repo
uses: actions/checkout@v3
- name: Install compile from source dependencies
run: |
conda install cmake pkg-config -c conda-forge
- name: Install test dependencies
run: |
python -m pip install --pre torchvision --index-url https://download.pytorch.org/whl/nightly/cpu
# Ideally we would find a way to get those dependencies from pyproject.toml
python -m pip install numpy pytest pillow
- name: Install torchcodec from source, building against non-GPL FFmpeg
run: |
BUILD_AGAINST_ALL_FFMPEG_FROM_S3=1 pip install -e ".[dev]" --no-build-isolation
- name: Install ffmpeg, post build
run: |
# Ideally we would have checked for that before installing the wheel,
# but we need to checkout the repo to access this file, and we don't
# want to checkout the repo before installing the wheel to avoid any
# side-effect. It's OK.
source packaging/helpers.sh
assert_ffmpeg_not_installed

conda install "ffmpeg=${{ matrix.ffmpeg-version-for-tests }}" -c conda-forge
ffmpeg -version
- name: Smoke test
run: |
python test/decoders/manual_smoke_test.py
- name: Run Python tests
run: |
pytest test -vvv
3 changes: 2 additions & 1 deletion src/torchcodec/decoders/_core/VideoDecoderOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <cstdint>
#include <sstream>
#include <string>
#include "c10/util/Exception.h"
#include "c10/core/SymIntArrayRef.h"
#include "src/torchcodec/decoders/_core/VideoDecoder.h"

Expand Down Expand Up @@ -144,7 +145,7 @@ OpsDecodedOutput get_next_frame(at::Tensor& decoder) {
try {
result = videoDecoder->getNextDecodedOutputNoDemux();
} catch (const VideoDecoder::EndOfFileException& e) {
throw pybind11::stop_iteration(e.what());
C10_THROW_ERROR(IndexError, e.what());
}
if (result.frame.sizes().size() != 3) {
throw std::runtime_error(
Expand Down
180 changes: 151 additions & 29 deletions src/torchcodec/decoders/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,150 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
endif()

include(FetchContent)

set(
base_url
https://pytorch.s3.amazonaws.com/torchcodec/ffmpeg/2024-06-11/linux_x86_64
https://pytorch.s3.amazonaws.com/torchcodec/ffmpeg/2024-09-13
)

if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(
platform_url
${base_url}/linux_x86_64
)

set(
f4_sha256
07d3e33281f0dce04d3e987d20cce03b155b0c39965333960689c625f451f93a
)
set(
f5_sha256
1a2227445f513deb8f4f339050a160fa2419ca494a7f981df93e747d00eeaa69
)
set(
f6_sha256
63320ec05ae9341ba307ff0005ac853bcec0b9d2cb55a580d1a72731de2bb5d8
)
set(
f7_sha256
0b7c983b5d675441a1c1756eefa23cb24450af6bae5ae2011d9e5807a315d7df
)

set(
f4_library_file_names
libavutil.so.56
libavcodec.so.58
libavformat.so.58
libavdevice.so.58
libavfilter.so.7
)
set(
f5_library_file_names
libavutil.so.57
libavcodec.so.59
libavformat.so.59
libavdevice.so.59
libavfilter.so.8
)
set(
f6_library_file_names
libavutil.so.58
libavcodec.so.60
libavformat.so.60
libavdevice.so.60
libavfilter.so.9
)
set(
f7_library_file_names
libavutil.so.59
libavcodec.so.61
libavformat.so.61
libavdevice.so.61
libavfilter.so.10
)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
set(
platform_url
${base_url}/macos_arm64
)
set(
f4_sha256
7839bebecb9a25f470405a745225d29a5a7f43f4e6d9a57868732aa897ce32be
)
set(
f5_sha256
df204c89ae52d3af16eb23604955e8cfbee649845d3ae737778a264346ab0063
)
set(
f6_sha256
8a82e9ae2eabb23ba546e2c96ba7f1bd656b4db38679876df936db7a92c15677
)
set(
f7_sha256
39d96d8191c58ff439d674701d83c775b2b57019a1c2436aa78e7bc9ab74445b
)
set(
f4_library_file_names
libavutil.56.dylib
libavcodec.58.dylib
libavformat.58.dylib
libavdevice.58.dylib
libavfilter.7.dylib
)
set(
f5_library_file_names
libavutil.57.dylib
libavcodec.59.dylib
libavformat.59.dylib
libavdevice.59.dylib
libavfilter.8.dylib
)
set(
f6_library_file_names
libavutil.58.dylib
libavcodec.60.dylib
libavformat.60.dylib
libavdevice.60.dylib
libavfilter.9.dylib
)
set(
f7_library_file_names
libavutil.59.dylib
libavcodec.61.dylib
libavformat.61.dylib
libavdevice.61.dylib
libavfilter.10.dylib
)
else()
message(
FATAL_ERROR
"Unsupported operating system: ${CMAKE_SYSTEM_NAME}"
)
endif()

FetchContent_Declare(
f4
URL ${base_url}/ffmpeg_4.4.4.tar.gz
URL ${platform_url}/4.4.4.tar.gz
URL_HASH
SHA256=a564721e51038d01ead4bbc7a482398929101ca4c80e5ce5c42042637235a297
SHA256=${f4_sha256}
)
FetchContent_Declare(
f5
URL ${base_url}/ffmpeg_5.1.4.tar.gz
URL ${platform_url}/5.1.4.tar.gz
URL_HASH
SHA256=d9c2d3a355c091ddc3205ae73426d0d6402ad8a31212dc920daabbaa5fdae944
SHA256=${f5_sha256}
)
FetchContent_Declare(
f6
URL ${base_url}/ffmpeg_6.1.1.tar.gz
URL ${platform_url}/6.1.1.tar.gz
URL_HASH
SHA256=7ee5830dc09fed7270aa575650474ab16e18477551e5511f256ce92daa30b136
SHA256=${f6_sha256}
)
FetchContent_Declare(
f7
URL ${base_url}/ffmpeg_7.0.1.tar.gz
URL ${platform_url}/7.0.1.tar.gz
URL_HASH
SHA256=fa4cda7aa67fcd58428017f7ebd2a981b0c6babba7ec89f71d6840877712ddcd
SHA256=${f7_sha256}
)

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

list(
TRANSFORM f4_library_file_names
PREPEND ${f4_SOURCE_DIR}/lib/
OUTPUT_VARIABLE f4_library_paths
)
list(
TRANSFORM f5_library_file_names
PREPEND ${f5_SOURCE_DIR}/lib/
OUTPUT_VARIABLE f5_library_paths
)
list(
TRANSFORM f6_library_file_names
PREPEND ${f6_SOURCE_DIR}/lib/
OUTPUT_VARIABLE f6_library_paths
)
list(
TRANSFORM f7_library_file_names
PREPEND ${f7_SOURCE_DIR}/lib/
OUTPUT_VARIABLE f7_library_paths
)

target_link_libraries(
ffmpeg4
INTERFACE
${f4_SOURCE_DIR}/lib/libavutil.so.56
${f4_SOURCE_DIR}/lib/libavcodec.so.58
${f4_SOURCE_DIR}/lib/libavformat.so.58
${f4_SOURCE_DIR}/lib/libavdevice.so.58
${f4_SOURCE_DIR}/lib/libavfilter.so.7
${f4_library_paths}
)
target_link_libraries(
ffmpeg5
INTERFACE
${f5_SOURCE_DIR}/lib/libavutil.so.57
${f5_SOURCE_DIR}/lib/libavcodec.so.59
${f5_SOURCE_DIR}/lib/libavformat.so.59
${f5_SOURCE_DIR}/lib/libavdevice.so.59
${f5_SOURCE_DIR}/lib/libavfilter.so.8
${f5_library_paths}
)
target_link_libraries(
ffmpeg6
INTERFACE
${f6_SOURCE_DIR}/lib/libavutil.so.58
${f6_SOURCE_DIR}/lib/libavcodec.so.60
${f6_SOURCE_DIR}/lib/libavformat.so.60
${f6_SOURCE_DIR}/lib/libavdevice.so.60
${f6_SOURCE_DIR}/lib/libavfilter.so.9
${f6_library_paths}
)
target_link_libraries(
ffmpeg7
INTERFACE
${f7_SOURCE_DIR}/lib/libavutil.so.59
${f7_SOURCE_DIR}/lib/libavcodec.so.61
${f7_SOURCE_DIR}/lib/libavformat.so.61
${f7_SOURCE_DIR}/lib/libavdevice.so.61
${f7_SOURCE_DIR}/lib/libavfilter.so.10
${f7_library_paths}
)
4 changes: 2 additions & 2 deletions test/decoders/test_video_decoder_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,15 @@ def test_throws_exception_at_eof(self):
last_frame, _, _ = get_next_frame(decoder)
reference_last_frame = NASA_VIDEO.get_frame_by_name("time12.979633")
assert_tensor_equal(last_frame, reference_last_frame)
with pytest.raises(StopIteration, match="no more frames"):
with pytest.raises(IndexError, match="no more frames"):
get_next_frame(decoder)

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

def test_compile_seek_and_next(self):
Expand Down
15 changes: 10 additions & 5 deletions test/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import importlib
import os
import pathlib
import sys

from dataclasses import dataclass
from typing import Dict
Expand All @@ -10,12 +11,16 @@
import torch


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


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