squashed a bunch of subtle array indexing bugs, fixed block mapping
authorMike Melanson <mike@multimedia.cx>
Sat, 10 May 2003 21:46:17 +0000 (21:46 +0000)
committerMike Melanson <mike@multimedia.cx>
Sat, 10 May 2003 21:46:17 +0000 (21:46 +0000)
added more error checking, added (and enabled, for the time being) a
keyframe-only mode

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

libavcodec/vp3.c

index e5ecd67..461be09 100644 (file)
@@ -225,6 +225,10 @@ typedef struct Vp3DecodeContext {
     int superblock_count;
     int superblock_width;
     int superblock_height;
+    int y_superblock_width;
+    int y_superblock_height;
+    int c_superblock_width;
+    int c_superblock_height;
     int u_superblock_start;
     int v_superblock_start;
     unsigned char *superblock_coding;
@@ -292,8 +296,10 @@ typedef struct Vp3DecodeContext {
  * This function sets up all of the various blocks mappings:
  * superblocks <-> fragments, macroblocks <-> fragments,
  * superblocks <-> macroblocks
+ *
+ * Returns 0 is successful; returns 1 if *anything* went wrong.
  */
-static void init_block_mapping(Vp3DecodeContext *s) 
+static int init_block_mapping(Vp3DecodeContext *s) 
 {
     int i, j;
     signed int hilbert_walk_y[16];
@@ -387,9 +393,10 @@ static void init_block_mapping(Vp3DecodeContext *s)
             /* start of Y superblocks */
             right_edge = s->fragment_width;
             bottom_edge = s->fragment_height;
-            current_width = 0;
+            current_width = -1;
             current_height = 0;
-            superblock_row_inc = 3 * s->fragment_width;
+            superblock_row_inc = 3 * s->fragment_width - 
+                (s->y_superblock_width * 4 - s->fragment_width);
             hilbert = hilbert_walk_y;
 
             /* the first operation for this variable is to advance by 1 */
@@ -400,9 +407,10 @@ static void init_block_mapping(Vp3DecodeContext *s)
             /* start of U superblocks */
             right_edge = s->fragment_width / 2;
             bottom_edge = s->fragment_height / 2;
-            current_width = 0;
+            current_width = -1;
             current_height = 0;
-            superblock_row_inc = 3 * (s->fragment_width / 2);
+            superblock_row_inc = 3 * (s->fragment_width / 2) - 
+                (s->c_superblock_width * 4 - s->fragment_width / 2);
             hilbert = hilbert_walk_c;
 
             /* the first operation for this variable is to advance by 1 */
@@ -413,9 +421,10 @@ static void init_block_mapping(Vp3DecodeContext *s)
             /* start of V superblocks */
             right_edge = s->fragment_width / 2;
             bottom_edge = s->fragment_height / 2;
-            current_width = 0;
+            current_width = -1;
             current_height = 0;
-            superblock_row_inc = 3 * (s->fragment_width / 2);
+            superblock_row_inc = 3 * (s->fragment_width / 2) - 
+                (s->c_superblock_width * 4 - s->fragment_width / 2);
             hilbert = hilbert_walk_c;
 
             /* the first operation for this variable is to advance by 1 */
@@ -423,9 +432,9 @@ static void init_block_mapping(Vp3DecodeContext *s)
 
         }
 
-        if (current_width >= right_edge) {
+        if (current_width >= right_edge - 1) {
             /* reset width and move to next superblock row */
-            current_width = 0;
+            current_width = -1;
             current_height += 4;
 
             /* fragment is now at the start of a new superblock row */
@@ -435,21 +444,23 @@ static void init_block_mapping(Vp3DecodeContext *s)
         /* iterate through all 16 fragments in a superblock */
         for (j = 0; j < 16; j++) {
             current_fragment += hilbert[j];
+            current_width += travel_width[j];
             current_height += travel_height[j];
 
             /* check if the fragment is in bounds */
-            if ((current_width <= right_edge) &&
+            if ((current_width < right_edge) &&
                 (current_height < bottom_edge)) {
                 s->superblock_fragments[mapping_index] = current_fragment;
-                debug_init("    mapping fragment %d to superblock %d, position %d\n", 
-                    s->superblock_fragments[mapping_index], i, j);
+                debug_init("    mapping fragment %d to superblock %d, position %d (%d/%d x %d/%d)\n", 
+                    s->superblock_fragments[mapping_index], i, j,
+                    current_width, right_edge, current_height, bottom_edge);
             } else {
                 s->superblock_fragments[mapping_index] = -1;
-                debug_init("    superblock %d, position %d has no fragment\n", 
-                    i, j);
+                debug_init("    superblock %d, position %d has no fragment (%d/%d x %d/%d)\n", 
+                    i, j,
+                    current_width, right_edge, current_height, bottom_edge);
             }
 
-            current_width += travel_width[j];
             mapping_index++;
         }
     }
@@ -458,9 +469,10 @@ static void init_block_mapping(Vp3DecodeContext *s)
      * all of the Y plane superblocks to build this mapping */
     right_edge = s->macroblock_width;
     bottom_edge = s->macroblock_height;
-    current_width = 0;
+    current_width = -1;
     current_height = 0;
-    superblock_row_inc = s->macroblock_width;
+    superblock_row_inc = s->macroblock_width -
+        (s->y_superblock_width * 2 - s->macroblock_width);;
     hilbert = hilbert_walk_mb;
     mapping_index = 0;
     current_macroblock = -1;
@@ -478,10 +490,11 @@ static void init_block_mapping(Vp3DecodeContext *s)
         /* iterate through each potential macroblock in the superblock */
         for (j = 0; j < 4; j++) {
             current_macroblock += hilbert_walk_mb[j];
+            current_width += travel_width_mb[j];
             current_height += travel_height_mb[j];
 
             /* check if the macroblock is in bounds */
-            if ((current_width <= right_edge) &&
+            if ((current_width < right_edge) &&
                 (current_height < bottom_edge)) {
                 s->superblock_macroblocks[mapping_index] = current_macroblock;
                 debug_init("    mapping macroblock %d to superblock %d, position %d\n",
@@ -492,7 +505,6 @@ static void init_block_mapping(Vp3DecodeContext *s)
                     i, j);
             }
 
-            current_width += travel_width_mb[j];
             mapping_index++;
         }
     }
@@ -538,13 +550,13 @@ static void init_block_mapping(Vp3DecodeContext *s)
             /* C planes */
             c_fragment = s->u_fragment_start + 
                 (i * s->fragment_width / 4) + (j / 2);
-        s->all_fragments[c_fragment].macroblock = s->macroblock_count;
+            s->all_fragments[c_fragment].macroblock = s->macroblock_count;
             s->macroblock_fragments[mapping_index++] = c_fragment;
             debug_init("%d ", c_fragment);
 
             c_fragment = s->v_fragment_start + 
                 (i * s->fragment_width / 4) + (j / 2);
-        s->all_fragments[c_fragment].macroblock = s->macroblock_count;
+            s->all_fragments[c_fragment].macroblock = s->macroblock_count;
             s->macroblock_fragments[mapping_index++] = c_fragment;
             debug_init("%d ", c_fragment);
 
@@ -559,6 +571,8 @@ static void init_block_mapping(Vp3DecodeContext *s)
 
         current_fragment += s->fragment_width;
     }
+
+    return 0;  /* successful path out */
 }
 
 /*
@@ -1106,7 +1120,7 @@ static int get_motion_vector_fixed(GetBitContext *gb)
  * This function unpacks all of the superblock/macroblock/fragment coding 
  * information from the bitstream.
  */
-static void unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb)
+static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb)
 {
     int bit = 0;
     int current_superblock = 0;
@@ -1212,6 +1226,11 @@ static void unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb)
 
             /* if the fragment is in bounds, check its coding status */
             current_fragment = s->superblock_fragments[i * 16 + j];
