Skip to content

Commit

Permalink
Add AVIF_ENABLE_COMPLIANCE_WARDEN
Browse files Browse the repository at this point in the history
Use github.com/gpac/ComplianceWarden as a dependency to check all
AVIF bitstreams output by avifEncoderFinish().

Add ext/compliance_warden.sh. Introduce AVIF_USE_CXX in
CMakeLists.txt to regroup the use cases requiring C++.
  • Loading branch information
y-guyon authored Sep 21, 2023
1 parent b4fa94b commit ef6da2a
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/obj*
/ext/aom
/ext/avm
/ext/ComplianceWarden
/ext/dav1d
/ext/fuzztest
/ext/googletest
Expand Down
65 changes: 60 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,16 @@ option(AVIF_LOCAL_AVM "Build the AVM (AV2) codec by providing your own copy of t
OFF
)

option(
AVIF_ENABLE_COMPLIANCE_WARDEN
"Check all avifEncoderFinish() output for AVIF specification compliance. Depends on gpac/ComplianceWarden which can be added with ext/compliance_warden.sh"
OFF
)

set(AVIF_USE_CXX OFF)

if(AVIF_LOCAL_LIBGAV1)
enable_language(CXX)
set(AVIF_USE_CXX ON)
endif()

if(APPLE)
Expand Down Expand Up @@ -340,6 +348,45 @@ if(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
list(APPEND AVIF_SRCS src/gainmap.c)
endif()

if(AVIF_ENABLE_COMPLIANCE_WARDEN)
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ext/ComplianceWarden")
message(FATAL_ERROR "AVIF_ENABLE_COMPLIANCE_WARDEN: ext/ComplianceWarden is missing, bailing out")
endif()

set(AVIF_USE_CXX ON)
add_compile_definitions(AVIF_ENABLE_COMPLIANCE_WARDEN)

list(
APPEND
AVIF_SRCS
src/compliance.cc
ext/ComplianceWarden/src/app/cw.cpp
ext/ComplianceWarden/src/app/options.cpp
ext/ComplianceWarden/src/app/report_std.cpp
ext/ComplianceWarden/src/app/report_json.cpp
ext/ComplianceWarden/src/utils/common_boxes.cpp
ext/ComplianceWarden/src/utils/tools.cpp
ext/ComplianceWarden/src/utils/av1_utils.cpp
ext/ComplianceWarden/src/utils/isobmff_utils.cpp
ext/ComplianceWarden/src/utils/isobmff_derivations.cpp
ext/ComplianceWarden/src/utils/spec_utils.cpp
ext/ComplianceWarden/src/specs/av1_hdr10plus/av1_hdr10plus.cpp
ext/ComplianceWarden/src/specs/avif/avif.cpp
ext/ComplianceWarden/src/specs/avif/profiles.cpp
ext/ComplianceWarden/src/specs/avif/utils.cpp
ext/ComplianceWarden/src/specs/isobmff/isobmff.cpp
ext/ComplianceWarden/src/specs/heif/heif.cpp
ext/ComplianceWarden/src/specs/miaf/miaf.cpp
ext/ComplianceWarden/src/specs/miaf/audio.cpp
ext/ComplianceWarden/src/specs/miaf/brands.cpp
ext/ComplianceWarden/src/specs/miaf/derivations.cpp
ext/ComplianceWarden/src/specs/miaf/colours.cpp
ext/ComplianceWarden/src/specs/miaf/num_pixels.cpp
ext/ComplianceWarden/src/specs/miaf/profiles.cpp
ext/ComplianceWarden/src/cw_version.cpp
)
endif()

# Only applicable to macOS. In GitHub CI's macos-latest os image, this prevents using the libpng
# and libjpeg headers from /Library/Frameworks/Mono.framework/Headers instead of
# /usr/local/include.
Expand Down Expand Up @@ -594,11 +641,14 @@ target_include_directories(avif SYSTEM PRIVATE ${AVIF_PLATFORM_SYSTEM_INCLUDES}
if(NOT libyuv_FOUND)
target_include_directories(avif PRIVATE ${libavif_SOURCE_DIR}/third_party/libyuv/include/)
endif()
if(AVIF_ENABLE_COMPLIANCE_WARDEN)
target_include_directories(avif PRIVATE ${libavif_SOURCE_DIR}/ext/ComplianceWarden/src/utils/)
endif()
set(AVIF_PKG_CONFIG_EXTRA_CFLAGS "")
if(BUILD_SHARED_LIBS)
target_compile_definitions(avif PUBLIC AVIF_DLL PRIVATE AVIF_BUILDING_SHARED_LIBS)
set(AVIF_PKG_CONFIG_EXTRA_CFLAGS " -DAVIF_DLL")
if(AVIF_LOCAL_LIBGAV1)
if(AVIF_USE_CXX)
set_target_properties(avif PROPERTIES LINKER_LANGUAGE "CXX")
endif()
endif()
Expand Down Expand Up @@ -626,7 +676,7 @@ if(AVIF_BUILD_EXAMPLES)

foreach(EXAMPLE ${AVIF_EXAMPLES})
add_executable(${EXAMPLE} examples/${EXAMPLE}.c)
if(AVIF_LOCAL_LIBGAV1)
if(AVIF_USE_CXX)
set_target_properties(${EXAMPLE} PROPERTIES LINKER_LANGUAGE "CXX")
endif()
target_link_libraries(${EXAMPLE} avif ${AVIF_PLATFORM_LIBRARIES})
Expand All @@ -642,6 +692,11 @@ if(NOT SKIP_INSTALL_ALL)
endif()

if(AVIF_CODEC_LIBRARIES MATCHES vmaf)
# vmaf only impacts the avifenc and avifdec targets below, not the library avif target above.
set(AVIF_USE_CXX ON)
endif()

if(AVIF_USE_CXX)
enable_language(CXX)
endif()

Expand Down Expand Up @@ -697,12 +752,12 @@ endif()

if(AVIF_BUILD_APPS)
add_executable(avifenc apps/avifenc.c)
if(AVIF_LOCAL_LIBGAV1 OR AVIF_CODEC_LIBRARIES MATCHES vmaf)
if(AVIF_USE_CXX)
set_target_properties(avifenc PROPERTIES LINKER_LANGUAGE "CXX")
endif()
target_link_libraries(avifenc avif_apps)
add_executable(avifdec apps/avifdec.c)
if(AVIF_LOCAL_LIBGAV1 OR AVIF_CODEC_LIBRARIES MATCHES vmaf)
if(AVIF_USE_CXX)
set_target_properties(avifdec PROPERTIES LINKER_LANGUAGE "CXX")
endif()
target_link_libraries(avifdec avif_apps)
Expand Down
20 changes: 20 additions & 0 deletions ext/compliance_warden.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash
#
# Local clone of ComplianceWarden (part of the gpac organization) for tests.
# Used when AVIF_ENABLE_COMPLIANCE_WARDEN is ON.

set -e

git clone -b v33 --depth 1 https://github.com/gpac/ComplianceWarden.git
# The provided Makefile only builds bin/cw.exe and objects.
# We are interested in the library, so the files are directly used instead of building with make -j.
ComplianceWarden/scripts/version.sh > ComplianceWarden/src/cw_version.cpp

# registerSpec() does not seem to be called in the static 'registered' local variables,
# for example in avif.cpp. Use the following hack to access the static SpecDescs.
# Feel free to replace by a prettier solution.
printf "extern const SpecDesc *const globalSpecAvif = &specAvif;\n" >> ComplianceWarden/src/specs/avif/avif.cpp
printf "extern const SpecDesc *const globalSpecAv1Hdr10plus = &specAv1Hdr10plus;\n" >> ComplianceWarden/src/specs/av1_hdr10plus/av1_hdr10plus.cpp
printf "extern const SpecDesc *const globalSpecHeif = &specHeif;\n" >> ComplianceWarden/src/specs/heif/heif.cpp
printf "extern const SpecDesc *const globalSpecIsobmff = &specIsobmff;\n" >> ComplianceWarden/src/specs/isobmff/isobmff.cpp
printf "extern const SpecDesc *const globalSpecMiaf = &specMiaf;\n" >> ComplianceWarden/src/specs/miaf/miaf.cpp
4 changes: 4 additions & 0 deletions include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,10 @@ __attribute__((__format__(__printf__, 2, 3)))
#endif
void avifDiagnosticsPrintf(avifDiagnostics * diag, const char * format, ...);

#if defined(AVIF_ENABLE_COMPLIANCE_WARDEN)
avifResult avifIsCompliant(uint8_t * data, size_t size);
#endif

// ---------------------------------------------------------------------------
// avifStream
//
Expand Down
54 changes: 54 additions & 0 deletions src/compliance.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2023 Google LLC
// SPDX-License-Identifier: BSD-2-Clause

#include <cstddef>
#include <cstdint>
#include <limits>

#include "avif/internal.h"

// From ../ext/ComplianceWarden/src/utils/
#include "box_reader_impl.h"
#include "spec.h"

bool checkComplianceStd(Box const & file, SpecDesc const * spec);

SpecDesc const * specFind(const char * name);
std::vector<SpecDesc const *> & g_allSpecs();
extern const SpecDesc * const globalSpecAvif;
extern const SpecDesc * const globalSpecAv1Hdr10plus;
extern const SpecDesc * const globalSpecHeif;
extern const SpecDesc * const globalSpecIsobmff;
extern const SpecDesc * const globalSpecMiaf;

avifResult avifIsCompliant(uint8_t * data, size_t size)
{
// See compliance_warden.sh.
if (g_allSpecs().empty()) {
registerSpec(globalSpecAvif);
registerSpec(globalSpecAv1Hdr10plus);
registerSpec(globalSpecHeif);
registerSpec(globalSpecIsobmff);
registerSpec(globalSpecMiaf);
}

// Inspired from ext/ComplianceWarden/src/app/cw.cpp
BoxReader topReader;
for (char sym : { 'f', 'i', 'l', 'e', '.', 'a', 'v', 'i', 'f' }) {
// Setting made-up file name (letter by letter).
topReader.myBox.syms.push_back({ "filename", static_cast<int64_t>(sym), 8 });
}
AVIF_CHECKERR(size <= std::numeric_limits<int>::max(), AVIF_RESULT_INVALID_ARGUMENT);
topReader.br = { data, static_cast<int>(size) };
topReader.myBox.original = data;
topReader.myBox.position = 0;
topReader.myBox.size = size;
topReader.myBox.fourcc = FOURCC("root");
topReader.specs = { specFind("avif") };
AVIF_CHECKERR(topReader.specs[0] != nullptr, AVIF_RESULT_UNKNOWN_ERROR);
auto parseFunc = getParseFunction(topReader.myBox.fourcc);
parseFunc(&topReader);
// gpac/ComplianceWarden will print the formatted result page to stdout, warnings and errors inclusive.
AVIF_CHECKERR(!checkComplianceStd(topReader.myBox, topReader.specs[0]), AVIF_RESULT_BMFF_PARSE_FAILED);
return AVIF_RESULT_OK;
}
4 changes: 4 additions & 0 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -2866,6 +2866,10 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output)

avifRWStreamFinishWrite(&s);

#if defined(AVIF_ENABLE_COMPLIANCE_WARDEN)
AVIF_CHECKRES(avifIsCompliant(output->data, output->size));
#endif

return AVIF_RESULT_OK;
}

Expand Down

0 comments on commit ef6da2a

Please sign in to comment.