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 Jul 28, 2023
1 parent 2f25613 commit a1b326b
Show file tree
Hide file tree
Showing 12 changed files with 1,099 additions and 63 deletions.
6 changes: 6 additions & 0 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ 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");
printf(" --minimize : Minimize header if possible\n");
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 @@ -665,6 +666,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 @@ -882,6 +884,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 @@ -1101,6 +1104,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 @@ -1171,6 +1175,8 @@ int main(int argc, char * argv[])
} else if (!strcmp(arg, "-o") || !strcmp(arg, "--output")) {
NEXTARG();
outputFilename = arg;
} else if (!strcmp(arg, "--minimize")) {
settings.headerStrategy = AVIF_ENCODER_MINIMIZE_HEADER;
} else if (!strcmp(arg, "-d") || !strcmp(arg, "--depth")) {
NEXTARG();
input.requestedDepth = atoi(arg);
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 with a MetaBox and all its required boxes for maximum compatibility.
AVIF_ENCODER_FULL_HEADER,
// 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,
} avifEncoderHeaderStrategy;

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

Expand Down Expand Up @@ -1137,6 +1149,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
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 a1b326b

Please sign in to comment.