X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Froqvideoenc.c;h=1ab0de0abbd3ca1995519dcfb10d1e2aafcca9dd;hb=caaf4633117f77a545975dac18e85b8fcdbc9ce7;hp=ac05123dc66cde0952b257d3f7cae14218be60ec;hpb=fbd607dd560afe44c3b90de1e6cbe5265cac8f1e;p=ffmpeg diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index ac05123dc66..1ab0de0abbd 100644 --- a/libavcodec/roqvideoenc.c +++ b/libavcodec/roqvideoenc.c @@ -57,6 +57,7 @@ #include #include "libavutil/attributes.h" +#include "libavutil/lfg.h" #include "libavutil/opt.h" #include "roqvideo.h" #include "bytestream.h" @@ -77,6 +78,85 @@ /* The cast is useful when multiplying it by INT_MAX */ #define ROQ_LAMBDA_SCALE ((uint64_t) FF_LAMBDA_SCALE) +typedef struct RoqCodebooks { + int numCB4; + int numCB2; + int usedCB2[MAX_CBS_2x2]; + int usedCB4[MAX_CBS_4x4]; + uint8_t unpacked_cb2[MAX_CBS_2x2*2*2*3]; + uint8_t unpacked_cb4[MAX_CBS_4x4*4*4*3]; + uint8_t unpacked_cb4_enlarged[MAX_CBS_4x4*8*8*3]; +} RoqCodebooks; + +/** + * Temporary vars + */ +typedef struct RoqTempData +{ + int f2i4[MAX_CBS_4x4]; + int i2f4[MAX_CBS_4x4]; + int f2i2[MAX_CBS_2x2]; + int i2f2[MAX_CBS_2x2]; + + int mainChunkSize; + + int numCB4; + int numCB2; + + RoqCodebooks codebooks; + + int used_option[4]; +} RoqTempData; + +typedef struct SubcelEvaluation { + int eval_dist[4]; + int best_bit_use; + int best_coding; + + int subCels[4]; + motion_vect motion; + int cbEntry; +} SubcelEvaluation; + +typedef struct CelEvaluation { + int eval_dist[4]; + int best_coding; + + SubcelEvaluation subCels[4]; + + motion_vect motion; + int cbEntry; + + int sourceX, sourceY; +} CelEvaluation; + +typedef struct RoqEncContext { + RoqContext common; + AVLFG randctx; + uint64_t lambda; + + motion_vect *this_motion4; + motion_vect *last_motion4; + + motion_vect *this_motion8; + motion_vect *last_motion8; + + unsigned int framesSinceKeyframe; + + const AVFrame *frame_to_enc; + uint8_t *out_buf; + RoqTempData tmp_data; + roq_cell results4[4 * MAX_CBS_4x4]; + int tmp_codebook_buf[FFMAX(24 * MAX_CBS_4x4, 6 * MAX_CBS_2x2)]; + + CelEvaluation *cel_evals; + int *closest_cb; + int *points; // Allocated together with closest_cb + + int first_frame; + int quake3_compat; // Quake 3 compatibility option +} RoqEncContext; + /* Macroblock support functions */ static void unpack_roq_cell(roq_cell *cell, uint8_t u[4*3]) { @@ -143,9 +223,10 @@ static int block_sse(uint8_t * const *buf1, uint8_t * const *buf2, int x1, int y return sse; } -static int eval_motion_dist(RoqContext *enc, int x, int y, motion_vect vect, +static int eval_motion_dist(RoqEncContext *enc, int x, int y, motion_vect vect, int size) { + RoqContext *const roq = &enc->common; int mx=vect.d[0]; int my=vect.d[1]; @@ -158,12 +239,12 @@ static int eval_motion_dist(RoqContext *enc, int x, int y, motion_vect vect, mx += x; my += y; - if ((unsigned) mx > enc->width-size || (unsigned) my > enc->height-size) + if ((unsigned) mx > roq->width-size || (unsigned) my > roq->height-size) return INT_MAX; - return block_sse(enc->frame_to_enc->data, enc->last_frame->data, x, y, + return block_sse(enc->frame_to_enc->data, roq->last_frame->data, x, y, mx, my, - enc->frame_to_enc->linesize, enc->last_frame->linesize, + enc->frame_to_enc->linesize, roq->last_frame->linesize, size); } @@ -184,78 +265,23 @@ static inline int squared_diff_macroblock(uint8_t a[], uint8_t b[], int size) return sdiff; } -typedef struct SubcelEvaluation { - int eval_dist[4]; - int best_bit_use; - int best_coding; - - int subCels[4]; - motion_vect motion; - int cbEntry; -} SubcelEvaluation; - -typedef struct CelEvaluation { - int eval_dist[4]; - int best_coding; - - SubcelEvaluation subCels[4]; - - motion_vect motion; - int cbEntry; - - int sourceX, sourceY; -} CelEvaluation; - -typedef struct RoqCodebooks { - int numCB4; - int numCB2; - int usedCB2[MAX_CBS_2x2]; - int usedCB4[MAX_CBS_4x4]; - uint8_t unpacked_cb2[MAX_CBS_2x2*2*2*3]; - uint8_t unpacked_cb4[MAX_CBS_4x4*4*4*3]; - uint8_t unpacked_cb4_enlarged[MAX_CBS_4x4*8*8*3]; -} RoqCodebooks; - -/** - * Temporary vars - */ -typedef struct RoqTempData -{ - CelEvaluation *cel_evals; - - int f2i4[MAX_CBS_4x4]; - int i2f4[MAX_CBS_4x4]; - int f2i2[MAX_CBS_2x2]; - int i2f2[MAX_CBS_2x2]; - - int mainChunkSize; - - int numCB4; - int numCB2; - - RoqCodebooks codebooks; - - int *closest_cb2; - int used_option[4]; -} RoqTempdata; - /** * Initialize cel evaluators and set their source coordinates */ -static int create_cel_evals(RoqContext *enc, RoqTempdata *tempData) +static int create_cel_evals(RoqEncContext *enc) { - int n=0, x, y, i; + RoqContext *const roq = &enc->common; - tempData->cel_evals = av_malloc_array(enc->width*enc->height/64, sizeof(CelEvaluation)); - if (!tempData->cel_evals) + enc->cel_evals = av_malloc_array(roq->width * roq->height / 64, sizeof(CelEvaluation)); + if (!enc->cel_evals) return AVERROR(ENOMEM); /* Map to the ROQ quadtree order */ - for (y=0; yheight; y+=16) - for (x=0; xwidth; x+=16) - for(i=0; i<4; i++) { - tempData->cel_evals[n ].sourceX = x + (i&1)*8; - tempData->cel_evals[n++].sourceY = y + (i&2)*4; + for (int y = 0, n = 0; y < roq->height; y += 16) + for (int x = 0; x < roq->width; x += 16) + for(int i = 0; i < 4; i++) { + enc->cel_evals[n ].sourceX = x + (i&1)*8; + enc->cel_evals[n++].sourceY = y + (i&2)*4; } return 0; @@ -307,7 +333,7 @@ static int index_mb(uint8_t cluster[], uint8_t cb[], int numCB, } \ } while(0) -static void motion_search(RoqContext *enc, int blocksize) +static void motion_search(RoqEncContext *enc, int blocksize) { static const motion_vect offsets[8] = { {{ 0,-1}}, @@ -320,6 +346,7 @@ static void motion_search(RoqContext *enc, int blocksize) {{ 1, 1}}, }; + RoqContext *const roq = &enc->common; int diff, lowestdiff, oldbest; int off[3]; motion_vect bestpick = {{0,0}}; @@ -328,8 +355,7 @@ static void motion_search(RoqContext *enc, int blocksize) motion_vect *last_motion; motion_vect *this_motion; motion_vect vect, vect2; - - int max=(enc->width/blocksize)*enc->height/blocksize; + const int max = (roq->width / blocksize) * roq->height / blocksize; if (blocksize == 4) { last_motion = enc->last_motion4; @@ -339,17 +365,17 @@ static void motion_search(RoqContext *enc, int blocksize) this_motion = enc->this_motion8; } - for (i=0; iheight; i+=blocksize) - for (j=0; jwidth; j+=blocksize) { + for (i = 0; i< roq->height; i += blocksize) + for (j = 0; j < roq->width; j += blocksize) { lowestdiff = eval_motion_dist(enc, j, i, (motion_vect) {{0,0}}, blocksize); bestpick.d[0] = 0; bestpick.d[1] = 0; if (blocksize == 4) - EVAL_MOTION(enc->this_motion8[(i/8)*(enc->width/8) + j/8]); + EVAL_MOTION(enc->this_motion8[(i/8) * (roq->width/8) + j/8]); - offset = (i/blocksize)*enc->width/blocksize + j/blocksize; + offset = (i/blocksize) * roq->width / blocksize + j / blocksize; if (offset < max && offset >= 0) EVAL_MOTION(last_motion[offset]); @@ -357,12 +383,12 @@ static void motion_search(RoqContext *enc, int blocksize) if (offset < max && offset >= 0) EVAL_MOTION(last_motion[offset]); - offset = (i/blocksize + 1)*enc->width/blocksize + j/blocksize; + offset = (i/blocksize + 1) * roq->width / blocksize + j / blocksize; if (offset < max && offset >= 0) EVAL_MOTION(last_motion[offset]); - off[0]= (i/blocksize)*enc->width/blocksize + j/blocksize - 1; - off[1]= off[0] - enc->width/blocksize + 1; + off[0]= (i/blocksize) * roq->width / blocksize + j/blocksize - 1; + off[1]= off[0] - roq->width / blocksize + 1; off[2]= off[1] + 1; if (i) { @@ -391,7 +417,7 @@ static void motion_search(RoqContext *enc, int blocksize) } vect = bestpick; } - offset = (i/blocksize)*enc->width/blocksize + j/blocksize; + offset = (i/blocksize) * roq->width / blocksize + j/blocksize; this_motion[offset] = bestpick; } } @@ -400,8 +426,10 @@ static void motion_search(RoqContext *enc, int blocksize) * Get distortion for all options available to a subcel */ static void gather_data_for_subcel(SubcelEvaluation *subcel, int x, - int y, RoqContext *enc, RoqTempdata *tempData) + int y, RoqEncContext *enc) { + RoqContext *const roq = &enc->common; + RoqTempData *const tempData = &enc->tmp_data; uint8_t mb4[4*4*3]; uint8_t mb2[2*2*3]; int cluster_index; @@ -410,25 +438,25 @@ static void gather_data_for_subcel(SubcelEvaluation *subcel, int x, static const int bitsUsed[4] = {2, 10, 10, 34}; if (enc->framesSinceKeyframe >= 1) { - subcel->motion = enc->this_motion4[y*enc->width/16 + x/4]; + subcel->motion = enc->this_motion4[y * roq->width / 16 + x / 4]; subcel->eval_dist[RoQ_ID_FCC] = eval_motion_dist(enc, x, y, - enc->this_motion4[y*enc->width/16 + x/4], 4); + enc->this_motion4[y * roq->width / 16 + x / 4], 4); } else subcel->eval_dist[RoQ_ID_FCC] = INT_MAX; if (enc->framesSinceKeyframe >= 2) subcel->eval_dist[RoQ_ID_MOT] = block_sse(enc->frame_to_enc->data, - enc->current_frame->data, x, + roq->current_frame->data, x, y, x, y, enc->frame_to_enc->linesize, - enc->current_frame->linesize, + roq->current_frame->linesize, 4); else subcel->eval_dist[RoQ_ID_MOT] = INT_MAX; - cluster_index = y*enc->width/16 + x/4; + cluster_index = y * roq->width / 16 + x / 4; get_frame_mb(enc->frame_to_enc, x, y, mb4, 4); @@ -440,7 +468,7 @@ static void gather_data_for_subcel(SubcelEvaluation *subcel, int x, subcel->eval_dist[RoQ_ID_CCC] = 0; for(i=0;i<4;i++) { - subcel->subCels[i] = tempData->closest_cb2[cluster_index*4+i]; + subcel->subCels[i] = enc->closest_cb[cluster_index*4+i]; get_frame_mb(enc->frame_to_enc, x+2*(i&1), y+(i&2), mb2, 2); @@ -463,11 +491,12 @@ static void gather_data_for_subcel(SubcelEvaluation *subcel, int x, /** * Get distortion for all options available to a cel */ -static void gather_data_for_cel(CelEvaluation *cel, RoqContext *enc, - RoqTempdata *tempData) +static void gather_data_for_cel(CelEvaluation *cel, RoqEncContext *enc) { + RoqContext *const roq = &enc->common; + RoqTempData *const tempData = &enc->tmp_data; uint8_t mb8[8*8*3]; - int index = cel->sourceY*enc->width/64 + cel->sourceX/8; + int index = cel->sourceY * roq->width / 64 + cel->sourceX/8; int i, j, best_dist, divide_bit_use; int bitsUsed[4] = {2, 10, 10, 0}; @@ -483,11 +512,11 @@ static void gather_data_for_cel(CelEvaluation *cel, RoqContext *enc, if (enc->framesSinceKeyframe >= 2) cel->eval_dist[RoQ_ID_MOT] = block_sse(enc->frame_to_enc->data, - enc->current_frame->data, + roq->current_frame->data, cel->sourceX, cel->sourceY, cel->sourceX, cel->sourceY, enc->frame_to_enc->linesize, - enc->current_frame->linesize,8); + roq->current_frame->linesize,8); else cel->eval_dist[RoQ_ID_MOT] = INT_MAX; @@ -497,10 +526,10 @@ static void gather_data_for_cel(CelEvaluation *cel, RoqContext *enc, index_mb(mb8, tempData->codebooks.unpacked_cb4_enlarged, tempData->codebooks.numCB4, &cel->cbEntry, 8); - gather_data_for_subcel(cel->subCels + 0, cel->sourceX+0, cel->sourceY+0, enc, tempData); - gather_data_for_subcel(cel->subCels + 1, cel->sourceX+4, cel->sourceY+0, enc, tempData); - gather_data_for_subcel(cel->subCels + 2, cel->sourceX+0, cel->sourceY+4, enc, tempData); - gather_data_for_subcel(cel->subCels + 3, cel->sourceX+4, cel->sourceY+4, enc, tempData); + gather_data_for_subcel(cel->subCels + 0, cel->sourceX+0, cel->sourceY+0, enc); + gather_data_for_subcel(cel->subCels + 1, cel->sourceX+4, cel->sourceY+0, enc); + gather_data_for_subcel(cel->subCels + 2, cel->sourceX+0, cel->sourceY+4, enc); + gather_data_for_subcel(cel->subCels + 3, cel->sourceX+4, cel->sourceY+4, enc); cel->eval_dist[RoQ_ID_CCC] = 0; divide_bit_use = 0; @@ -537,8 +566,10 @@ static void gather_data_for_cel(CelEvaluation *cel, RoqContext *enc, } } -static void remap_codebooks(RoqContext *enc, RoqTempdata *tempData) +static void remap_codebooks(RoqEncContext *enc) { + RoqContext *const roq = &enc->common; + RoqTempData *const tempData = &enc->tmp_data; int i, j, idx=0; /* Make remaps for the final codebook usage */ @@ -547,7 +578,7 @@ static void remap_codebooks(RoqContext *enc, RoqTempdata *tempData) tempData->i2f4[i] = idx; tempData->f2i4[idx] = i; for (j=0; j<4; j++) - tempData->codebooks.usedCB2[enc->cb4x4[i].idx[j]]++; + tempData->codebooks.usedCB2[roq->cb4x4[i].idx[j]]++; idx++; } } @@ -569,8 +600,10 @@ static void remap_codebooks(RoqContext *enc, RoqTempdata *tempData) /** * Write codebook chunk */ -static void write_codebooks(RoqContext *enc, RoqTempdata *tempData) +static void write_codebooks(RoqEncContext *enc) { + RoqContext *const roq = &enc->common; + RoqTempData *const tempData = &enc->tmp_data; int i, j; uint8_t **outp= &enc->out_buf; @@ -581,14 +614,14 @@ static void write_codebooks(RoqContext *enc, RoqTempdata *tempData) bytestream_put_byte(outp, tempData->numCB2); for (i=0; inumCB2; i++) { - bytestream_put_buffer(outp, enc->cb2x2[tempData->f2i2[i]].y, 4); - bytestream_put_byte(outp, enc->cb2x2[tempData->f2i2[i]].u); - bytestream_put_byte(outp, enc->cb2x2[tempData->f2i2[i]].v); + bytestream_put_buffer(outp, roq->cb2x2[tempData->f2i2[i]].y, 4); + bytestream_put_byte(outp, roq->cb2x2[tempData->f2i2[i]].u); + bytestream_put_byte(outp, roq->cb2x2[tempData->f2i2[i]].v); } for (i=0; inumCB4; i++) for (j=0; j<4; j++) - bytestream_put_byte(outp, tempData->i2f2[enc->cb4x4[tempData->f2i4[i]].idx[j]]); + bytestream_put_byte(outp, tempData->i2f2[roq->cb4x4[tempData->f2i4[i]].idx[j]]); } } @@ -623,8 +656,11 @@ static void write_typecode(CodingSpool *s, uint8_t type) } } -static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, int w, int h, int numBlocks) +static void reconstruct_and_encode_image(RoqEncContext *enc, + int w, int h, int numBlocks) { + RoqContext *const roq = &enc->common; + RoqTempData *const tempData = &enc->tmp_data; int i, j, k; int x, y; int subX, subY; @@ -650,7 +686,7 @@ static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, bytestream_put_byte(&enc->out_buf, 0x0); for (i=0; icel_evals + i; + eval = enc->cel_evals + i; x = eval->sourceX; y = eval->sourceY; @@ -665,7 +701,7 @@ static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, bytestream_put_byte(&spool.args, motion_arg(eval->motion)); write_typecode(&spool, RoQ_ID_FCC); - ff_apply_motion_8x8(enc, x, y, + ff_apply_motion_8x8(roq, x, y, eval->motion.d[0], eval->motion.d[1]); break; @@ -673,11 +709,11 @@ static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, bytestream_put_byte(&spool.args, tempData->i2f4[eval->cbEntry]); write_typecode(&spool, RoQ_ID_SLD); - qcell = enc->cb4x4 + eval->cbEntry; - ff_apply_vector_4x4(enc, x , y , enc->cb2x2 + qcell->idx[0]); - ff_apply_vector_4x4(enc, x+4, y , enc->cb2x2 + qcell->idx[1]); - ff_apply_vector_4x4(enc, x , y+4, enc->cb2x2 + qcell->idx[2]); - ff_apply_vector_4x4(enc, x+4, y+4, enc->cb2x2 + qcell->idx[3]); + qcell = roq->cb4x4 + eval->cbEntry; + ff_apply_vector_4x4(roq, x , y , roq->cb2x2 + qcell->idx[0]); + ff_apply_vector_4x4(roq, x+4, y , roq->cb2x2 + qcell->idx[1]); + ff_apply_vector_4x4(roq, x , y+4, roq->cb2x2 + qcell->idx[2]); + ff_apply_vector_4x4(roq, x+4, y+4, roq->cb2x2 + qcell->idx[3]); break; case RoQ_ID_CCC: @@ -695,7 +731,7 @@ static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, bytestream_put_byte(&spool.args, motion_arg(eval->subCels[j].motion)); - ff_apply_motion_4x4(enc, subX, subY, + ff_apply_motion_4x4(roq, subX, subY, eval->subCels[j].motion.d[0], eval->subCels[j].motion.d[1]); break; @@ -704,16 +740,16 @@ static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, bytestream_put_byte(&spool.args, tempData->i2f4[eval->subCels[j].cbEntry]); - qcell = enc->cb4x4 + eval->subCels[j].cbEntry; + qcell = roq->cb4x4 + eval->subCels[j].cbEntry; - ff_apply_vector_2x2(enc, subX , subY , - enc->cb2x2 + qcell->idx[0]); - ff_apply_vector_2x2(enc, subX+2, subY , - enc->cb2x2 + qcell->idx[1]); - ff_apply_vector_2x2(enc, subX , subY+2, - enc->cb2x2 + qcell->idx[2]); - ff_apply_vector_2x2(enc, subX+2, subY+2, - enc->cb2x2 + qcell->idx[3]); + ff_apply_vector_2x2(roq, subX , subY , + roq->cb2x2 + qcell->idx[0]); + ff_apply_vector_2x2(roq, subX+2, subY , + roq->cb2x2 + qcell->idx[1]); + ff_apply_vector_2x2(roq, subX , subY+2, + roq->cb2x2 + qcell->idx[2]); + ff_apply_vector_2x2(roq, subX+2, subY+2, + roq->cb2x2 + qcell->idx[3]); break; case RoQ_ID_CCC: @@ -722,8 +758,8 @@ static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, bytestream_put_byte(&spool.args, tempData->i2f2[cb_idx]); - ff_apply_vector_2x2(enc, subX + 2*(k&1), subY + (k&2), - enc->cb2x2 + cb_idx); + ff_apply_vector_2x2(roq, subX + 2*(k&1), subY + (k&2), + roq->cb2x2 + cb_idx); } break; } @@ -742,7 +778,7 @@ static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, /** * Create a single YUV cell from a 2x2 section of the image */ -static inline void frame_block_to_cell(uint8_t *block, uint8_t * const *data, +static inline void frame_block_to_cell(int *block, uint8_t * const *data, int top, int left, const int *stride) { int i, j, u=0, v=0; @@ -756,14 +792,14 @@ static inline void frame_block_to_cell(uint8_t *block, uint8_t * const *data, v += data[2][x]; } - *block++ = (u+2)/4; - *block++ = (v+2)/4; + *block++ = (u + 2) / 4 * CHROMA_BIAS; + *block++ = (v + 2) / 4 * CHROMA_BIAS; } /** * Create YUV clusters for the entire image */ -static void create_clusters(const AVFrame *frame, int w, int h, uint8_t *yuvClusters) +static void create_clusters(const AVFrame *frame, int w, int h, int *points) { int i, j, k, l; @@ -771,42 +807,30 @@ static void create_clusters(const AVFrame *frame, int w, int h, uint8_t *yuvClus for (j=0; jdata, + frame_block_to_cell(points + (l + 2*k)*6, frame->data, i+2*k, j+2*l, frame->linesize); - yuvClusters += 24; + points += 24; } } -static int generate_codebook(RoqContext *enc, RoqTempdata *tempdata, +static int generate_codebook(RoqEncContext *enc, int *points, int inputCount, roq_cell *results, int size, int cbsize) { int i, j, k, ret = 0; int c_size = size*size/4; int *buf; - int *codebook = av_malloc_array(6*c_size, cbsize*sizeof(int)); - int *closest_cb; - - if (!codebook) - return AVERROR(ENOMEM); - - if (size == 4) { - closest_cb = av_malloc_array(6*c_size, inputCount*sizeof(int)); - if (!closest_cb) { - ret = AVERROR(ENOMEM); - goto out; - } - } else - closest_cb = tempdata->closest_cb2; + int *codebook = enc->tmp_codebook_buf; + int *closest_cb = enc->closest_cb; ret = avpriv_init_elbg(points, 6 * c_size, inputCount, codebook, cbsize, 1, closest_cb, &enc->randctx); if (ret < 0) - goto out; + return ret; ret = avpriv_do_elbg(points, 6 * c_size, inputCount, codebook, cbsize, 1, closest_cb, &enc->randctx); if (ret < 0) - goto out; + return ret; buf = codebook; for (i=0; iv = (*buf++ + CHROMA_BIAS/2)/CHROMA_BIAS; results++; } -out: - if (size == 4) - av_free(closest_cb); - av_free(codebook); - return ret; + return 0; } -static int generate_new_codebooks(RoqContext *enc, RoqTempdata *tempData) +static int generate_new_codebooks(RoqEncContext *enc) { int i, j, ret = 0; - RoqCodebooks *codebooks = &tempData->codebooks; - int max = enc->width*enc->height/16; + RoqCodebooks *codebooks = &enc->tmp_data.codebooks; + RoqContext *const roq = &enc->common; + int max = roq->width * roq->height / 16; uint8_t mb2[3*4]; - roq_cell *results4 = av_malloc(sizeof(roq_cell)*MAX_CBS_4x4*4); - uint8_t *yuvClusters=av_malloc_array(max, sizeof(int)*6*4); - int *points = av_malloc_array(max, 6*4*sizeof(int)); - int bias; - - if (!results4 || !yuvClusters || !points) { - ret = AVERROR(ENOMEM); - goto out; - } + int *points = enc->points; /* Subsample YUV data */ - create_clusters(enc->frame_to_enc, enc->width, enc->height, yuvClusters); - - /* Cast to integer and apply chroma bias */ - for (i=0; iquake3_compat ? MAX_CBS_4x4-1 : MAX_CBS_4x4))) < 0) - goto out; + create_clusters(enc->frame_to_enc, roq->width, roq->height, points); codebooks->numCB4 = (enc->quake3_compat ? MAX_CBS_4x4-1 : MAX_CBS_4x4); - tempData->closest_cb2 = av_malloc_array(max, 4*sizeof(int)); - if (!tempData->closest_cb2) { - ret = AVERROR(ENOMEM); - goto out; - } + /* Create 4x4 codebooks */ + if ((ret = generate_codebook(enc, points, max, enc->results4, + 4, codebooks->numCB4)) < 0) + return ret; /* Create 2x2 codebooks */ - if ((ret = generate_codebook(enc, tempData, points, max * 4, - enc->cb2x2, 2, MAX_CBS_2x2)) < 0) - goto out; + if ((ret = generate_codebook(enc, points, max * 4, + roq->cb2x2, 2, MAX_CBS_2x2)) < 0) + return ret; codebooks->numCB2 = MAX_CBS_2x2; /* Unpack 2x2 codebook clusters */ for (i=0; inumCB2; i++) - unpack_roq_cell(enc->cb2x2 + i, codebooks->unpacked_cb2 + i*2*2*3); + unpack_roq_cell(roq->cb2x2 + i, codebooks->unpacked_cb2 + i*2*2*3); /* Index all 4x4 entries to the 2x2 entries, unpack, and enlarge */ for (i=0; inumCB4; i++) { for (j=0; j<4; j++) { - unpack_roq_cell(&results4[4*i + j], mb2); + unpack_roq_cell(&enc->results4[4*i + j], mb2); index_mb(mb2, codebooks->unpacked_cb2, codebooks->numCB2, - &enc->cb4x4[i].idx[j], 2); + &roq->cb4x4[i].idx[j], 2); } - unpack_roq_qcell(codebooks->unpacked_cb2, enc->cb4x4 + i, + unpack_roq_qcell(codebooks->unpacked_cb2, roq->cb4x4 + i, codebooks->unpacked_cb4 + i*4*4*3); enlarge_roq_mb4(codebooks->unpacked_cb4 + i*4*4*3, codebooks->unpacked_cb4_enlarged + i*8*8*3); } -out: - av_free(yuvClusters); - av_free(points); - av_free(results4); - return ret; + + return 0; } -static int roq_encode_video(RoqContext *enc) +static int roq_encode_video(RoqEncContext *enc) { - RoqTempdata *tempData = enc->tmpData; - int i, ret; + RoqTempData *const tempData = &enc->tmp_data; + RoqContext *const roq = &enc->common; + int ret; memset(tempData, 0, sizeof(*tempData)); - ret = create_cel_evals(enc, tempData); - if (ret < 0) - return ret; - - ret = generate_new_codebooks(enc, tempData); + ret = generate_new_codebooks(enc); if (ret < 0) return ret; @@ -914,16 +909,16 @@ static int roq_encode_video(RoqContext *enc) } retry_encode: - for (i=0; iwidth*enc->height/64; i++) - gather_data_for_cel(tempData->cel_evals + i, enc, tempData); + for (int i = 0; i < roq->width * roq->height / 64; i++) + gather_data_for_cel(enc->cel_evals + i, enc); /* Quake 3 can't handle chunks bigger than 65535 bytes */ if (tempData->mainChunkSize/8 > 65535 && enc->quake3_compat) { if (enc->lambda > 100000) { - av_log(enc->avctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n"); + av_log(roq->avctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n"); return AVERROR(EINVAL); } - av_log(enc->avctx, AV_LOG_ERROR, + av_log(roq->avctx, AV_LOG_ERROR, "Warning, generated a frame too big for Quake (%d > 65535), " "now switching to a bigger qscale value.\n", tempData->mainChunkSize/8); @@ -938,21 +933,18 @@ static int roq_encode_video(RoqContext *enc) goto retry_encode; } - remap_codebooks(enc, tempData); + remap_codebooks(enc); - write_codebooks(enc, tempData); + write_codebooks(enc); - reconstruct_and_encode_image(enc, tempData, enc->width, enc->height, - enc->width*enc->height/64); + reconstruct_and_encode_image(enc, roq->width, roq->height, + roq->width * roq->height / 64); /* Rotate frame history */ - FFSWAP(AVFrame *, enc->current_frame, enc->last_frame); + FFSWAP(AVFrame *, roq->current_frame, roq->last_frame); FFSWAP(motion_vect *, enc->last_motion4, enc->this_motion4); FFSWAP(motion_vect *, enc->last_motion8, enc->this_motion8); - av_freep(&tempData->cel_evals); - av_freep(&tempData->closest_cb2); - enc->framesSinceKeyframe++; return 0; @@ -960,12 +952,13 @@ static int roq_encode_video(RoqContext *enc) static av_cold int roq_encode_end(AVCodecContext *avctx) { - RoqContext *enc = avctx->priv_data; + RoqEncContext *const enc = avctx->priv_data; - av_frame_free(&enc->current_frame); - av_frame_free(&enc->last_frame); + av_frame_free(&enc->common.current_frame); + av_frame_free(&enc->common.last_frame); - av_freep(&enc->tmpData); + av_freep(&enc->cel_evals); + av_freep(&enc->closest_cb); av_freep(&enc->this_motion4); av_freep(&enc->last_motion4); av_freep(&enc->this_motion8); @@ -976,11 +969,12 @@ static av_cold int roq_encode_end(AVCodecContext *avctx) static av_cold int roq_encode_init(AVCodecContext *avctx) { - RoqContext *enc = avctx->priv_data; + RoqEncContext *const enc = avctx->priv_data; + RoqContext *const roq = &enc->common; av_lfg_init(&enc->randctx, 1); - enc->avctx = avctx; + roq->avctx = avctx; enc->framesSinceKeyframe = 0; if ((avctx->width & 0xf) || (avctx->height & 0xf)) { @@ -996,43 +990,44 @@ static av_cold int roq_encode_init(AVCodecContext *avctx) if (((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx->height-1))) av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power of two, this is not supported by quake\n"); - enc->width = avctx->width; - enc->height = avctx->height; + roq->width = avctx->width; + roq->height = avctx->height; enc->framesSinceKeyframe = 0; enc->first_frame = 1; - enc->last_frame = av_frame_alloc(); - enc->current_frame = av_frame_alloc(); - if (!enc->last_frame || !enc->current_frame) { - roq_encode_end(avctx); + roq->last_frame = av_frame_alloc(); + roq->current_frame = av_frame_alloc(); + if (!roq->last_frame || !roq->current_frame) return AVERROR(ENOMEM); - } - - enc->tmpData = av_malloc(sizeof(RoqTempdata)); enc->this_motion4 = - av_mallocz_array((enc->width*enc->height/16), sizeof(motion_vect)); + av_mallocz_array(roq->width * roq->height / 16, sizeof(motion_vect)); enc->last_motion4 = - av_malloc_array ((enc->width*enc->height/16), sizeof(motion_vect)); + av_malloc_array (roq->width * roq->height / 16, sizeof(motion_vect)); enc->this_motion8 = - av_mallocz_array((enc->width*enc->height/64), sizeof(motion_vect)); + av_mallocz_array(roq->width * roq->height / 64, sizeof(motion_vect)); enc->last_motion8 = - av_malloc_array ((enc->width*enc->height/64), sizeof(motion_vect)); + av_malloc_array (roq->width * roq->height / 64, sizeof(motion_vect)); - if (!enc->tmpData || !enc->this_motion4 || !enc->last_motion4 || - !enc->this_motion8 || !enc->last_motion8) { - roq_encode_end(avctx); + /* 4x4 codebook needs 6 * 4 * 4 / 4 * width * height / 16 * sizeof(int); + * and so does the points buffer. */ + enc->closest_cb = + av_malloc_array(roq->width * roq->height, 3 * sizeof(int)); + + if (!enc->this_motion4 || !enc->last_motion4 || + !enc->this_motion8 || !enc->last_motion8 || !enc->closest_cb) return AVERROR(ENOMEM); - } - return 0; + enc->points = enc->closest_cb + roq->width * roq->height * 3 / 2; + + return create_cel_evals(enc); } -static void roq_write_video_info_chunk(RoqContext *enc) +static void roq_write_video_info_chunk(RoqEncContext *enc) { /* ROQ info chunk */ bytestream_put_le16(&enc->out_buf, RoQ_INFO); @@ -1045,10 +1040,10 @@ static void roq_write_video_info_chunk(RoqContext *enc) bytestream_put_byte(&enc->out_buf, 0x00); /* Width */ - bytestream_put_le16(&enc->out_buf, enc->width); + bytestream_put_le16(&enc->out_buf, enc->common.width); /* Height */ - bytestream_put_le16(&enc->out_buf, enc->height); + bytestream_put_le16(&enc->out_buf, enc->common.height); /* Unused in Quake 3, mimics the output of the real encoder */ bytestream_put_byte(&enc->out_buf, 0x08); @@ -1060,10 +1055,11 @@ static void roq_write_video_info_chunk(RoqContext *enc) static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) { - RoqContext *enc = avctx->priv_data; + RoqEncContext *const enc = avctx->priv_data; + RoqContext *const roq = &enc->common; int size, ret; - enc->avctx = avctx; + roq->avctx = avctx; enc->frame_to_enc = frame; @@ -1074,7 +1070,7 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, /* 138 bits max per 8x8 block + * 256 codebooks*(6 bytes 2x2 + 4 bytes 4x4) + 8 bytes frame header */ - size = ((enc->width * enc->height / 64) * 138 + 7) / 8 + 256 * (6 + 4) + 8; + size = ((roq->width * roq->height / 64) * 138 + 7) / 8 + 256 * (6 + 4) + 8; if ((ret = ff_alloc_packet2(avctx, pkt, size, 0)) < 0) return ret; enc->out_buf = pkt->data; @@ -1086,8 +1082,8 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, if (enc->first_frame) { /* Alloc memory for the reconstruction data (we must know the stride for that) */ - if ((ret = ff_get_buffer(avctx, enc->current_frame, 0)) < 0 || - (ret = ff_get_buffer(avctx, enc->last_frame, 0)) < 0) + if ((ret = ff_get_buffer(avctx, roq->current_frame, 0)) < 0 || + (ret = ff_get_buffer(avctx, roq->last_frame, 0)) < 0) return ret; /* Before the first video frame, write a "video info" chunk */ @@ -1109,7 +1105,7 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, return 0; } -#define OFFSET(x) offsetof(RoqContext, x) +#define OFFSET(x) offsetof(RoqEncContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { { "quake3_compat", "Whether to respect known limitations in Quake 3 decoder", OFFSET(quake3_compat), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, @@ -1128,11 +1124,12 @@ AVCodec ff_roq_encoder = { .long_name = NULL_IF_CONFIG_SMALL("id RoQ video"), .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_ROQ, - .priv_data_size = sizeof(RoqContext), + .priv_data_size = sizeof(RoqEncContext), .init = roq_encode_init, .encode2 = roq_encode_frame, .close = roq_encode_end, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE }, .priv_class = &roq_class, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, };