Opened 7 months ago

Closed 5 months ago

#7600 closed enhancement (invalid)

Add lossless option to libaom-av1

Reported by: Ewout Owned by:
Priority: wish Component: avcodec
Version: git-master Keywords: libaom
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

libaom supports lossless encoding, an option could be added to the CLI to enable this mode.

in libvpx-vp9:
-lossless <int> E..V..... Lossless mode (from -1 to 1) (default -1)

in aomenc:
--lossless=<arg> Lossless mode (0: false (default), 1: true)

Change History (13)

comment:1 Changed 7 months ago by cehoyos

  • Component changed from undetermined to avcodec
  • Priority changed from normal to wish
  • Version changed from unspecified to git-master

Please test this patch:

diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
index 09ef423..2b44bda 100644
--- a/libavcodec/libaomenc.c
+++ b/libavcodec/libaomenc.c
@@ -79,6 +79,7 @@ typedef struct AOMEncoderContext {
     aom_superblock_size_t superblock_size;
     int uniform_tiles;
     int row_mt;
+    int lossless;
 } AOMContext;

 static const char *const ctlidstr[] = {
@@ -96,6 +97,9 @@ static const char *const ctlidstr[] = {
 #ifdef AOM_CTRL_AV1E_SET_ROW_MT
     [AV1E_SET_ROW_MT]           = "AV1E_SET_ROW_MT",
 #endif
+#ifdef AOM_CTRL_AV1E_SET_LOSSLESS
+    [AV1E_SET_LOSSLESS]         = "AV1E_SET_LOSSLESS",
+#endif
 };

 static av_cold void log_encoder_error(AVCodecContext *avctx, const char *desc)
@@ -657,6 +661,9 @@ static av_cold int aom_init(AVCodecContext *avctx,
 #ifdef AOM_CTRL_AV1E_SET_ROW_MT
     codecctl_int(avctx, AV1E_SET_ROW_MT, ctx->row_mt);
 #endif
+#ifdef AOM_CTRL_AV1E_SET_LOSSLESS
+    codecctl_int(avctx, AV1E_SET_LOSSLESS, ctx->lossless);
+#endif

     // provide dummy value to initialize wrapper, values will be updated each _encode()
     aom_img_wrap(&ctx->rawimg, img_fmt, avctx->width, avctx->height, 1,
@@ -992,6 +999,7 @@ static const AVOption options[] = {
     { "tile-columns",     "Log2 of number of tile columns to use", OFFSET(tile_cols_log2), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 6, VE},
     { "tile-rows",        "Log2 of number of tile rows to use",    OFFSET(tile_rows_log2), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 6, VE},
     { "row-mt",           "Enable row based multi-threading",      OFFSET(row_mt),         AV_OPT_TYPE_BOOL, {.i64 = 0},  0, 1, VE},
+    { "lossless",         "Lossless mode",                         OFFSET(lossless),       AV_OPT_TYPE_BOOL, {.i64 = 0},  0, 1, VE}
     { NULL }
 };

comment:2 Changed 7 months ago by jamrial

AOM_CTRL_AV1E_SET_LOSSLESS exists in libaom 1.0.0, so no need for preprocessor guards.

comment:3 Changed 7 months ago by cehoyos

Like this?

diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
index 09ef423..9ec5c19 100644
--- a/libavcodec/libaomenc.c
+++ b/libavcodec/libaomenc.c
@@ -79,6 +79,7 @@ typedef struct AOMEncoderContext {
     aom_superblock_size_t superblock_size;
     int uniform_tiles;
     int row_mt;
+    int lossless;
 } AOMContext;

 static const char *const ctlidstr[] = {
@@ -96,6 +97,7 @@ static const char *const ctlidstr[] = {
 #ifdef AOM_CTRL_AV1E_SET_ROW_MT
     [AV1E_SET_ROW_MT]           = "AV1E_SET_ROW_MT",
 #endif
+    [AV1E_SET_LOSSLESS]         = "AV1E_SET_LOSSLESS",
 };

 static av_cold void log_encoder_error(AVCodecContext *avctx, const char *desc)
@@ -657,6 +659,7 @@ static av_cold int aom_init(AVCodecContext *avctx,
 #ifdef AOM_CTRL_AV1E_SET_ROW_MT
     codecctl_int(avctx, AV1E_SET_ROW_MT, ctx->row_mt);
 #endif
+    codecctl_int(avctx, AV1E_SET_LOSSLESS, ctx->lossless);

     // provide dummy value to initialize wrapper, values will be updated each _encode()
     aom_img_wrap(&ctx->rawimg, img_fmt, avctx->width, avctx->height, 1,
@@ -992,6 +995,7 @@ static const AVOption options[] = {
     { "tile-columns",     "Log2 of number of tile columns to use", OFFSET(tile_cols_log2), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 6, VE},
     { "tile-rows",        "Log2 of number of tile rows to use",    OFFSET(tile_rows_log2), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 6, VE},
     { "row-mt",           "Enable row based multi-threading",      OFFSET(row_mt),         AV_OPT_TYPE_BOOL, {.i64 = 0},  0, 1, VE},
+    { "lossless",         "Lossless mode",                         OFFSET(lossless),       AV_OPT_TYPE_BOOL, {.i64 = 0},  0, 1, VE}
     { NULL }
 };


comment:4 Changed 7 months ago by Ewout

Thanks! Would love to test it, how can I build FFmpeg with this patch?

comment:5 Changed 7 months ago by cehoyos

Just apply it and rebuild as usual, nothing special should be needed.

comment:6 Changed 7 months ago by Ewout

  • Blocking set to #7621

Sorry, I'm not experienced enough with compiling FFmpeg. If someone could provide me a Windows build with this patch (or if someone wants to talk me through it) I would be happy to test if the results are comparable to aomenc.

Otherwise, if someone else is able to test it please do. I think compressing a lossless PNG and checking if there's no PSNR loss should suffice. Comparing the output with aomenc with the --lossless options should also work.

cehoyos thanks for the patch anyway!

comment:7 Changed 7 months ago by cehoyos

Just out of curiosity: How did you compile HandBrake?
What’s wrong with ./configure --enable-libaom && make ffmpeg?

comment:8 Changed 7 months ago by cehoyos

  • Blocking #7621 deleted

comment:9 Changed 7 months ago by Ewout

GitHub? can use CI (AppVeyor? in this case) which did the trick. In general, I don't make things, I break them and then report where I did :).

comment:10 Changed 6 months ago by Ewout

I did the following test:

  1. Encoded a .png screenshot to .png with FFmpeg
  2. Encoded this screenshot to a AV1 with -c:v libaom-av1 -cpu-used 0, once with -lossless 1 and once without.
  3. Encoded these .mp4's back to .png's
  4. Repeated step 2 and 3 once more.

This resulted in 5 .png's, the input and two output files from both the lossy and lossless.

  • Input: 185.760 bytes
  • Lossless Round 1: 171.312 bytes
  • Lossless Round 2: 171.312 bytes
  • Lossy Round 1: 263.532 bytes
  • Lossy Round 2: 249.829 bytes

There were also 4 MP4's generated in the process:

  • Lossless Round 1: 59.061 bytes
  • Lossless Round 2: 59.061 bytes
  • Lossy Round 1: 11.260 bytes
  • Lossy Round 2: 11.064 bytes

Where the lossy files are losing data, the lossless files aren't. I also used diffchecker to check for difference between the input and output png's, the lossy showed a difference while the lossless didn't. Looks like it works great and can be merged.

All files and the commands used can be found here: https://mega.nz/#F!W24HjSjJ!Z266_XE4Ln4KTmP3ZOR4GA

Small note: Line 33 misses a comma at the end.

{ "lossless",         "Lossless mode",                         OFFSET(lossless),       AV_OPT_TYPE_BOOL, {.i64 = 0},  0, 1, VE}

Thanks to u/MrSmilingWolf for the Windows build and spotting the missing comma.

comment:11 Changed 6 months ago by Ewout

cehoyos are you ready to submit the patch or do you want/need any more tests done?

comment:12 Changed 6 months ago by cehoyos

Please test with unpatched FFmpeg if -b:v 0 -crf 0 also produces lossless output.

comment:13 Changed 5 months ago by cehoyos

  • Resolution set to invalid
  • Status changed from new to closed

I can confirm that lossless encoding works fine with -crf 0.

Note: See TracTickets for help on using tickets.