+            if (current_fragment >= s->fragment_count) {
+                printf ("  vp3:unpack_superblocks(): bad fragment number (%d >= %d)\n",
+                    current_fragment, s->fragment_count);
+                return 1;
+            }
             if (current_fragment != -1) {
                 if (s->superblock_coding[i] == SB_NOT_CODED) {
 
@@ -1286,13 +1305,15 @@ static void unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb)
         s->last_coded_y_fragment,
         s->first_coded_c_fragment,
         s->last_coded_c_fragment);
+
+    return 0;
 }
 
 /*
  * This function unpacks all the coding mode data for individual macroblocks
  * from the bitstream.
  */
-static void unpack_modes(Vp3DecodeContext *s, GetBitContext *gb)
+static int unpack_modes(Vp3DecodeContext *s, GetBitContext *gb)
 {
     int i, j, k;
     int scheme;
@@ -1334,6 +1355,11 @@ static void unpack_modes(Vp3DecodeContext *s, GetBitContext *gb)
                 if ((current_macroblock == -1) ||
                     (!s->macroblock_coded[current_macroblock]))
                     continue;
+                if (current_macroblock >= s->macroblock_count) {
+                    printf ("  vp3:unpack_modes(): bad macroblock number (%d >= %d)\n",
+                        current_macroblock, s->macroblock_count);
+                    return 1;
+                }
 
                 /* mode 7 means get 3 bits for each coding mode */
                 if (scheme == 7)
@@ -1344,6 +1370,13 @@ static void unpack_modes(Vp3DecodeContext *s, GetBitContext *gb)
                 for (k = 0; k < 6; k++) {
                     current_fragment = 
                         s->macroblock_fragments[current_macroblock * 6 + k];
+                    if (current_fragment == -1)
+                        continue;
+                    if (current_fragment >= s->fragment_count) {
+                        printf ("  vp3:unpack_modes(): bad fragment number (%d >= %d)\n",
+                            current_fragment, s->fragment_count);
+                        return 1;
+                    }
                     if (s->all_fragments[current_fragment].coding_method != 
                         MODE_COPY)
                         s->all_fragments[current_fragment].coding_method =
@@ -1355,6 +1388,8 @@ static void unpack_modes(Vp3DecodeContext *s, GetBitContext *gb)
             }
         }
     }
