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

Add AV2 support #1361

Merged
merged 25 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
071b312
Duplicate ext/avm.cmd, src/codec_avm.c and ci-unix
y-guyon Apr 12, 2023
4e626f3
Add avm as a codec dependency in ext/
y-guyon Apr 12, 2023
4ab0ead
Add AV2 support
y-guyon Apr 18, 2023
2449a7b
Apply CMake-format
y-guyon Apr 24, 2023
ea69b6d
Add experiment warning when AVIF_CODEC_AVM is ON
y-guyon Apr 24, 2023
1d99bc4
Modify comment in obu.c about AV2 spec
y-guyon Apr 26, 2023
6a98d37
Check aom_codec_version() for avm anchor 4
y-guyon Apr 26, 2023
f90d660
Ignore av02 items and tracks unless AVIF_CODEC_AVM
y-guyon Apr 26, 2023
507df43
Replace function pointer in obu.c by boolean
y-guyon Apr 27, 2023
2bddab1
avifEncoderUseAV2() in write.c
y-guyon Apr 27, 2023
0c8f953
Use uint8_t* instead of [4] in read.c
y-guyon Apr 27, 2023
ca55e47
Use snprintf instead of memcpy in read.c
y-guyon Apr 27, 2023
acbd773
Guard av2C by AVIF_CODEC_AVM in read.c
y-guyon Apr 27, 2023
edc65ff
Add avifCodecType in internal.h
y-guyon Apr 27, 2023
4a7db05
Add entries to CHANGELOG.md
y-guyon Apr 27, 2023
e85667b
Apply clang-format
y-guyon Apr 27, 2023
042eb58
Remove internal changes from CHANGELOG.md
y-guyon Apr 28, 2023
f885a69
Replace AVIF_CHECKERR by if conditions
y-guyon Apr 28, 2023
4f4c1d8
Check MIAF grid constraints at decoding
y-guyon Apr 28, 2023
8c61709
Skip unrelevant tiles in DecoderItemValidateProps
y-guyon Apr 28, 2023
0a6577f
Revert avifTilesCanBeDecodedWithSameCodecInstance
y-guyon Apr 28, 2023
261a053
Add avmScaleQuantizer to codec_avm.c
y-guyon May 2, 2023
f63da74
Replace libaom mentions by libavm in codec_avm.c
y-guyon May 3, 2023
854e19e
Add more test cases in avifavmtest
y-guyon May 3, 2023
ca0250a
Remove AVIF_CODEC_TYPE_COUNT
y-guyon May 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/ci-unix-static-av2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: CI
on: [push, pull_request]

permissions:
contents: read

jobs:
build-static-av2:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
also-enable-av1-codecs: [ON, OFF] # On top of enabling AV2 codecs.

steps:
- uses: actions/checkout@v3
- name: Set GCC & G++ 10 compiler (on Linux)
if: runner.os == 'Linux'
run: echo "CC=gcc-10" >> $GITHUB_ENV && echo "CXX=g++-10" >> $GITHUB_ENV
- uses: actions/setup-python@v4
with:
python-version: '3.x'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true

- name: Cache external dependencies
id: cache-ext
uses: actions/cache@v3
with:
path: ext
key: ${{ runner.os }}-static-av2-${{ hashFiles('ext/*.cmd') }}
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.13
with:
# CMake version 3.17 is required to build libwebp (which libsharpyuv is part of) on macOS.
cmake-version: '3.17.x'
- name: Print cmake version
run: cmake --version
- uses: ilammy/setup-nasm@v1
with:
version: 2.15.05
- uses: seanmiddleditch/gha-setup-ninja@v3
- run: pip install meson
- name: Build avm
if: steps.cache-ext.outputs.cache-hit != 'true'
working-directory: ./ext
run: bash avm.cmd
- name: Build dav1d
if: steps.cache-ext.outputs.cache-hit != 'true'
working-directory: ./ext
run: bash dav1d.cmd
- name: Build rav1e
if: steps.cache-ext.outputs.cache-hit != 'true'
working-directory: ./ext
run: bash rav1e.cmd
- name: Build libyuv
if: steps.cache-ext.outputs.cache-hit != 'true'
working-directory: ./ext
run: bash libyuv.cmd
- name: Build libsharpyuv
if: steps.cache-ext.outputs.cache-hit != 'true'
working-directory: ./ext
run: bash libsharpyuv.cmd
- name: Build GoogleTest
if: steps.cache-ext.outputs.cache-hit != 'true'
working-directory: ./ext
# Note: "apt install googletest" is sometimes insufficient for find_package(GTest) so build in ext/ instead.
run: bash googletest.cmd

