/src/ffmpeg/libavcodec/rpzaenc.c
Line | Count | Source |
1 | | /* |
2 | | * QuickTime RPZA Video Encoder |
3 | | * |
4 | | * This file is part of FFmpeg. |
5 | | * |
6 | | * FFmpeg is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * FFmpeg is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with FFmpeg; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | /** |
22 | | * @file rpzaenc.c |
23 | | * QT RPZA Video Encoder by Todd Kirby <doubleshot@pacbell.net> and David Adler |
24 | | */ |
25 | | |
26 | | #include "libavutil/avassert.h" |
27 | | #include "libavutil/common.h" |
28 | | #include "libavutil/opt.h" |
29 | | |
30 | | #include "avcodec.h" |
31 | | #include "codec_internal.h" |
32 | | #include "encode.h" |
33 | | #include "mathops.h" |
34 | | #include "put_bits.h" |
35 | | |
36 | | typedef struct RpzaContext { |
37 | | AVClass *avclass; |
38 | | |
39 | | int skip_frame_thresh; |
40 | | int start_one_color_thresh; |
41 | | int continue_one_color_thresh; |
42 | | int sixteen_color_thresh; |
43 | | |
44 | | AVFrame *prev_frame; // buffer for previous source frame |
45 | | PutBitContext pb; // buffer for encoded frame data. |
46 | | |
47 | | int frame_width; // width in pixels of source frame |
48 | | int frame_height; // height in pixesl of source frame |
49 | | |
50 | | int first_frame; // flag set to one when the first frame is being processed |
51 | | // so that comparisons with previous frame data in not attempted |
52 | | } RpzaContext; |
53 | | |
54 | | typedef enum channel_offset { |
55 | | RED = 2, |
56 | | GREEN = 1, |
57 | | BLUE = 0, |
58 | | } channel_offset; |
59 | | |
60 | | typedef struct rgb { |
61 | | uint8_t r; |
62 | | uint8_t g; |
63 | | uint8_t b; |
64 | | } rgb; |
65 | | |
66 | 0 | #define SQR(x) ((x) * (x)) |
67 | | |
68 | | /* 15 bit components */ |
69 | 79.0M | #define GET_CHAN(color, chan) (((color) >> ((chan) * 5) & 0x1F)) |
70 | 16.2M | #define R(color) GET_CHAN(color, RED) |
71 | 16.2M | #define G(color) GET_CHAN(color, GREEN) |
72 | 16.2M | #define B(color) GET_CHAN(color, BLUE) |
73 | | |
74 | | typedef struct BlockInfo { |
75 | | int row; |
76 | | int col; |
77 | | int block_width; |
78 | | int block_height; |
79 | | int image_width; |
80 | | int image_height; |
81 | | int block_index; |
82 | | uint16_t start; |
83 | | int rowstride; |
84 | | int prev_rowstride; |
85 | | int blocks_per_row; |
86 | | int total_blocks; |
87 | | } BlockInfo; |
88 | | |
89 | | static void get_colors(const uint8_t *min, const uint8_t *max, uint8_t color4[4][3]) |
90 | 0 | { |
91 | 0 | uint8_t step; |
92 | |
|
93 | 0 | color4[0][0] = min[0]; |
94 | 0 | color4[0][1] = min[1]; |
95 | 0 | color4[0][2] = min[2]; |
96 | |
|
97 | 0 | color4[3][0] = max[0]; |
98 | 0 | color4[3][1] = max[1]; |
99 | 0 | color4[3][2] = max[2]; |
100 | | |
101 | | // red components |
102 | 0 | step = (color4[3][0] - color4[0][0] + 1) / 3; |
103 | 0 | color4[1][0] = color4[0][0] + step; |
104 | 0 | color4[2][0] = color4[3][0] - step; |
105 | | |
106 | | // green components |
107 | 0 | step = (color4[3][1] - color4[0][1] + 1) / 3; |
108 | 0 | color4[1][1] = color4[0][1] + step; |
109 | 0 | color4[2][1] = color4[3][1] - step; |
110 | | |
111 | | // blue components |
112 | 0 | step = (color4[3][2] - color4[0][2] + 1) / 3; |
113 | 0 | color4[1][2] = color4[0][2] + step; |
114 | 0 | color4[2][2] = color4[3][2] - step; |
115 | 0 | } |
116 | | |
117 | | /* Fill BlockInfo struct with information about a 4x4 block of the image */ |
118 | | static int get_block_info(BlockInfo *bi, int block, int prev_frame) |
119 | 4.67M | { |
120 | 4.67M | bi->row = block / bi->blocks_per_row; |
121 | 4.67M | bi->col = block % bi->blocks_per_row; |
122 | | |
123 | | // test for right edge block |
124 | 4.67M | if (bi->col == bi->blocks_per_row - 1 && (bi->image_width % 4) != 0) { |
125 | 1.09M | bi->block_width = bi->image_width % 4; |
126 | 3.58M | } else { |
127 | 3.58M | bi->block_width = 4; |
128 | 3.58M | } |
129 | | |
130 | | // test for bottom edge block |
131 | 4.67M | if (bi->row == (bi->image_height / 4) && (bi->image_height % 4) != 0) { |
132 | 1.51M | bi->block_height = bi->image_height % 4; |
133 | 3.16M | } else { |
134 | 3.16M | bi->block_height = 4; |
135 | 3.16M | } |
136 | | |
137 | 4.67M | return block ? (bi->col * 4) + (bi->row * (prev_frame ? bi->prev_rowstride : bi->rowstride) * 4) : 0; |
138 | 4.67M | } |
139 | | |
140 | | static uint16_t rgb24_to_rgb555(const uint8_t *rgb24) |
141 | 378k | { |
142 | 378k | uint16_t rgb555 = 0; |
143 | 378k | uint32_t r, g, b; |
144 | | |
145 | 378k | r = rgb24[0]; |
146 | 378k | g = rgb24[1]; |
147 | 378k | b = rgb24[2]; |
148 | | |
149 | 378k | rgb555 |= (r << 10); |
150 | 378k | rgb555 |= (g << 5); |
151 | 378k | rgb555 |= (b << 0); |
152 | | |
153 | 378k | return rgb555; |
154 | 378k | } |
155 | | |
156 | | /* |
157 | | * Returns the total difference between two 24 bit color values |
158 | | */ |
159 | | static int diff_colors(const uint8_t *colorA, const uint8_t *colorB) |
160 | 0 | { |
161 | 0 | int tot; |
162 | |
|
163 | 0 | tot = SQR(colorA[0] - colorB[0]); |
164 | 0 | tot += SQR(colorA[1] - colorB[1]); |
165 | 0 | tot += SQR(colorA[2] - colorB[2]); |
166 | |
|
167 | 0 | return tot; |
168 | 0 | } |
169 | | |
170 | | /* |
171 | | * Returns the maximum channel difference |
172 | | */ |
173 | | static int max_component_diff(const uint16_t *colorA, const uint16_t *colorB) |
174 | 1.03M | { |
175 | 1.03M | int diff, max = 0; |
176 | | |
177 | 1.03M | diff = FFABS(R(colorA[0]) - R(colorB[0])); |
178 | 1.03M | if (diff > max) { |
179 | 46.1k | max = diff; |
180 | 46.1k | } |
181 | 1.03M | diff = FFABS(G(colorA[0]) - G(colorB[0])); |
182 | 1.03M | if (diff > max) { |
183 | 29.1k | max = diff; |
184 | 29.1k | } |
185 | 1.03M | diff = FFABS(B(colorA[0]) - B(colorB[0])); |
186 | 1.03M | if (diff > max) { |
187 | 16.7k | max = diff; |
188 | 16.7k | } |
189 | 1.03M | return max; |
190 | 1.03M | } |
191 | | |
192 | | /* |
193 | | * Find the channel that has the largest difference between minimum and maximum |
194 | | * color values. Put the minimum value in min, maximum in max and the channel |
195 | | * in chan. |
196 | | */ |
197 | | static void get_max_component_diff(const BlockInfo *bi, const uint16_t *block_ptr, |
198 | | uint8_t *min, uint8_t *max, channel_offset *chan) |
199 | 208k | { |
200 | 208k | int x, y; |
201 | 208k | uint8_t min_r, max_r, min_g, max_g, min_b, max_b; |
202 | 208k | uint8_t r, g, b; |
203 | | |
204 | | // fix warning about uninitialized vars |
205 | 208k | min_r = min_g = min_b = UINT8_MAX; |
206 | 208k | max_r = max_g = max_b = 0; |
207 | | |
208 | | // loop thru and compare pixels |
209 | 933k | for (y = 0; y < bi->block_height; y++) { |
210 | 3.49M | for (x = 0; x < bi->block_width; x++) { |
211 | | // TODO: optimize |
212 | 2.77M | min_r = FFMIN(R(block_ptr[x]), min_r); |
213 | 2.77M | min_g = FFMIN(G(block_ptr[x]), min_g); |
214 | 2.77M | min_b = FFMIN(B(block_ptr[x]), min_b); |
215 | | |
216 | 2.77M | max_r = FFMAX(R(block_ptr[x]), max_r); |
217 | 2.77M | max_g = FFMAX(G(block_ptr[x]), max_g); |
218 | 2.77M | max_b = FFMAX(B(block_ptr[x]), max_b); |
219 | 2.77M | } |
220 | 724k | block_ptr += bi->rowstride; |
221 | 724k | } |
222 | | |
223 | 208k | r = max_r - min_r; |
224 | 208k | g = max_g - min_g; |
225 | 208k | b = max_b - min_b; |
226 | | |
227 | 208k | if (r > g && r > b) { |
228 | 70.4k | *max = max_r; |
229 | 70.4k | *min = min_r; |
230 | 70.4k | *chan = RED; |
231 | 138k | } else if (g > b && g >= r) { |
232 | 55.8k | *max = max_g; |
233 | 55.8k | *min = min_g; |
234 | 55.8k | *chan = GREEN; |
235 | 82.2k | } else { |
236 | 82.2k | *max = max_b; |
237 | 82.2k | *min = min_b; |
238 | 82.2k | *chan = BLUE; |
239 | 82.2k | } |
240 | 208k | } |
241 | | |
242 | | /* |
243 | | * Compare two 4x4 blocks to determine if the total difference between the |
244 | | * blocks is greater than the thresh parameter. Returns -1 if difference |
245 | | * exceeds threshold or zero otherwise. |
246 | | */ |
247 | | static int compare_blocks(const uint16_t *block1, const uint16_t *block2, |
248 | | const BlockInfo *bi, int thresh) |
249 | 127k | { |
250 | 127k | int x, y, diff = 0; |
251 | 429k | for (y = 0; y < bi->block_height; y++) { |
252 | 1.33M | for (x = 0; x < bi->block_width; x++) { |
253 | 1.03M | diff = max_component_diff(&block1[x], &block2[x]); |
254 | 1.03M | if (diff >= thresh) { |
255 | 53.8k | return -1; |
256 | 53.8k | } |
257 | 1.03M | } |
258 | 302k | block1 += bi->prev_rowstride; |
259 | 302k | block2 += bi->rowstride; |
260 | 302k | } |
261 | 73.4k | return 0; |
262 | 127k | } |
263 | | |
264 | | /* |
265 | | * Determine the fit of one channel to another within a 4x4 block. This |
266 | | * is used to determine the best palette choices for 4-color encoding. |
267 | | */ |
268 | | static int leastsquares(const uint16_t *block_ptr, const BlockInfo *bi, |
269 | | channel_offset xchannel, channel_offset ychannel, |
270 | | int *slope, int *y_intercept, int *correlation_coef) |
271 | 417k | { |
272 | 417k | int sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0, |
273 | 417k | sumx_sq = 0, sumy_sq = 0, tmp, tmp2; |
274 | 417k | int i, j, count; |
275 | 417k | uint8_t x, y; |
276 | | |
277 | 417k | count = bi->block_height * bi->block_width; |
278 | | |
279 | 417k | if (count < 2) |
280 | 0 | return -1; |
281 | | |
282 | 1.86M | for (i = 0; i < bi->block_height; i++) { |
283 | 6.99M | for (j = 0; j < bi->block_width; j++) { |
284 | 5.54M | x = GET_CHAN(block_ptr[j], xchannel); |
285 | 5.54M | y = GET_CHAN(block_ptr[j], ychannel); |
286 | 5.54M | sumx += x; |
287 | 5.54M | sumy += y; |
288 | 5.54M | sumx2 += x * x; |
289 | 5.54M | sumy2 += y * y; |
290 | 5.54M | sumxy += x * y; |
291 | 5.54M | } |
292 | 1.44M | block_ptr += bi->rowstride; |
293 | 1.44M | } |
294 | | |
295 | 417k | sumx_sq = sumx * sumx; |
296 | 417k | tmp = (count * sumx2 - sumx_sq); |
297 | | |
298 | | // guard against div/0 |
299 | 417k | if (tmp == 0) |
300 | 0 | return -2; |
301 | | |
302 | 417k | sumy_sq = sumy * sumy; |
303 | | |
304 | 417k | *slope = (sumx * sumy - sumxy) / tmp; |
305 | 417k | *y_intercept = (sumy - (*slope) * sumx) / count; |
306 | | |
307 | 417k | tmp2 = count * sumy2 - sumy_sq; |
308 | 417k | if (tmp2 == 0) { |
309 | 18.9k | *correlation_coef = 0; |
310 | 398k | } else { |
311 | 398k | *correlation_coef = (count * sumxy - sumx * sumy) / |
312 | 398k | ff_sqrt((unsigned)tmp * tmp2); |
313 | 398k | } |
314 | | |
315 | 417k | return 0; // success |
316 | 417k | } |
317 | | |
318 | | /* |
319 | | * Determine the amount of error in the leastsquares fit. |
320 | | */ |
321 | | static int calc_lsq_max_fit_error(const uint16_t *block_ptr, const BlockInfo *bi, |
322 | | int min, int max, int tmp_min, int tmp_max, |
323 | | channel_offset xchannel, channel_offset ychannel) |
324 | 720k | { |
325 | 720k | int i, j, x, y; |
326 | 720k | int err; |
327 | 720k | int max_err = 0; |
328 | | |
329 | 3.22M | for (i = 0; i < bi->block_height; i++) { |
330 | 12.0M | for (j = 0; j < bi->block_width; j++) { |
331 | 9.57M | int x_inc, lin_y, lin_x; |
332 | 9.57M | x = GET_CHAN(block_ptr[j], xchannel); |
333 | 9.57M | y = GET_CHAN(block_ptr[j], ychannel); |
334 | | |
335 | | /* calculate x_inc as the 4-color index (0..3) */ |
336 | 9.57M | x_inc = (x - min) * 3 / (max - min) + 1; |
337 | 9.57M | x_inc = FFMAX(FFMIN(3, x_inc), 0); |
338 | | |
339 | | /* calculate lin_y corresponding to x_inc */ |
340 | 9.57M | lin_y = tmp_min + (tmp_max - tmp_min) * x_inc / 3 + 1; |
341 | | |
342 | 9.57M | err = FFABS(lin_y - y); |
343 | 9.57M | if (err > max_err) |
344 | 1.23M | max_err = err; |
345 | | |
346 | | /* calculate lin_x corresponding to x_inc */ |
347 | 9.57M | lin_x = min + (max - min) * x_inc / 3 + 1; |
348 | | |
349 | 9.57M | err = FFABS(lin_x - x); |
350 | 9.57M | if (err > max_err) |
351 | 366k | max_err += err; |
352 | 9.57M | } |
353 | 2.50M | block_ptr += bi->rowstride; |
354 | 2.50M | } |
355 | | |
356 | 720k | return max_err; |
357 | 720k | } |
358 | | |
359 | | /* |
360 | | * Find the closest match to a color within the 4-color palette |
361 | | */ |
362 | | static int match_color(const uint16_t *color, uint8_t colors[4][3]) |
363 | 0 | { |
364 | 0 | int ret = 0; |
365 | 0 | int smallest_variance = INT_MAX; |
366 | 0 | uint8_t dithered_color[3]; |
367 | |
|
368 | 0 | for (int channel = 0; channel < 3; channel++) { |
369 | 0 | dithered_color[channel] = GET_CHAN(color[0], channel); |
370 | 0 | } |
371 | |
|
372 | 0 | for (int palette_entry = 0; palette_entry < 4; palette_entry++) { |
373 | 0 | int variance = diff_colors(dithered_color, colors[palette_entry]); |
374 | |
|
375 | 0 | if (variance < smallest_variance) { |
376 | 0 | smallest_variance = variance; |
377 | 0 | ret = palette_entry; |
378 | 0 | } |
379 | 0 | } |
380 | |
|
381 | 0 | return ret; |
382 | 0 | } |
383 | | |
384 | | /* |
385 | | * Encode a block using the 4-color opcode and palette. return number of |
386 | | * blocks encoded (until we implement multi-block 4 color runs this will |
387 | | * always be 1) |
388 | | */ |
389 | | static int encode_four_color_block(const uint8_t *min_color, const uint8_t *max_color, |
390 | | PutBitContext *pb, const uint16_t *block_ptr, const BlockInfo *bi) |
391 | 0 | { |
392 | 0 | const int y_size = FFMIN(4, bi->image_height - bi->row * 4); |
393 | 0 | const int x_size = FFMIN(4, bi->image_width - bi->col * 4); |
394 | 0 | uint8_t color4[4][3]; |
395 | 0 | uint16_t rounded_max, rounded_min; |
396 | 0 | int idx; |
397 | | |
398 | | // round min and max wider |
399 | 0 | rounded_min = rgb24_to_rgb555(min_color); |
400 | 0 | rounded_max = rgb24_to_rgb555(max_color); |
401 | | |
402 | | // put a and b colors |
403 | | // encode 4 colors = first 16 bit color with MSB zeroed and... |
404 | 0 | put_bits(pb, 16, rounded_max & ~0x8000); |
405 | | // ...second 16 bit color with MSB on. |
406 | 0 | put_bits(pb, 16, rounded_min | 0x8000); |
407 | |
|
408 | 0 | get_colors(min_color, max_color, color4); |
409 | |
|
410 | 0 | for (int y = 0; y < y_size; y++) { |
411 | 0 | for (int x = 0; x < x_size; x++) { |
412 | 0 | idx = match_color(&block_ptr[x], color4); |
413 | 0 | put_bits(pb, 2, idx); |
414 | 0 | } |
415 | |
|
416 | 0 | for (int x = x_size; x < 4; x++) |
417 | 0 | put_bits(pb, 2, idx); |
418 | 0 | block_ptr += bi->rowstride; |
419 | 0 | } |
420 | |
|
421 | 0 | for (int y = y_size; y < 4; y++) { |
422 | 0 | for (int x = 0; x < 4; x++) |
423 | 0 | put_bits(pb, 2, 0); |
424 | 0 | } |
425 | 0 | return 1; // num blocks encoded |
426 | 0 | } |
427 | | |
428 | | /* |
429 | | * Copy a 4x4 block from the current frame buffer to the previous frame buffer. |
430 | | */ |
431 | | static void update_block_in_prev_frame(const uint16_t *src_pixels, |
432 | | uint16_t *dest_pixels, |
433 | | const BlockInfo *bi, int block_counter) |
434 | 1.69M | { |
435 | 1.69M | const int y_size = FFMIN(4, bi->image_height - bi->row * 4); |
436 | 1.69M | const int x_size = FFMIN(4, bi->image_width - bi->col * 4) * 2; |
437 | | |
438 | 6.48M | for (int y = 0; y < y_size; y++) { |
439 | 4.79M | memcpy(dest_pixels, src_pixels, x_size); |
440 | 4.79M | dest_pixels += bi->prev_rowstride; |
441 | 4.79M | src_pixels += bi->rowstride; |
442 | 4.79M | } |
443 | 1.69M | } |
444 | | |
445 | | /* |
446 | | * update statistics for the specified block. If first_block, |
447 | | * it initializes the statistics. Otherwise it updates the statistics IF THIS |
448 | | * BLOCK IS SUITABLE TO CONTINUE A 1-COLOR RUN. That is, it checks whether |
449 | | * the range of colors (since the routine was called first_block != 0) are |
450 | | * all close enough intensities to be represented by a single color. |
451 | | |
452 | | * The routine returns 0 if this block is too different to be part of |
453 | | * the same run of 1-color blocks. The routine returns 1 if this |
454 | | * block can be part of the same 1-color block run. |
455 | | |
456 | | * If the routine returns 1, it also updates its arguments to include |
457 | | * the statistics of this block. Otherwise, the stats are unchanged |
458 | | * and don't include the current block. |
459 | | */ |
460 | | static int update_block_stats(RpzaContext *s, const BlockInfo *bi, const uint16_t *block, |
461 | | uint8_t min_color[3], uint8_t max_color[3], |
462 | | int *total_rgb, int *total_pixels, |
463 | | uint8_t avg_color[3], int first_block) |
464 | 1.70M | { |
465 | 1.70M | int x, y; |
466 | 1.70M | int is_in_range; |
467 | 1.70M | int total_pixels_blk; |
468 | 1.70M | int threshold; |
469 | | |
470 | 1.70M | uint8_t min_color_blk[3], max_color_blk[3]; |
471 | 1.70M | int total_rgb_blk[3]; |
472 | 1.70M | uint8_t avg_color_blk[3]; |
473 | | |
474 | 1.70M | if (first_block) { |
475 | 587k | min_color[0] = UINT8_MAX; |
476 | 587k | min_color[1] = UINT8_MAX; |
477 | 587k | min_color[2] = UINT8_MAX; |
478 | 587k | max_color[0] = 0; |
479 | 587k | max_color[1] = 0; |
480 | 587k | max_color[2] = 0; |
481 | 587k | total_rgb[0] = 0; |
482 | 587k | total_rgb[1] = 0; |
483 | 587k | total_rgb[2] = 0; |
484 | 587k | *total_pixels = 0; |
485 | 587k | threshold = s->start_one_color_thresh; |
486 | 1.11M | } else { |
487 | 1.11M | threshold = s->continue_one_color_thresh; |
488 | 1.11M | } |
489 | | |
490 | | /* |
491 | | The *_blk variables will include the current block. |
492 | | Initialize them based on the blocks so far. |
493 | | */ |
494 | 1.70M | min_color_blk[0] = min_color[0]; |
495 | 1.70M | min_color_blk[1] = min_color[1]; |
496 | 1.70M | min_color_blk[2] = min_color[2]; |
497 | 1.70M | max_color_blk[0] = max_color[0]; |
498 | 1.70M | max_color_blk[1] = max_color[1]; |
499 | 1.70M | max_color_blk[2] = max_color[2]; |
500 | 1.70M | total_rgb_blk[0] = total_rgb[0]; |
501 | 1.70M | total_rgb_blk[1] = total_rgb[1]; |
502 | 1.70M | total_rgb_blk[2] = total_rgb[2]; |
503 | 1.70M | total_pixels_blk = *total_pixels + bi->block_height * bi->block_width; |
504 | | |
505 | | /* |
506 | | Update stats for this block's pixels |
507 | | */ |
508 | 6.52M | for (y = 0; y < bi->block_height; y++) { |
509 | 21.0M | for (x = 0; x < bi->block_width; x++) { |
510 | 16.2M | total_rgb_blk[0] += R(block[x]); |
511 | 16.2M | total_rgb_blk[1] += G(block[x]); |
512 | 16.2M | total_rgb_blk[2] += B(block[x]); |
513 | | |
514 | 16.2M | min_color_blk[0] = FFMIN(R(block[x]), min_color_blk[0]); |
515 | 16.2M | min_color_blk[1] = FFMIN(G(block[x]), min_color_blk[1]); |
516 | 16.2M | min_color_blk[2] = FFMIN(B(block[x]), min_color_blk[2]); |
517 | | |
518 | 16.2M | max_color_blk[0] = FFMAX(R(block[x]), max_color_blk[0]); |
519 | 16.2M | max_color_blk[1] = FFMAX(G(block[x]), max_color_blk[1]); |
520 | 16.2M | max_color_blk[2] = FFMAX(B(block[x]), max_color_blk[2]); |
521 | 16.2M | } |
522 | 4.82M | block += bi->rowstride; |
523 | 4.82M | } |
524 | | |
525 | | /* |
526 | | Calculate average color including current block. |
527 | | */ |
528 | 1.70M | avg_color_blk[0] = total_rgb_blk[0] / total_pixels_blk; |
529 | 1.70M | avg_color_blk[1] = total_rgb_blk[1] / total_pixels_blk; |
530 | 1.70M | avg_color_blk[2] = total_rgb_blk[2] / total_pixels_blk; |
531 | | |
532 | | /* |
533 | | Are all the pixels within threshold of the average color? |
534 | | */ |
535 | 1.70M | is_in_range = (max_color_blk[0] - avg_color_blk[0] <= threshold && |
536 | 1.50M | max_color_blk[1] - avg_color_blk[1] <= threshold && |
537 | 1.49M | max_color_blk[2] - avg_color_blk[2] <= threshold && |
538 | 1.48M | avg_color_blk[0] - min_color_blk[0] <= threshold && |
539 | 1.48M | avg_color_blk[1] - min_color_blk[1] <= threshold && |
540 | 1.48M | avg_color_blk[2] - min_color_blk[2] <= threshold); |
541 | | |
542 | 1.70M | if (is_in_range) { |
543 | | /* |
544 | | Set the output variables to include this block. |
545 | | */ |
546 | 1.48M | min_color[0] = min_color_blk[0]; |
547 | 1.48M | min_color[1] = min_color_blk[1]; |
548 | 1.48M | min_color[2] = min_color_blk[2]; |
549 | 1.48M | max_color[0] = max_color_blk[0]; |
550 | 1.48M | max_color[1] = max_color_blk[1]; |
551 | 1.48M | max_color[2] = max_color_blk[2]; |
552 | 1.48M | total_rgb[0] = total_rgb_blk[0]; |
553 | 1.48M | total_rgb[1] = total_rgb_blk[1]; |
554 | 1.48M | total_rgb[2] = total_rgb_blk[2]; |
555 | 1.48M | *total_pixels = total_pixels_blk; |
556 | 1.48M | avg_color[0] = avg_color_blk[0]; |
557 | 1.48M | avg_color[1] = avg_color_blk[1]; |
558 | 1.48M | avg_color[2] = avg_color_blk[2]; |
559 | 1.48M | } |
560 | | |
561 | 1.70M | return is_in_range; |
562 | 1.70M | } |
563 | | |
564 | | static void rpza_encode_stream(RpzaContext *s, const AVFrame *pict) |
565 | 9.01k | { |
566 | 9.01k | BlockInfo bi; |
567 | 9.01k | int block_counter = 0; |
568 | 9.01k | int n_blocks; |
569 | 9.01k | int total_blocks; |
570 | 9.01k | int prev_block_offset; |
571 | 9.01k | int block_offset = 0; |
572 | 9.01k | int pblock_offset = 0; |
573 | 9.01k | uint8_t min = 0, max = 0; |
574 | 9.01k | channel_offset chan; |
575 | 9.01k | int i; |
576 | 9.01k | int tmp_min, tmp_max; |
577 | 9.01k | int total_rgb[3]; |
578 | 9.01k | uint8_t avg_color[3]; |
579 | 9.01k | int pixel_count; |
580 | 9.01k | uint8_t min_color[3], max_color[3]; |
581 | 9.01k | int slope, y_intercept, correlation_coef; |
582 | 9.01k | const uint16_t *src_pixels = (const uint16_t *)pict->data[0]; |
583 | 9.01k | uint16_t *prev_pixels = (uint16_t *)s->prev_frame->data[0]; |
584 | | |
585 | | /* Number of 4x4 blocks in frame. */ |
586 | 9.01k | total_blocks = ((s->frame_width + 3) / 4) * ((s->frame_height + 3) / 4); |
587 | | |
588 | 9.01k | bi.image_width = s->frame_width; |
589 | 9.01k | bi.image_height = s->frame_height; |
590 | 9.01k | bi.rowstride = pict->linesize[0] / 2; |
591 | 9.01k | bi.prev_rowstride = s->prev_frame->linesize[0] / 2; |
592 | | |
593 | 9.01k | bi.blocks_per_row = (s->frame_width + 3) / 4; |
594 | | |
595 | 617k | while (block_counter < total_blocks) { |
596 | | // SKIP CHECK |
597 | | // make sure we have a valid previous frame and we're not writing |
598 | | // a key frame |
599 | 608k | if (!s->first_frame) { |
600 | 74.9k | n_blocks = 0; |
601 | 74.9k | prev_block_offset = 0; |
602 | | |
603 | 148k | while (n_blocks < 32 && block_counter + n_blocks < total_blocks) { |
604 | 144k | block_offset = get_block_info(&bi, block_counter + n_blocks, 0); |
605 | 144k | pblock_offset = get_block_info(&bi, block_counter + n_blocks, 1); |
606 | | |
607 | | // multi-block opcodes cannot span multiple rows. |
608 | | // If we're starting a new row, break out and write the opcode |
609 | | /* TODO: Should eventually use bi.row here to determine when a |
610 | | row break occurs, but that is currently breaking the |
611 | | quicktime player. This is probably due to a bug in the |
612 | | way I'm calculating the current row. |
613 | | */ |
614 | 144k | if (prev_block_offset && block_offset - prev_block_offset > 12) { |
615 | 16.9k | break; |
616 | 16.9k | } |
617 | | |
618 | 127k | prev_block_offset = block_offset; |
619 | | |
620 | 127k | if (compare_blocks(&prev_pixels[pblock_offset], |
621 | 127k | &src_pixels[block_offset], &bi, s->skip_frame_thresh) != 0) { |
622 | | // write out skippable blocks |
623 | 53.8k | if (n_blocks) { |
624 | | |
625 | | // write skip opcode |
626 | 2.83k | put_bits(&s->pb, 8, 0x80 | (n_blocks - 1)); |
627 | 2.83k | block_counter += n_blocks; |
628 | | |
629 | 2.83k | goto post_skip; |
630 | 2.83k | } |
631 | 51.0k | break; |
632 | 53.8k | } |
633 | | |
634 | | /* |
635 | | * NOTE: we don't update skipped blocks in the previous frame buffer |
636 | | * since skipped needs always to be compared against the first skipped |
637 | | * block to avoid artifacts during gradual fade in/outs. |
638 | | */ |
639 | | |
640 | | // update_block_in_prev_frame(&src_pixels[block_offset], |
641 | | // &prev_pixels[pblock_offset], &bi, block_counter + n_blocks); |
642 | | |
643 | 73.4k | n_blocks++; |
644 | 73.4k | } |
645 | | |
646 | | // we're either at the end of the frame or we've reached the maximum |
647 | | // of 32 blocks in a run. Write out the run. |
648 | 72.0k | if (n_blocks) { |
649 | | // write skip opcode |
650 | 21.0k | put_bits(&s->pb, 8, 0x80 | (n_blocks - 1)); |
651 | 21.0k | block_counter += n_blocks; |
652 | | |
653 | 21.0k | continue; |
654 | 21.0k | } |
655 | | |
656 | 533k | } else { |
657 | 533k | block_offset = get_block_info(&bi, block_counter, 0); |
658 | 533k | pblock_offset = get_block_info(&bi, block_counter, 1); |
659 | 533k | } |
660 | 587k | post_skip : |
661 | | |
662 | | // ONE COLOR CHECK |
663 | 587k | if (update_block_stats(s, &bi, &src_pixels[block_offset], |
664 | 587k | min_color, max_color, |
665 | 587k | total_rgb, &pixel_count, avg_color, 1)) { |
666 | 378k | prev_block_offset = block_offset; |
667 | | |
668 | 378k | n_blocks = 1; |
669 | | |
670 | | /* update this block in the previous frame buffer */ |
671 | 378k | update_block_in_prev_frame(&src_pixels[block_offset], |
672 | 378k | &prev_pixels[pblock_offset], &bi, block_counter + n_blocks); |
673 | | |
674 | | // check for subsequent blocks with the same color |
675 | 1.48M | while (n_blocks < 32 && block_counter + n_blocks < total_blocks) { |
676 | 1.45M | block_offset = get_block_info(&bi, block_counter + n_blocks, 0); |
677 | 1.45M | pblock_offset = get_block_info(&bi, block_counter + n_blocks, 1); |
678 | | |
679 | | // multi-block opcodes cannot span multiple rows. |
680 | | // If we've hit end of a row, break out and write the opcode |
681 | 1.45M | if (block_offset - prev_block_offset > 12) { |
682 | 336k | break; |
683 | 336k | } |
684 | | |
685 | 1.11M | if (!update_block_stats(s, &bi, &src_pixels[block_offset], |
686 | 1.11M | min_color, max_color, |
687 | 1.11M | total_rgb, &pixel_count, avg_color, 0)) { |
688 | 8.49k | break; |
689 | 8.49k | } |
690 | | |
691 | 1.10M | prev_block_offset = block_offset; |
692 | | |
693 | | /* update this block in the previous frame buffer */ |
694 | 1.10M | update_block_in_prev_frame(&src_pixels[block_offset], |
695 | 1.10M | &prev_pixels[pblock_offset], &bi, block_counter + n_blocks); |
696 | | |
697 | 1.10M | n_blocks++; |
698 | 1.10M | } |
699 | | |
700 | | // write one color opcode. |
701 | 378k | put_bits(&s->pb, 8, 0xa0 | (n_blocks - 1)); |
702 | | // write color to encode. |
703 | 378k | put_bits(&s->pb, 16, rgb24_to_rgb555(avg_color)); |
704 | | // skip past the blocks we've just encoded. |
705 | 378k | block_counter += n_blocks; |
706 | 378k | } else { // FOUR COLOR CHECK |
707 | 208k | int err = 0; |
708 | | |
709 | | // get max component diff for block |
710 | 208k | get_max_component_diff(&bi, &src_pixels[block_offset], &min, &max, &chan); |
711 | | |
712 | 208k | min_color[0] = 0; |
713 | 208k | max_color[0] = 0; |
714 | 208k | min_color[1] = 0; |
715 | 208k | max_color[1] = 0; |
716 | 208k | min_color[2] = 0; |
717 | 208k | max_color[2] = 0; |
718 | | |
719 | | // run least squares against other two components |
720 | 834k | for (i = 0; i < 3; i++) { |
721 | 625k | if (i == chan) { |
722 | 208k | min_color[i] = min; |
723 | 208k | max_color[i] = max; |
724 | 208k | continue; |
725 | 208k | } |
726 | | |
727 | 417k | slope = y_intercept = correlation_coef = 0; |
728 | | |
729 | 417k | if (leastsquares(&src_pixels[block_offset], &bi, chan, i, |
730 | 417k | &slope, &y_intercept, &correlation_coef)) { |
731 | 0 | min_color[i] = GET_CHAN(src_pixels[block_offset], i); |
732 | 0 | max_color[i] = GET_CHAN(src_pixels[block_offset], i); |
733 | 417k | } else { |
734 | 417k | tmp_min = 1 + min * slope + y_intercept; |
735 | 417k | tmp_max = 1 + max * slope + y_intercept; |
736 | | |
737 | 417k | av_assert0(tmp_min <= tmp_max); |
738 | | // clamp min and max color values |
739 | 417k | tmp_min = av_clip_uint8(tmp_min); |
740 | 417k | tmp_max = av_clip_uint8(tmp_max); |
741 | | |
742 | 417k | err = FFMAX(calc_lsq_max_fit_error(&src_pixels[block_offset], &bi, |
743 | 417k | min, max, tmp_min, tmp_max, chan, i), err); |
744 | | |
745 | 417k | min_color[i] = tmp_min; |
746 | 417k | max_color[i] = tmp_max; |
747 | 417k | } |
748 | 417k | } |
749 | | |
750 | 208k | if (err > s->sixteen_color_thresh) { // DO SIXTEEN COLOR BLOCK |
751 | 208k | const uint16_t *row_ptr; |
752 | 208k | int y_size, x_size, rgb555; |
753 | | |
754 | 208k | block_offset = get_block_info(&bi, block_counter, 0); |
755 | 208k | pblock_offset = get_block_info(&bi, block_counter, 1); |
756 | | |
757 | 208k | row_ptr = &src_pixels[block_offset]; |
758 | 208k | y_size = FFMIN(4, bi.image_height - bi.row * 4); |
759 | 208k | x_size = FFMIN(4, bi.image_width - bi.col * 4); |
760 | | |
761 | 933k | for (int y = 0; y < y_size; y++) { |
762 | 3.49M | for (int x = 0; x < x_size; x++) { |
763 | 2.77M | rgb555 = row_ptr[x] & ~0x8000; |
764 | | |
765 | 2.77M | put_bits(&s->pb, 16, rgb555); |
766 | 2.77M | } |
767 | 853k | for (int x = x_size; x < 4; x++) |
768 | 128k | put_bits(&s->pb, 16, 0); |
769 | | |
770 | 724k | row_ptr += bi.rowstride; |
771 | 724k | } |
772 | | |
773 | 317k | for (int y = y_size; y < 4; y++) { |
774 | 546k | for (int x = 0; x < 4; x++) |
775 | 437k | put_bits(&s->pb, 16, 0); |
776 | 109k | } |
777 | | |
778 | 208k | block_counter++; |
779 | 208k | } else { // FOUR COLOR BLOCK |
780 | 0 | block_counter += encode_four_color_block(min_color, max_color, |
781 | 0 | &s->pb, &src_pixels[block_offset], &bi); |
782 | 0 | } |
783 | | |
784 | | /* update this block in the previous frame buffer */ |
785 | 208k | update_block_in_prev_frame(&src_pixels[block_offset], |
786 | 208k | &prev_pixels[pblock_offset], &bi, block_counter); |
787 | 208k | } |
788 | 587k | } |
789 | 9.01k | } |
790 | | |
791 | | static av_cold int rpza_encode_init(AVCodecContext *avctx) |
792 | 476 | { |
793 | 476 | RpzaContext *s = avctx->priv_data; |
794 | | |
795 | 476 | s->frame_width = avctx->width; |
796 | 476 | s->frame_height = avctx->height; |
797 | | |
798 | 476 | s->prev_frame = av_frame_alloc(); |
799 | 476 | if (!s->prev_frame) |
800 | 0 | return AVERROR(ENOMEM); |
801 | | |
802 | 476 | return 0; |
803 | 476 | } |
804 | | |
805 | | static int rpza_encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
806 | | const AVFrame *pict, int *got_packet) |
807 | 9.01k | { |
808 | 9.01k | RpzaContext *s = avctx->priv_data; |
809 | 9.01k | uint8_t *buf; |
810 | 9.01k | int ret = ff_alloc_packet(avctx, pkt, 4LL + 6LL * FFMAX(avctx->height, 4) * FFMAX(avctx->width, 4)); |
811 | | |
812 | 9.01k | if (ret < 0) |
813 | 0 | return ret; |
814 | | |
815 | 9.01k | init_put_bits(&s->pb, pkt->data, pkt->size); |
816 | | |
817 | | // skip 4 byte header, write it later once the size of the chunk is known |
818 | 9.01k | put_bits32(&s->pb, 0x00); |
819 | | |
820 | 9.01k | if (!s->prev_frame->data[0]) { |
821 | 476 | s->first_frame = 1; |
822 | 476 | s->prev_frame->format = pict->format; |
823 | 476 | s->prev_frame->width = pict->width; |
824 | 476 | s->prev_frame->height = pict->height; |
825 | 476 | ret = av_frame_get_buffer(s->prev_frame, 0); |
826 | 476 | if (ret < 0) |
827 | 0 | return ret; |
828 | 8.54k | } else { |
829 | 8.54k | s->first_frame = 0; |
830 | 8.54k | } |
831 | | |
832 | 9.01k | rpza_encode_stream(s, pict); |
833 | | |
834 | 9.01k | flush_put_bits(&s->pb); |
835 | | |
836 | 9.01k | av_shrink_packet(pkt, put_bytes_output(&s->pb)); |
837 | 9.01k | buf = pkt->data; |
838 | | |
839 | | // write header opcode |
840 | 9.01k | buf[0] = 0xe1; // chunk opcode |
841 | | |
842 | | // write chunk length |
843 | 9.01k | AV_WB24(buf + 1, pkt->size); |
844 | | |
845 | 9.01k | *got_packet = 1; |
846 | | |
847 | 9.01k | return 0; |
848 | 9.01k | } |
849 | | |
850 | | static av_cold int rpza_encode_end(AVCodecContext *avctx) |
851 | 476 | { |
852 | 476 | RpzaContext *s = (RpzaContext *)avctx->priv_data; |
853 | | |
854 | 476 | av_frame_free(&s->prev_frame); |
855 | | |
856 | 476 | return 0; |
857 | 476 | } |
858 | | |
859 | | #define OFFSET(x) offsetof(RpzaContext, x) |
860 | | #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM |
861 | | static const AVOption options[] = { |
862 | | { "skip_frame_thresh", NULL, OFFSET(skip_frame_thresh), AV_OPT_TYPE_INT, {.i64=1}, 0, 24, VE}, |
863 | | { "start_one_color_thresh", NULL, OFFSET(start_one_color_thresh), AV_OPT_TYPE_INT, {.i64=1}, 0, 24, VE}, |
864 | | { "continue_one_color_thresh", NULL, OFFSET(continue_one_color_thresh), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, VE}, |
865 | | { "sixteen_color_thresh", NULL, OFFSET(sixteen_color_thresh), AV_OPT_TYPE_INT, {.i64=1}, 0, 24, VE}, |
866 | | { NULL }, |
867 | | }; |
868 | | |
869 | | static const AVClass rpza_class = { |
870 | | .class_name = "rpza", |
871 | | .item_name = av_default_item_name, |
872 | | .option = options, |
873 | | .version = LIBAVUTIL_VERSION_INT, |
874 | | }; |
875 | | |
876 | | const FFCodec ff_rpza_encoder = { |
877 | | .p.name = "rpza", |
878 | | CODEC_LONG_NAME("QuickTime video (RPZA)"), |
879 | | .p.type = AVMEDIA_TYPE_VIDEO, |
880 | | .p.id = AV_CODEC_ID_RPZA, |
881 | | .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, |
882 | | .priv_data_size = sizeof(RpzaContext), |
883 | | .p.priv_class = &rpza_class, |
884 | | .init = rpza_encode_init, |
885 | | FF_CODEC_ENCODE_CB(rpza_encode_frame), |
886 | | .close = rpza_encode_end, |
887 | | CODEC_PIXFMTS(AV_PIX_FMT_RGB555), |
888 | | }; |