Skip to content

Commit

Permalink
Experimental reduced HEIF header
Browse files Browse the repository at this point in the history
Add AVIF_ENABLE_EXPERIMENTAL_AVIR flag.
Remove a few hundred bytes of container boilerplate for simple images.
This prototype uses a small box that expands in meaning to a full meta
box with all defined items and properties.

Warning: This feature is experimental and forbidden by the current
AVIF specification.
  • Loading branch information
y-guyon committed Aug 3, 2023
1 parent 3e66456 commit f47f892
Show file tree
Hide file tree
Showing 16 changed files with 1,167 additions and 69 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci-unix-static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ jobs:
-DAVIF_LOCAL_LIBSHARPYUV=ON
-DAVIF_BUILD_EXAMPLES=ON -DAVIF_BUILD_APPS=ON
-DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_LOCAL_GTEST=ON
-DAVIF_ENABLE_EXPERIMENTAL_YCGCO_R=ON
-DAVIF_ENABLE_EXPERIMENTAL_AVIR=ON
- name: Build libavif (ninja)
working-directory: ./build
run: ninja
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ jobs:
-DAVIF_LOCAL_ZLIBPNG=ON
-DAVIF_BUILD_EXAMPLES=ON -DAVIF_BUILD_APPS=ON
-DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_LOCAL_GTEST=ON
-DAVIF_ENABLE_EXPERIMENTAL_YCGCO_R=ON
-DAVIF_ENABLE_EXPERIMENTAL_AVIR=ON
- name: Build libavif (ninja)
working-directory: ./build
run: ninja
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ option(BUILD_SHARED_LIBS "Build shared avif library" ON)
option(AVIF_ENABLE_WERROR "Treat all compiler warnings as errors" ON)

option(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R "Enable experimental YCgCo-R matrix code" OFF)
option(AVIF_ENABLE_EXPERIMENTAL_AVIR "Enable experimental reduced header" OFF)

option(AVIF_CODEC_AOM "Use the AOM codec for encoding/decoding (see AVIF_CODEC_AOM_DECODE/AVIF_CODEC_AOM_ENCODE)" OFF)
option(AVIF_CODEC_DAV1D "Use the dav1d codec for decoding" OFF)
Expand Down Expand Up @@ -262,6 +263,10 @@ if(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R)
add_compile_definitions(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R)
endif()

if(AVIF_ENABLE_EXPERIMENTAL_AVIR)
add_compile_definitions(AVIF_ENABLE_EXPERIMENTAL_AVIR)
endif()