- name: Prepare libavif (cmake)
run: >
mkdir build && cd build

cmake .. -G Ninja
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF
-DAVIF_CODEC_AVM=ON -DAVIF_LOCAL_AVM=ON
-DAVIF_CODEC_DAV1D=${{ runner.also-enable-av1-codecs }} -DAVIF_LOCAL_DAV1D=ON
-DAVIF_CODEC_RAV1E=${{ runner.also-enable-av1-codecs }} -DAVIF_LOCAL_RAV1E=ON
-DAVIF_LOCAL_LIBYUV=ON
-DAVIF_LOCAL_LIBSHARPYUV=ON
-DAVIF_BUILD_EXAMPLES=ON -DAVIF_BUILD_APPS=ON
-DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_LOCAL_GTEST=ON
- name: Build libavif (ninja)
working-directory: ./build
run: ninja
- name: Run AVIF Tests
working-directory: ./build
run: ctest -j $(getconf _NPROCESSORS_ONLN) --output-on-failure
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/build*
/obj*
/ext/aom
/ext/avm
/ext/dav1d
/ext/googletest
/ext/libjpeg
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ List of incompatible ABI changes in this release:
* Add the public API function avifImageIsOpaque() in avif.h.
* Add experimental API for progressive AVIF encoding.
* Add API for multi-threaded YUV to RGB color conversion.
* Add experimental support for AV2 behind the compilation flag AVIF_CODEC_AVM.
AVIF_CODEC_CHOICE_AVM is now part of avifCodecChoice.

### Changed
* Exif and XMP metadata is exported to PNG and JPEG files by default,
Expand Down
45 changes: 44 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ option(AVIF_CODEC_DAV1D "Use the dav1d codec for decoding" OFF)
option(AVIF_CODEC_LIBGAV1 "Use the libgav1 codec for decoding" OFF)
option(AVIF_CODEC_RAV1E "Use the rav1e codec for encoding" OFF)
option(AVIF_CODEC_SVT "Use the SVT-AV1 codec for encoding" OFF)
option(AVIF_CODEC_AVM "Use the AVM (AV2) codec for encoding/decoding (EXPERIMENTAL)" OFF)

