[FFmpeg-devel] [PATCH] lavc/ffv1: Properly check that the 4th and 5th quant tables are zeroes

Derek Buitenhuis derek.buitenhuis at gmail.com
Thu Jan 2 17:22:25 EET 2020


Currently, the decoder checks the 128th value of the 4th quant table during
while deriving the context on each sample, in order to speed itself up. This
is due to relying on the behavior of FFmpeg's FFV1 encoder, in which if that
value is zero, the entire 4th and 5th quant tables are assumed to be entirely
zero.

This does not match the FFV1 spec, which has no such restriction, and after
some discussion, it was decided to fix FFmpeg to abide by the spec, rather
than change the spec.

We will now check whether the 4th and 5th quant tables are zero properly,
using a full table compare, once, when they are read in. There should be
no speed loss with this method.

For further context, the FFV1 issue in question is located at:

    https://github.com/FFmpeg/FFV1/issues/169

Signed-off-by: Derek Buitenhuis <derek.buitenhuis at gmail.com>
---
 libavcodec/ffv1.h          | 15 +++++++++++++++
 libavcodec/ffv1_template.c |  2 +-
 libavcodec/ffv1dec.c       |  5 +++++
 libavcodec/ffv1enc.c       |  6 ++++++
 4 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/libavcodec/ffv1.h b/libavcodec/ffv1.h