+
+    return 0;
 }
 
 /*
@@ -1410,7 +1445,7 @@ static inline int adjust_vector(int *x, int *y, int c_plane)
  * This function unpacks all the motion vectors for the individual
  * macroblocks from the bitstream.
  */
-static void unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb)
+static int unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb)
 {
     int i, j, k;
     int coding_mode;
@@ -1448,8 +1483,18 @@ static void unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb)
                 if ((current_macroblock == -1) ||
                     (!s->macroblock_coded[current_macroblock]))
                     continue;
+                if (current_macroblock >= s->macroblock_count) {
+                    printf ("  vp3:unpack_vectors(): bad macroblock number (%d >= %d)\n",
+                        current_macroblock, s->macroblock_count);
+                    return 1;
+                }
 
                 current_fragment = s->macroblock_fragments[current_macroblock * 6];
+                if (current_fragment >= s->fragment_count) {
+                    printf ("  vp3:unpack_vectors(): bad fragment number (%d >= %d\n",
+                        current_fragment, s->fragment_count);
+                    return 1;
+                }
                 switch (s->all_fragments[current_fragment].coding_method) {
 
                 case MODE_INTER_PLUS_MV:
@@ -1559,6 +1604,13 @@ static void unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb)
                 for (k = 0; k < 6; k++) {
                     current_fragment = 
                         s->macroblock_fragments[current_macroblock * 6 + k];
+                    if (current_fragment == -1)
+                        continue;
+                    if (current_fragment >= s->fragment_count) {
+                        printf ("  vp3:unpack_vectors(): bad fragment number (%d >= %d)\n",
+                            current_fragment, s->fragment_count);
+                        return 1;
+                    }
                     s->all_fragments[current_fragment].motion_halfpel_index =
                         adjust_vector(&motion_x[k], &motion_y[k],
                         ((k == 4) || (k == 5)));
@@ -1571,6 +1623,8 @@ static void unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb)
             }
         }
     }
