Skip to content

Commit

Permalink
Experimental reduced HEIF header
Browse files Browse the repository at this point in the history
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 Jun 13, 2023
1 parent ddffb77 commit bc92180
Show file tree
Hide file tree
Showing 11 changed files with 776 additions and 57 deletions.
6 changes: 6 additions & 0 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ static void syntax(void)
printf(" -s,--speed S : Encoder speed (%d-%d, slowest-fastest, 'default' or 'd' for codec internal defaults. default speed: 6)\n",
AVIF_SPEED_SLOWEST,
AVIF_SPEED_FASTEST);
printf(" --minimize : Minimize header if possible\n");
printf(" -c,--codec C : AV1 codec to use (choose from versions list below)\n");
printf(" --exif FILENAME : Provide an Exif metadata payload to be associated with the primary item (implies --ignore-exif)\n");
printf(" --xmp FILENAME : Provide an XMP metadata payload to be associated with the primary item (implies --ignore-xmp)\n");
Expand Down Expand Up @@ -630,6 +631,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 @@ -846,6 +848,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 @@ -1064,6 +1067,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 @@ -1291,6 +1295,8 @@ int main(int argc, char * argv[])
settings.speed = AVIF_SPEED_SLOWEST;
}
}
} else if (!strcmp(arg, "--minimize")) {
settings.headerStrategy = AVIF_ENCODER_MINIMIZE_HEADER;
} else if (!strcmp(arg, "--exif")) {
NEXTARG();
if (!readEntireFile(arg, &exifOverride)) {
Expand Down
15 changes: 15 additions & 0 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@ typedef enum avifResult

AVIF_API const char * avifResultToString(avifResult result);

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

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

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

Expand Down Expand Up @@ -1136,6 +1148,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
23 changes: 23 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,8 @@ 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);
void avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount);
void avifRWStreamWriteVarInt(avifRWStream * stream, uint32_t v);

// 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
Loading

0 comments on commit bc92180

Please sign in to comment.