# These options allow libavif to only link against / use libaom's encoder or decoder, instead of being forced to use both
option(AVIF_CODEC_AOM_DECODE "if AVIF_CODEC_AOM is on, use/offer libaom's decoder" ON)
Expand All @@ -59,6 +60,9 @@ option(AVIF_LOCAL_SVT
option(AVIF_LOCAL_GTEST
"Build the GoogleTest framework by providing your own copy of the repo in ext/googletest (see Local Builds in README)" OFF
)
option(AVIF_LOCAL_AVM "Build the AVM (AV2) codec by providing your own copy of the repo in ext/avm (see Local Builds in README)"
OFF
)

if(AVIF_LOCAL_LIBGAV1)
enable_language(CXX)
Expand Down Expand Up @@ -445,7 +449,46 @@ if(AVIF_CODEC_AOM)
message(STATUS "libavif: Codec enabled: aom (${AVIF_CODEC_AOM_ENCODE_DECODE_CONFIG})")
endif()

if(NOT AVIF_CODEC_AOM AND NOT AVIF_CODEC_DAV1D AND NOT AVIF_CODEC_LIBGAV1)
if(AVIF_CODEC_AVM)
message(WARNING "libavif: AV2 support with avm is experimental. Only use for testing.")

# The avm repository is a fork of aom and inherited a lot of folders, files and build artifacts named the same way.
# Having both dependencies at the same time generates conflicts in includes, binary lookups etc.
wantehchang marked this conversation as resolved.
Show resolved Hide resolved
if(AVIF_CODEC_AOM)
wantehchang marked this conversation as resolved.
Show resolved Hide resolved
message(FATAL_ERROR "libavif: aom conflicts with avm, bailing out")
endif()

y-guyon marked this conversation as resolved.
Show resolved Hide resolved
set(AVIF_CODEC_DEFINITIONS ${AVIF_CODEC_DEFINITIONS} -DAVIF_CODEC_AVM=1)
set(AVIF_SRCS ${AVIF_SRCS} src/codec_avm.c)
if(AVIF_LOCAL_AVM)
# Building the avm repository generates files such as "libaom.a" because it is a fork of aom.
set(LIB_FILENAME "${CMAKE_CURRENT_SOURCE_DIR}/ext/avm/build.libavif/${AVIF_LIBRARY_PREFIX}aom${AVIF_LIBRARY_SUFFIX}")
if(NOT EXISTS "${LIB_FILENAME}")
message(FATAL_ERROR "libavif: ${LIB_FILENAME} (from avm) is missing, bailing out")
endif()

set(AVIF_CODEC_INCLUDES ${AVIF_CODEC_INCLUDES} "${CMAKE_CURRENT_SOURCE_DIR}/ext/avm")
set(AVIF_CODEC_LIBRARIES ${AVIF_CODEC_LIBRARIES} ${LIB_FILENAME})

# ext/avm/aom/aom_encoder.h includes config/aom_config.h which is generated by the local build of avm.
set(AVIF_CODEC_INCLUDES ${AVIF_CODEC_INCLUDES} "${CMAKE_CURRENT_SOURCE_DIR}/ext/avm/build.libavif")
else()
# Check to see if avm is independently being built by the outer CMake project
if(NOT TARGET avm)
find_package(avm REQUIRED)
set(AVIF_CODEC_INCLUDES ${AVIF_CODEC_INCLUDES} ${AVM_INCLUDE_DIR})
endif()
set(AVIF_CODEC_LIBRARIES ${AVIF_CODEC_LIBRARIES} ${AVM_LIBRARIES})
endif()

message(STATUS "libavif: Codec enabled: avm (encode/decode)")
endif()

if(NOT AVIF_CODEC_AOM
AND NOT AVIF_CODEC_DAV1D
AND NOT AVIF_CODEC_LIBGAV1
AND NOT AVIF_CODEC_AVM
)
message(WARNING "libavif: No decoding library is enabled.")
endif()

Expand Down
25 changes: 25 additions & 0 deletions ext/avm.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
: # If you want to use a local build of libavm, you must clone the avm repo in this directory first, then enable CMake's AVIF_CODEC_AVM and AVIF_LOCAL_AVM options.
: # The git SHA below is known to work, and will occasionally be updated. Feel free to use a more recent commit.

: # The odd choice of comment style in this file is to try to share this script between *nix and win32.

: # cmake and ninja must be in your PATH.

: # If you're running this on Windows, be sure you've already run this (from your VC2019 install dir):
: # "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars64.bat"

git clone -b research-v4.0.0 --depth 1 https://gitlab.com/AOMediaCodec/avm.git

cd avm

: # The following fix avoids errors such as "libaom.a: in function `write_frame_hash': bitstream.c: undefined reference to `MD5Init'"
: # TODO(yguyon): Fix in avm and remove from here
sed 's-"${AOM_ROOT}/av1/encoder/dwt.h"-"${AOM_ROOT}/av1/encoder/dwt.h"\n "${AOM_ROOT}/common/md5_utils.c"\n "${AOM_ROOT}/common/md5_utils.h"\n "${AOM_ROOT}/common/rawenc.c"\n "${AOM_ROOT}/common/rawenc.h"-g' av1/av1.cmake > av1/av1.cmake.sed
mv av1/av1.cmake.sed av1/av1.cmake

mkdir build.libavif
cd build.libavif

cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DENABLE_DOCS=0 -DENABLE_EXAMPLES=0 -DENABLE_TESTS=0 -DENABLE_TOOLS=0 ..
ninja
cd ../..
5 changes: 3 additions & 2 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ typedef enum avifResult
AVIF_RESULT_ENCODE_COLOR_FAILED,
AVIF_RESULT_ENCODE_ALPHA_FAILED,
AVIF_RESULT_BMFF_PARSE_FAILED,
AVIF_RESULT_NO_AV1_ITEMS_FOUND,
AVIF_RESULT_NO_AV1_ITEMS_FOUND, // TODO(yguyon): Rename or add AVIF_RESULT_NO_AV2_ITEMS_FOUND
wantehchang marked this conversation as resolved.
Show resolved Hide resolved
AVIF_RESULT_DECODE_COLOR_FAILED,
AVIF_RESULT_DECODE_ALPHA_FAILED,
AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH,
Expand Down Expand Up @@ -717,7 +717,8 @@ typedef enum avifCodecChoice
AVIF_CODEC_CHOICE_DAV1D, // Decode only
AVIF_CODEC_CHOICE_LIBGAV1, // Decode only
AVIF_CODEC_CHOICE_RAV1E, // Encode only
AVIF_CODEC_CHOICE_SVT // Encode only
AVIF_CODEC_CHOICE_SVT, // Encode only
AVIF_CODEC_CHOICE_AVM // Experimental (AV2)
} avifCodecChoice;

