1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * RoQ Video Encoder. 3cabdff1aSopenharmony_ci * 4cabdff1aSopenharmony_ci * Copyright (C) 2007 Vitor Sessak <vitor1001@gmail.com> 5cabdff1aSopenharmony_ci * Copyright (C) 2004-2007 Eric Lasota 6cabdff1aSopenharmony_ci * Based on RoQ specs (C) 2001 Tim Ferguson 7cabdff1aSopenharmony_ci * 8cabdff1aSopenharmony_ci * This file is part of FFmpeg. 9cabdff1aSopenharmony_ci * 10cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 11cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 12cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 13cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 14cabdff1aSopenharmony_ci * 15cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 16cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 17cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18cabdff1aSopenharmony_ci * Lesser General Public License for more details. 19cabdff1aSopenharmony_ci * 20cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 21cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 22cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23cabdff1aSopenharmony_ci */ 24cabdff1aSopenharmony_ci 25cabdff1aSopenharmony_ci/** 26cabdff1aSopenharmony_ci * @file 27cabdff1aSopenharmony_ci * id RoQ encoder by Vitor. Based on the Switchblade3 library and the 28cabdff1aSopenharmony_ci * Switchblade3 FFmpeg glue by Eric Lasota. 29cabdff1aSopenharmony_ci */ 30cabdff1aSopenharmony_ci 31cabdff1aSopenharmony_ci/* 32cabdff1aSopenharmony_ci * COSTS: 33cabdff1aSopenharmony_ci * Level 1: 34cabdff1aSopenharmony_ci * SKIP - 2 bits 35cabdff1aSopenharmony_ci * MOTION - 2 + 8 bits 36cabdff1aSopenharmony_ci * CODEBOOK - 2 + 8 bits 37cabdff1aSopenharmony_ci * SUBDIVIDE - 2 + combined subcel cost 38cabdff1aSopenharmony_ci * 39cabdff1aSopenharmony_ci * Level 2: 40cabdff1aSopenharmony_ci * SKIP - 2 bits 41cabdff1aSopenharmony_ci * MOTION - 2 + 8 bits 42cabdff1aSopenharmony_ci * CODEBOOK - 2 + 8 bits 43cabdff1aSopenharmony_ci * SUBDIVIDE - 2 + 4*8 bits 44cabdff1aSopenharmony_ci * 45cabdff1aSopenharmony_ci * Maximum cost: 138 bits per cel 46cabdff1aSopenharmony_ci * 47cabdff1aSopenharmony_ci * Proper evaluation requires LCD fraction comparison, which requires 48cabdff1aSopenharmony_ci * Squared Error (SE) loss * savings increase 49cabdff1aSopenharmony_ci * 50cabdff1aSopenharmony_ci * Maximum savings increase: 136 bits 51cabdff1aSopenharmony_ci * Maximum SE loss without overflow: 31580641 52cabdff1aSopenharmony_ci * Components in 8x8 supercel: 192 53cabdff1aSopenharmony_ci * Maximum SE precision per component: 164482 54cabdff1aSopenharmony_ci * >65025, so no truncation is needed (phew) 55cabdff1aSopenharmony_ci */ 56cabdff1aSopenharmony_ci 57cabdff1aSopenharmony_ci#include <string.h> 58cabdff1aSopenharmony_ci 59cabdff1aSopenharmony_ci#include "libavutil/attributes.h" 60cabdff1aSopenharmony_ci#include "libavutil/lfg.h" 61cabdff1aSopenharmony_ci#include "libavutil/opt.h" 62cabdff1aSopenharmony_ci#include "roqvideo.h" 63cabdff1aSopenharmony_ci#include "bytestream.h" 64cabdff1aSopenharmony_ci#include "codec_internal.h" 65cabdff1aSopenharmony_ci#include "elbg.h" 66cabdff1aSopenharmony_ci#include "encode.h" 67cabdff1aSopenharmony_ci#include "internal.h" 68cabdff1aSopenharmony_ci#include "mathops.h" 69cabdff1aSopenharmony_ci 70cabdff1aSopenharmony_ci#define CHROMA_BIAS 1 71cabdff1aSopenharmony_ci 72cabdff1aSopenharmony_ci/** 73cabdff1aSopenharmony_ci * Maximum number of generated 4x4 codebooks. Can't be 256 to workaround a 74cabdff1aSopenharmony_ci * Quake 3 bug. 75cabdff1aSopenharmony_ci */ 76cabdff1aSopenharmony_ci#define MAX_CBS_4x4 256 77cabdff1aSopenharmony_ci 78cabdff1aSopenharmony_ci#define MAX_CBS_2x2 256 ///< Maximum number of 2x2 codebooks. 79cabdff1aSopenharmony_ci 80cabdff1aSopenharmony_ci/* The cast is useful when multiplying it by INT_MAX */ 81cabdff1aSopenharmony_ci#define ROQ_LAMBDA_SCALE ((uint64_t) FF_LAMBDA_SCALE) 82cabdff1aSopenharmony_ci 83cabdff1aSopenharmony_citypedef struct RoqCodebooks { 84cabdff1aSopenharmony_ci int numCB4; 85cabdff1aSopenharmony_ci int numCB2; 86cabdff1aSopenharmony_ci int usedCB2[MAX_CBS_2x2]; 87cabdff1aSopenharmony_ci int usedCB4[MAX_CBS_4x4]; 88cabdff1aSopenharmony_ci uint8_t unpacked_cb2[MAX_CBS_2x2*2*2*3]; 89cabdff1aSopenharmony_ci uint8_t unpacked_cb4[MAX_CBS_4x4*4*4*3]; 90cabdff1aSopenharmony_ci uint8_t unpacked_cb4_enlarged[MAX_CBS_4x4*8*8*3]; 91cabdff1aSopenharmony_ci} RoqCodebooks; 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_ci/** 94cabdff1aSopenharmony_ci * Temporary vars 95cabdff1aSopenharmony_ci */ 96cabdff1aSopenharmony_citypedef struct RoqTempData 97cabdff1aSopenharmony_ci{ 98cabdff1aSopenharmony_ci int f2i4[MAX_CBS_4x4]; 99cabdff1aSopenharmony_ci int i2f4[MAX_CBS_4x4]; 100cabdff1aSopenharmony_ci int f2i2[MAX_CBS_2x2]; 101cabdff1aSopenharmony_ci int i2f2[MAX_CBS_2x2]; 102cabdff1aSopenharmony_ci 103cabdff1aSopenharmony_ci int mainChunkSize; 104cabdff1aSopenharmony_ci 105cabdff1aSopenharmony_ci int numCB4; 106cabdff1aSopenharmony_ci int numCB2; 107cabdff1aSopenharmony_ci 108cabdff1aSopenharmony_ci RoqCodebooks codebooks; 109cabdff1aSopenharmony_ci 110cabdff1aSopenharmony_ci int used_option[4]; 111cabdff1aSopenharmony_ci} RoqTempData; 112cabdff1aSopenharmony_ci 113cabdff1aSopenharmony_citypedef struct SubcelEvaluation { 114cabdff1aSopenharmony_ci int eval_dist[4]; 115cabdff1aSopenharmony_ci int best_bit_use; 116cabdff1aSopenharmony_ci int best_coding; 117cabdff1aSopenharmony_ci 118cabdff1aSopenharmony_ci int subCels[4]; 119cabdff1aSopenharmony_ci motion_vect motion; 120cabdff1aSopenharmony_ci int cbEntry; 121cabdff1aSopenharmony_ci} SubcelEvaluation; 122cabdff1aSopenharmony_ci 123cabdff1aSopenharmony_citypedef struct CelEvaluation { 124cabdff1aSopenharmony_ci int eval_dist[4]; 125cabdff1aSopenharmony_ci int best_coding; 126cabdff1aSopenharmony_ci 127cabdff1aSopenharmony_ci SubcelEvaluation subCels[4]; 128cabdff1aSopenharmony_ci 129cabdff1aSopenharmony_ci motion_vect motion; 130cabdff1aSopenharmony_ci int cbEntry; 131cabdff1aSopenharmony_ci 132cabdff1aSopenharmony_ci int sourceX, sourceY; 133cabdff1aSopenharmony_ci} CelEvaluation; 134cabdff1aSopenharmony_ci 135cabdff1aSopenharmony_citypedef struct RoqEncContext { 136cabdff1aSopenharmony_ci RoqContext common; 137cabdff1aSopenharmony_ci struct ELBGContext *elbg; 138cabdff1aSopenharmony_ci AVLFG randctx; 139cabdff1aSopenharmony_ci uint64_t lambda; 140cabdff1aSopenharmony_ci 141cabdff1aSopenharmony_ci motion_vect *this_motion4; 142cabdff1aSopenharmony_ci motion_vect *last_motion4; 143cabdff1aSopenharmony_ci 144cabdff1aSopenharmony_ci motion_vect *this_motion8; 145cabdff1aSopenharmony_ci motion_vect *last_motion8; 146cabdff1aSopenharmony_ci 147cabdff1aSopenharmony_ci unsigned int framesSinceKeyframe; 148cabdff1aSopenharmony_ci 149cabdff1aSopenharmony_ci const AVFrame *frame_to_enc; 150cabdff1aSopenharmony_ci uint8_t *out_buf; 151cabdff1aSopenharmony_ci RoqTempData tmp_data; 152cabdff1aSopenharmony_ci roq_cell results4[4 * MAX_CBS_4x4]; 153cabdff1aSopenharmony_ci int tmp_codebook_buf[FFMAX(24 * MAX_CBS_4x4, 6 * MAX_CBS_2x2)]; 154cabdff1aSopenharmony_ci 155cabdff1aSopenharmony_ci CelEvaluation *cel_evals; 156cabdff1aSopenharmony_ci int *closest_cb; 157cabdff1aSopenharmony_ci int *points; // Allocated together with closest_cb 158cabdff1aSopenharmony_ci 159cabdff1aSopenharmony_ci int first_frame; 160cabdff1aSopenharmony_ci int quake3_compat; // Quake 3 compatibility option 161cabdff1aSopenharmony_ci} RoqEncContext; 162cabdff1aSopenharmony_ci 163cabdff1aSopenharmony_ci/* Macroblock support functions */ 164cabdff1aSopenharmony_cistatic void unpack_roq_cell(roq_cell *cell, uint8_t u[4*3]) 165cabdff1aSopenharmony_ci{ 166cabdff1aSopenharmony_ci memcpy(u , cell->y, 4); 167cabdff1aSopenharmony_ci memset(u+4, cell->u, 4); 168cabdff1aSopenharmony_ci memset(u+8, cell->v, 4); 169cabdff1aSopenharmony_ci} 170cabdff1aSopenharmony_ci 171cabdff1aSopenharmony_cistatic void unpack_roq_qcell(uint8_t cb2[], roq_qcell *qcell, uint8_t u[4*4*3]) 172cabdff1aSopenharmony_ci{ 173cabdff1aSopenharmony_ci int i,cp; 174cabdff1aSopenharmony_ci static const int offsets[4] = {0, 2, 8, 10}; 175cabdff1aSopenharmony_ci 176cabdff1aSopenharmony_ci for (cp=0; cp<3; cp++) 177cabdff1aSopenharmony_ci for (i=0; i<4; i++) { 178cabdff1aSopenharmony_ci u[4*4*cp + offsets[i] ] = cb2[qcell->idx[i]*2*2*3 + 4*cp ]; 179cabdff1aSopenharmony_ci u[4*4*cp + offsets[i]+1] = cb2[qcell->idx[i]*2*2*3 + 4*cp+1]; 180cabdff1aSopenharmony_ci u[4*4*cp + offsets[i]+4] = cb2[qcell->idx[i]*2*2*3 + 4*cp+2]; 181cabdff1aSopenharmony_ci u[4*4*cp + offsets[i]+5] = cb2[qcell->idx[i]*2*2*3 + 4*cp+3]; 182cabdff1aSopenharmony_ci } 183cabdff1aSopenharmony_ci} 184cabdff1aSopenharmony_ci 185cabdff1aSopenharmony_ci 186cabdff1aSopenharmony_cistatic void enlarge_roq_mb4(uint8_t base[3*16], uint8_t u[3*64]) 187cabdff1aSopenharmony_ci{ 188cabdff1aSopenharmony_ci int x,y,cp; 189cabdff1aSopenharmony_ci 190cabdff1aSopenharmony_ci for(cp=0; cp<3; cp++) 191cabdff1aSopenharmony_ci for(y=0; y<8; y++) 192cabdff1aSopenharmony_ci for(x=0; x<8; x++) 193cabdff1aSopenharmony_ci *u++ = base[(y/2)*4 + (x/2) + 16*cp]; 194cabdff1aSopenharmony_ci} 195cabdff1aSopenharmony_ci 196cabdff1aSopenharmony_cistatic inline int square(int x) 197cabdff1aSopenharmony_ci{ 198cabdff1aSopenharmony_ci return x*x; 199cabdff1aSopenharmony_ci} 200cabdff1aSopenharmony_ci 201cabdff1aSopenharmony_cistatic inline int eval_sse(const uint8_t *a, const uint8_t *b, int count) 202cabdff1aSopenharmony_ci{ 203cabdff1aSopenharmony_ci int diff=0; 204cabdff1aSopenharmony_ci 205cabdff1aSopenharmony_ci while(count--) 206cabdff1aSopenharmony_ci diff += square(*b++ - *a++); 207cabdff1aSopenharmony_ci 208cabdff1aSopenharmony_ci return diff; 209cabdff1aSopenharmony_ci} 210cabdff1aSopenharmony_ci 211cabdff1aSopenharmony_ci// FIXME Could use DSPContext.sse, but it is not so speed critical (used 212cabdff1aSopenharmony_ci// just for motion estimation). 213cabdff1aSopenharmony_cistatic int block_sse(uint8_t * const *buf1, uint8_t * const *buf2, int x1, int y1, 214cabdff1aSopenharmony_ci int x2, int y2, const int *stride1, const int *stride2, int size) 215cabdff1aSopenharmony_ci{ 216cabdff1aSopenharmony_ci int i, k; 217cabdff1aSopenharmony_ci int sse=0; 218cabdff1aSopenharmony_ci 219cabdff1aSopenharmony_ci for (k=0; k<3; k++) { 220cabdff1aSopenharmony_ci int bias = (k ? CHROMA_BIAS : 4); 221cabdff1aSopenharmony_ci for (i=0; i<size; i++) 222cabdff1aSopenharmony_ci sse += bias*eval_sse(buf1[k] + (y1+i)*stride1[k] + x1, 223cabdff1aSopenharmony_ci buf2[k] + (y2+i)*stride2[k] + x2, size); 224cabdff1aSopenharmony_ci } 225cabdff1aSopenharmony_ci 226cabdff1aSopenharmony_ci return sse; 227cabdff1aSopenharmony_ci} 228cabdff1aSopenharmony_ci 229cabdff1aSopenharmony_cistatic int eval_motion_dist(RoqEncContext *enc, int x, int y, motion_vect vect, 230cabdff1aSopenharmony_ci int size) 231cabdff1aSopenharmony_ci{ 232cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 233cabdff1aSopenharmony_ci int mx=vect.d[0]; 234cabdff1aSopenharmony_ci int my=vect.d[1]; 235cabdff1aSopenharmony_ci 236cabdff1aSopenharmony_ci if (mx < -7 || mx > 7) 237cabdff1aSopenharmony_ci return INT_MAX; 238cabdff1aSopenharmony_ci 239cabdff1aSopenharmony_ci if (my < -7 || my > 7) 240cabdff1aSopenharmony_ci return INT_MAX; 241cabdff1aSopenharmony_ci 242cabdff1aSopenharmony_ci mx += x; 243cabdff1aSopenharmony_ci my += y; 244cabdff1aSopenharmony_ci 245cabdff1aSopenharmony_ci if ((unsigned) mx > roq->width-size || (unsigned) my > roq->height-size) 246cabdff1aSopenharmony_ci return INT_MAX; 247cabdff1aSopenharmony_ci 248cabdff1aSopenharmony_ci return block_sse(enc->frame_to_enc->data, roq->last_frame->data, x, y, 249cabdff1aSopenharmony_ci mx, my, 250cabdff1aSopenharmony_ci enc->frame_to_enc->linesize, roq->last_frame->linesize, 251cabdff1aSopenharmony_ci size); 252cabdff1aSopenharmony_ci} 253cabdff1aSopenharmony_ci 254cabdff1aSopenharmony_ci/** 255cabdff1aSopenharmony_ci * @return distortion between two macroblocks 256cabdff1aSopenharmony_ci */ 257cabdff1aSopenharmony_cistatic inline int squared_diff_macroblock(uint8_t a[], uint8_t b[], int size) 258cabdff1aSopenharmony_ci{ 259cabdff1aSopenharmony_ci int cp, sdiff=0; 260cabdff1aSopenharmony_ci 261cabdff1aSopenharmony_ci for(cp=0;cp<3;cp++) { 262cabdff1aSopenharmony_ci int bias = (cp ? CHROMA_BIAS : 4); 263cabdff1aSopenharmony_ci sdiff += bias*eval_sse(a, b, size*size); 264cabdff1aSopenharmony_ci a += size*size; 265cabdff1aSopenharmony_ci b += size*size; 266cabdff1aSopenharmony_ci } 267cabdff1aSopenharmony_ci 268cabdff1aSopenharmony_ci return sdiff; 269cabdff1aSopenharmony_ci} 270cabdff1aSopenharmony_ci 271cabdff1aSopenharmony_ci/** 272cabdff1aSopenharmony_ci * Initialize cel evaluators and set their source coordinates 273cabdff1aSopenharmony_ci */ 274cabdff1aSopenharmony_cistatic int create_cel_evals(RoqEncContext *enc) 275cabdff1aSopenharmony_ci{ 276cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 277cabdff1aSopenharmony_ci 278cabdff1aSopenharmony_ci enc->cel_evals = av_malloc_array(roq->width * roq->height / 64, sizeof(CelEvaluation)); 279cabdff1aSopenharmony_ci if (!enc->cel_evals) 280cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 281cabdff1aSopenharmony_ci 282cabdff1aSopenharmony_ci /* Map to the ROQ quadtree order */ 283cabdff1aSopenharmony_ci for (int y = 0, n = 0; y < roq->height; y += 16) 284cabdff1aSopenharmony_ci for (int x = 0; x < roq->width; x += 16) 285cabdff1aSopenharmony_ci for(int i = 0; i < 4; i++) { 286cabdff1aSopenharmony_ci enc->cel_evals[n ].sourceX = x + (i&1)*8; 287cabdff1aSopenharmony_ci enc->cel_evals[n++].sourceY = y + (i&2)*4; 288cabdff1aSopenharmony_ci } 289cabdff1aSopenharmony_ci 290cabdff1aSopenharmony_ci return 0; 291cabdff1aSopenharmony_ci} 292cabdff1aSopenharmony_ci 293cabdff1aSopenharmony_ci/** 294cabdff1aSopenharmony_ci * Get macroblocks from parts of the image 295cabdff1aSopenharmony_ci */ 296cabdff1aSopenharmony_cistatic void get_frame_mb(const AVFrame *frame, int x, int y, uint8_t mb[], int dim) 297cabdff1aSopenharmony_ci{ 298cabdff1aSopenharmony_ci int i, j, cp; 299cabdff1aSopenharmony_ci 300cabdff1aSopenharmony_ci for (cp=0; cp<3; cp++) { 301cabdff1aSopenharmony_ci int stride = frame->linesize[cp]; 302cabdff1aSopenharmony_ci for (i=0; i<dim; i++) 303cabdff1aSopenharmony_ci for (j=0; j<dim; j++) 304cabdff1aSopenharmony_ci *mb++ = frame->data[cp][(y+i)*stride + x + j]; 305cabdff1aSopenharmony_ci } 306cabdff1aSopenharmony_ci} 307cabdff1aSopenharmony_ci 308cabdff1aSopenharmony_ci/** 309cabdff1aSopenharmony_ci * Find the codebook with the lowest distortion from an image 310cabdff1aSopenharmony_ci */ 311cabdff1aSopenharmony_cistatic int index_mb(uint8_t cluster[], uint8_t cb[], int numCB, 312cabdff1aSopenharmony_ci int *outIndex, int dim) 313cabdff1aSopenharmony_ci{ 314cabdff1aSopenharmony_ci int i, lDiff = INT_MAX, pick=0; 315cabdff1aSopenharmony_ci 316cabdff1aSopenharmony_ci /* Diff against the others */ 317cabdff1aSopenharmony_ci for (i=0; i<numCB; i++) { 318cabdff1aSopenharmony_ci int diff = squared_diff_macroblock(cluster, cb + i*dim*dim*3, dim); 319cabdff1aSopenharmony_ci if (diff < lDiff) { 320cabdff1aSopenharmony_ci lDiff = diff; 321cabdff1aSopenharmony_ci pick = i; 322cabdff1aSopenharmony_ci } 323cabdff1aSopenharmony_ci } 324cabdff1aSopenharmony_ci 325cabdff1aSopenharmony_ci *outIndex = pick; 326cabdff1aSopenharmony_ci return lDiff; 327cabdff1aSopenharmony_ci} 328cabdff1aSopenharmony_ci 329cabdff1aSopenharmony_ci#define EVAL_MOTION(MOTION) \ 330cabdff1aSopenharmony_ci do { \ 331cabdff1aSopenharmony_ci diff = eval_motion_dist(enc, j, i, MOTION, blocksize); \ 332cabdff1aSopenharmony_ci \ 333cabdff1aSopenharmony_ci if (diff < lowestdiff) { \ 334cabdff1aSopenharmony_ci lowestdiff = diff; \ 335cabdff1aSopenharmony_ci bestpick = MOTION; \ 336cabdff1aSopenharmony_ci } \ 337cabdff1aSopenharmony_ci } while(0) 338cabdff1aSopenharmony_ci 339cabdff1aSopenharmony_cistatic void motion_search(RoqEncContext *enc, int blocksize) 340cabdff1aSopenharmony_ci{ 341cabdff1aSopenharmony_ci static const motion_vect offsets[8] = { 342cabdff1aSopenharmony_ci {{ 0,-1}}, 343cabdff1aSopenharmony_ci {{ 0, 1}}, 344cabdff1aSopenharmony_ci {{-1, 0}}, 345cabdff1aSopenharmony_ci {{ 1, 0}}, 346cabdff1aSopenharmony_ci {{-1, 1}}, 347cabdff1aSopenharmony_ci {{ 1,-1}}, 348cabdff1aSopenharmony_ci {{-1,-1}}, 349cabdff1aSopenharmony_ci {{ 1, 1}}, 350cabdff1aSopenharmony_ci }; 351cabdff1aSopenharmony_ci 352cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 353cabdff1aSopenharmony_ci int diff, lowestdiff, oldbest; 354cabdff1aSopenharmony_ci int off[3]; 355cabdff1aSopenharmony_ci motion_vect bestpick = {{0,0}}; 356cabdff1aSopenharmony_ci int i, j, k, offset; 357cabdff1aSopenharmony_ci 358cabdff1aSopenharmony_ci motion_vect *last_motion; 359cabdff1aSopenharmony_ci motion_vect *this_motion; 360cabdff1aSopenharmony_ci motion_vect vect, vect2; 361cabdff1aSopenharmony_ci const int max = (roq->width / blocksize) * roq->height / blocksize; 362cabdff1aSopenharmony_ci 363cabdff1aSopenharmony_ci if (blocksize == 4) { 364cabdff1aSopenharmony_ci last_motion = enc->last_motion4; 365cabdff1aSopenharmony_ci this_motion = enc->this_motion4; 366cabdff1aSopenharmony_ci } else { 367cabdff1aSopenharmony_ci last_motion = enc->last_motion8; 368cabdff1aSopenharmony_ci this_motion = enc->this_motion8; 369cabdff1aSopenharmony_ci } 370cabdff1aSopenharmony_ci 371cabdff1aSopenharmony_ci for (i = 0; i< roq->height; i += blocksize) 372cabdff1aSopenharmony_ci for (j = 0; j < roq->width; j += blocksize) { 373cabdff1aSopenharmony_ci lowestdiff = eval_motion_dist(enc, j, i, (motion_vect) {{0,0}}, 374cabdff1aSopenharmony_ci blocksize); 375cabdff1aSopenharmony_ci bestpick.d[0] = 0; 376cabdff1aSopenharmony_ci bestpick.d[1] = 0; 377cabdff1aSopenharmony_ci 378cabdff1aSopenharmony_ci if (blocksize == 4) 379cabdff1aSopenharmony_ci EVAL_MOTION(enc->this_motion8[(i/8) * (roq->width/8) + j/8]); 380cabdff1aSopenharmony_ci 381cabdff1aSopenharmony_ci offset = (i/blocksize) * roq->width / blocksize + j / blocksize; 382cabdff1aSopenharmony_ci if (offset < max && offset >= 0) 383cabdff1aSopenharmony_ci EVAL_MOTION(last_motion[offset]); 384cabdff1aSopenharmony_ci 385cabdff1aSopenharmony_ci offset++; 386cabdff1aSopenharmony_ci if (offset < max && offset >= 0) 387cabdff1aSopenharmony_ci EVAL_MOTION(last_motion[offset]); 388cabdff1aSopenharmony_ci 389cabdff1aSopenharmony_ci offset = (i/blocksize + 1) * roq->width / blocksize + j / blocksize; 390cabdff1aSopenharmony_ci if (offset < max && offset >= 0) 391cabdff1aSopenharmony_ci EVAL_MOTION(last_motion[offset]); 392cabdff1aSopenharmony_ci 393cabdff1aSopenharmony_ci off[0]= (i/blocksize) * roq->width / blocksize + j/blocksize - 1; 394cabdff1aSopenharmony_ci off[1]= off[0] - roq->width / blocksize + 1; 395cabdff1aSopenharmony_ci off[2]= off[1] + 1; 396cabdff1aSopenharmony_ci 397cabdff1aSopenharmony_ci if (i) { 398cabdff1aSopenharmony_ci 399cabdff1aSopenharmony_ci for(k=0; k<2; k++) 400cabdff1aSopenharmony_ci vect.d[k]= mid_pred(this_motion[off[0]].d[k], 401cabdff1aSopenharmony_ci this_motion[off[1]].d[k], 402cabdff1aSopenharmony_ci this_motion[off[2]].d[k]); 403cabdff1aSopenharmony_ci 404cabdff1aSopenharmony_ci EVAL_MOTION(vect); 405cabdff1aSopenharmony_ci for(k=0; k<3; k++) 406cabdff1aSopenharmony_ci EVAL_MOTION(this_motion[off[k]]); 407cabdff1aSopenharmony_ci } else if(j) 408cabdff1aSopenharmony_ci EVAL_MOTION(this_motion[off[0]]); 409cabdff1aSopenharmony_ci 410cabdff1aSopenharmony_ci vect = bestpick; 411cabdff1aSopenharmony_ci 412cabdff1aSopenharmony_ci oldbest = -1; 413cabdff1aSopenharmony_ci while (oldbest != lowestdiff) { 414cabdff1aSopenharmony_ci oldbest = lowestdiff; 415cabdff1aSopenharmony_ci for (k=0; k<8; k++) { 416cabdff1aSopenharmony_ci vect2 = vect; 417cabdff1aSopenharmony_ci vect2.d[0] += offsets[k].d[0]; 418cabdff1aSopenharmony_ci vect2.d[1] += offsets[k].d[1]; 419cabdff1aSopenharmony_ci EVAL_MOTION(vect2); 420cabdff1aSopenharmony_ci } 421cabdff1aSopenharmony_ci vect = bestpick; 422cabdff1aSopenharmony_ci } 423cabdff1aSopenharmony_ci offset = (i/blocksize) * roq->width / blocksize + j/blocksize; 424cabdff1aSopenharmony_ci this_motion[offset] = bestpick; 425cabdff1aSopenharmony_ci } 426cabdff1aSopenharmony_ci} 427cabdff1aSopenharmony_ci 428cabdff1aSopenharmony_ci/** 429cabdff1aSopenharmony_ci * Get distortion for all options available to a subcel 430cabdff1aSopenharmony_ci */ 431cabdff1aSopenharmony_cistatic void gather_data_for_subcel(SubcelEvaluation *subcel, int x, 432cabdff1aSopenharmony_ci int y, RoqEncContext *enc) 433cabdff1aSopenharmony_ci{ 434cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 435cabdff1aSopenharmony_ci RoqTempData *const tempData = &enc->tmp_data; 436cabdff1aSopenharmony_ci uint8_t mb4[4*4*3]; 437cabdff1aSopenharmony_ci uint8_t mb2[2*2*3]; 438cabdff1aSopenharmony_ci int cluster_index; 439cabdff1aSopenharmony_ci int i, best_dist; 440cabdff1aSopenharmony_ci 441cabdff1aSopenharmony_ci static const int bitsUsed[4] = {2, 10, 10, 34}; 442cabdff1aSopenharmony_ci 443cabdff1aSopenharmony_ci if (enc->framesSinceKeyframe >= 1) { 444cabdff1aSopenharmony_ci subcel->motion = enc->this_motion4[y * roq->width / 16 + x / 4]; 445cabdff1aSopenharmony_ci 446cabdff1aSopenharmony_ci subcel->eval_dist[RoQ_ID_FCC] = 447cabdff1aSopenharmony_ci eval_motion_dist(enc, x, y, 448cabdff1aSopenharmony_ci enc->this_motion4[y * roq->width / 16 + x / 4], 4); 449cabdff1aSopenharmony_ci } else 450cabdff1aSopenharmony_ci subcel->eval_dist[RoQ_ID_FCC] = INT_MAX; 451cabdff1aSopenharmony_ci 452cabdff1aSopenharmony_ci if (enc->framesSinceKeyframe >= 2) 453cabdff1aSopenharmony_ci subcel->eval_dist[RoQ_ID_MOT] = block_sse(enc->frame_to_enc->data, 454cabdff1aSopenharmony_ci roq->current_frame->data, x, 455cabdff1aSopenharmony_ci y, x, y, 456cabdff1aSopenharmony_ci enc->frame_to_enc->linesize, 457cabdff1aSopenharmony_ci roq->current_frame->linesize, 458cabdff1aSopenharmony_ci 4); 459cabdff1aSopenharmony_ci else 460cabdff1aSopenharmony_ci subcel->eval_dist[RoQ_ID_MOT] = INT_MAX; 461cabdff1aSopenharmony_ci 462cabdff1aSopenharmony_ci cluster_index = y * roq->width / 16 + x / 4; 463cabdff1aSopenharmony_ci 464cabdff1aSopenharmony_ci get_frame_mb(enc->frame_to_enc, x, y, mb4, 4); 465cabdff1aSopenharmony_ci 466cabdff1aSopenharmony_ci subcel->eval_dist[RoQ_ID_SLD] = index_mb(mb4, 467cabdff1aSopenharmony_ci tempData->codebooks.unpacked_cb4, 468cabdff1aSopenharmony_ci tempData->codebooks.numCB4, 469cabdff1aSopenharmony_ci &subcel->cbEntry, 4); 470cabdff1aSopenharmony_ci 471cabdff1aSopenharmony_ci subcel->eval_dist[RoQ_ID_CCC] = 0; 472cabdff1aSopenharmony_ci 473cabdff1aSopenharmony_ci for(i=0;i<4;i++) { 474cabdff1aSopenharmony_ci subcel->subCels[i] = enc->closest_cb[cluster_index*4+i]; 475cabdff1aSopenharmony_ci 476cabdff1aSopenharmony_ci get_frame_mb(enc->frame_to_enc, x+2*(i&1), 477cabdff1aSopenharmony_ci y+(i&2), mb2, 2); 478cabdff1aSopenharmony_ci 479cabdff1aSopenharmony_ci subcel->eval_dist[RoQ_ID_CCC] += 480cabdff1aSopenharmony_ci squared_diff_macroblock(tempData->codebooks.unpacked_cb2 + subcel->subCels[i]*2*2*3, mb2, 2); 481cabdff1aSopenharmony_ci } 482cabdff1aSopenharmony_ci 483cabdff1aSopenharmony_ci best_dist = INT_MAX; 484cabdff1aSopenharmony_ci for (i=0; i<4; i++) 485cabdff1aSopenharmony_ci if (ROQ_LAMBDA_SCALE*subcel->eval_dist[i] + enc->lambda*bitsUsed[i] < 486cabdff1aSopenharmony_ci best_dist) { 487cabdff1aSopenharmony_ci subcel->best_coding = i; 488cabdff1aSopenharmony_ci subcel->best_bit_use = bitsUsed[i]; 489cabdff1aSopenharmony_ci best_dist = ROQ_LAMBDA_SCALE*subcel->eval_dist[i] + 490cabdff1aSopenharmony_ci enc->lambda*bitsUsed[i]; 491cabdff1aSopenharmony_ci } 492cabdff1aSopenharmony_ci} 493cabdff1aSopenharmony_ci 494cabdff1aSopenharmony_ci/** 495cabdff1aSopenharmony_ci * Get distortion for all options available to a cel 496cabdff1aSopenharmony_ci */ 497cabdff1aSopenharmony_cistatic void gather_data_for_cel(CelEvaluation *cel, RoqEncContext *enc) 498cabdff1aSopenharmony_ci{ 499cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 500cabdff1aSopenharmony_ci RoqTempData *const tempData = &enc->tmp_data; 501cabdff1aSopenharmony_ci uint8_t mb8[8*8*3]; 502cabdff1aSopenharmony_ci int index = cel->sourceY * roq->width / 64 + cel->sourceX/8; 503cabdff1aSopenharmony_ci int i, j, best_dist, divide_bit_use; 504cabdff1aSopenharmony_ci 505cabdff1aSopenharmony_ci int bitsUsed[4] = {2, 10, 10, 0}; 506cabdff1aSopenharmony_ci 507cabdff1aSopenharmony_ci if (enc->framesSinceKeyframe >= 1) { 508cabdff1aSopenharmony_ci cel->motion = enc->this_motion8[index]; 509cabdff1aSopenharmony_ci 510cabdff1aSopenharmony_ci cel->eval_dist[RoQ_ID_FCC] = 511cabdff1aSopenharmony_ci eval_motion_dist(enc, cel->sourceX, cel->sourceY, 512cabdff1aSopenharmony_ci enc->this_motion8[index], 8); 513cabdff1aSopenharmony_ci } else 514cabdff1aSopenharmony_ci cel->eval_dist[RoQ_ID_FCC] = INT_MAX; 515cabdff1aSopenharmony_ci 516cabdff1aSopenharmony_ci if (enc->framesSinceKeyframe >= 2) 517cabdff1aSopenharmony_ci cel->eval_dist[RoQ_ID_MOT] = block_sse(enc->frame_to_enc->data, 518cabdff1aSopenharmony_ci roq->current_frame->data, 519cabdff1aSopenharmony_ci cel->sourceX, cel->sourceY, 520cabdff1aSopenharmony_ci cel->sourceX, cel->sourceY, 521cabdff1aSopenharmony_ci enc->frame_to_enc->linesize, 522cabdff1aSopenharmony_ci roq->current_frame->linesize,8); 523cabdff1aSopenharmony_ci else 524cabdff1aSopenharmony_ci cel->eval_dist[RoQ_ID_MOT] = INT_MAX; 525cabdff1aSopenharmony_ci 526cabdff1aSopenharmony_ci get_frame_mb(enc->frame_to_enc, cel->sourceX, cel->sourceY, mb8, 8); 527cabdff1aSopenharmony_ci 528cabdff1aSopenharmony_ci cel->eval_dist[RoQ_ID_SLD] = 529cabdff1aSopenharmony_ci index_mb(mb8, tempData->codebooks.unpacked_cb4_enlarged, 530cabdff1aSopenharmony_ci tempData->codebooks.numCB4, &cel->cbEntry, 8); 531cabdff1aSopenharmony_ci 532cabdff1aSopenharmony_ci gather_data_for_subcel(cel->subCels + 0, cel->sourceX+0, cel->sourceY+0, enc); 533cabdff1aSopenharmony_ci gather_data_for_subcel(cel->subCels + 1, cel->sourceX+4, cel->sourceY+0, enc); 534cabdff1aSopenharmony_ci gather_data_for_subcel(cel->subCels + 2, cel->sourceX+0, cel->sourceY+4, enc); 535cabdff1aSopenharmony_ci gather_data_for_subcel(cel->subCels + 3, cel->sourceX+4, cel->sourceY+4, enc); 536cabdff1aSopenharmony_ci 537cabdff1aSopenharmony_ci cel->eval_dist[RoQ_ID_CCC] = 0; 538cabdff1aSopenharmony_ci divide_bit_use = 0; 539cabdff1aSopenharmony_ci for (i=0; i<4; i++) { 540cabdff1aSopenharmony_ci cel->eval_dist[RoQ_ID_CCC] += 541cabdff1aSopenharmony_ci cel->subCels[i].eval_dist[cel->subCels[i].best_coding]; 542cabdff1aSopenharmony_ci divide_bit_use += cel->subCels[i].best_bit_use; 543cabdff1aSopenharmony_ci } 544cabdff1aSopenharmony_ci 545cabdff1aSopenharmony_ci best_dist = INT_MAX; 546cabdff1aSopenharmony_ci bitsUsed[3] = 2 + divide_bit_use; 547cabdff1aSopenharmony_ci 548cabdff1aSopenharmony_ci for (i=0; i<4; i++) 549cabdff1aSopenharmony_ci if (ROQ_LAMBDA_SCALE*cel->eval_dist[i] + enc->lambda*bitsUsed[i] < 550cabdff1aSopenharmony_ci best_dist) { 551cabdff1aSopenharmony_ci cel->best_coding = i; 552cabdff1aSopenharmony_ci best_dist = ROQ_LAMBDA_SCALE*cel->eval_dist[i] + 553cabdff1aSopenharmony_ci enc->lambda*bitsUsed[i]; 554cabdff1aSopenharmony_ci } 555cabdff1aSopenharmony_ci 556cabdff1aSopenharmony_ci tempData->used_option[cel->best_coding]++; 557cabdff1aSopenharmony_ci tempData->mainChunkSize += bitsUsed[cel->best_coding]; 558cabdff1aSopenharmony_ci 559cabdff1aSopenharmony_ci if (cel->best_coding == RoQ_ID_SLD) 560cabdff1aSopenharmony_ci tempData->codebooks.usedCB4[cel->cbEntry]++; 561cabdff1aSopenharmony_ci 562cabdff1aSopenharmony_ci if (cel->best_coding == RoQ_ID_CCC) 563cabdff1aSopenharmony_ci for (i=0; i<4; i++) { 564cabdff1aSopenharmony_ci if (cel->subCels[i].best_coding == RoQ_ID_SLD) 565cabdff1aSopenharmony_ci tempData->codebooks.usedCB4[cel->subCels[i].cbEntry]++; 566cabdff1aSopenharmony_ci else if (cel->subCels[i].best_coding == RoQ_ID_CCC) 567cabdff1aSopenharmony_ci for (j=0; j<4; j++) 568cabdff1aSopenharmony_ci tempData->codebooks.usedCB2[cel->subCels[i].subCels[j]]++; 569cabdff1aSopenharmony_ci } 570cabdff1aSopenharmony_ci} 571cabdff1aSopenharmony_ci 572cabdff1aSopenharmony_cistatic void remap_codebooks(RoqEncContext *enc) 573cabdff1aSopenharmony_ci{ 574cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 575cabdff1aSopenharmony_ci RoqTempData *const tempData = &enc->tmp_data; 576cabdff1aSopenharmony_ci int i, j, idx=0; 577cabdff1aSopenharmony_ci 578cabdff1aSopenharmony_ci /* Make remaps for the final codebook usage */ 579cabdff1aSopenharmony_ci for (i=0; i<(enc->quake3_compat ? MAX_CBS_4x4-1 : MAX_CBS_4x4); i++) { 580cabdff1aSopenharmony_ci if (tempData->codebooks.usedCB4[i]) { 581cabdff1aSopenharmony_ci tempData->i2f4[i] = idx; 582cabdff1aSopenharmony_ci tempData->f2i4[idx] = i; 583cabdff1aSopenharmony_ci for (j=0; j<4; j++) 584cabdff1aSopenharmony_ci tempData->codebooks.usedCB2[roq->cb4x4[i].idx[j]]++; 585cabdff1aSopenharmony_ci idx++; 586cabdff1aSopenharmony_ci } 587cabdff1aSopenharmony_ci } 588cabdff1aSopenharmony_ci 589cabdff1aSopenharmony_ci tempData->numCB4 = idx; 590cabdff1aSopenharmony_ci 591cabdff1aSopenharmony_ci idx = 0; 592cabdff1aSopenharmony_ci for (i=0; i<MAX_CBS_2x2; i++) { 593cabdff1aSopenharmony_ci if (tempData->codebooks.usedCB2[i]) { 594cabdff1aSopenharmony_ci tempData->i2f2[i] = idx; 595cabdff1aSopenharmony_ci tempData->f2i2[idx] = i; 596cabdff1aSopenharmony_ci idx++; 597cabdff1aSopenharmony_ci } 598cabdff1aSopenharmony_ci } 599cabdff1aSopenharmony_ci tempData->numCB2 = idx; 600cabdff1aSopenharmony_ci 601cabdff1aSopenharmony_ci} 602cabdff1aSopenharmony_ci 603cabdff1aSopenharmony_ci/** 604cabdff1aSopenharmony_ci * Write codebook chunk 605cabdff1aSopenharmony_ci */ 606cabdff1aSopenharmony_cistatic void write_codebooks(RoqEncContext *enc) 607cabdff1aSopenharmony_ci{ 608cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 609cabdff1aSopenharmony_ci RoqTempData *const tempData = &enc->tmp_data; 610cabdff1aSopenharmony_ci int i, j; 611cabdff1aSopenharmony_ci uint8_t **outp= &enc->out_buf; 612cabdff1aSopenharmony_ci 613cabdff1aSopenharmony_ci if (tempData->numCB2) { 614cabdff1aSopenharmony_ci bytestream_put_le16(outp, RoQ_QUAD_CODEBOOK); 615cabdff1aSopenharmony_ci bytestream_put_le32(outp, tempData->numCB2*6 + tempData->numCB4*4); 616cabdff1aSopenharmony_ci bytestream_put_byte(outp, tempData->numCB4); 617cabdff1aSopenharmony_ci bytestream_put_byte(outp, tempData->numCB2); 618cabdff1aSopenharmony_ci 619cabdff1aSopenharmony_ci for (i=0; i<tempData->numCB2; i++) { 620cabdff1aSopenharmony_ci bytestream_put_buffer(outp, roq->cb2x2[tempData->f2i2[i]].y, 4); 621cabdff1aSopenharmony_ci bytestream_put_byte(outp, roq->cb2x2[tempData->f2i2[i]].u); 622cabdff1aSopenharmony_ci bytestream_put_byte(outp, roq->cb2x2[tempData->f2i2[i]].v); 623cabdff1aSopenharmony_ci } 624cabdff1aSopenharmony_ci 625cabdff1aSopenharmony_ci for (i=0; i<tempData->numCB4; i++) 626cabdff1aSopenharmony_ci for (j=0; j<4; j++) 627cabdff1aSopenharmony_ci bytestream_put_byte(outp, tempData->i2f2[roq->cb4x4[tempData->f2i4[i]].idx[j]]); 628cabdff1aSopenharmony_ci 629cabdff1aSopenharmony_ci } 630cabdff1aSopenharmony_ci} 631cabdff1aSopenharmony_ci 632cabdff1aSopenharmony_cistatic inline uint8_t motion_arg(motion_vect mot) 633cabdff1aSopenharmony_ci{ 634cabdff1aSopenharmony_ci uint8_t ax = 8 - ((uint8_t) mot.d[0]); 635cabdff1aSopenharmony_ci uint8_t ay = 8 - ((uint8_t) mot.d[1]); 636cabdff1aSopenharmony_ci return ((ax&15)<<4) | (ay&15); 637cabdff1aSopenharmony_ci} 638cabdff1aSopenharmony_ci 639cabdff1aSopenharmony_citypedef struct CodingSpool { 640cabdff1aSopenharmony_ci int typeSpool; 641cabdff1aSopenharmony_ci int typeSpoolLength; 642cabdff1aSopenharmony_ci uint8_t argumentSpool[64]; 643cabdff1aSopenharmony_ci uint8_t *args; 644cabdff1aSopenharmony_ci uint8_t **pout; 645cabdff1aSopenharmony_ci} CodingSpool; 646cabdff1aSopenharmony_ci 647cabdff1aSopenharmony_ci/* NOTE: Typecodes must be spooled AFTER arguments!! */ 648cabdff1aSopenharmony_cistatic void write_typecode(CodingSpool *s, uint8_t type) 649cabdff1aSopenharmony_ci{ 650cabdff1aSopenharmony_ci s->typeSpool |= (type & 3) << (14 - s->typeSpoolLength); 651cabdff1aSopenharmony_ci s->typeSpoolLength += 2; 652cabdff1aSopenharmony_ci if (s->typeSpoolLength == 16) { 653cabdff1aSopenharmony_ci bytestream_put_le16(s->pout, s->typeSpool); 654cabdff1aSopenharmony_ci bytestream_put_buffer(s->pout, s->argumentSpool, 655cabdff1aSopenharmony_ci s->args - s->argumentSpool); 656cabdff1aSopenharmony_ci s->typeSpoolLength = 0; 657cabdff1aSopenharmony_ci s->typeSpool = 0; 658cabdff1aSopenharmony_ci s->args = s->argumentSpool; 659cabdff1aSopenharmony_ci } 660cabdff1aSopenharmony_ci} 661cabdff1aSopenharmony_ci 662cabdff1aSopenharmony_cistatic void reconstruct_and_encode_image(RoqEncContext *enc, 663cabdff1aSopenharmony_ci int w, int h, int numBlocks) 664cabdff1aSopenharmony_ci{ 665cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 666cabdff1aSopenharmony_ci RoqTempData *const tempData = &enc->tmp_data; 667cabdff1aSopenharmony_ci int i, j, k; 668cabdff1aSopenharmony_ci int x, y; 669cabdff1aSopenharmony_ci int subX, subY; 670cabdff1aSopenharmony_ci 671cabdff1aSopenharmony_ci roq_qcell *qcell; 672cabdff1aSopenharmony_ci CelEvaluation *eval; 673cabdff1aSopenharmony_ci 674cabdff1aSopenharmony_ci CodingSpool spool; 675cabdff1aSopenharmony_ci 676cabdff1aSopenharmony_ci spool.typeSpool=0; 677cabdff1aSopenharmony_ci spool.typeSpoolLength=0; 678cabdff1aSopenharmony_ci spool.args = spool.argumentSpool; 679cabdff1aSopenharmony_ci spool.pout = &enc->out_buf; 680cabdff1aSopenharmony_ci 681cabdff1aSopenharmony_ci if (tempData->used_option[RoQ_ID_CCC]%2) 682cabdff1aSopenharmony_ci tempData->mainChunkSize+=8; //FIXME 683cabdff1aSopenharmony_ci 684cabdff1aSopenharmony_ci /* Write the video chunk header */ 685cabdff1aSopenharmony_ci bytestream_put_le16(&enc->out_buf, RoQ_QUAD_VQ); 686cabdff1aSopenharmony_ci bytestream_put_le32(&enc->out_buf, tempData->mainChunkSize/8); 687cabdff1aSopenharmony_ci bytestream_put_byte(&enc->out_buf, 0x0); 688cabdff1aSopenharmony_ci bytestream_put_byte(&enc->out_buf, 0x0); 689cabdff1aSopenharmony_ci 690cabdff1aSopenharmony_ci for (i=0; i<numBlocks; i++) { 691cabdff1aSopenharmony_ci eval = enc->cel_evals + i; 692cabdff1aSopenharmony_ci 693cabdff1aSopenharmony_ci x = eval->sourceX; 694cabdff1aSopenharmony_ci y = eval->sourceY; 695cabdff1aSopenharmony_ci 696cabdff1aSopenharmony_ci switch (eval->best_coding) { 697cabdff1aSopenharmony_ci case RoQ_ID_MOT: 698cabdff1aSopenharmony_ci write_typecode(&spool, RoQ_ID_MOT); 699cabdff1aSopenharmony_ci break; 700cabdff1aSopenharmony_ci 701cabdff1aSopenharmony_ci case RoQ_ID_FCC: 702cabdff1aSopenharmony_ci bytestream_put_byte(&spool.args, motion_arg(eval->motion)); 703cabdff1aSopenharmony_ci 704cabdff1aSopenharmony_ci write_typecode(&spool, RoQ_ID_FCC); 705cabdff1aSopenharmony_ci ff_apply_motion_8x8(roq, x, y, 706cabdff1aSopenharmony_ci eval->motion.d[0], eval->motion.d[1]); 707cabdff1aSopenharmony_ci break; 708cabdff1aSopenharmony_ci 709cabdff1aSopenharmony_ci case RoQ_ID_SLD: 710cabdff1aSopenharmony_ci bytestream_put_byte(&spool.args, tempData->i2f4[eval->cbEntry]); 711cabdff1aSopenharmony_ci write_typecode(&spool, RoQ_ID_SLD); 712cabdff1aSopenharmony_ci 713cabdff1aSopenharmony_ci qcell = roq->cb4x4 + eval->cbEntry; 714cabdff1aSopenharmony_ci ff_apply_vector_4x4(roq, x , y , roq->cb2x2 + qcell->idx[0]); 715cabdff1aSopenharmony_ci ff_apply_vector_4x4(roq, x+4, y , roq->cb2x2 + qcell->idx[1]); 716cabdff1aSopenharmony_ci ff_apply_vector_4x4(roq, x , y+4, roq->cb2x2 + qcell->idx[2]); 717cabdff1aSopenharmony_ci ff_apply_vector_4x4(roq, x+4, y+4, roq->cb2x2 + qcell->idx[3]); 718cabdff1aSopenharmony_ci break; 719cabdff1aSopenharmony_ci 720cabdff1aSopenharmony_ci case RoQ_ID_CCC: 721cabdff1aSopenharmony_ci write_typecode(&spool, RoQ_ID_CCC); 722cabdff1aSopenharmony_ci 723cabdff1aSopenharmony_ci for (j=0; j<4; j++) { 724cabdff1aSopenharmony_ci subX = x + 4*(j&1); 725cabdff1aSopenharmony_ci subY = y + 2*(j&2); 726cabdff1aSopenharmony_ci 727cabdff1aSopenharmony_ci switch(eval->subCels[j].best_coding) { 728cabdff1aSopenharmony_ci case RoQ_ID_MOT: 729cabdff1aSopenharmony_ci break; 730cabdff1aSopenharmony_ci 731cabdff1aSopenharmony_ci case RoQ_ID_FCC: 732cabdff1aSopenharmony_ci bytestream_put_byte(&spool.args, 733cabdff1aSopenharmony_ci motion_arg(eval->subCels[j].motion)); 734cabdff1aSopenharmony_ci 735cabdff1aSopenharmony_ci ff_apply_motion_4x4(roq, subX, subY, 736cabdff1aSopenharmony_ci eval->subCels[j].motion.d[0], 737cabdff1aSopenharmony_ci eval->subCels[j].motion.d[1]); 738cabdff1aSopenharmony_ci break; 739cabdff1aSopenharmony_ci 740cabdff1aSopenharmony_ci case RoQ_ID_SLD: 741cabdff1aSopenharmony_ci bytestream_put_byte(&spool.args, 742cabdff1aSopenharmony_ci tempData->i2f4[eval->subCels[j].cbEntry]); 743cabdff1aSopenharmony_ci 744cabdff1aSopenharmony_ci qcell = roq->cb4x4 + eval->subCels[j].cbEntry; 745cabdff1aSopenharmony_ci 746cabdff1aSopenharmony_ci ff_apply_vector_2x2(roq, subX , subY , 747cabdff1aSopenharmony_ci roq->cb2x2 + qcell->idx[0]); 748cabdff1aSopenharmony_ci ff_apply_vector_2x2(roq, subX+2, subY , 749cabdff1aSopenharmony_ci roq->cb2x2 + qcell->idx[1]); 750cabdff1aSopenharmony_ci ff_apply_vector_2x2(roq, subX , subY+2, 751cabdff1aSopenharmony_ci roq->cb2x2 + qcell->idx[2]); 752cabdff1aSopenharmony_ci ff_apply_vector_2x2(roq, subX+2, subY+2, 753cabdff1aSopenharmony_ci roq->cb2x2 + qcell->idx[3]); 754cabdff1aSopenharmony_ci break; 755cabdff1aSopenharmony_ci 756cabdff1aSopenharmony_ci case RoQ_ID_CCC: 757cabdff1aSopenharmony_ci for (k=0; k<4; k++) { 758cabdff1aSopenharmony_ci int cb_idx = eval->subCels[j].subCels[k]; 759cabdff1aSopenharmony_ci bytestream_put_byte(&spool.args, 760cabdff1aSopenharmony_ci tempData->i2f2[cb_idx]); 761cabdff1aSopenharmony_ci 762cabdff1aSopenharmony_ci ff_apply_vector_2x2(roq, subX + 2*(k&1), subY + (k&2), 763cabdff1aSopenharmony_ci roq->cb2x2 + cb_idx); 764cabdff1aSopenharmony_ci } 765cabdff1aSopenharmony_ci break; 766cabdff1aSopenharmony_ci } 767cabdff1aSopenharmony_ci write_typecode(&spool, eval->subCels[j].best_coding); 768cabdff1aSopenharmony_ci } 769cabdff1aSopenharmony_ci break; 770cabdff1aSopenharmony_ci } 771cabdff1aSopenharmony_ci } 772cabdff1aSopenharmony_ci 773cabdff1aSopenharmony_ci /* Flush the remainder of the argument/type spool */ 774cabdff1aSopenharmony_ci while (spool.typeSpoolLength) 775cabdff1aSopenharmony_ci write_typecode(&spool, 0x0); 776cabdff1aSopenharmony_ci} 777cabdff1aSopenharmony_ci 778cabdff1aSopenharmony_ci 779cabdff1aSopenharmony_ci/** 780cabdff1aSopenharmony_ci * Create a single YUV cell from a 2x2 section of the image 781cabdff1aSopenharmony_ci */ 782cabdff1aSopenharmony_cistatic inline void frame_block_to_cell(int *block, uint8_t * const *data, 783cabdff1aSopenharmony_ci int top, int left, const int *stride) 784cabdff1aSopenharmony_ci{ 785cabdff1aSopenharmony_ci int i, j, u=0, v=0; 786cabdff1aSopenharmony_ci 787cabdff1aSopenharmony_ci for (i=0; i<2; i++) 788cabdff1aSopenharmony_ci for (j=0; j<2; j++) { 789cabdff1aSopenharmony_ci int x = (top+i)*stride[0] + left + j; 790cabdff1aSopenharmony_ci *block++ = data[0][x]; 791cabdff1aSopenharmony_ci x = (top+i)*stride[1] + left + j; 792cabdff1aSopenharmony_ci u += data[1][x]; 793cabdff1aSopenharmony_ci v += data[2][x]; 794cabdff1aSopenharmony_ci } 795cabdff1aSopenharmony_ci 796cabdff1aSopenharmony_ci *block++ = (u + 2) / 4 * CHROMA_BIAS; 797cabdff1aSopenharmony_ci *block++ = (v + 2) / 4 * CHROMA_BIAS; 798cabdff1aSopenharmony_ci} 799cabdff1aSopenharmony_ci 800cabdff1aSopenharmony_ci/** 801cabdff1aSopenharmony_ci * Create YUV clusters for the entire image 802cabdff1aSopenharmony_ci */ 803cabdff1aSopenharmony_cistatic void create_clusters(const AVFrame *frame, int w, int h, int *points) 804cabdff1aSopenharmony_ci{ 805cabdff1aSopenharmony_ci int i, j, k, l; 806cabdff1aSopenharmony_ci 807cabdff1aSopenharmony_ci for (i=0; i<h; i+=4) 808cabdff1aSopenharmony_ci for (j=0; j<w; j+=4) { 809cabdff1aSopenharmony_ci for (k=0; k < 2; k++) 810cabdff1aSopenharmony_ci for (l=0; l < 2; l++) 811cabdff1aSopenharmony_ci frame_block_to_cell(points + (l + 2*k)*6, frame->data, 812cabdff1aSopenharmony_ci i+2*k, j+2*l, frame->linesize); 813cabdff1aSopenharmony_ci points += 24; 814cabdff1aSopenharmony_ci } 815cabdff1aSopenharmony_ci} 816cabdff1aSopenharmony_ci 817cabdff1aSopenharmony_cistatic int generate_codebook(RoqEncContext *enc, 818cabdff1aSopenharmony_ci int *points, int inputCount, roq_cell *results, 819cabdff1aSopenharmony_ci int size, int cbsize) 820cabdff1aSopenharmony_ci{ 821cabdff1aSopenharmony_ci int i, j, k, ret = 0; 822cabdff1aSopenharmony_ci int c_size = size*size/4; 823cabdff1aSopenharmony_ci int *buf; 824cabdff1aSopenharmony_ci int *codebook = enc->tmp_codebook_buf; 825cabdff1aSopenharmony_ci int *closest_cb = enc->closest_cb; 826cabdff1aSopenharmony_ci 827cabdff1aSopenharmony_ci ret = avpriv_elbg_do(&enc->elbg, points, 6 * c_size, inputCount, codebook, 828cabdff1aSopenharmony_ci cbsize, 1, closest_cb, &enc->randctx, 0); 829cabdff1aSopenharmony_ci if (ret < 0) 830cabdff1aSopenharmony_ci return ret; 831cabdff1aSopenharmony_ci 832cabdff1aSopenharmony_ci buf = codebook; 833cabdff1aSopenharmony_ci for (i=0; i<cbsize; i++) 834cabdff1aSopenharmony_ci for (k=0; k<c_size; k++) { 835cabdff1aSopenharmony_ci for(j=0; j<4; j++) 836cabdff1aSopenharmony_ci results->y[j] = *buf++; 837cabdff1aSopenharmony_ci 838cabdff1aSopenharmony_ci results->u = (*buf++ + CHROMA_BIAS/2)/CHROMA_BIAS; 839cabdff1aSopenharmony_ci results->v = (*buf++ + CHROMA_BIAS/2)/CHROMA_BIAS; 840cabdff1aSopenharmony_ci results++; 841cabdff1aSopenharmony_ci } 842cabdff1aSopenharmony_ci return 0; 843cabdff1aSopenharmony_ci} 844cabdff1aSopenharmony_ci 845cabdff1aSopenharmony_cistatic int generate_new_codebooks(RoqEncContext *enc) 846cabdff1aSopenharmony_ci{ 847cabdff1aSopenharmony_ci int i, j, ret = 0; 848cabdff1aSopenharmony_ci RoqCodebooks *codebooks = &enc->tmp_data.codebooks; 849cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 850cabdff1aSopenharmony_ci int max = roq->width * roq->height / 16; 851cabdff1aSopenharmony_ci uint8_t mb2[3*4]; 852cabdff1aSopenharmony_ci int *points = enc->points; 853cabdff1aSopenharmony_ci 854cabdff1aSopenharmony_ci /* Subsample YUV data */ 855cabdff1aSopenharmony_ci create_clusters(enc->frame_to_enc, roq->width, roq->height, points); 856cabdff1aSopenharmony_ci 857cabdff1aSopenharmony_ci codebooks->numCB4 = (enc->quake3_compat ? MAX_CBS_4x4-1 : MAX_CBS_4x4); 858cabdff1aSopenharmony_ci 859cabdff1aSopenharmony_ci /* Create 4x4 codebooks */ 860cabdff1aSopenharmony_ci if ((ret = generate_codebook(enc, points, max, enc->results4, 861cabdff1aSopenharmony_ci 4, codebooks->numCB4)) < 0) 862cabdff1aSopenharmony_ci return ret; 863cabdff1aSopenharmony_ci 864cabdff1aSopenharmony_ci /* Create 2x2 codebooks */ 865cabdff1aSopenharmony_ci if ((ret = generate_codebook(enc, points, max * 4, 866cabdff1aSopenharmony_ci roq->cb2x2, 2, MAX_CBS_2x2)) < 0) 867cabdff1aSopenharmony_ci return ret; 868cabdff1aSopenharmony_ci 869cabdff1aSopenharmony_ci codebooks->numCB2 = MAX_CBS_2x2; 870cabdff1aSopenharmony_ci 871cabdff1aSopenharmony_ci /* Unpack 2x2 codebook clusters */ 872cabdff1aSopenharmony_ci for (i=0; i<codebooks->numCB2; i++) 873cabdff1aSopenharmony_ci unpack_roq_cell(roq->cb2x2 + i, codebooks->unpacked_cb2 + i*2*2*3); 874cabdff1aSopenharmony_ci 875cabdff1aSopenharmony_ci /* Index all 4x4 entries to the 2x2 entries, unpack, and enlarge */ 876cabdff1aSopenharmony_ci for (i=0; i<codebooks->numCB4; i++) { 877cabdff1aSopenharmony_ci for (j=0; j<4; j++) { 878cabdff1aSopenharmony_ci unpack_roq_cell(&enc->results4[4*i + j], mb2); 879cabdff1aSopenharmony_ci index_mb(mb2, codebooks->unpacked_cb2, codebooks->numCB2, 880cabdff1aSopenharmony_ci &roq->cb4x4[i].idx[j], 2); 881cabdff1aSopenharmony_ci } 882cabdff1aSopenharmony_ci unpack_roq_qcell(codebooks->unpacked_cb2, roq->cb4x4 + i, 883cabdff1aSopenharmony_ci codebooks->unpacked_cb4 + i*4*4*3); 884cabdff1aSopenharmony_ci enlarge_roq_mb4(codebooks->unpacked_cb4 + i*4*4*3, 885cabdff1aSopenharmony_ci codebooks->unpacked_cb4_enlarged + i*8*8*3); 886cabdff1aSopenharmony_ci } 887cabdff1aSopenharmony_ci 888cabdff1aSopenharmony_ci return 0; 889cabdff1aSopenharmony_ci} 890cabdff1aSopenharmony_ci 891cabdff1aSopenharmony_cistatic int roq_encode_video(RoqEncContext *enc) 892cabdff1aSopenharmony_ci{ 893cabdff1aSopenharmony_ci RoqTempData *const tempData = &enc->tmp_data; 894cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 895cabdff1aSopenharmony_ci int ret; 896cabdff1aSopenharmony_ci 897cabdff1aSopenharmony_ci memset(tempData, 0, sizeof(*tempData)); 898cabdff1aSopenharmony_ci 899cabdff1aSopenharmony_ci ret = generate_new_codebooks(enc); 900cabdff1aSopenharmony_ci if (ret < 0) 901cabdff1aSopenharmony_ci return ret; 902cabdff1aSopenharmony_ci 903cabdff1aSopenharmony_ci if (enc->framesSinceKeyframe >= 1) { 904cabdff1aSopenharmony_ci motion_search(enc, 8); 905cabdff1aSopenharmony_ci motion_search(enc, 4); 906cabdff1aSopenharmony_ci } 907cabdff1aSopenharmony_ci 908cabdff1aSopenharmony_ci retry_encode: 909cabdff1aSopenharmony_ci for (int i = 0; i < roq->width * roq->height / 64; i++) 910cabdff1aSopenharmony_ci gather_data_for_cel(enc->cel_evals + i, enc); 911cabdff1aSopenharmony_ci 912cabdff1aSopenharmony_ci /* Quake 3 can't handle chunks bigger than 65535 bytes */ 913cabdff1aSopenharmony_ci if (tempData->mainChunkSize/8 > 65535 && enc->quake3_compat) { 914cabdff1aSopenharmony_ci if (enc->lambda > 100000) { 915cabdff1aSopenharmony_ci av_log(roq->avctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n"); 916cabdff1aSopenharmony_ci return AVERROR(EINVAL); 917cabdff1aSopenharmony_ci } 918cabdff1aSopenharmony_ci av_log(roq->avctx, AV_LOG_ERROR, 919cabdff1aSopenharmony_ci "Warning, generated a frame too big for Quake (%d > 65535), " 920cabdff1aSopenharmony_ci "now switching to a bigger qscale value.\n", 921cabdff1aSopenharmony_ci tempData->mainChunkSize/8); 922cabdff1aSopenharmony_ci enc->lambda *= 1.5; 923cabdff1aSopenharmony_ci tempData->mainChunkSize = 0; 924cabdff1aSopenharmony_ci memset(tempData->used_option, 0, sizeof(tempData->used_option)); 925cabdff1aSopenharmony_ci memset(tempData->codebooks.usedCB4, 0, 926cabdff1aSopenharmony_ci sizeof(tempData->codebooks.usedCB4)); 927cabdff1aSopenharmony_ci memset(tempData->codebooks.usedCB2, 0, 928cabdff1aSopenharmony_ci sizeof(tempData->codebooks.usedCB2)); 929cabdff1aSopenharmony_ci 930cabdff1aSopenharmony_ci goto retry_encode; 931cabdff1aSopenharmony_ci } 932cabdff1aSopenharmony_ci 933cabdff1aSopenharmony_ci remap_codebooks(enc); 934cabdff1aSopenharmony_ci 935cabdff1aSopenharmony_ci write_codebooks(enc); 936cabdff1aSopenharmony_ci 937cabdff1aSopenharmony_ci reconstruct_and_encode_image(enc, roq->width, roq->height, 938cabdff1aSopenharmony_ci roq->width * roq->height / 64); 939cabdff1aSopenharmony_ci 940cabdff1aSopenharmony_ci /* Rotate frame history */ 941cabdff1aSopenharmony_ci FFSWAP(AVFrame *, roq->current_frame, roq->last_frame); 942cabdff1aSopenharmony_ci FFSWAP(motion_vect *, enc->last_motion4, enc->this_motion4); 943cabdff1aSopenharmony_ci FFSWAP(motion_vect *, enc->last_motion8, enc->this_motion8); 944cabdff1aSopenharmony_ci 945cabdff1aSopenharmony_ci enc->framesSinceKeyframe++; 946cabdff1aSopenharmony_ci 947cabdff1aSopenharmony_ci return 0; 948cabdff1aSopenharmony_ci} 949cabdff1aSopenharmony_ci 950cabdff1aSopenharmony_cistatic av_cold int roq_encode_end(AVCodecContext *avctx) 951cabdff1aSopenharmony_ci{ 952cabdff1aSopenharmony_ci RoqEncContext *const enc = avctx->priv_data; 953cabdff1aSopenharmony_ci 954cabdff1aSopenharmony_ci av_frame_free(&enc->common.current_frame); 955cabdff1aSopenharmony_ci av_frame_free(&enc->common.last_frame); 956cabdff1aSopenharmony_ci 957cabdff1aSopenharmony_ci av_freep(&enc->cel_evals); 958cabdff1aSopenharmony_ci av_freep(&enc->closest_cb); 959cabdff1aSopenharmony_ci av_freep(&enc->this_motion4); 960cabdff1aSopenharmony_ci av_freep(&enc->last_motion4); 961cabdff1aSopenharmony_ci av_freep(&enc->this_motion8); 962cabdff1aSopenharmony_ci av_freep(&enc->last_motion8); 963cabdff1aSopenharmony_ci 964cabdff1aSopenharmony_ci avpriv_elbg_free(&enc->elbg); 965cabdff1aSopenharmony_ci 966cabdff1aSopenharmony_ci return 0; 967cabdff1aSopenharmony_ci} 968cabdff1aSopenharmony_ci 969cabdff1aSopenharmony_cistatic av_cold int roq_encode_init(AVCodecContext *avctx) 970cabdff1aSopenharmony_ci{ 971cabdff1aSopenharmony_ci RoqEncContext *const enc = avctx->priv_data; 972cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 973cabdff1aSopenharmony_ci 974cabdff1aSopenharmony_ci av_lfg_init(&enc->randctx, 1); 975cabdff1aSopenharmony_ci 976cabdff1aSopenharmony_ci roq->avctx = avctx; 977cabdff1aSopenharmony_ci 978cabdff1aSopenharmony_ci enc->framesSinceKeyframe = 0; 979cabdff1aSopenharmony_ci if ((avctx->width & 0xf) || (avctx->height & 0xf)) { 980cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Dimensions must be divisible by 16\n"); 981cabdff1aSopenharmony_ci return AVERROR(EINVAL); 982cabdff1aSopenharmony_ci } 983cabdff1aSopenharmony_ci 984cabdff1aSopenharmony_ci if (avctx->width > 65535 || avctx->height > 65535) { 985cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n", enc->quake3_compat ? 32768 : 65535); 986cabdff1aSopenharmony_ci return AVERROR(EINVAL); 987cabdff1aSopenharmony_ci } 988cabdff1aSopenharmony_ci 989cabdff1aSopenharmony_ci if (((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx->height-1))) 990cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power of two, this is not supported by quake\n"); 991cabdff1aSopenharmony_ci 992cabdff1aSopenharmony_ci roq->width = avctx->width; 993cabdff1aSopenharmony_ci roq->height = avctx->height; 994cabdff1aSopenharmony_ci 995cabdff1aSopenharmony_ci enc->framesSinceKeyframe = 0; 996cabdff1aSopenharmony_ci enc->first_frame = 1; 997cabdff1aSopenharmony_ci 998cabdff1aSopenharmony_ci roq->last_frame = av_frame_alloc(); 999cabdff1aSopenharmony_ci roq->current_frame = av_frame_alloc(); 1000cabdff1aSopenharmony_ci if (!roq->last_frame || !roq->current_frame) 1001cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 1002cabdff1aSopenharmony_ci 1003cabdff1aSopenharmony_ci enc->this_motion4 = 1004cabdff1aSopenharmony_ci av_calloc(roq->width * roq->height / 16, sizeof(*enc->this_motion4)); 1005cabdff1aSopenharmony_ci 1006cabdff1aSopenharmony_ci enc->last_motion4 = 1007cabdff1aSopenharmony_ci av_malloc_array (roq->width * roq->height / 16, sizeof(motion_vect)); 1008cabdff1aSopenharmony_ci 1009cabdff1aSopenharmony_ci enc->this_motion8 = 1010cabdff1aSopenharmony_ci av_calloc(roq->width * roq->height / 64, sizeof(*enc->this_motion8)); 1011cabdff1aSopenharmony_ci 1012cabdff1aSopenharmony_ci enc->last_motion8 = 1013cabdff1aSopenharmony_ci av_malloc_array (roq->width * roq->height / 64, sizeof(motion_vect)); 1014cabdff1aSopenharmony_ci 1015cabdff1aSopenharmony_ci /* 4x4 codebook needs 6 * 4 * 4 / 4 * width * height / 16 * sizeof(int); 1016cabdff1aSopenharmony_ci * and so does the points buffer. */ 1017cabdff1aSopenharmony_ci enc->closest_cb = 1018cabdff1aSopenharmony_ci av_malloc_array(roq->width * roq->height, 3 * sizeof(int)); 1019cabdff1aSopenharmony_ci 1020cabdff1aSopenharmony_ci if (!enc->this_motion4 || !enc->last_motion4 || 1021cabdff1aSopenharmony_ci !enc->this_motion8 || !enc->last_motion8 || !enc->closest_cb) 1022cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 1023cabdff1aSopenharmony_ci 1024cabdff1aSopenharmony_ci enc->points = enc->closest_cb + roq->width * roq->height * 3 / 2; 1025cabdff1aSopenharmony_ci 1026cabdff1aSopenharmony_ci return create_cel_evals(enc); 1027cabdff1aSopenharmony_ci} 1028cabdff1aSopenharmony_ci 1029cabdff1aSopenharmony_cistatic void roq_write_video_info_chunk(RoqEncContext *enc) 1030cabdff1aSopenharmony_ci{ 1031cabdff1aSopenharmony_ci /* ROQ info chunk */ 1032cabdff1aSopenharmony_ci bytestream_put_le16(&enc->out_buf, RoQ_INFO); 1033cabdff1aSopenharmony_ci 1034cabdff1aSopenharmony_ci /* Size: 8 bytes */ 1035cabdff1aSopenharmony_ci bytestream_put_le32(&enc->out_buf, 8); 1036cabdff1aSopenharmony_ci 1037cabdff1aSopenharmony_ci /* Unused argument */ 1038cabdff1aSopenharmony_ci bytestream_put_byte(&enc->out_buf, 0x00); 1039cabdff1aSopenharmony_ci bytestream_put_byte(&enc->out_buf, 0x00); 1040cabdff1aSopenharmony_ci 1041cabdff1aSopenharmony_ci /* Width */ 1042cabdff1aSopenharmony_ci bytestream_put_le16(&enc->out_buf, enc->common.width); 1043cabdff1aSopenharmony_ci 1044cabdff1aSopenharmony_ci /* Height */ 1045cabdff1aSopenharmony_ci bytestream_put_le16(&enc->out_buf, enc->common.height); 1046cabdff1aSopenharmony_ci 1047cabdff1aSopenharmony_ci /* Unused in Quake 3, mimics the output of the real encoder */ 1048cabdff1aSopenharmony_ci bytestream_put_byte(&enc->out_buf, 0x08); 1049cabdff1aSopenharmony_ci bytestream_put_byte(&enc->out_buf, 0x00); 1050cabdff1aSopenharmony_ci bytestream_put_byte(&enc->out_buf, 0x04); 1051cabdff1aSopenharmony_ci bytestream_put_byte(&enc->out_buf, 0x00); 1052cabdff1aSopenharmony_ci} 1053cabdff1aSopenharmony_ci 1054cabdff1aSopenharmony_cistatic int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, 1055cabdff1aSopenharmony_ci const AVFrame *frame, int *got_packet) 1056cabdff1aSopenharmony_ci{ 1057cabdff1aSopenharmony_ci RoqEncContext *const enc = avctx->priv_data; 1058cabdff1aSopenharmony_ci RoqContext *const roq = &enc->common; 1059cabdff1aSopenharmony_ci int size, ret; 1060cabdff1aSopenharmony_ci 1061cabdff1aSopenharmony_ci roq->avctx = avctx; 1062cabdff1aSopenharmony_ci 1063cabdff1aSopenharmony_ci enc->frame_to_enc = frame; 1064cabdff1aSopenharmony_ci 1065cabdff1aSopenharmony_ci if (frame->quality) 1066cabdff1aSopenharmony_ci enc->lambda = frame->quality - 1; 1067cabdff1aSopenharmony_ci else 1068cabdff1aSopenharmony_ci enc->lambda = 2*ROQ_LAMBDA_SCALE; 1069cabdff1aSopenharmony_ci 1070cabdff1aSopenharmony_ci /* 138 bits max per 8x8 block + 1071cabdff1aSopenharmony_ci * 256 codebooks*(6 bytes 2x2 + 4 bytes 4x4) + 8 bytes frame header */ 1072cabdff1aSopenharmony_ci size = ((roq->width * roq->height / 64) * 138 + 7) / 8 + 256 * (6 + 4) + 8; 1073cabdff1aSopenharmony_ci if ((ret = ff_alloc_packet(avctx, pkt, size)) < 0) 1074cabdff1aSopenharmony_ci return ret; 1075cabdff1aSopenharmony_ci enc->out_buf = pkt->data; 1076cabdff1aSopenharmony_ci 1077cabdff1aSopenharmony_ci /* Check for I-frame */ 1078cabdff1aSopenharmony_ci if (enc->framesSinceKeyframe == avctx->gop_size) 1079cabdff1aSopenharmony_ci enc->framesSinceKeyframe = 0; 1080cabdff1aSopenharmony_ci 1081cabdff1aSopenharmony_ci if (enc->first_frame) { 1082cabdff1aSopenharmony_ci /* Alloc memory for the reconstruction data (we must know the stride 1083cabdff1aSopenharmony_ci for that) */ 1084cabdff1aSopenharmony_ci if ((ret = ff_encode_alloc_frame(avctx, roq->current_frame)) < 0 || 1085cabdff1aSopenharmony_ci (ret = ff_encode_alloc_frame(avctx, roq->last_frame )) < 0) 1086cabdff1aSopenharmony_ci return ret; 1087cabdff1aSopenharmony_ci 1088cabdff1aSopenharmony_ci /* Before the first video frame, write a "video info" chunk */ 1089cabdff1aSopenharmony_ci roq_write_video_info_chunk(enc); 1090cabdff1aSopenharmony_ci 1091cabdff1aSopenharmony_ci enc->first_frame = 0; 1092cabdff1aSopenharmony_ci } 1093cabdff1aSopenharmony_ci 1094cabdff1aSopenharmony_ci /* Encode the actual frame */ 1095cabdff1aSopenharmony_ci ret = roq_encode_video(enc); 1096cabdff1aSopenharmony_ci if (ret < 0) 1097cabdff1aSopenharmony_ci return ret; 1098cabdff1aSopenharmony_ci 1099cabdff1aSopenharmony_ci pkt->size = enc->out_buf - pkt->data; 1100cabdff1aSopenharmony_ci if (enc->framesSinceKeyframe == 1) 1101cabdff1aSopenharmony_ci pkt->flags |= AV_PKT_FLAG_KEY; 1102cabdff1aSopenharmony_ci *got_packet = 1; 1103cabdff1aSopenharmony_ci 1104cabdff1aSopenharmony_ci return 0; 1105cabdff1aSopenharmony_ci} 1106cabdff1aSopenharmony_ci 1107cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(RoqEncContext, x) 1108cabdff1aSopenharmony_ci#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM 1109cabdff1aSopenharmony_cistatic const AVOption options[] = { 1110cabdff1aSopenharmony_ci { "quake3_compat", "Whether to respect known limitations in Quake 3 decoder", OFFSET(quake3_compat), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, 1111cabdff1aSopenharmony_ci { NULL }, 1112cabdff1aSopenharmony_ci}; 1113cabdff1aSopenharmony_ci 1114cabdff1aSopenharmony_cistatic const AVClass roq_class = { 1115cabdff1aSopenharmony_ci .class_name = "RoQ", 1116cabdff1aSopenharmony_ci .item_name = av_default_item_name, 1117cabdff1aSopenharmony_ci .option = options, 1118cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 1119cabdff1aSopenharmony_ci}; 1120cabdff1aSopenharmony_ci 1121cabdff1aSopenharmony_ciconst FFCodec ff_roq_encoder = { 1122cabdff1aSopenharmony_ci .p.name = "roqvideo", 1123cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("id RoQ video"), 1124cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 1125cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_ROQ, 1126cabdff1aSopenharmony_ci .priv_data_size = sizeof(RoqEncContext), 1127cabdff1aSopenharmony_ci .init = roq_encode_init, 1128cabdff1aSopenharmony_ci FF_CODEC_ENCODE_CB(roq_encode_frame), 1129cabdff1aSopenharmony_ci .close = roq_encode_end, 1130cabdff1aSopenharmony_ci .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUVJ444P, 1131cabdff1aSopenharmony_ci AV_PIX_FMT_NONE }, 1132cabdff1aSopenharmony_ci .p.priv_class = &roq_class, 1133cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, 1134cabdff1aSopenharmony_ci}; 1135