Skip to content

Commit

Permalink
avifEncoder now has a speed setting, codec_aom only flushes encoder w…
Browse files Browse the repository at this point in the history
…hen necessary (avoids lost frame packets), minor cleanup
  • Loading branch information
Joe Drago committed Nov 16, 2019
1 parent dae3b9c commit 4bf0187
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 14 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- avifEncoder now has a speed setting
- codec_aom only flushes encoder when necessary (avoids lost frame packets)

### Changed
- Updated libaom to more recent SHA in aom.cmd
- Tweaked AVIF_LOCAL_AOM settings to play nice with libaom's usage of CMake's option()
- Minor cleanup

## [0.4.7] - 2019-11-11
### Changed
Expand Down
17 changes: 13 additions & 4 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ typedef int avifBool;
#define AVIF_PLANE_COUNT_RGB 3
#define AVIF_PLANE_COUNT_YUV 3

#define AVIF_SPEED_DEFAULT -1
#define AVIF_SPEED_SLOWEST 0
#define AVIF_SPEED_FASTEST 10

enum avifPlanesFlags
{
AVIF_PLANES_RGB = (1 << 0),
Expand Down Expand Up @@ -480,22 +484,27 @@ uint32_t avifDecoderNearestKeyframe(avifDecoder * decoder, uint32_t frameIndex);
// avifEncoder

// Notes:
// * if avifEncoderWrite() returns AVIF_RESULT_OK, output must be freed with avifRWDataFree()
// * if (maxThreads < 2), multithreading is disabled
// * quality range: [AVIF_BEST_QUALITY - AVIF_WORST_QUALITY]
// * If avifEncoderWrite() returns AVIF_RESULT_OK, output must be freed with avifRWDataFree()
// * If (maxThreads < 2), multithreading is disabled
// * Quality range: [AVIF_BEST_QUALITY - AVIF_WORST_QUALITY]
// * To enable tiling, set tileRowsLog2 > 0 and/or tileColsLog2 > 0.
// Tiling values range [0-6], where the value indicates a request for 2^n tiles in that dimension.
// * Speed range: [AVIF_SPEED_SLOWEST - AVIF_SPEED_FASTEST]. Slower should make for a better quality
// image in less bytes. AVIF_SPEED_DEFAULT means "Leave the AV1 codec to its default speed settings"./
// If avifEncoder uses rav1e, the speed value is directly passed through (0-10). If libaom is used,
// a combination of settings are tweaked to simulate this speed range.
typedef struct avifEncoder
{
// Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c)
avifCodecChoice codecChoice;

// settings
// settings (see Notes above)
int maxThreads;
int minQuantizer;
int maxQuantizer;
int tileRowsLog2;
int tileColsLog2;
int speed;

// stats from the most recent write
avifIOStats ioStats;
Expand Down
41 changes: 37 additions & 4 deletions src/codec_aom.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,30 @@ static avifBool aomCodecEncodeImage(avifCodec * codec, avifImage * image, avifEn
aom_codec_iface_t * encoder_interface = aom_codec_av1_cx();
aom_codec_ctx_t aomEncoder;

// Map encoder speed to AOM usage + CpuUsed:
// Speed 0: GoodQuality CpuUsed 0
// Speed 1: GoodQuality CpuUsed 1
// Speed 2: GoodQuality CpuUsed 2
// Speed 3: GoodQuality CpuUsed 3
// Speed 4: GoodQuality CpuUsed 4
// Speed 5: GoodQuality CpuUsed 5
// Speed 6: RealTime CpuUsed 4
// Speed 7: RealTime CpuUsed 5
// Speed 8: RealTime CpuUsed 6
// Speed 9: RealTime CpuUsed 7
// Speed 10: RealTime CpuUsed 8
unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY;
int aomCpuUsed = -1;
if (encoder->speed != AVIF_SPEED_DEFAULT) {
if (encoder->speed < 6) {
aomUsage = AOM_USAGE_GOOD_QUALITY;
aomCpuUsed = AVIF_CLAMP(encoder->speed, 0, 5);
} else {
aomUsage = AOM_USAGE_REALTIME;
aomCpuUsed = AVIF_CLAMP(encoder->speed - 2, 4, 8);
}
}

int yShift = 0;
aom_img_fmt_t aomFormat = avifImageCalcAOMFmt(image, alpha, &yShift);
if (aomFormat == AOM_IMG_FMT_NONE) {
Expand All @@ -244,7 +268,7 @@ static avifBool aomCodecEncodeImage(avifCodec * codec, avifImage * image, avifEn
avifGetPixelFormatInfo(image->yuvFormat, &formatInfo);

struct aom_codec_enc_cfg cfg;
aom_codec_enc_config_default(encoder_interface, &cfg, 0);
aom_codec_enc_config_default(encoder_interface, &cfg, aomUsage);

cfg.g_profile = codec->configBox.seqProfile;
cfg.g_bit_depth = image->depth;
Expand Down Expand Up @@ -287,6 +311,9 @@ static avifBool aomCodecEncodeImage(avifCodec * codec, avifImage * image, avifEn
int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6);
aom_codec_control(&aomEncoder, AV1E_SET_TILE_COLUMNS, tileColsLog2);
}
if (aomCpuUsed != -1) {
aom_codec_control(&aomEncoder, AOME_SET_CPUUSED, aomCpuUsed);
}

uint32_t uvHeight = image->height >> yShift;
aom_image_t * aomImage = aom_img_alloc(NULL, aomFormat, image->width, image->height, 16);
Expand Down Expand Up @@ -338,13 +365,19 @@ static avifBool aomCodecEncodeImage(avifCodec * codec, avifImage * image, avifEn
}

aom_codec_encode(&aomEncoder, aomImage, 0, 1, 0);
aom_codec_encode(&aomEncoder, NULL, 0, 1, 0); // flush

avifBool flushed = AVIF_FALSE;
aom_codec_iter_t iter = NULL;
for (;;) {
const aom_codec_cx_pkt_t * pkt = aom_codec_get_cx_data(&aomEncoder, &iter);
if (pkt == NULL)
break;
if (pkt == NULL) {
if (flushed)
break;

aom_codec_encode(&aomEncoder, NULL, 0, 1, 0); // flush
flushed = AVIF_TRUE;
continue;
}
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
avifRWDataSet(obu, pkt->data.frame.buf, pkt->data.frame.sz);
success = AVIF_TRUE;
Expand Down
7 changes: 6 additions & 1 deletion src/codec_rav1e.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ static avifBool rav1eCodecEncodeImage(avifCodec * codec, avifImage * image, avif
if (rav1e_config_parse_int(rav1eConfig, "quantizer", maxQuantizer) == -1) {
goto cleanup;
}

if (encoder->tileRowsLog2 != 0) {
int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6);
if (rav1e_config_parse_int(rav1eConfig, "tile_rows", 1 << tileRowsLog2) == -1) {
Expand All @@ -104,6 +103,12 @@ static avifBool rav1eCodecEncodeImage(avifCodec * codec, avifImage * image, avif
goto cleanup;
}
}
if (encoder->speed != AVIF_SPEED_DEFAULT) {
int speed = AVIF_CLAMP(encoder->speed, 0, 10);
if (rav1e_config_parse_int(rav1eConfig, "speed", speed) == -1) {
goto cleanup;
}
}

if (image->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
rav1e_config_set_color_description(rav1eConfig,
Expand Down
8 changes: 3 additions & 5 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ avifEncoder * avifEncoderCreate(void)
encoder->maxThreads = 1;
encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
encoder->tileRowsLog2 = 0;
encoder->tileColsLog2 = 0;
encoder->speed = AVIF_SPEED_DEFAULT;
return encoder;
}

Expand Down Expand Up @@ -106,11 +109,6 @@ avifResult avifEncoderWrite(avifEncoder * encoder, avifImage * image, avifRWData
// -----------------------------------------------------------------------
// Encode AV1 OBUs

// avifRWData * alphaOBUPtr = &alphaOBU;
// if (avifImageIsOpaque(image)) {
// alphaOBUPtr = NULL;
// }

if (!codec[AVIF_CODEC_PLANES_COLOR]->encodeImage(codec[AVIF_CODEC_PLANES_COLOR], image, encoder, &colorOBU, AVIF_FALSE)) {
result = AVIF_RESULT_ENCODE_COLOR_FAILED;
goto writeCleanup;
Expand Down

0 comments on commit 4bf0187

Please sign in to comment.