set(AVIF_SRCS
src/alpha.c
src/avif.c
Expand Down
10 changes: 10 additions & 0 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ static void syntaxLong(void)
printf(" -j,--jobs J : Number of jobs (worker threads, default: 1. Use \"all\" to use all available cores)\n");
printf(" --no-overwrite : Never overwrite existing output file\n");
printf(" -o,--output FILENAME : Instead of using the last filename given as output, use this filename\n");
#if defined(AVIF_ENABLE_EXPERIMENTAL_AVIR)
printf(" --avir : Minimize header if possible\n");
#endif
printf(" -l,--lossless : Set all defaults to encode losslessly, and emit warnings when settings/input don't allow for it\n");
printf(" -d,--depth D : Output depth [8,10,12]. (JPEG/PNG only; For y4m or stdin, depth is retained)\n");
printf(" -y,--yuv FORMAT : Output format [default=auto, 444, 422, 420, 400]. Ignored for y4m or stdin (y4m format is retained)\n");
Expand Down Expand Up @@ -669,6 +672,7 @@ typedef struct
avifBool autoTiling;
avifBool progressive;
int speed;
avifEncoderHeaderStrategy headerStrategy;

int paspCount;
uint32_t paspValues[8]; // only the first two are used
Expand Down Expand Up @@ -888,6 +892,7 @@ static avifBool avifEncodeImagesFixedQuality(const avifSettings * settings,
encoder->autoTiling = settings->autoTiling;
encoder->codecChoice = settings->codecChoice;
encoder->speed = settings->speed;
encoder->headerStrategy = settings->headerStrategy;
encoder->timescale = settings->outputTiming.timescale;
encoder->keyframeInterval = settings->keyframeInterval;
encoder->repetitionCount = settings->repetitionCount;
Expand Down Expand Up @@ -1107,6 +1112,7 @@ int main(int argc, char * argv[])
settings.autoTiling = AVIF_FALSE;
settings.progressive = AVIF_FALSE;
settings.speed = 6;
settings.headerStrategy = AVIF_ENCODER_FULL_HEADER;
settings.repetitionCount = AVIF_REPETITION_COUNT_INFINITE;
settings.keyframeInterval = 0;
settings.ignoreExif = AVIF_FALSE;
Expand Down Expand Up @@ -1177,6 +1183,10 @@ int main(int argc, char * argv[])
} else if (!strcmp(arg, "-o") || !strcmp(arg, "--output")) {
NEXTARG();
outputFilename = arg;
#if defined(AVIF_ENABLE_EXPERIMENTAL_AVIR)
} else if (!strcmp(arg, "--avir")) {
settings.headerStrategy = AVIF_ENCODER_MINIMIZE_HEADER;
#endif // AVIF_ENABLE_EXPERIMENTAL_AVIR
} else if (!strcmp(arg, "-d") || !strcmp(arg, "--depth")) {
NEXTARG();
input.requestedDepth = atoi(arg);
Expand Down
17 changes: 17 additions & 0 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,20 @@ typedef enum avifResult

AVIF_API const char * avifResultToString(avifResult result);

// ---------------------------------------------------------------------------
// avifEncoderHeaderStrategy

typedef enum avifEncoderHeaderStrategy
{
// Encodes as "avif" brand with a MetaBox and all its required boxes for maximum compatibility.
AVIF_ENCODER_FULL_HEADER,
#if defined(AVIF_ENABLE_EXPERIMENTAL_AVIR)
// Encodes as "avir" brand with a CondensedImageBox to reduce the encoded file size.
// WARNING: Experimental feature. Produces files that are incompatible with older decoders.
AVIF_ENCODER_MINIMIZE_HEADER,
#endif
} avifEncoderHeaderStrategy;

// ---------------------------------------------------------------------------
// avifROData/avifRWData: Generic raw memory storage

Expand Down Expand Up @@ -1138,6 +1152,9 @@ typedef struct avifScalingMode
// call to avifEncoderAddImage().
typedef struct avifEncoder
{
// Defaults to AVIF_ENCODER_FULL_HEADER
avifEncoderHeaderStrategy headerStrategy;

// Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c)
avifCodecChoice codecChoice;

Expand Down
25 changes: 25 additions & 0 deletions include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,14 +431,25 @@ typedef size_t avifBoxMarker;

typedef struct avifBoxHeader
{
// Size of the box in bytes, excluding the number of bytes read to know the size and the type.
size_t size;
uint8_t type[4];
} avifBoxHeader;

typedef struct avifROStream
{
avifROData * raw;

// Index of the next byte in the raw stream.
size_t offset;

// Index of the currently selected partial byte. Must be lower than offset.
// Only meaningful if numUsedBitsInPartialByte is not zero.
size_t offsetOfPartialByte;
// Number of bits already used in the currently selected partial byte.
size_t numUsedBitsInPartialByte;

// Error information, if any.
avifDiagnostics * diag;
const char * diagContext;
} avifROStream;
Expand All @@ -458,6 +469,8 @@ avifBool avifROStreamReadU32(avifROStream * stream, uint32_t * v);
avifBool avifROStreamReadU32Endianness(avifROStream * stream, uint32_t * v, avifBool littleEndian);
avifBool avifROStreamReadUX8(avifROStream * stream, uint64_t * v, uint64_t factor); // Reads a factor*8 sized uint, saves in v
avifBool avifROStreamReadU64(avifROStream * stream, uint64_t * v);
avifBool avifROStreamReadBits(avifROStream * stream, uint32_t * v, size_t bitCount);
avifBool avifROStreamReadVarInt(avifROStream * stream, uint32_t * v);
avifBool avifROStreamReadString(avifROStream * stream, char * output, size_t outputSize);
avifBool avifROStreamReadBoxHeader(avifROStream * stream, avifBoxHeader * header); // This fails if the size reported by the header cannot fit in the stream
avifBool avifROStreamReadBoxHeaderPartial(avifROStream * stream, avifBoxHeader * header); // This doesn't require that the full box can fit in the stream
Expand All @@ -467,7 +480,15 @@ avifBool avifROStreamReadAndEnforceVersion(avifROStream * stream, uint8_t enforc
typedef struct avifRWStream
{
avifRWData * raw;

// Index of the next byte in the raw stream.
size_t offset;

// Index of the currently selected partial byte. Must be lower than offset.
// Only meaningful if numUsedBitsInPartialByte is not zero.
size_t offsetOfPartialByte;
// Number of bits already used in the currently selected partial byte.
size_t numUsedBitsInPartialByte;
} avifRWStream;

uint8_t * avifRWStreamCurrent(avifRWStream * stream);
Expand All @@ -486,6 +507,10 @@ void avifRWStreamWriteU16(avifRWStream * stream, uint16_t v);
void avifRWStreamWriteU32(avifRWStream * stream, uint32_t v);
void avifRWStreamWriteU64(avifRWStream * stream, uint64_t v);
void avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount);
#if defined(AVIF_ENABLE_EXPERIMENTAL_AVIR)
void avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount);
void avifRWStreamWriteVarInt(avifRWStream * stream, uint32_t v);
#endif // AVIF_ENABLE_EXPERIMENTAL_AVIR

// 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
Expand Down
8 changes: 6 additions & 2 deletions src/avif.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,9 @@ avifBool avifCropRectConvertCleanApertureBox(avifCropRect * cropRect,
avifPixelFormat yuvFormat,
avifDiagnostics * diag)
{
avifDiagnosticsClearError(diag);
if (diag) {
avifDiagnosticsClearError(diag);
}

// ISO/IEC 14496-12:2020, Section 12.1.4.1:
// For horizOff and vertOff, D shall be strictly positive and N may be
Expand Down Expand Up @@ -772,7 +774,9 @@ avifBool avifCleanApertureBoxConvertCropRect(avifCleanApertureBox * clap,
avifPixelFormat yuvFormat,
avifDiagnostics * diag)
{
avifDiagnosticsClearError(diag);
if (diag) {
avifDiagnosticsClearError(diag);
}

if (!avifCropRectIsValid(cropRect, imageW, imageH, yuvFormat, diag)) {
return AVIF_FALSE;
Expand Down
Loading

0 comments on commit f47f892

Please sign in to comment.