Skip to content

Commit

Permalink
minQuantizerAlpha/maxQuantizerAlpha support in avifEncoder, avifenc, …
Browse files Browse the repository at this point in the history
…444alpha support in y4m layer (avifenc, avifdec)
  • Loading branch information
Joe Drago committed Mar 3, 2020
1 parent d5b3099 commit 56fa2bc
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 15 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- libgav1 decode codec support. (wantehchang @Google)
- Expose codec selection to avifdec/avifenc, speed to avifenc
- Image grid support (Summer_in_Tomsk_720p_5x4_grid)
- `minQuantizerAlpha`/`maxQuantizerAlpha` support in avifEncoder, avifenc
- 444alpha support in y4m layer (avifenc, avifdec)

### Changed
- AppVeyor builds now compile with dav1d (EwoutH)
Expand Down
41 changes: 38 additions & 3 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,19 @@ static int syntax(void)
printf(" T = enum avifNclxTransferCharacteristics\n");
printf(" M = enum avifNclxMatrixCoefficients\n");
printf(" R = avifNclxRangeFlag (any nonzero value becomes AVIF_NCLX_FULL_RANGE)\n");
printf(" --min Q : Set min quantizer (%d-%d, where %d is lossless)\n",
printf(" --min Q : Set min quantizer for color (%d-%d, where %d is lossless)\n",
AVIF_QUANTIZER_BEST_QUALITY,
AVIF_QUANTIZER_WORST_QUALITY,
AVIF_QUANTIZER_LOSSLESS);
printf(" --max Q : Set max quantizer (%d-%d, where %d is lossless)\n",
printf(" --max Q : Set max quantizer for color (%d-%d, where %d is lossless)\n",
AVIF_QUANTIZER_BEST_QUALITY,
AVIF_QUANTIZER_WORST_QUALITY,
AVIF_QUANTIZER_LOSSLESS);
printf(" --minalpha Q : Set min quantizer for alpha (%d-%d, where %d is lossless)\n",
AVIF_QUANTIZER_BEST_QUALITY,
AVIF_QUANTIZER_WORST_QUALITY,
AVIF_QUANTIZER_LOSSLESS);
printf(" --maxalpha Q : Set max quantizer for alpha (%d-%d, where %d is lossless)\n",
AVIF_QUANTIZER_BEST_QUALITY,
AVIF_QUANTIZER_WORST_QUALITY,
AVIF_QUANTIZER_LOSSLESS);
Expand Down Expand Up @@ -102,6 +110,8 @@ int main(int argc, char * argv[])
int jobs = 1;
int minQuantizer = AVIF_QUANTIZER_BEST_QUALITY;
int maxQuantizer = AVIF_QUANTIZER_BEST_QUALITY;
int minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
int maxQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
int speed = AVIF_SPEED_DEFAULT;
avifCodecChoice codecChoice = AVIF_CODEC_CHOICE_AUTO;
avifBool nclxSet = AVIF_FALSE;
Expand Down Expand Up @@ -140,6 +150,24 @@ int main(int argc, char * argv[])
if (maxQuantizer > AVIF_QUANTIZER_WORST_QUALITY) {
maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY;
}
} else if (!strcmp(arg, "--minalpha")) {
NEXTARG();
minQuantizerAlpha = atoi(arg);
if (minQuantizerAlpha < AVIF_QUANTIZER_BEST_QUALITY) {
minQuantizerAlpha = AVIF_QUANTIZER_BEST_QUALITY;
}
if (minQuantizerAlpha > AVIF_QUANTIZER_WORST_QUALITY) {
minQuantizerAlpha = AVIF_QUANTIZER_WORST_QUALITY;
}
} else if (!strcmp(arg, "--maxalpha")) {
NEXTARG();
maxQuantizerAlpha = atoi(arg);
if (maxQuantizerAlpha < AVIF_QUANTIZER_BEST_QUALITY) {
maxQuantizerAlpha = AVIF_QUANTIZER_BEST_QUALITY;
}
if (maxQuantizerAlpha > AVIF_QUANTIZER_WORST_QUALITY) {
maxQuantizerAlpha = AVIF_QUANTIZER_WORST_QUALITY;
}
} else if (!strcmp(arg, "-n") || !strcmp(arg, "--nclx")) {
NEXTARG();
if (!parseNCLX(&nclx, arg)) {
Expand Down Expand Up @@ -205,17 +233,23 @@ int main(int argc, char * argv[])
printf("AVIF to be written:\n");
avifImageDump(avif);

printf("Encoding with AV1 codec '%s', quantizer range [%d (%s) <-> %d (%s)], %d worker thread(s), please wait...\n",
printf("Encoding with AV1 codec '%s', color QP [%d (%s) <-> %d (%s)], alpha QP [%d (%s) <-> %d (%s)], %d worker thread(s), please wait...\n",
avifCodecName(codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE),
minQuantizer,
quantizerString(minQuantizer),
maxQuantizer,
quantizerString(maxQuantizer),
minQuantizerAlpha,
quantizerString(minQuantizerAlpha),
maxQuantizerAlpha,
quantizerString(maxQuantizerAlpha),
jobs);
encoder = avifEncoderCreate();
encoder->maxThreads = jobs;
encoder->minQuantizer = minQuantizer;
encoder->maxQuantizer = maxQuantizer;
encoder->minQuantizerAlpha = minQuantizerAlpha;
encoder->maxQuantizerAlpha = maxQuantizerAlpha;
encoder->codecChoice = codecChoice;
encoder->speed = speed;
avifResult encodeResult = avifEncoderWrite(encoder, avif, &raw);
Expand All @@ -226,6 +260,7 @@ int main(int argc, char * argv[])

printf("Encoded successfully.\n");
printf(" * ColorOBU size: %zu bytes\n", encoder->ioStats.colorOBUSize);
printf(" * AlphaOBU size: %zu bytes\n", encoder->ioStats.alphaOBUSize);
FILE * f = fopen(outputFilename, "wb");
if (!f) {
fprintf(stderr, "ERROR: Failed to open file for write: %s\n", outputFilename);
Expand Down
1 change: 1 addition & 0 deletions apps/shared/avifutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ void avifImageDump(avifImage * avif)
printf(" * Resolution : %dx%d\n", avif->width, avif->height);
printf(" * Bit Depth : %d\n", avif->depth);
printf(" * Format : %s\n", avifPixelFormatToString(avif->yuvFormat));
printf(" * Alpha : %s\n", (avif->alphaPlane && (avif->alphaRowBytes > 0)) ? "Present" : "Absent");
switch (avif->profileFormat) {
case AVIF_PROFILE_FORMAT_NONE:
printf(" * Color Profile: None\n");
Expand Down
43 changes: 38 additions & 5 deletions apps/shared/y4m.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#include <stdlib.h>
#include <string.h>

static avifBool y4mColorSpaceToFormatAndDepth(const char * formatString, avifPixelFormat * format, int * depth)
static avifBool y4mColorSpaceParse(const char * formatString, avifPixelFormat * format, int * depth, avifBool * hasAlpha)
{
*hasAlpha = AVIF_FALSE;

if (!strcmp(formatString, "C420jpeg")) {
*format = AVIF_PIXEL_FORMAT_YUV420;
*depth = 8;
Expand Down Expand Up @@ -61,6 +63,12 @@ static avifBool y4mColorSpaceToFormatAndDepth(const char * formatString, avifPix
*depth = 8;
return AVIF_TRUE;
}
if (!strcmp(formatString, "C444alpha")) {
*format = AVIF_PIXEL_FORMAT_YUV444;
*depth = 8;
*hasAlpha = AVIF_TRUE;
return AVIF_TRUE;
}
if (!strcmp(formatString, "C422")) {
*format = AVIF_PIXEL_FORMAT_YUV422;
*depth = 8;
Expand Down Expand Up @@ -146,6 +154,7 @@ avifBool y4mRead(avifImage * avif, const char * inputFilename)
int width = -1;
int height = -1;
int depth = -1;
avifBool hasAlpha = AVIF_FALSE;
avifPixelFormat format = AVIF_PIXEL_FORMAT_NONE;
avifNclxRangeFlag rangeFlag = AVIF_NCLX_LIMITED_RANGE;
while (p != end) {
Expand All @@ -161,7 +170,7 @@ avifBool y4mRead(avifImage * avif, const char * inputFilename)
fprintf(stderr, "Bad y4m header: %s\n", inputFilename);
goto cleanup;
}
if (!y4mColorSpaceToFormatAndDepth(tmpBuffer, &format, &depth)) {
if (!y4mColorSpaceParse(tmpBuffer, &format, &depth, &hasAlpha)) {
fprintf(stderr, "Unsupported y4m pixel format: %s\n", inputFilename);
goto cleanup;
}
Expand Down Expand Up @@ -233,12 +242,17 @@ avifBool y4mRead(avifImage * avif, const char * inputFilename)
avifPixelFormatInfo info;
avifGetPixelFormatInfo(avif->yuvFormat, &info);

uint32_t planeBytes[3];
uint32_t planeBytes[4];
planeBytes[0] = avif->yuvRowBytes[0] * avif->height;
planeBytes[1] = avif->yuvRowBytes[1] * (avif->height >> info.chromaShiftY);
planeBytes[2] = avif->yuvRowBytes[2] * (avif->height >> info.chromaShiftY);
if (hasAlpha) {
planeBytes[3] = avif->yuvRowBytes[0] * avif->height;
} else {
planeBytes[3] = 0;
}

uint32_t bytesNeeded = planeBytes[0] + planeBytes[1] + planeBytes[2];
uint32_t bytesNeeded = planeBytes[0] + planeBytes[1] + planeBytes[2] + planeBytes[3];
remainingBytes = end - p;
if (bytesNeeded > remainingBytes) {
fprintf(stderr, "Not enough bytes in y4m for first frame: %s\n", inputFilename);
Expand All @@ -249,6 +263,10 @@ avifBool y4mRead(avifImage * avif, const char * inputFilename)
memcpy(avif->yuvPlanes[i], p, planeBytes[i]);
p += planeBytes[i];
}
if (hasAlpha) {
avifImageAllocatePlanes(avif, AVIF_PLANES_A);
memcpy(avif->alphaPlane, p, planeBytes[3]);
}

result = AVIF_TRUE;
cleanup:
Expand All @@ -259,12 +277,24 @@ avifBool y4mRead(avifImage * avif, const char * inputFilename)
avifBool y4mWrite(avifImage * avif, const char * outputFilename)
{
avifBool swapUV = AVIF_FALSE;
avifBool hasAlpha = (avif->alphaPlane && (avif->alphaRowBytes > 0)) ? AVIF_TRUE : AVIF_FALSE;
avifBool writeAlpha = AVIF_FALSE;
char * y4mHeaderFormat = NULL;

if (hasAlpha && ((avif->depth != 8) || (avif->yuvFormat != AVIF_PIXEL_FORMAT_YUV444))) {
fprintf(stderr, "WARNING: writing alpha is currently only supported in 8bpc YUV444, ignoring alpha channel: %s\n", outputFilename);
}

switch (avif->depth) {
case 8:
switch (avif->yuvFormat) {
case AVIF_PIXEL_FORMAT_YUV444:
y4mHeaderFormat = "C444 XYSCSS=444";
if (hasAlpha) {
y4mHeaderFormat = "C444alpha XYSCSS=444";
writeAlpha = AVIF_TRUE;
} else {
y4mHeaderFormat = "C444 XYSCSS=444";
}
break;
case AVIF_PIXEL_FORMAT_YUV422:
y4mHeaderFormat = "C422 XYSCSS=422";
Expand Down Expand Up @@ -370,6 +400,9 @@ avifBool y4mWrite(avifImage * avif, const char * outputFilename)
for (int i = 0; i < 3; ++i) {
fwrite(planes[i], 1, planeBytes[i], f);
}
if (writeAlpha) {
fwrite(avif->alphaPlane, 1, avif->alphaRowBytes * avif->height, f);
}

fclose(f);
printf("Wrote: %s\n", outputFilename);
Expand Down
2 changes: 2 additions & 0 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,8 @@ typedef struct avifEncoder
int maxThreads;
int minQuantizer;
int maxQuantizer;
int minQuantizerAlpha;
int maxQuantizerAlpha;
int tileRowsLog2;
int tileColsLog2;
int speed;
Expand Down
8 changes: 3 additions & 5 deletions src/codec_aom.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,12 +304,10 @@ static avifBool aomCodecEncodeImage(avifCodec * codec, avifImage * image, avifEn
int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63);
int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63);
if (alpha) {
minQuantizer = AVIF_QUANTIZER_LOSSLESS;
maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63);
maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63);
}
avifBool lossless = ((encoder->minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (encoder->maxQuantizer == AVIF_QUANTIZER_LOSSLESS))
? AVIF_TRUE
: AVIF_FALSE;
avifBool lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS)) ? AVIF_TRUE : AVIF_FALSE;
cfg.rc_min_quantizer = minQuantizer;
cfg.rc_max_quantizer = maxQuantizer;

Expand Down
4 changes: 2 additions & 2 deletions src/codec_rav1e.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ static avifBool rav1eCodecEncodeImage(avifCodec * codec, avifImage * image, avif
int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63);
int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63);
if (alpha) {
minQuantizer = AVIF_QUANTIZER_LOSSLESS;
maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63);
maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63);
}
minQuantizer = (minQuantizer * 255) / 63; // Rescale quantizer values as rav1e's QP range is [0,255]
maxQuantizer = (maxQuantizer * 255) / 63;
Expand Down
2 changes: 2 additions & 0 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ avifEncoder * avifEncoderCreate(void)
encoder->maxThreads = 1;
encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
encoder->maxQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
encoder->tileRowsLog2 = 0;
encoder->tileColsLog2 = 0;
encoder->speed = AVIF_SPEED_DEFAULT;
Expand Down

0 comments on commit 56fa2bc

Please sign in to comment.