typedef enum avifCodecFlag
Expand Down
28 changes: 25 additions & 3 deletions include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,24 @@ void avifCodecSpecificOptionsDestroy(avifCodecSpecificOptions * csOptions);
avifResult avifCodecSpecificOptionsSet(avifCodecSpecificOptions * csOptions, const char * key, const char * value); // if(value==NULL), key is deleted

// ---------------------------------------------------------------------------
// avifCodec (abstraction layer to use different AV1 implementations)
// avifCodecType (underlying video format)

// Alliance for Open Media video formats that can be used in the AVIF image format.
typedef enum avifCodecType
{
AVIF_CODEC_TYPE_UNKNOWN,
AVIF_CODEC_TYPE_AV1,
#if defined(AVIF_CODEC_AVM)
AVIF_CODEC_TYPE_AV2, // Experimental.
#endif
AVIF_CODEC_TYPE_COUNT
y-guyon marked this conversation as resolved.
Show resolved Hide resolved
} avifCodecType;

// Returns AVIF_CODEC_TYPE_UNKNOWN unless the chosen codec is available with the requiredFlags.
avifCodecType avifCodecTypeFromChoice(avifCodecChoice choice, avifCodecFlags requiredFlags);

// ---------------------------------------------------------------------------
// avifCodec (abstraction layer to use different codec implementations)

struct avifCodec;
struct avifCodecInternal;
Expand Down Expand Up @@ -388,6 +405,8 @@ avifCodec * avifCodecCreateRav1e(void); // requires AVIF_CODEC_RAV1E (codec_ra
const char * avifCodecVersionRav1e(void); // requires AVIF_CODEC_RAV1E (codec_rav1e.c)
avifCodec * avifCodecCreateSvt(void); // requires AVIF_CODEC_SVT (codec_svt.c)
const char * avifCodecVersionSvt(void); // requires AVIF_CODEC_SVT (codec_svt.c)
avifCodec * avifCodecCreateAVM(void); // requires AVIF_CODEC_AVM (codec_avm.c)
const char * avifCodecVersionAVM(void); // requires AVIF_CODEC_AVM (codec_avm.c)

// ---------------------------------------------------------------------------
// avifDiagnostics
Expand Down Expand Up @@ -465,6 +484,7 @@ void avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount);
// This is to make it clear that the box size is currently unknown, and will be determined later (with a call to avifRWStreamFinishBox)
#define AVIF_BOX_SIZE_TBD 0