+
+    return 0;
 }
 
 /* 
@@ -1632,7 +1686,7 @@ static int unpack_vlcs(Vp3DecodeContext *s, GetBitContext *gb,
  * This function unpacks all of the DCT coefficient data from the
  * bitstream.
  */
-static void unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb)
+static int unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb)
 {
     int i;
     int dc_y_table;
@@ -1716,6 +1770,8 @@ static void unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb)
         residual_eob_run = unpack_vlcs(s, gb, &s->ac_vlc_4[ac_c_table], i, 
             s->first_coded_c_fragment, s->last_coded_c_fragment, residual_eob_run);
     }
+
+    return 0;
 }
 
 /*
@@ -2072,6 +2128,7 @@ static void render_fragments(Vp3DecodeContext *s,
                     (motion_source > lower_motion_limit)) {
 //                    printf ("  vp3: help! motion source (%d) out of range (%d..%d)\n",
 //                        motion_source, upper_motion_limit, lower_motion_limit);
+                    continue;
                 }
 
                 /* first, take care of copying a block from either the
@@ -2210,6 +2267,10 @@ static int vp3_decode_init(AVCodecContext *avctx)
 {
     Vp3DecodeContext *s = avctx->priv_data;
     int i;
+    int c_width;
+    int c_height;
+    int y_superblock_count;
+    int c_superblock_count;
 
     s->avctx = avctx;
     s->width = avctx->width;
@@ -2222,11 +2283,20 @@ static int vp3_decode_init(AVCodecContext *avctx)
      * in the first frame decode */
     s->quality_index = -1;
 
-    s->superblock_width = (s->width + 31) / 32;
-    s->superblock_height = (s->height + 31) / 32;
-    s->superblock_count = s->superblock_width * s->superblock_height * 3 / 2;
-    s->u_superblock_start = s->superblock_width * s->superblock_height;
-    s->v_superblock_start = s->superblock_width * s->superblock_height * 5 / 4;
+    s->y_superblock_width = (s->width + 31) / 32;
+    s->y_superblock_height = (s->height + 31) / 32;
+    y_superblock_count = s->y_superblock_width * s->y_superblock_height;
+
+    /* work out the dimensions for the C planes */
+    c_width = s->width / 2;
+    c_height = s->height / 2;
+    s->c_superblock_width = (c_width + 31) / 32;
+    s->c_superblock_height = (c_height + 31) / 32;
+    c_superblock_count = s->c_superblock_width * s->c_superblock_height;
+
+    s->superblock_count = y_superblock_count + (c_superblock_count * 2);
+    s->u_superblock_start = y_superblock_count;
+    s->v_superblock_start = s->u_superblock_start + c_superblock_count;
     s->superblock_coding = av_malloc(s->superblock_count);
 
     s->macroblock_width = (s->width + 15) / 16;
@@ -2241,9 +2311,14 @@ static int vp3_decode_init(AVCodecContext *avctx)
     s->u_fragment_start = s->fragment_width * s->fragment_height;
     s->v_fragment_start = s->fragment_width * s->fragment_height * 5 / 4;
 
