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