adpcm: Use a hash table to improve checking for duplicate samples
authorMartin Storsjö <martin@martin.st>
Fri, 19 Nov 2010 17:35:52 +0000 (17:35 +0000)
committerMartin Storsjö <martin@martin.st>
Fri, 19 Nov 2010 17:35:52 +0000 (17:35 +0000)
This lowers the run time from 158 to 21 seconds, for -trellis 8
with a 30 second sample on my machine.

This requires 64 KB additional memory.

Originally committed as revision 25768 to svn://svn.ffmpeg.org/ffmpeg/trunk

libavcodec/adpcm.c

index 56eb602df1e108452b55c5b11a121a3d672f6a35..6a6c77b5741857562df23daaffd0758e1a53f5d9 100644 (file)
@@ -163,6 +163,7 @@ typedef struct ADPCMContext {
     TrellisPath *paths;
     TrellisNode *node_buf;
     TrellisNode **nodep_buf;
     TrellisPath *paths;
     TrellisNode *node_buf;
     TrellisNode **nodep_buf;
+    uint8_t *trellis_hash;
 } ADPCMContext;
 
 #define FREEZE_INTERVAL 128
 } ADPCMContext;
 
 #define FREEZE_INTERVAL 128
@@ -189,6 +190,7 @@ static av_cold int adpcm_encode_init(AVCodecContext *avctx)
         FF_ALLOC_OR_GOTO(avctx, s->paths,     max_paths * sizeof(*s->paths), error);
         FF_ALLOC_OR_GOTO(avctx, s->node_buf,  2 * frontier * sizeof(*s->node_buf), error);
         FF_ALLOC_OR_GOTO(avctx, s->nodep_buf, 2 * frontier * sizeof(*s->nodep_buf), error);
         FF_ALLOC_OR_GOTO(avctx, s->paths,     max_paths * sizeof(*s->paths), error);
         FF_ALLOC_OR_GOTO(avctx, s->node_buf,  2 * frontier * sizeof(*s->node_buf), error);
         FF_ALLOC_OR_GOTO(avctx, s->nodep_buf, 2 * frontier * sizeof(*s->nodep_buf), error);
+        FF_ALLOC_OR_GOTO(avctx, s->trellis_hash, 65536 * sizeof(*s->trellis_hash), error);
     }
 
     switch(avctx->codec->id) {
     }
 
     switch(avctx->codec->id) {
@@ -242,6 +244,7 @@ error:
     av_freep(&s->paths);
     av_freep(&s->node_buf);
     av_freep(&s->nodep_buf);
     av_freep(&s->paths);
     av_freep(&s->node_buf);
     av_freep(&s->nodep_buf);
+    av_freep(&s->trellis_hash);
     return -1;
 }
 
     return -1;
 }
 
@@ -252,6 +255,7 @@ static av_cold int adpcm_encode_close(AVCodecContext *avctx)
     av_freep(&s->paths);
     av_freep(&s->node_buf);
     av_freep(&s->nodep_buf);
     av_freep(&s->paths);
     av_freep(&s->node_buf);
     av_freep(&s->nodep_buf);
+    av_freep(&s->trellis_hash);
 
     return 0;
 }
 
     return 0;
 }
@@ -325,7 +329,9 @@ static void adpcm_compress_trellis(AVCodecContext *avctx, const short *samples,
     TrellisNode **nodep_buf = s->nodep_buf;
     TrellisNode **nodes = nodep_buf; // nodes[] is always sorted by .ssd
     TrellisNode **nodes_next = nodep_buf + frontier;
     TrellisNode **nodep_buf = s->nodep_buf;
     TrellisNode **nodes = nodep_buf; // nodes[] is always sorted by .ssd
     TrellisNode **nodes_next = nodep_buf + frontier;
-    int pathn = 0, froze = -1, i, j, k;
+    int pathn = 0, froze = -1, i, j, k, generation = 0;
+    uint8_t *hash = s->trellis_hash;
+    memset(hash, 0xff, 65536 * sizeof(*hash));
 
     memset(nodep_buf, 0, 2 * frontier * sizeof(*nodep_buf));
     nodes[0] = node_buf + frontier;
 
     memset(nodep_buf, 0, 2 * frontier * sizeof(*nodep_buf));
     nodes[0] = node_buf + frontier;
@@ -372,18 +378,24 @@ static void adpcm_compress_trellis(AVCodecContext *avctx, const short *samples,
                     uint32_t ssd;\
                     int pos;\
                     TrellisNode *u;\
                     uint32_t ssd;\
                     int pos;\
                     TrellisNode *u;\
+                    uint8_t *h;\
                     dec_sample = av_clip_int16(dec_sample);\
                     d = sample - dec_sample;\
                     ssd = nodes[j]->ssd + d*d;\
                     /* Collapse any two states with the same previous sample value. \
                      * One could also distinguish states by step and by 2nd to last
                     dec_sample = av_clip_int16(dec_sample);\
                     d = sample - dec_sample;\
                     ssd = nodes[j]->ssd + d*d;\
                     /* Collapse any two states with the same previous sample value. \
                      * One could also distinguish states by step and by 2nd to last
-                     * sample, but the effects of that are negligible. */\
-                    for(k=0; k<frontier && nodes_next[k]; k++) {\
-                        if(dec_sample == nodes_next[k]->sample1) {\
-                            assert(ssd >= nodes_next[k]->ssd);\
-                            goto next_##NAME;\
-                        }\
-                    }\
+                     * sample, but the effects of that are negligible.
+                     * Since nodes in the previous generation are iterated
+                     * through a heap, they're roughly ordered from better to
+                     * worse, but not strictly ordered. Therefore, an earlier
+                     * node with the same sample value is better in most cases
+                     * (and thus the current is skipped), but not strictly
+                     * in all cases. Only skipping samples where ssd >=
+                     * ssd of the earlier node with the same sample gives
+                     * slightly worse quality, though, for some reason. */ \
+                    h = &hash[(uint16_t) dec_sample];\
+                    if (*h == generation)\
+                        goto next_##NAME;\
                     if (heap_pos < frontier) {\
                         pos = heap_pos++;\
                     } else {\
                     if (heap_pos < frontier) {\
                         pos = heap_pos++;\
                     } else {\
@@ -393,6 +405,7 @@ static void adpcm_compress_trellis(AVCodecContext *avctx, const short *samples,
                         if (ssd > nodes_next[pos]->ssd)\
                             goto next_##NAME;\
                     }\
                         if (ssd > nodes_next[pos]->ssd)\
                             goto next_##NAME;\
                     }\
+                    *h = generation;\
                     u = nodes_next[pos];\
                     if(!u) {\
                         assert(pathn < FREEZE_INTERVAL<<avctx->trellis);\
                     u = nodes_next[pos];\
                     if(!u) {\
                         assert(pathn < FREEZE_INTERVAL<<avctx->trellis);\
@@ -443,6 +456,12 @@ static void adpcm_compress_trellis(AVCodecContext *avctx, const short *samples,
         nodes = nodes_next;
         nodes_next = u;
 
         nodes = nodes_next;
         nodes_next = u;
 
+        generation++;
+        if (generation == 255) {
+            memset(hash, 0xff, 65536 * sizeof(*hash));
+            generation = 0;
+        }
+
         // prevent overflow
         if(nodes[0]->ssd > (1<<28)) {
             for(j=1; j<frontier && nodes[j]; j++)
         // prevent overflow
         if(nodes[0]->ssd > (1<<28)) {
             for(j=1; j<frontier && nodes[j]; j++)