-    debug_init("  width: %d x %d\n", s->width, s->height);
-    debug_init("  superblocks: %d x %d, %d total\n",
-        s->superblock_width, s->superblock_height, s->superblock_count);
+    debug_init("  Y plane: %d x %d\n", s->width, s->height);
+    debug_init("  C plane: %d x %d\n", c_width, c_height);
+    debug_init("  Y superblocks: %d x %d, %d total\n",
+        s->y_superblock_width, s->y_superblock_height, y_superblock_count);
+    debug_init("  C superblocks: %d x %d, %d total\n",
+        s->c_superblock_width, s->c_superblock_height, c_superblock_count);
+    debug_init("  total superblocks = %d, U starts @ %d, V starts @ %d\n", 
+        s->superblock_count, s->u_superblock_start, s->v_superblock_start);
     debug_init("  macroblocks: %d x %d, %d total\n",
         s->macroblock_width, s->macroblock_height, s->macroblock_count);
     debug_init("  %d fragments, %d x %d, u starts @ %d, v starts @ %d\n",
@@ -2333,12 +2408,10 @@ static int vp3_decode_frame(AVCodecContext *avctx,
     counter++;
 
     if (s->keyframe) {
-        /* release the previous golden frame and get a new one */
-        if (s->golden_frame.data[0])
+        if ((s->golden_frame.data[0]) &&
+            (s->last_frame.data[0] == s->golden_frame.data[0]))
             avctx->release_buffer(avctx, &s->golden_frame);
-
-        /* last frame, if allocated, is hereby invalidated */
-        if (s->last_frame.data[0])
+        else if (s->last_frame.data[0])
             avctx->release_buffer(avctx, &s->last_frame);
 
         s->golden_frame.reference = 0;
@@ -2374,10 +2447,28 @@ static int vp3_decode_frame(AVCodecContext *avctx,
 
     init_frame(s, &gb);
 
-    unpack_superblocks(s, &gb);
-    unpack_modes(s, &gb);
-    unpack_vectors(s, &gb);
-    unpack_dct_coeffs(s, &gb);
+#define KEYFRAMES_ONLY 1
+#if KEYFRAMES_ONLY
+if (!s->keyframe) {
+
+    memcpy(s->current_frame.data[0], s->golden_frame.data[0],
+        s->current_frame.linesize[0] * s->height);
+    memcpy(s->current_frame.data[1], s->golden_frame.data[1],
+        s->current_frame.linesize[1] * s->height / 2);
+    memcpy(s->current_frame.data[2], s->golden_frame.data[2],
+        s->current_frame.linesize[2] * s->height / 2);
+
+} else {
+#endif
+
+    if (unpack_superblocks(s, &gb) ||
+        unpack_modes(s, &gb) ||
+        unpack_vectors(s, &gb) ||
+        unpack_dct_coeffs(s, &gb)) {
+
+        printf("  vp3: could not decode frame\n");
+        return -1;
+    }
 
     reverse_dc_prediction(s, 0, s->fragment_width, s->fragment_height);
     reverse_dc_prediction(s, s->u_fragment_start,
@@ -2389,6 +2480,10 @@ static int vp3_decode_frame(AVCodecContext *avctx,
     render_fragments(s, s->u_fragment_start, s->width / 2, s->height / 2, 1);
     render_fragments(s, s->v_fragment_start, s->width / 2, s->height / 2, 2);
 
+#if KEYFRAMES_ONLY
+}
+#endif
+
     *data_size=sizeof(AVFrame);
     *(AVFrame*)data= s->current_frame;
 
@@ -2419,9 +2514,12 @@ static int vp3_decode_end(AVCodecContext *avctx)
     av_free(s->macroblock_coded);
 
     /* release all frames */
-    avctx->release_buffer(avctx, &s->golden_frame);
-    avctx->release_buffer(avctx, &s->last_frame);
-    avctx->release_buffer(avctx, &s->current_frame);
+    if (s->golden_frame.data[0])
+        avctx->release_buffer(avctx, &s->golden_frame);
+    if (s->last_frame.data[0])
+        avctx->release_buffer(avctx, &s->last_frame);
+    /* no need to release the current_frame since it will always be pointing
+     * to the same frame as either the golden or last frame */
 
     return 0;
 }