• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavcodec/roqvideoenc.c

Go to the documentation of this file.
00001 /*
00002  * RoQ Video Encoder.
00003  *
00004  * Copyright (C) 2007 Vitor Sessak <vitor1001@gmail.com>
00005  * Copyright (C) 2004-2007 Eric Lasota
00006  *    Based on RoQ specs (C) 2001 Tim Ferguson
00007  *
00008  * This file is part of FFmpeg.
00009  *
00010  * FFmpeg is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Lesser General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2.1 of the License, or (at your option) any later version.
00014  *
00015  * FFmpeg is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Lesser General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU Lesser General Public
00021  * License along with FFmpeg; if not, write to the Free Software
00022  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00023  */
00024 
00031 /*
00032  * COSTS:
00033  * Level 1:
00034  *  SKIP - 2 bits
00035  *  MOTION - 2 + 8 bits
00036  *  CODEBOOK - 2 + 8 bits
00037  *  SUBDIVIDE - 2 + combined subcel cost
00038  *
00039  * Level 2:
00040  *  SKIP - 2 bits
00041  *  MOTION - 2 + 8 bits
00042  *  CODEBOOK - 2 + 8 bits
00043  *  SUBDIVIDE - 2 + 4*8 bits
00044  *
00045  * Maximum cost: 138 bits per cel
00046  *
00047  * Proper evaluation requires LCD fraction comparison, which requires
00048  * Squared Error (SE) loss * savings increase
00049  *
00050  * Maximum savings increase: 136 bits
00051  * Maximum SE loss without overflow: 31580641
00052  * Components in 8x8 supercel: 192
00053  * Maximum SE precision per component: 164482
00054  *    >65025, so no truncation is needed (phew)
00055  */
00056 
00057 #include <string.h>
00058 
00059 #include "roqvideo.h"
00060 #include "bytestream.h"
00061 #include "elbg.h"
00062 #include "mathops.h"
00063 
00064 #define CHROMA_BIAS 1
00065 
00070 #define MAX_CBS_4x4 255
00071 
00072 #define MAX_CBS_2x2 256 ///< Maximum number of 2x2 codebooks.
00073 
00074 /* The cast is useful when multiplying it by INT_MAX */
00075 #define ROQ_LAMBDA_SCALE ((uint64_t) FF_LAMBDA_SCALE)
00076 
00077 /* Macroblock support functions */
00078 static void unpack_roq_cell(roq_cell *cell, uint8_t u[4*3])
00079 {
00080     memcpy(u  , cell->y, 4);
00081     memset(u+4, cell->u, 4);
00082     memset(u+8, cell->v, 4);
00083 }
00084 
00085 static void unpack_roq_qcell(uint8_t cb2[], roq_qcell *qcell, uint8_t u[4*4*3])
00086 {
00087     int i,cp;
00088     static const int offsets[4] = {0, 2, 8, 10};
00089 
00090     for (cp=0; cp<3; cp++)
00091         for (i=0; i<4; i++) {
00092             u[4*4*cp + offsets[i]  ] = cb2[qcell->idx[i]*2*2*3 + 4*cp  ];
00093             u[4*4*cp + offsets[i]+1] = cb2[qcell->idx[i]*2*2*3 + 4*cp+1];
00094             u[4*4*cp + offsets[i]+4] = cb2[qcell->idx[i]*2*2*3 + 4*cp+2];
00095             u[4*4*cp + offsets[i]+5] = cb2[qcell->idx[i]*2*2*3 + 4*cp+3];
00096         }
00097 }
00098 
00099 
00100 static void enlarge_roq_mb4(uint8_t base[3*16], uint8_t u[3*64])
00101 {
00102     int x,y,cp;
00103 
00104     for(cp=0; cp<3; cp++)
00105         for(y=0; y<8; y++)
00106             for(x=0; x<8; x++)
00107                 *u++ = base[(y/2)*4 + (x/2) + 16*cp];
00108 }
00109 
00110 static inline int square(int x)
00111 {
00112     return x*x;
00113 }
00114 
00115 static inline int eval_sse(uint8_t *a, uint8_t *b, int count)
00116 {
00117     int diff=0;
00118 
00119     while(count--)
00120         diff += square(*b++ - *a++);
00121 
00122     return diff;
00123 }
00124 
00125 // FIXME Could use DSPContext.sse, but it is not so speed critical (used
00126 // just for motion estimation).
00127 static int block_sse(uint8_t **buf1, uint8_t **buf2, int x1, int y1, int x2,
00128                      int y2, int *stride1, int *stride2, int size)
00129 {
00130     int i, k;
00131     int sse=0;
00132 
00133     for (k=0; k<3; k++) {
00134         int bias = (k ? CHROMA_BIAS : 4);
00135         for (i=0; i<size; i++)
00136             sse += bias*eval_sse(buf1[k] + (y1+i)*stride1[k] + x1,
00137                                  buf2[k] + (y2+i)*stride2[k] + x2, size);
00138     }
00139 
00140     return sse;
00141 }
00142 
00143 static int eval_motion_dist(RoqContext *enc, int x, int y, motion_vect vect,
00144                              int size)
00145 {
00146     int mx=vect.d[0];
00147     int my=vect.d[1];
00148 
00149     if (mx < -7 || mx > 7)
00150         return INT_MAX;
00151 
00152     if (my < -7 || my > 7)
00153         return INT_MAX;
00154 
00155     mx += x;
00156     my += y;
00157 
00158     if ((unsigned) mx > enc->width-size || (unsigned) my > enc->height-size)
00159         return INT_MAX;
00160 
00161     return block_sse(enc->frame_to_enc->data, enc->last_frame->data, x, y,
00162                      mx, my,
00163                      enc->frame_to_enc->linesize, enc->last_frame->linesize,
00164                      size);
00165 }
00166 
00170 static inline int squared_diff_macroblock(uint8_t a[], uint8_t b[], int size)
00171 {
00172     int cp, sdiff=0;
00173 
00174     for(cp=0;cp<3;cp++) {
00175         int bias = (cp ? CHROMA_BIAS : 4);
00176         sdiff += bias*eval_sse(a, b, size*size);
00177         a += size*size;
00178         b += size*size;
00179     }
00180 
00181     return sdiff;
00182 }
00183 
00184 typedef struct
00185 {
00186     int eval_dist[4];
00187     int best_bit_use;
00188     int best_coding;
00189 
00190     int subCels[4];
00191     motion_vect motion;
00192     int cbEntry;
00193 } SubcelEvaluation;
00194 
00195 typedef struct
00196 {
00197     int eval_dist[4];
00198     int best_coding;
00199 
00200     SubcelEvaluation subCels[4];
00201 
00202     motion_vect motion;
00203     int cbEntry;
00204 
00205     int sourceX, sourceY;
00206 } CelEvaluation;
00207 
00208 typedef struct
00209 {
00210     int numCB4;
00211     int numCB2;
00212     int usedCB2[MAX_CBS_2x2];
00213     int usedCB4[MAX_CBS_4x4];
00214     uint8_t unpacked_cb2[MAX_CBS_2x2*2*2*3];
00215     uint8_t unpacked_cb4[MAX_CBS_4x4*4*4*3];
00216     uint8_t unpacked_cb4_enlarged[MAX_CBS_4x4*8*8*3];
00217 } RoqCodebooks;
00218 
00222 typedef struct RoqTempData
00223 {
00224     CelEvaluation *cel_evals;
00225 
00226     int f2i4[MAX_CBS_4x4];
00227     int i2f4[MAX_CBS_4x4];
00228     int f2i2[MAX_CBS_2x2];
00229     int i2f2[MAX_CBS_2x2];
00230 
00231     int mainChunkSize;
00232 
00233     int numCB4;
00234     int numCB2;
00235 
00236     RoqCodebooks codebooks;
00237 
00238     int *closest_cb2;
00239     int used_option[4];
00240 } RoqTempdata;
00241 
00245 static void create_cel_evals(RoqContext *enc, RoqTempdata *tempData)
00246 {
00247     int n=0, x, y, i;
00248 
00249     tempData->cel_evals = av_malloc(enc->width*enc->height/64 * sizeof(CelEvaluation));
00250 
00251     /* Map to the ROQ quadtree order */
00252     for (y=0; y<enc->height; y+=16)
00253         for (x=0; x<enc->width; x+=16)
00254             for(i=0; i<4; i++) {
00255                 tempData->cel_evals[n  ].sourceX = x + (i&1)*8;
00256                 tempData->cel_evals[n++].sourceY = y + (i&2)*4;
00257             }
00258 }
00259 
00263 static void get_frame_mb(AVFrame *frame, int x, int y, uint8_t mb[], int dim)
00264 {
00265     int i, j, cp;
00266 
00267     for (cp=0; cp<3; cp++) {
00268         int stride = frame->linesize[cp];
00269         for (i=0; i<dim; i++)
00270             for (j=0; j<dim; j++)
00271                 *mb++ = frame->data[cp][(y+i)*stride + x + j];
00272     }
00273 }
00274 
00278 static int index_mb(uint8_t cluster[], uint8_t cb[], int numCB,
00279                     int *outIndex, int dim)
00280 {
00281     int i, lDiff = INT_MAX, pick=0;
00282 
00283     /* Diff against the others */
00284     for (i=0; i<numCB; i++) {
00285         int diff = squared_diff_macroblock(cluster, cb + i*dim*dim*3, dim);
00286         if (diff < lDiff) {
00287             lDiff = diff;
00288             pick = i;
00289         }
00290     }
00291 
00292     *outIndex = pick;
00293     return lDiff;
00294 }
00295 
00296 #define EVAL_MOTION(MOTION) \
00297     do { \
00298         diff = eval_motion_dist(enc, j, i, MOTION, blocksize); \
00299             \
00300         if (diff < lowestdiff) { \
00301             lowestdiff = diff; \
00302             bestpick = MOTION; \
00303         } \
00304     } while(0)
00305 
00306 static void motion_search(RoqContext *enc, int blocksize)
00307 {
00308     static const motion_vect offsets[8] = {
00309         {{ 0,-1}},
00310         {{ 0, 1}},
00311         {{-1, 0}},
00312         {{ 1, 0}},
00313         {{-1, 1}},
00314         {{ 1,-1}},
00315         {{-1,-1}},
00316         {{ 1, 1}},
00317     };
00318 
00319     int diff, lowestdiff, oldbest;
00320     int off[3];
00321     motion_vect bestpick = {{0,0}};
00322     int i, j, k, offset;
00323 
00324     motion_vect *last_motion;
00325     motion_vect *this_motion;
00326     motion_vect vect, vect2;
00327 
00328     int max=(enc->width/blocksize)*enc->height/blocksize;
00329 
00330     if (blocksize == 4) {
00331         last_motion = enc->last_motion4;
00332         this_motion = enc->this_motion4;
00333     } else {
00334         last_motion = enc->last_motion8;
00335         this_motion = enc->this_motion8;
00336     }
00337 
00338     for (i=0; i<enc->height; i+=blocksize)
00339         for (j=0; j<enc->width; j+=blocksize) {
00340             lowestdiff = eval_motion_dist(enc, j, i, (motion_vect) {{0,0}},
00341                                           blocksize);
00342             bestpick.d[0] = 0;
00343             bestpick.d[1] = 0;
00344 
00345             if (blocksize == 4)
00346                 EVAL_MOTION(enc->this_motion8[(i/8)*(enc->width/8) + j/8]);
00347 
00348             offset = (i/blocksize)*enc->width/blocksize + j/blocksize;
00349             if (offset < max && offset >= 0)
00350                 EVAL_MOTION(last_motion[offset]);
00351 
00352             offset++;
00353             if (offset < max && offset >= 0)
00354                 EVAL_MOTION(last_motion[offset]);
00355 
00356             offset = (i/blocksize + 1)*enc->width/blocksize + j/blocksize;
00357             if (offset < max && offset >= 0)
00358                 EVAL_MOTION(last_motion[offset]);
00359 
00360             off[0]= (i/blocksize)*enc->width/blocksize + j/blocksize - 1;
00361             off[1]= off[0] - enc->width/blocksize + 1;
00362             off[2]= off[1] + 1;
00363 
00364             if (i) {
00365 
00366                 for(k=0; k<2; k++)
00367                     vect.d[k]= mid_pred(this_motion[off[0]].d[k],
00368                                         this_motion[off[1]].d[k],
00369                                         this_motion[off[2]].d[k]);
00370 
00371                 EVAL_MOTION(vect);
00372                 for(k=0; k<3; k++)
00373                     EVAL_MOTION(this_motion[off[k]]);
00374             } else if(j)
00375                 EVAL_MOTION(this_motion[off[0]]);
00376 
00377             vect = bestpick;
00378 
00379             oldbest = -1;
00380             while (oldbest != lowestdiff) {
00381                 oldbest = lowestdiff;
00382                 for (k=0; k<8; k++) {
00383                     vect2 = vect;
00384                     vect2.d[0] += offsets[k].d[0];
00385                     vect2.d[1] += offsets[k].d[1];
00386                     EVAL_MOTION(vect2);
00387                 }
00388                 vect = bestpick;
00389             }
00390             offset = (i/blocksize)*enc->width/blocksize + j/blocksize;
00391             this_motion[offset] = bestpick;
00392         }
00393 }
00394 
00398 static void gather_data_for_subcel(SubcelEvaluation *subcel, int x,
00399                                    int y, RoqContext *enc, RoqTempdata *tempData)
00400 {
00401     uint8_t mb4[4*4*3];
00402     uint8_t mb2[2*2*3];
00403     int cluster_index;
00404     int i, best_dist;
00405 
00406     static const int bitsUsed[4] = {2, 10, 10, 34};
00407 
00408     if (enc->framesSinceKeyframe >= 1) {
00409         subcel->motion = enc->this_motion4[y*enc->width/16 + x/4];
00410 
00411         subcel->eval_dist[RoQ_ID_FCC] =
00412             eval_motion_dist(enc, x, y,
00413                              enc->this_motion4[y*enc->width/16 + x/4], 4);
00414     } else
00415         subcel->eval_dist[RoQ_ID_FCC] = INT_MAX;
00416 
00417     if (enc->framesSinceKeyframe >= 2)
00418         subcel->eval_dist[RoQ_ID_MOT] = block_sse(enc->frame_to_enc->data,
00419                                                   enc->current_frame->data, x,
00420                                                   y, x, y,
00421                                                   enc->frame_to_enc->linesize,
00422                                                   enc->current_frame->linesize,
00423                                                   4);
00424     else
00425         subcel->eval_dist[RoQ_ID_MOT] = INT_MAX;
00426 
00427     cluster_index = y*enc->width/16 + x/4;
00428 
00429     get_frame_mb(enc->frame_to_enc, x, y, mb4, 4);
00430 
00431     subcel->eval_dist[RoQ_ID_SLD] = index_mb(mb4,
00432                                              tempData->codebooks.unpacked_cb4,
00433                                              tempData->codebooks.numCB4,
00434                                              &subcel->cbEntry, 4);
00435 
00436     subcel->eval_dist[RoQ_ID_CCC] = 0;
00437 
00438     for(i=0;i<4;i++) {
00439         subcel->subCels[i] = tempData->closest_cb2[cluster_index*4+i];
00440 
00441         get_frame_mb(enc->frame_to_enc, x+2*(i&1),
00442                      y+(i&2), mb2, 2);
00443 
00444         subcel->eval_dist[RoQ_ID_CCC] +=
00445             squared_diff_macroblock(tempData->codebooks.unpacked_cb2 + subcel->subCels[i]*2*2*3, mb2, 2);
00446     }
00447 
00448     best_dist = INT_MAX;
00449     for (i=0; i<4; i++)
00450         if (ROQ_LAMBDA_SCALE*subcel->eval_dist[i] + enc->lambda*bitsUsed[i] <
00451             best_dist) {
00452             subcel->best_coding = i;
00453             subcel->best_bit_use = bitsUsed[i];
00454             best_dist = ROQ_LAMBDA_SCALE*subcel->eval_dist[i] +
00455                 enc->lambda*bitsUsed[i];
00456         }
00457 }
00458 
00462 static void gather_data_for_cel(CelEvaluation *cel, RoqContext *enc,
00463                                 RoqTempdata *tempData)
00464 {
00465     uint8_t mb8[8*8*3];
00466     int index = cel->sourceY*enc->width/64 + cel->sourceX/8;
00467     int i, j, best_dist, divide_bit_use;
00468 
00469     int bitsUsed[4] = {2, 10, 10, 0};
00470 
00471     if (enc->framesSinceKeyframe >= 1) {
00472         cel->motion = enc->this_motion8[index];
00473 
00474         cel->eval_dist[RoQ_ID_FCC] =
00475             eval_motion_dist(enc, cel->sourceX, cel->sourceY,
00476                              enc->this_motion8[index], 8);
00477     } else
00478         cel->eval_dist[RoQ_ID_FCC] = INT_MAX;
00479 
00480     if (enc->framesSinceKeyframe >= 2)
00481         cel->eval_dist[RoQ_ID_MOT] = block_sse(enc->frame_to_enc->data,
00482                                                enc->current_frame->data,
00483                                                cel->sourceX, cel->sourceY,
00484                                                cel->sourceX, cel->sourceY,
00485                                                enc->frame_to_enc->linesize,
00486                                                enc->current_frame->linesize,8);
00487     else
00488         cel->eval_dist[RoQ_ID_MOT] = INT_MAX;
00489 
00490     get_frame_mb(enc->frame_to_enc, cel->sourceX, cel->sourceY, mb8, 8);
00491 
00492     cel->eval_dist[RoQ_ID_SLD] =
00493         index_mb(mb8, tempData->codebooks.unpacked_cb4_enlarged,
00494                  tempData->codebooks.numCB4, &cel->cbEntry, 8);
00495 
00496     gather_data_for_subcel(cel->subCels + 0, cel->sourceX+0, cel->sourceY+0, enc, tempData);
00497     gather_data_for_subcel(cel->subCels + 1, cel->sourceX+4, cel->sourceY+0, enc, tempData);
00498     gather_data_for_subcel(cel->subCels + 2, cel->sourceX+0, cel->sourceY+4, enc, tempData);
00499     gather_data_for_subcel(cel->subCels + 3, cel->sourceX+4, cel->sourceY+4, enc, tempData);
00500 
00501     cel->eval_dist[RoQ_ID_CCC] = 0;
00502     divide_bit_use = 0;
00503     for (i=0; i<4; i++) {
00504         cel->eval_dist[RoQ_ID_CCC] +=
00505             cel->subCels[i].eval_dist[cel->subCels[i].best_coding];
00506         divide_bit_use += cel->subCels[i].best_bit_use;
00507     }
00508 
00509     best_dist = INT_MAX;
00510     bitsUsed[3] = 2 + divide_bit_use;
00511 
00512     for (i=0; i<4; i++)
00513         if (ROQ_LAMBDA_SCALE*cel->eval_dist[i] + enc->lambda*bitsUsed[i] <
00514             best_dist) {
00515             cel->best_coding = i;
00516             best_dist = ROQ_LAMBDA_SCALE*cel->eval_dist[i] +
00517                 enc->lambda*bitsUsed[i];
00518         }
00519 
00520     tempData->used_option[cel->best_coding]++;
00521     tempData->mainChunkSize += bitsUsed[cel->best_coding];
00522 
00523     if (cel->best_coding == RoQ_ID_SLD)
00524         tempData->codebooks.usedCB4[cel->cbEntry]++;
00525 
00526     if (cel->best_coding == RoQ_ID_CCC)
00527         for (i=0; i<4; i++) {
00528             if (cel->subCels[i].best_coding == RoQ_ID_SLD)
00529                 tempData->codebooks.usedCB4[cel->subCels[i].cbEntry]++;
00530             else if (cel->subCels[i].best_coding == RoQ_ID_CCC)
00531                 for (j=0; j<4; j++)
00532                     tempData->codebooks.usedCB2[cel->subCels[i].subCels[j]]++;
00533         }
00534 }
00535 
00536 static void remap_codebooks(RoqContext *enc, RoqTempdata *tempData)
00537 {
00538     int i, j, idx=0;
00539 
00540     /* Make remaps for the final codebook usage */
00541     for (i=0; i<MAX_CBS_4x4; i++) {
00542         if (tempData->codebooks.usedCB4[i]) {
00543             tempData->i2f4[i] = idx;
00544             tempData->f2i4[idx] = i;
00545             for (j=0; j<4; j++)
00546                 tempData->codebooks.usedCB2[enc->cb4x4[i].idx[j]]++;
00547             idx++;
00548         }
00549     }
00550 
00551     tempData->numCB4 = idx;
00552 
00553     idx = 0;
00554     for (i=0; i<MAX_CBS_2x2; i++) {
00555         if (tempData->codebooks.usedCB2[i]) {
00556             tempData->i2f2[i] = idx;
00557             tempData->f2i2[idx] = i;
00558             idx++;
00559         }
00560     }
00561     tempData->numCB2 = idx;
00562 
00563 }
00564 
00568 static void write_codebooks(RoqContext *enc, RoqTempdata *tempData)
00569 {
00570     int i, j;
00571     uint8_t **outp= &enc->out_buf;
00572 
00573     if (tempData->numCB2) {
00574         bytestream_put_le16(outp, RoQ_QUAD_CODEBOOK);
00575         bytestream_put_le32(outp, tempData->numCB2*6 + tempData->numCB4*4);
00576         bytestream_put_byte(outp, tempData->numCB4);
00577         bytestream_put_byte(outp, tempData->numCB2);
00578 
00579         for (i=0; i<tempData->numCB2; i++) {
00580             bytestream_put_buffer(outp, enc->cb2x2[tempData->f2i2[i]].y, 4);
00581             bytestream_put_byte(outp, enc->cb2x2[tempData->f2i2[i]].u);
00582             bytestream_put_byte(outp, enc->cb2x2[tempData->f2i2[i]].v);
00583         }
00584 
00585         for (i=0; i<tempData->numCB4; i++)
00586             for (j=0; j<4; j++)
00587                 bytestream_put_byte(outp, tempData->i2f2[enc->cb4x4[tempData->f2i4[i]].idx[j]]);
00588 
00589     }
00590 }
00591 
00592 static inline uint8_t motion_arg(motion_vect mot)
00593 {
00594     uint8_t ax = 8 - ((uint8_t) mot.d[0]);
00595     uint8_t ay = 8 - ((uint8_t) mot.d[1]);
00596     return ((ax&15)<<4) | (ay&15);
00597 }
00598 
00599 typedef struct
00600 {
00601     int typeSpool;
00602     int typeSpoolLength;
00603     uint8_t argumentSpool[64];
00604     uint8_t *args;
00605     uint8_t **pout;
00606 } CodingSpool;
00607 
00608 /* NOTE: Typecodes must be spooled AFTER arguments!! */
00609 static void write_typecode(CodingSpool *s, uint8_t type)
00610 {
00611     s->typeSpool |= (type & 3) << (14 - s->typeSpoolLength);
00612     s->typeSpoolLength += 2;
00613     if (s->typeSpoolLength == 16) {
00614         bytestream_put_le16(s->pout, s->typeSpool);
00615         bytestream_put_buffer(s->pout, s->argumentSpool,
00616                               s->args - s->argumentSpool);
00617         s->typeSpoolLength = 0;
00618         s->typeSpool = 0;
00619         s->args = s->argumentSpool;
00620     }
00621 }
00622 
00623 static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, int w, int h, int numBlocks)
00624 {
00625     int i, j, k;
00626     int x, y;
00627     int subX, subY;
00628     int dist=0;
00629 
00630     roq_qcell *qcell;
00631     CelEvaluation *eval;
00632 
00633     CodingSpool spool;
00634 
00635     spool.typeSpool=0;
00636     spool.typeSpoolLength=0;
00637     spool.args = spool.argumentSpool;
00638     spool.pout = &enc->out_buf;
00639 
00640     if (tempData->used_option[RoQ_ID_CCC]%2)
00641         tempData->mainChunkSize+=8; //FIXME
00642 
00643     /* Write the video chunk header */
00644     bytestream_put_le16(&enc->out_buf, RoQ_QUAD_VQ);
00645     bytestream_put_le32(&enc->out_buf, tempData->mainChunkSize/8);
00646     bytestream_put_byte(&enc->out_buf, 0x0);
00647     bytestream_put_byte(&enc->out_buf, 0x0);
00648 
00649     for (i=0; i<numBlocks; i++) {
00650         eval = tempData->cel_evals + i;
00651 
00652         x = eval->sourceX;
00653         y = eval->sourceY;
00654         dist += eval->eval_dist[eval->best_coding];
00655 
00656         switch (eval->best_coding) {
00657         case RoQ_ID_MOT:
00658             write_typecode(&spool, RoQ_ID_MOT);
00659             break;
00660 
00661         case RoQ_ID_FCC:
00662             bytestream_put_byte(&spool.args, motion_arg(eval->motion));
00663 
00664             write_typecode(&spool, RoQ_ID_FCC);
00665             ff_apply_motion_8x8(enc, x, y,
00666                                 eval->motion.d[0], eval->motion.d[1]);
00667             break;
00668 
00669         case RoQ_ID_SLD:
00670             bytestream_put_byte(&spool.args, tempData->i2f4[eval->cbEntry]);
00671             write_typecode(&spool, RoQ_ID_SLD);
00672 
00673             qcell = enc->cb4x4 + eval->cbEntry;
00674             ff_apply_vector_4x4(enc, x  , y  , enc->cb2x2 + qcell->idx[0]);
00675             ff_apply_vector_4x4(enc, x+4, y  , enc->cb2x2 + qcell->idx[1]);
00676             ff_apply_vector_4x4(enc, x  , y+4, enc->cb2x2 + qcell->idx[2]);
00677             ff_apply_vector_4x4(enc, x+4, y+4, enc->cb2x2 + qcell->idx[3]);
00678             break;
00679 
00680         case RoQ_ID_CCC:
00681             write_typecode(&spool, RoQ_ID_CCC);
00682 
00683             for (j=0; j<4; j++) {
00684                 subX = x + 4*(j&1);
00685                 subY = y + 2*(j&2);
00686 
00687                 switch(eval->subCels[j].best_coding) {
00688                 case RoQ_ID_MOT:
00689                     break;
00690 
00691                 case RoQ_ID_FCC:
00692                     bytestream_put_byte(&spool.args,
00693                                         motion_arg(eval->subCels[j].motion));
00694 
00695                     ff_apply_motion_4x4(enc, subX, subY,
00696                                         eval->subCels[j].motion.d[0],
00697                                         eval->subCels[j].motion.d[1]);
00698                     break;
00699 
00700                 case RoQ_ID_SLD:
00701                     bytestream_put_byte(&spool.args,
00702                                         tempData->i2f4[eval->subCels[j].cbEntry]);
00703 
00704                     qcell = enc->cb4x4 + eval->subCels[j].cbEntry;
00705 
00706                     ff_apply_vector_2x2(enc, subX  , subY  ,
00707                                         enc->cb2x2 + qcell->idx[0]);
00708                     ff_apply_vector_2x2(enc, subX+2, subY  ,
00709                                         enc->cb2x2 + qcell->idx[1]);
00710                     ff_apply_vector_2x2(enc, subX  , subY+2,
00711                                         enc->cb2x2 + qcell->idx[2]);
00712                     ff_apply_vector_2x2(enc, subX+2, subY+2,
00713                                         enc->cb2x2 + qcell->idx[3]);
00714                     break;
00715 
00716                 case RoQ_ID_CCC:
00717                     for (k=0; k<4; k++) {
00718                         int cb_idx = eval->subCels[j].subCels[k];
00719                         bytestream_put_byte(&spool.args,
00720                                             tempData->i2f2[cb_idx]);
00721 
00722                         ff_apply_vector_2x2(enc, subX + 2*(k&1), subY + (k&2),
00723                                             enc->cb2x2 + cb_idx);
00724                     }
00725                     break;
00726                 }
00727                 write_typecode(&spool, eval->subCels[j].best_coding);
00728             }
00729             break;
00730         }
00731     }
00732 
00733     /* Flush the remainder of the argument/type spool */
00734     while (spool.typeSpoolLength)
00735         write_typecode(&spool, 0x0);
00736 
00737 #if 0
00738     uint8_t *fdata[3] = {enc->frame_to_enc->data[0],
00739                            enc->frame_to_enc->data[1],
00740                            enc->frame_to_enc->data[2]};
00741     uint8_t *cdata[3] = {enc->current_frame->data[0],
00742                            enc->current_frame->data[1],
00743                            enc->current_frame->data[2]};
00744     av_log(enc->avctx, AV_LOG_ERROR, "Expected distortion: %i Actual: %i\n",
00745            dist,
00746            block_sse(fdata, cdata, 0, 0, 0, 0,
00747                      enc->frame_to_enc->linesize,
00748                      enc->current_frame->linesize,
00749                      enc->width));  //WARNING: Square dimensions implied...
00750 #endif
00751 }
00752 
00753 
00757 static inline void frame_block_to_cell(uint8_t *block, uint8_t **data,
00758                                        int top, int left, int *stride)
00759 {
00760     int i, j, u=0, v=0;
00761 
00762     for (i=0; i<2; i++)
00763         for (j=0; j<2; j++) {
00764             int x = (top+i)*stride[0] + left + j;
00765             *block++ = data[0][x];
00766             x = (top+i)*stride[1] + left + j;
00767             u       += data[1][x];
00768             v       += data[2][x];
00769         }
00770 
00771     *block++ = (u+2)/4;
00772     *block++ = (v+2)/4;
00773 }
00774 
00778 static void create_clusters(AVFrame *frame, int w, int h, uint8_t *yuvClusters)
00779 {
00780     int i, j, k, l;
00781 
00782     for (i=0; i<h; i+=4)
00783         for (j=0; j<w; j+=4) {
00784             for (k=0; k < 2; k++)
00785                 for (l=0; l < 2; l++)
00786                     frame_block_to_cell(yuvClusters + (l + 2*k)*6, frame->data,
00787                                         i+2*k, j+2*l, frame->linesize);
00788             yuvClusters += 24;
00789         }
00790 }
00791 
00792 static void generate_codebook(RoqContext *enc, RoqTempdata *tempdata,
00793                               int *points, int inputCount, roq_cell *results,
00794                               int size, int cbsize)
00795 {
00796     int i, j, k;
00797     int c_size = size*size/4;
00798     int *buf;
00799     int *codebook = av_malloc(6*c_size*cbsize*sizeof(int));
00800     int *closest_cb;
00801 
00802     if (size == 4)
00803         closest_cb = av_malloc(6*c_size*inputCount*sizeof(int));
00804     else
00805         closest_cb = tempdata->closest_cb2;
00806 
00807     ff_init_elbg(points, 6*c_size, inputCount, codebook, cbsize, 1, closest_cb, &enc->randctx);
00808     ff_do_elbg(points, 6*c_size, inputCount, codebook, cbsize, 1, closest_cb, &enc->randctx);
00809 
00810     if (size == 4)
00811         av_free(closest_cb);
00812 
00813     buf = codebook;
00814     for (i=0; i<cbsize; i++)
00815         for (k=0; k<c_size; k++) {
00816             for(j=0; j<4; j++)
00817                 results->y[j] = *buf++;
00818 
00819             results->u =    (*buf++ + CHROMA_BIAS/2)/CHROMA_BIAS;
00820             results->v =    (*buf++ + CHROMA_BIAS/2)/CHROMA_BIAS;
00821             results++;
00822         }
00823 
00824     av_free(codebook);
00825 }
00826 
00827 static void generate_new_codebooks(RoqContext *enc, RoqTempdata *tempData)
00828 {
00829     int i,j;
00830     RoqCodebooks *codebooks = &tempData->codebooks;
00831     int max = enc->width*enc->height/16;
00832     uint8_t mb2[3*4];
00833     roq_cell *results4 = av_malloc(sizeof(roq_cell)*MAX_CBS_4x4*4);
00834     uint8_t *yuvClusters=av_malloc(sizeof(int)*max*6*4);
00835     int *points = av_malloc(max*6*4*sizeof(int));
00836     int bias;
00837 
00838     /* Subsample YUV data */
00839     create_clusters(enc->frame_to_enc, enc->width, enc->height, yuvClusters);
00840 
00841     /* Cast to integer and apply chroma bias */
00842     for (i=0; i<max*24; i++) {
00843         bias = ((i%6)<4) ? 1 : CHROMA_BIAS;
00844         points[i] = bias*yuvClusters[i];
00845     }
00846 
00847     /* Create 4x4 codebooks */
00848     generate_codebook(enc, tempData, points, max, results4, 4, MAX_CBS_4x4);
00849 
00850     codebooks->numCB4 = MAX_CBS_4x4;
00851 
00852     tempData->closest_cb2 = av_malloc(max*4*sizeof(int));
00853 
00854     /* Create 2x2 codebooks */
00855     generate_codebook(enc, tempData, points, max*4, enc->cb2x2, 2, MAX_CBS_2x2);
00856 
00857     codebooks->numCB2 = MAX_CBS_2x2;
00858 
00859     /* Unpack 2x2 codebook clusters */
00860     for (i=0; i<codebooks->numCB2; i++)
00861         unpack_roq_cell(enc->cb2x2 + i, codebooks->unpacked_cb2 + i*2*2*3);
00862 
00863     /* Index all 4x4 entries to the 2x2 entries, unpack, and enlarge */
00864     for (i=0; i<codebooks->numCB4; i++) {
00865         for (j=0; j<4; j++) {
00866             unpack_roq_cell(&results4[4*i + j], mb2);
00867             index_mb(mb2, codebooks->unpacked_cb2, codebooks->numCB2,
00868                      &enc->cb4x4[i].idx[j], 2);
00869         }
00870         unpack_roq_qcell(codebooks->unpacked_cb2, enc->cb4x4 + i,
00871                          codebooks->unpacked_cb4 + i*4*4*3);
00872         enlarge_roq_mb4(codebooks->unpacked_cb4 + i*4*4*3,
00873                         codebooks->unpacked_cb4_enlarged + i*8*8*3);
00874     }
00875 
00876     av_free(yuvClusters);
00877     av_free(points);
00878     av_free(results4);
00879 }
00880 
00881 static void roq_encode_video(RoqContext *enc)
00882 {
00883     RoqTempdata *tempData = enc->tmpData;
00884     int i;
00885 
00886     memset(tempData, 0, sizeof(*tempData));
00887 
00888     create_cel_evals(enc, tempData);
00889 
00890     generate_new_codebooks(enc, tempData);
00891 
00892     if (enc->framesSinceKeyframe >= 1) {
00893         motion_search(enc, 8);
00894         motion_search(enc, 4);
00895     }
00896 
00897  retry_encode:
00898     for (i=0; i<enc->width*enc->height/64; i++)
00899         gather_data_for_cel(tempData->cel_evals + i, enc, tempData);
00900 
00901     /* Quake 3 can't handle chunks bigger than 65536 bytes */
00902     if (tempData->mainChunkSize/8 > 65536) {
00903         enc->lambda *= .8;
00904         goto retry_encode;
00905     }
00906 
00907     remap_codebooks(enc, tempData);
00908 
00909     write_codebooks(enc, tempData);
00910 
00911     reconstruct_and_encode_image(enc, tempData, enc->width, enc->height,
00912                                  enc->width*enc->height/64);
00913 
00914     enc->avctx->coded_frame = enc->current_frame;
00915 
00916     /* Rotate frame history */
00917     FFSWAP(AVFrame *, enc->current_frame, enc->last_frame);
00918     FFSWAP(motion_vect *, enc->last_motion4, enc->this_motion4);
00919     FFSWAP(motion_vect *, enc->last_motion8, enc->this_motion8);
00920 
00921     av_free(tempData->cel_evals);
00922     av_free(tempData->closest_cb2);
00923 
00924     enc->framesSinceKeyframe++;
00925 }
00926 
00927 static int roq_encode_init(AVCodecContext *avctx)
00928 {
00929     RoqContext *enc = avctx->priv_data;
00930 
00931     av_lfg_init(&enc->randctx, 1);
00932 
00933     enc->framesSinceKeyframe = 0;
00934     if ((avctx->width & 0xf) || (avctx->height & 0xf)) {
00935         av_log(avctx, AV_LOG_ERROR, "Dimensions must be divisible by 16\n");
00936         return -1;
00937     }
00938 
00939     if (((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx->height-1)))
00940         av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power of two\n");
00941 
00942     enc->width = avctx->width;
00943     enc->height = avctx->height;
00944 
00945     enc->framesSinceKeyframe = 0;
00946     enc->first_frame = 1;
00947 
00948     enc->last_frame    = &enc->frames[0];
00949     enc->current_frame = &enc->frames[1];
00950 
00951     enc->tmpData      = av_malloc(sizeof(RoqTempdata));
00952 
00953     enc->this_motion4 =
00954         av_mallocz((enc->width*enc->height/16)*sizeof(motion_vect));
00955 
00956     enc->last_motion4 =
00957         av_malloc ((enc->width*enc->height/16)*sizeof(motion_vect));
00958 
00959     enc->this_motion8 =
00960         av_mallocz((enc->width*enc->height/64)*sizeof(motion_vect));
00961 
00962     enc->last_motion8 =
00963         av_malloc ((enc->width*enc->height/64)*sizeof(motion_vect));
00964 
00965     return 0;
00966 }
00967 
00968 static void roq_write_video_info_chunk(RoqContext *enc)
00969 {
00970     /* ROQ info chunk */
00971     bytestream_put_le16(&enc->out_buf, RoQ_INFO);
00972 
00973     /* Size: 8 bytes */
00974     bytestream_put_le32(&enc->out_buf, 8);
00975 
00976     /* Unused argument */
00977     bytestream_put_byte(&enc->out_buf, 0x00);
00978     bytestream_put_byte(&enc->out_buf, 0x00);
00979 
00980     /* Width */
00981     bytestream_put_le16(&enc->out_buf, enc->width);
00982 
00983     /* Height */
00984     bytestream_put_le16(&enc->out_buf, enc->height);
00985 
00986     /* Unused in Quake 3, mimics the output of the real encoder */
00987     bytestream_put_byte(&enc->out_buf, 0x08);
00988     bytestream_put_byte(&enc->out_buf, 0x00);
00989     bytestream_put_byte(&enc->out_buf, 0x04);
00990     bytestream_put_byte(&enc->out_buf, 0x00);
00991 }
00992 
00993 static int roq_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data)
00994 {
00995     RoqContext *enc = avctx->priv_data;
00996     AVFrame *frame= data;
00997     uint8_t *buf_start = buf;
00998 
00999     enc->out_buf = buf;
01000     enc->avctx = avctx;
01001 
01002     enc->frame_to_enc = frame;
01003 
01004     if (frame->quality)
01005         enc->lambda = frame->quality - 1;
01006     else
01007         enc->lambda = 2*ROQ_LAMBDA_SCALE;
01008 
01009     /* 138 bits max per 8x8 block +
01010      *     256 codebooks*(6 bytes 2x2 + 4 bytes 4x4) + 8 bytes frame header */
01011     if (((enc->width*enc->height/64)*138+7)/8 + 256*(6+4) + 8 > buf_size) {
01012         av_log(avctx, AV_LOG_ERROR, "  RoQ: Output buffer too small!\n");
01013         return -1;
01014     }
01015 
01016     /* Check for I frame */
01017     if (enc->framesSinceKeyframe == avctx->gop_size)
01018         enc->framesSinceKeyframe = 0;
01019 
01020     if (enc->first_frame) {
01021         /* Alloc memory for the reconstruction data (we must know the stride
01022          for that) */
01023         if (avctx->get_buffer(avctx, enc->current_frame) ||
01024             avctx->get_buffer(avctx, enc->last_frame)) {
01025             av_log(avctx, AV_LOG_ERROR, "  RoQ: get_buffer() failed\n");
01026             return -1;
01027         }
01028 
01029         /* Before the first video frame, write a "video info" chunk */
01030         roq_write_video_info_chunk(enc);
01031 
01032         enc->first_frame = 0;
01033     }
01034 
01035     /* Encode the actual frame */
01036     roq_encode_video(enc);
01037 
01038     return enc->out_buf - buf_start;
01039 }
01040 
01041 static int roq_encode_end(AVCodecContext *avctx)
01042 {
01043     RoqContext *enc = avctx->priv_data;
01044 
01045     avctx->release_buffer(avctx, enc->last_frame);
01046     avctx->release_buffer(avctx, enc->current_frame);
01047 
01048     av_free(enc->tmpData);
01049     av_free(enc->this_motion4);
01050     av_free(enc->last_motion4);
01051     av_free(enc->this_motion8);
01052     av_free(enc->last_motion8);
01053 
01054     return 0;
01055 }
01056 
01057 AVCodec roq_encoder =
01058 {
01059     "roqvideo",
01060     AVMEDIA_TYPE_VIDEO,
01061     CODEC_ID_ROQ,
01062     sizeof(RoqContext),
01063     roq_encode_init,
01064     roq_encode_frame,
01065     roq_encode_end,
01066     .supported_framerates = (const AVRational[]){{30,1}, {0,0}},
01067     .pix_fmts = (const enum PixelFormat[]){PIX_FMT_YUV444P, PIX_FMT_NONE},
01068     .long_name = NULL_IF_CONFIG_SMALL("id RoQ video"),
01069 };

Generated on Fri Sep 16 2011 17:17:42 for FFmpeg by  doxygen 1.7.1