index f0bb19350a..115ea24120 100644
--- a/libavcodec/ffv1.h
+++ b/libavcodec/ffv1.h
@@ -67,6 +67,7 @@ typedef struct VlcState {
 
 typedef struct PlaneContext {
     int16_t quant_table[MAX_CONTEXT_INPUTS][256];
+    int three_quant_tables;
     int quant_table_index;
     int context_count;
     uint8_t (*state)[CONTEXT_SIZE];
@@ -102,7 +103,9 @@ typedef struct FFV1Context {
     int ac_byte_count;                   ///< number of bytes used for AC coding
     PlaneContext plane[MAX_PLANES];
     int16_t quant_table[MAX_CONTEXT_INPUTS][256];
+    int three_quant_table;
     int16_t quant_tables[MAX_QUANT_TABLES][MAX_CONTEXT_INPUTS][256];
+    int three_quant_tables[MAX_QUANT_TABLES];
     int context_count[MAX_QUANT_TABLES];
     uint8_t state_transition[256];
     uint8_t (*initial_states[MAX_QUANT_TABLES])[32];
@@ -148,6 +151,18 @@ int ff_ffv1_allocate_initial_states(FFV1Context *f);
 void ff_ffv1_clear_slice_state(FFV1Context *f, FFV1Context *fs);
 int ff_ffv1_close(AVCodecContext *avctx);
 
+/*
+ * The last two quant tables are often zeroes, so we can do a single check
+ * here, to speed up every get_context call on such files.
+ */
+static av_always_inline int is_three_quant_tables(const int16_t quant_table[MAX_CONTEXT_INPUTS][256])
+{
+    const int16_t zero_quant_table[256] = { 0 };
+
+    return !memcmp(zero_quant_table, quant_table[3], sizeof(zero_quant_table)) &&
+           !memcmp(zero_quant_table, quant_table[4], sizeof(zero_quant_table));
+}
+
 static av_always_inline int fold(int diff, int bits)
 {
     if (bits == 8)
diff --git a/libavcodec/ffv1_template.c b/libavcodec/ffv1_template.c
index f2ab93313a..6304b54ab1 100644
--- a/libavcodec/ffv1_template.c
+++ b/libavcodec/ffv1_template.c
@@ -37,7 +37,7 @@ static inline int RENAME(get_context)(PlaneContext *p, TYPE *src,
     const int RT = last[1];
     const int L  = src[-1];
 
-    if (p->quant_table[3][127]) {
+    if (!p->three_quant_tables) {
         const int TT = last2[0];
         const int LL = src[-2];
         return p->quant_table[0][(L - LT) & 0xFF] +
diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c
index e465ed49d7..5ac67daaa8 100644
--- a/libavcodec/ffv1dec.c
+++ b/libavcodec/ffv1dec.c
@@ -197,6 +197,7 @@ static int decode_slice_header(FFV1Context *f, FFV1Context *fs)
         }
         p->quant_table_index = idx;
         memcpy(p->quant_table, f->quant_tables[idx], sizeof(p->quant_table));
+        p->three_quant_tables = f->three_quant_tables[idx];
         context_count = f->context_count[idx];
 
         if (p->context_count < context_count) {
@@ -474,6 +475,7 @@ static int read_extra_header(FFV1Context *f)
 
     for (i = 0; i < f->quant_table_count; i++) {
         f->context_count[i] = read_quant_tables(c, f->quant_tables[i]);
+        f->three_quant_tables[i] = is_three_quant_tables(f->quant_tables[i]);
         if (f->context_count[i] < 0) {
             av_log(f->avctx, AV_LOG_ERROR, "read_quant_table error\n");
             return AVERROR_INVALIDDATA;
@@ -735,6 +737,7 @@ static int read_header(FFV1Context *f)
             f->chroma_h_shift, f->chroma_v_shift, f->avctx->pix_fmt);
     if (f->version < 2) {
         context_count = read_quant_tables(c, f->quant_table);
+        f->three_quant_table = is_three_quant_tables(f->quant_table);
         if (context_count < 0) {
             av_log(f->avctx, AV_LOG_ERROR, "read_quant_table error\n");
             return AVERROR_INVALIDDATA;
@@ -797,9 +800,11 @@ static int read_header(FFV1Context *f)
                 p->quant_table_index = idx;
                 memcpy(p->quant_table, f->quant_tables[idx],
                        sizeof(p->quant_table));
+                p->three_quant_tables = f->three_quant_tables[idx];
                 context_count = f->context_count[idx];
             } else {
                 memcpy(p->quant_table, f->quant_table, sizeof(p->quant_table));
+                p->three_quant_tables = f->three_quant_table;
             }
 
             if (f->version <= 2) {
diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c
index c521b7d445..e6e5171c8f 100644
--- a/libavcodec/ffv1enc.c
+++ b/libavcodec/ffv1enc.c
@@ -733,31 +733,37 @@ FF_ENABLE_DEPRECATION_WARNINGS
             s->quant_tables[0][0][i]=           quant11[i];
             s->quant_tables[0][1][i]=        11*quant11[i];
             s->quant_tables[0][2][i]=     11*11*quant11[i];
+            s->three_quant_tables[0]=                    1;
             s->quant_tables[1][0][i]=           quant11[i];
             s->quant_tables[1][1][i]=        11*quant11[i];
             s->quant_tables[1][2][i]=     11*11*quant5 [i];
             s->quant_tables[1][3][i]=   5*11*11*quant5 [i];
             s->quant_tables[1][4][i]= 5*5*11*11*quant5 [i];
+            s->three_quant_tables[1]=                    0;
         } else {
             s->quant_tables[0][0][i]=           quant9_10bit[i];
             s->quant_tables[0][1][i]=        11*quant9_10bit[i];
             s->quant_tables[0][2][i]=     11*11*quant9_10bit[i];
+            s->three_quant_tables[0]=                         1;
             s->quant_tables[1][0][i]=           quant9_10bit[i];
             s->quant_tables[1][1][i]=        11*quant9_10bit[i];
             s->quant_tables[1][2][i]=     11*11*quant5_10bit[i];
             s->quant_tables[1][3][i]=   5*11*11*quant5_10bit[i];
             s->quant_tables[1][4][i]= 5*5*11*11*quant5_10bit[i];
+            s->three_quant_tables[1]=                         0;
         }
     }
     s->context_count[0] = (11 * 11 * 11        + 1) / 2;
     s->context_count[1] = (11 * 11 * 5 * 5 * 5 + 1) / 2;
     memcpy(s->quant_table, s->quant_tables[s->context_model],
            sizeof(s->quant_table));
+    s->three_quant_table = s->three_quant_tables[s->context_model];
 
     for (i = 0; i < s->plane_count; i++) {
         PlaneContext *const p = &s->plane[i];
 
         memcpy(p->quant_table, s->quant_table, sizeof(p->quant_table));
+        p->three_quant_tables= s->three_quant_table;
         p->quant_table_index = s->context_model;
         p->context_count     = s->context_count[p->quant_table_index];
     }
-- 
2.25.0.rc0



More information about the ffmpeg-devel mailing list