[FFmpeg-devel] flashsvenc.c - sampling block size too low

Jason Askew jason.askew
Thu May 17 20:04:58 CEST 2007


diff of 2 pass encoding for flashsvenc.c attached

I'm still actively working on this.  A few things to know:

const int TP_BLCK_SIZE = 8;

This can go up to 16.  Things get even slower and the resulting gains
have been minimal to nonexistence.  That may be a different story
depending on what the screen cap is of.

Each possible block size is iterated through, compared, and zlib'ed.
This is not efficient.

Current optimization I'm working on is to do the block compare one
time only at pixel size of 16x16, and using that data over again for
larger block sizes.

The big bottle neck is zlib'ing blocks just to determine their size.
I'm investigating a theory that optimal block size can be 'chased
down' by following the least block size through an array, limiting the
number of zlib compressions that need to take place.

Some array pointer access can still be optimized.
-------------- next part --------------
--- flashsvenc_c.c	Thu May 17 10:32:47 2007
+++ flashsvenc.c	Thu May 17 12:54:56 2007
@@ -62,6 +62,14 @@
 #include "bitstream.h"
 #include "bytestream.h"
 
+const int TP_BLCK_SIZE = 8;
+
+//used to store multi pass stats
+typedef struct FlashSVSectionStat {
+    int frame_num;
+    int blk_size_w;
+    int blk_size_h;
+} FlashSVSectionStat;
 
 typedef struct FlashSVContext {
     AVCodecContext *avctx;
@@ -74,6 +82,10 @@ typedef struct FlashSVContext {
     int block_size;
     z_stream zstream;
     int last_key_frame;
+    int* tpSizes;
+    unsigned int key_frame_cnt;
+    FlashSVSectionStat *stats;
+    int stat_count;
 } FlashSVContext;
 
 static int copy_region_enc(uint8_t *sptr, uint8_t *dptr,
@@ -112,6 +124,67 @@ static int flashsv_encode_init(AVCodecCo
         return -1;
     }
 
+    //if this is pass2, parse log file
+    if((avctx->flags&CODEC_FLAG_PASS2)){
+
+        if(avctx->stats_in == NULL) {
+            av_log(avctx, AV_LOG_ERROR, " Second pass flag and stats_in is NULL.\n");
+            return -1;
+        }
+
+        int e;
+
+        //find the count at the end of stats_in
+        char* es = strrchr(avctx->stats_in, ':');
+        if(es==NULL) {
+            av_log(avctx, AV_LOG_ERROR, " Did not find count at end of log file.\n");
+            return -1;
+        }
+
+        int count;
+        e = sscanf(es, ":%d", &count);
+        if(e != 1) {
+            av_log(avctx, AV_LOG_ERROR, " Did not find count at end of log file.\n");
+            return -1;
+        }
+
+        //create array to hold stats data
+        s->stats = av_mallocz(count * sizeof(FlashSVSectionStat));
+        if (!s->stats) {
+            av_log(avctx, AV_LOG_ERROR, " Memory allocation failed s->stats.\n");
+            return -1;
+        }
+
+        //fill stats array from log file
+        int frame, x, y;
+        char* pNext = avctx->stats_in;
+        int statIndex = 0;
+
+        while (pNext!=NULL) {
+            e = sscanf(pNext, "%d:%d:%d", &x, &y, &frame);
+            if(e != 3) {
+                av_log(avctx, AV_LOG_ERROR, " Problems parsing log file.\n");
+                return -1;
+            }
+            if(statIndex > count) {
+                av_log(avctx, AV_LOG_ERROR, " Too many frames in log file.\n");
+                return -1;
+            }
+
+            s->stats[statIndex].frame_num = statIndex+1;
+            s->stats[statIndex].blk_size_w = x;
+            s->stats[statIndex].blk_size_h = y;
+            statIndex++;
+            pNext=strchr(pNext,'\n')+1;
+            //check to see if at end of frames
+            if(frame==count) {
+                s->stat_count = count;
+                pNext=NULL;
+            }
+            //av_log(avctx, AV_LOG_ERROR, " frame %d   %d x %d.\n", frame, x, y);
+        }
+    }
+
     // Needed if zlib unused or init aborted before deflateInit
     memset(&(s->zstream), 0, sizeof(z_stream));
 
@@ -123,14 +196,91 @@ static int flashsv_encode_init(AVCodecCo
     s->tmpblock = av_mallocz(3*256*256);
     s->encbuffer = av_mallocz(s->image_width*s->image_height*3);
 
+    s->key_frame_cnt = 0;
+
     if (!s->tmpblock || !s->encbuffer) {
         av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n");
         return -1;
     }
 
+    //setup of buffer to hold block size stat data
+    if((avctx->flags&CODEC_FLAG_PASS1)){
+        s->tpSizes = av_mallocz(sizeof(int)*TP_BLCK_SIZE*TP_BLCK_SIZE);
+        avctx->stats_out = av_mallocz(256);
+
+        if (!s->tpSizes || !avctx->stats_out) {
+            av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n");
+            return -1;
+        }
+    }
+
     return 0;
 }
 
+//performs the same actions as copy_region_enc but does not commit zlib data to buffer
+//and returns the size
+static int sample_block_size(FlashSVContext *s, AVFrame *p,
+        int block_width, int block_height, uint8_t *previous_frame, int* I_frame) {
+
+    int h_blocks, v_blocks, h_part, v_part, i, j;
+    int blockSize = 0;
+    int res;
+
+    int comBnd = compressBound(block_height * block_width * 3);
+    uint8_t *buf = av_mallocz(comBnd);
+
+    if (!buf) {
+        av_log(s->avctx, AV_LOG_ERROR, "Memory allocation failed - blocksize compression buffer.\n");
+        return -1;
+    }
+
+    //block header amount
+    blockSize += 4;
+
+    h_blocks = s->image_width / block_width;
+    h_part = s->image_width % block_width;
+    v_blocks = s->image_height / block_height;
+    v_part = s->image_height % block_height;
+
+    /* loop over all block columns */
+    for (j = 0; j < v_blocks + (v_part?1:0); j++)
+    {
+
+        int hp = j*block_height; // horiz position in frame
+        int hs = (j<v_blocks)?block_height:v_part; // size of block
+
+        /* loop over all block rows */
+        for (i = 0; i < h_blocks + (h_part?1:0); i++)
+        {
+            int wp = i*block_width; // vert position in frame
+            int ws = (i<h_blocks)?block_width:h_part; // size of block
+            int ret=Z_OK;
+
+            //copy the block to the temp buffer before compression (if it differs from the previous frame's block)
+            res = copy_region_enc(p->data[0], s->tmpblock, s->image_height-(hp+hs+1), wp, hs, ws, p->linesize[0], previous_frame);
+
+            if (res || *I_frame) {
+                unsigned long zsize;
+                zsize = comBnd;
+
+                ret = compress2(buf, &zsize, s->tmpblock, 3*ws*hs, 9);
+
+                if (ret != Z_OK)
+                    av_log(s->avctx, AV_LOG_ERROR, "error while compressing block %dx%d\n", i, j);
+
+                blockSize += zsize + 2;
+            } else {
+                blockSize += 2;
+            }
+        }
+    }
+    av_free(buf);
+
+    //av_log(s->avctx, AV_LOG_ERROR, "block size:  %d                             \n", blockSize);
+
+    return blockSize;
+}
+
 
 static int encode_bitstream(FlashSVContext *s, AVFrame *p, uint8_t *buf, int buf_size,
      int block_width, int block_height, uint8_t *previous_frame, int* I_frame) {
@@ -238,8 +388,79 @@ static int flashsv_encode_frame(AVCodecC
         }
     }
 
-    opt_w=4;
-    opt_h=4;
+    pfptr = s->previous_frame;
+    //if linesize is negative, prep pointer to match upside down ptr movement of data[0]
+    if(p->linesize[0] < 0) {
+        pfptr = pfptr - ((s->image_height-1) * p->linesize[0]);
+    }
+
+
+    int w, h;
+
+    //if first pass on a two pass, at each i-frame, record best block size for past i-frame set
+    if((avctx->flags&CODEC_FLAG_PASS1)) {
+        if(avctx->frame_number != 0 && I_frame == 1){
+            //av_log(avctx, AV_LOG_INFO, "Reseting frame size counts \n");
+            int smallW, smallH;
+            smallW = 0;
+            smallH = 0;
+            int sizeIndex = 0;
+            unsigned int smallest = s->tpSizes[0];
+            for (h=0 ; h<TP_BLCK_SIZE ; h++) {
+                for (w=0 ; w<TP_BLCK_SIZE ; w++) {
+                    if (s->tpSizes[sizeIndex] < smallest) {
+                        smallest = s->tpSizes[sizeIndex];
+                        smallW = w;
+                        smallH = h;
+                    }
+                    sizeIndex++;
+                    //av_log(avctx, AV_LOG_ERROR, "[%d][%d]size = %d                       \n",w,h,s->tpSizes[h*TP_BLCK_SIZE+w]);
+                    s->tpSizes[h*TP_BLCK_SIZE+w] = 0;
+                    //av_log(avctx, AV_LOG_ERROR, "[%d][%d]size = %d                       \n",w,h,s->tpSizes[h*TP_BLCK_SIZE+w]);
+                }
+            }
+            //av_log(avctx, AV_LOG_INFO, "  [%d][%d] smallest = %d   --------\n",smallW+1,smallH+1,smallest);
+
+            s->key_frame_cnt++;
+            snprintf(avctx->stats_out, 256, "%d:%d:%d\n",smallW+1, smallH+1, s->key_frame_cnt);
+        } else {
+            avctx->stats_out[0] = 0;
+        }
+    }
+
+    //pull best block size from first past data
+    if(!(avctx->flags&CODEC_FLAG_PASS2)) {
+        opt_w=4;
+        opt_h=4;
+    } else {
+        if(avctx->frame_number != 0 && I_frame == 1){
+            s->key_frame_cnt++;
+        }
+        //av_log(avctx, AV_LOG_INFO, "  attempting to load frame stats info [%d]\n",s->key_frame_cnt);
+        if(s->key_frame_cnt < s->stat_count-1) {
+            opt_w=s->stats[s->key_frame_cnt].blk_size_w;
+            opt_h=s->stats[s->key_frame_cnt].blk_size_h;
+        } else {
+            opt_w=s->stats[s->stat_count-1].blk_size_w;
+            opt_h=s->stats[s->stat_count-1].blk_size_h;
+        }
+        if(I_frame == 1){
+            //av_log(avctx, AV_LOG_INFO, "  got %d    %d x %d \n", s->stats[s->key_frame_cnt].frame_num, opt_w, opt_h);
+        }
+    }
+
+    //calc frame size for different block sizes
+    if((avctx->flags&CODEC_FLAG_PASS1) && I_frame == 0){
+
+        //Try all possible combinations and store the encoded frame sizes
+        for (h=0 ; h<TP_BLCK_SIZE ; h++) {
+            for (w=0 ; w<TP_BLCK_SIZE ; w++) {
+                s->tpSizes[h*TP_BLCK_SIZE+w] += sample_block_size(s, p, (w+1)*16, (h+1)*16, pfptr, &I_frame);
+                //av_log(avctx, AV_LOG_ERROR, "[%d][%d]size = %d                        \n",w+1,h+1,s->tpSizes[h*TP_BLCK_SIZE+w]);
+            }
+        }
+    }
+
 
     if (buf_size < s->image_width*s->image_height*3) {
         //Conservative upper bound check for compressed data
@@ -280,6 +501,15 @@ static int flashsv_encode_end(AVCodecCon
     av_free(s->encbuffer);
     av_free(s->previous_frame);
     av_free(s->tmpblock);
+
+    if((avctx->flags&CODEC_FLAG_PASS1)){
+        av_free(s->tpSizes);
+        av_free(avctx->stats_out);
+    }
+
+    if((avctx->flags&CODEC_FLAG_PASS2)){
+        av_free(s->stats);
+    }
 
     return 0;
 }



More information about the ffmpeg-devel mailing list