// Used for both av1C and av2C.
typedef struct avifCodecConfigurationBox
{
// [skipped; is constant] unsigned int (1)marker = 1;
Expand All @@ -491,6 +511,7 @@ typedef struct avifCodecConfigurationBox

typedef struct avifSequenceHeader
{
uint8_t reduced_still_picture_header;
uint32_t maxWidth;
uint32_t maxHeight;
uint32_t bitDepth;
Expand All @@ -500,9 +521,10 @@ typedef struct avifSequenceHeader
avifTransferCharacteristics transferCharacteristics;
avifMatrixCoefficients matrixCoefficients;
avifRange range;
avifCodecConfigurationBox av1C;
avifCodecConfigurationBox av1C; // TODO(yguyon): Rename or add av2C
} avifSequenceHeader;
avifBool avifSequenceHeaderParse(avifSequenceHeader * header, const avifROData * sample);

avifBool avifSequenceHeaderParse(avifSequenceHeader * header, const avifROData * sample, avifCodecType codecType);

#define AVIF_INDEFINITE_DURATION64 UINT64_MAX
#define AVIF_INDEFINITE_DURATION32 UINT32_MAX
Expand Down
28 changes: 23 additions & 5 deletions src/avif.c
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ typedef avifCodec * (*avifCodecCreateFunc)(void);
struct AvailableCodec
{
avifCodecChoice choice;
avifCodecType type;
const char * name;
versionFunc version;
avifCodecCreateFunc create;
Expand All @@ -977,13 +978,14 @@ static struct AvailableCodec availableCodecs[] = {
// Ordered by preference (for AUTO)

#if defined(AVIF_CODEC_DAV1D)
{ AVIF_CODEC_CHOICE_DAV1D, "dav1d", avifCodecVersionDav1d, avifCodecCreateDav1d, AVIF_CODEC_FLAG_CAN_DECODE },
{ AVIF_CODEC_CHOICE_DAV1D, AVIF_CODEC_TYPE_AV1, "dav1d", avifCodecVersionDav1d, avifCodecCreateDav1d, AVIF_CODEC_FLAG_CAN_DECODE },
#endif
#if defined(AVIF_CODEC_LIBGAV1)
{ AVIF_CODEC_CHOICE_LIBGAV1, "libgav1", avifCodecVersionGav1, avifCodecCreateGav1, AVIF_CODEC_FLAG_CAN_DECODE },
{ AVIF_CODEC_CHOICE_LIBGAV1, AVIF_CODEC_TYPE_AV1, "libgav1", avifCodecVersionGav1, avifCodecCreateGav1, AVIF_CODEC_FLAG_CAN_DECODE },
#endif
#if defined(AVIF_CODEC_AOM)
{ AVIF_CODEC_CHOICE_AOM,
AVIF_CODEC_TYPE_AV1,
"aom",
avifCodecVersionAOM,
avifCodecCreateAOM,
Expand All @@ -999,12 +1001,15 @@ static struct AvailableCodec availableCodecs[] = {
},
#endif
#if defined(AVIF_CODEC_RAV1E)
{ AVIF_CODEC_CHOICE_RAV1E, "rav1e", avifCodecVersionRav1e, avifCodecCreateRav1e, AVIF_CODEC_FLAG_CAN_ENCODE },
{ AVIF_CODEC_CHOICE_RAV1E, AVIF_CODEC_TYPE_AV1, "rav1e", avifCodecVersionRav1e, avifCodecCreateRav1e, AVIF_CODEC_FLAG_CAN_ENCODE },
#endif
#if defined(AVIF_CODEC_SVT)
{ AVIF_CODEC_CHOICE_SVT, "svt", avifCodecVersionSvt, avifCodecCreateSvt, AVIF_CODEC_FLAG_CAN_ENCODE },
{ AVIF_CODEC_CHOICE_SVT, AVIF_CODEC_TYPE_AV1, "svt", avifCodecVersionSvt, avifCodecCreateSvt, AVIF_CODEC_FLAG_CAN_ENCODE },
#endif
{ AVIF_CODEC_CHOICE_AUTO, NULL, NULL, NULL, 0 }
#if defined(AVIF_CODEC_AVM)
{ AVIF_CODEC_CHOICE_AVM, AVIF_CODEC_TYPE_AV2, "avm", avifCodecVersionAVM, avifCodecCreateAVM, AVIF_CODEC_FLAG_CAN_DECODE | AVIF_CODEC_FLAG_CAN_ENCODE },
#endif
{ AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_TYPE_UNKNOWN, NULL, NULL, NULL, 0 }
};

static const int availableCodecsCount = (sizeof(availableCodecs) / sizeof(availableCodecs[0])) - 1;
Expand All @@ -1018,6 +1023,10 @@ static struct AvailableCodec * findAvailableCodec(avifCodecChoice choice, avifCo
if (requiredFlags && ((availableCodecs[i].flags & requiredFlags) != requiredFlags)) {
continue;
}
if ((choice == AVIF_CODEC_CHOICE_AUTO) && (availableCodecs[i].choice == AVIF_CODEC_CHOICE_AVM)) {
// AV2 is experimental and cannot be the default, it must be explicitly selected.
continue;
}
return &availableCodecs[i];
}
return NULL;
Expand All @@ -1032,6 +1041,15 @@ const char * avifCodecName(avifCodecChoice choice, avifCodecFlags requiredFlags)
return NULL;
}

avifCodecType avifCodecTypeFromChoice(avifCodecChoice choice, avifCodecFlags requiredFlags)
{
struct AvailableCodec * availableCodec = findAvailableCodec(choice, requiredFlags);
if (availableCodec) {
y-guyon marked this conversation as resolved.
Show resolved Hide resolved
return availableCodec->type;
}
return AVIF_CODEC_TYPE_UNKNOWN;
}

avifCodecChoice avifCodecChoiceFromName(const char * name)
{
for (int i = 0; i < availableCodecsCount; ++i) {
Expand Down