Line | Branch | Exec | 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 | #define SQR(x) ((x) * (x)) | ||
67 | |||
68 | /* 15 bit components */ | ||
69 | #define GET_CHAN(color, chan) (((color) >> ((chan) * 5) & 0x1F)) | ||
70 | #define R(color) GET_CHAN(color, RED) | ||
71 | #define G(color) GET_CHAN(color, GREEN) | ||
72 | #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 | { | ||
91 | uint8_t step; | ||
92 | |||
93 | ✗ | color4[0][0] = min[0]; | |
94 | ✗ | color4[0][1] = min[1]; | |
95 | ✗ | color4[0][2] = min[2]; | |
96 | |||
97 | ✗ | color4[3][0] = max[0]; | |
98 | ✗ | color4[3][1] = max[1]; | |
99 | ✗ | color4[3][2] = max[2]; | |
100 | |||
101 | // red components | ||
102 | ✗ | step = (color4[3][0] - color4[0][0] + 1) / 3; | |
103 | ✗ | color4[1][0] = color4[0][0] + step; | |
104 | ✗ | color4[2][0] = color4[3][0] - step; | |
105 | |||
106 | // green components | ||
107 | ✗ | step = (color4[3][1] - color4[0][1] + 1) / 3; | |
108 | ✗ | color4[1][1] = color4[0][1] + step; | |
109 | ✗ | color4[2][1] = color4[3][1] - step; | |
110 | |||
111 | // blue components | ||
112 | ✗ | step = (color4[3][2] - color4[0][2] + 1) / 3; | |
113 | ✗ | color4[1][2] = color4[0][2] + step; | |
114 | ✗ | color4[2][2] = color4[3][2] - step; | |
115 | ✗ | } | |
116 | |||
117 | /* Fill BlockInfo struct with information about a 4x4 block of the image */ | ||
118 | 3808762 | static int get_block_info(BlockInfo *bi, int block, int prev_frame) | |
119 | { | ||
120 | 3808762 | bi->row = block / bi->blocks_per_row; | |
121 | 3808762 | bi->col = block % bi->blocks_per_row; | |
122 | |||
123 | // test for right edge block | ||
124 |
4/4✓ Branch 0 taken 45240 times.
✓ Branch 1 taken 3763522 times.
✓ Branch 2 taken 1800 times.
✓ Branch 3 taken 43440 times.
|
3808762 | if (bi->col == bi->blocks_per_row - 1 && (bi->image_width % 4) != 0) { |
125 | 1800 | bi->block_width = bi->image_width % 4; | |
126 | } else { | ||
127 | 3806962 | bi->block_width = 4; | |
128 | } | ||
129 | |||
130 | // test for bottom edge block | ||
131 |
3/4✓ Branch 0 taken 1800 times.
✓ Branch 1 taken 3806962 times.
✓ Branch 2 taken 1800 times.
✗ Branch 3 not taken.
|
3808762 | if (bi->row == (bi->image_height / 4) && (bi->image_height % 4) != 0) { |
132 | 1800 | bi->block_height = bi->image_height % 4; | |
133 | } else { | ||
134 | 3806962 | bi->block_height = 4; | |
135 | } | ||
136 | |||
137 |
4/4✓ Branch 0 taken 3807990 times.
✓ Branch 1 taken 772 times.
✓ Branch 2 taken 1903995 times.
✓ Branch 3 taken 1903995 times.
|
3808762 | return block ? (bi->col * 4) + (bi->row * (prev_frame ? bi->prev_rowstride : bi->rowstride) * 4) : 0; |
138 | } | ||
139 | |||
140 | 98843 | static uint16_t rgb24_to_rgb555(const uint8_t *rgb24) | |
141 | { | ||
142 | 98843 | uint16_t rgb555 = 0; | |
143 | uint32_t r, g, b; | ||
144 | |||
145 | 98843 | r = rgb24[0]; | |
146 | 98843 | g = rgb24[1]; | |
147 | 98843 | b = rgb24[2]; | |
148 | |||
149 | 98843 | rgb555 |= (r << 10); | |
150 | 98843 | rgb555 |= (g << 5); | |
151 | 98843 | rgb555 |= (b << 0); | |
152 | |||
153 | 98843 | return rgb555; | |
154 | } | ||
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 | { | ||
161 | int tot; | ||
162 | |||
163 | ✗ | tot = SQR(colorA[0] - colorB[0]); | |
164 | ✗ | tot += SQR(colorA[1] - colorB[1]); | |
165 | ✗ | tot += SQR(colorA[2] - colorB[2]); | |
166 | |||
167 | ✗ | return tot; | |
168 | } | ||
169 | |||
170 | /* | ||
171 | * Returns the maximum channel difference | ||
172 | */ | ||
173 | 1275563 | static int max_component_diff(const uint16_t *colorA, const uint16_t *colorB) | |
174 | { | ||
175 | 1275563 | int diff, max = 0; | |
176 | |||
177 | 1275563 | diff = FFABS(R(colorA[0]) - R(colorB[0])); | |
178 |
2/2✓ Branch 0 taken 674458 times.
✓ Branch 1 taken 601105 times.
|
1275563 | if (diff > max) { |
179 | 674458 | max = diff; | |
180 | } | ||
181 | 1275563 | diff = FFABS(G(colorA[0]) - G(colorB[0])); | |
182 |
2/2✓ Branch 0 taken 454108 times.
✓ Branch 1 taken 821455 times.
|
1275563 | if (diff > max) { |
183 | 454108 | max = diff; | |
184 | } | ||
185 | 1275563 | diff = FFABS(B(colorA[0]) - B(colorB[0])); | |
186 |
2/2✓ Branch 0 taken 187367 times.
✓ Branch 1 taken 1088196 times.
|
1275563 | if (diff > max) { |
187 | 187367 | max = diff; | |
188 | } | ||
189 | 1275563 | return max; | |
190 | } | ||
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 | 851101 | 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 | { | ||
200 | int x, y; | ||
201 | uint8_t min_r, max_r, min_g, max_g, min_b, max_b; | ||
202 | uint8_t r, g, b; | ||
203 | |||
204 | // fix warning about uninitialized vars | ||
205 | 851101 | min_r = min_g = min_b = UINT8_MAX; | |
206 | 851101 | max_r = max_g = max_b = 0; | |
207 | |||
208 | // loop thru and compare pixels | ||
209 |
2/2✓ Branch 0 taken 3403504 times.
✓ Branch 1 taken 851101 times.
|
4254605 | for (y = 0; y < bi->block_height; y++) { |
210 |
2/2✓ Branch 0 taken 13610616 times.
✓ Branch 1 taken 3403504 times.
|
17014120 | for (x = 0; x < bi->block_width; x++) { |
211 | // TODO: optimize | ||
212 | 13610616 | min_r = FFMIN(R(block_ptr[x]), min_r); | |
213 | 13610616 | min_g = FFMIN(G(block_ptr[x]), min_g); | |
214 | 13610616 | min_b = FFMIN(B(block_ptr[x]), min_b); | |
215 | |||
216 | 13610616 | max_r = FFMAX(R(block_ptr[x]), max_r); | |
217 | 13610616 | max_g = FFMAX(G(block_ptr[x]), max_g); | |
218 | 13610616 | max_b = FFMAX(B(block_ptr[x]), max_b); | |
219 | } | ||
220 | 3403504 | block_ptr += bi->rowstride; | |
221 | } | ||
222 | |||
223 | 851101 | r = max_r - min_r; | |
224 | 851101 | g = max_g - min_g; | |
225 | 851101 | b = max_b - min_b; | |
226 | |||
227 |
4/4✓ Branch 0 taken 206436 times.
✓ Branch 1 taken 644665 times.
✓ Branch 2 taken 170355 times.
✓ Branch 3 taken 36081 times.
|
851101 | if (r > g && r > b) { |
228 | 170355 | *max = max_r; | |
229 | 170355 | *min = min_r; | |
230 | 170355 | *chan = RED; | |
231 |
3/4✓ Branch 0 taken 404520 times.
✓ Branch 1 taken 276226 times.
✓ Branch 2 taken 404520 times.
✗ Branch 3 not taken.
|
680746 | } else if (g > b && g >= r) { |
232 | 404520 | *max = max_g; | |
233 | 404520 | *min = min_g; | |
234 | 404520 | *chan = GREEN; | |
235 | } else { | ||
236 | 276226 | *max = max_b; | |
237 | 276226 | *min = min_b; | |
238 | 276226 | *chan = BLUE; | |
239 | } | ||
240 | 851101 | } | |
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 | 934633 | static int compare_blocks(const uint16_t *block1, const uint16_t *block2, | |
248 | const BlockInfo *bi, int thresh) | ||
249 | { | ||
250 | 934633 | int x, y, diff = 0; | |
251 |
2/2✓ Branch 0 taken 974644 times.
✓ Branch 1 taken 3682 times.
|
978326 | for (y = 0; y < bi->block_height; y++) { |
252 |
2/2✓ Branch 0 taken 1275563 times.
✓ Branch 1 taken 43693 times.
|
1319256 | for (x = 0; x < bi->block_width; x++) { |
253 | 1275563 | diff = max_component_diff(&block1[x], &block2[x]); | |
254 |
2/2✓ Branch 0 taken 930951 times.
✓ Branch 1 taken 344612 times.
|
1275563 | if (diff >= thresh) { |
255 | 930951 | return -1; | |
256 | } | ||
257 | } | ||
258 | 43693 | block1 += bi->prev_rowstride; | |
259 | 43693 | block2 += bi->rowstride; | |
260 | } | ||
261 | 3682 | return 0; | |
262 | } | ||
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 | 1702202 | 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 | { | ||
272 | 1702202 | int sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0, | |
273 | 1702202 | sumx_sq = 0, sumy_sq = 0, tmp, tmp2; | |
274 | int i, j, count; | ||
275 | uint8_t x, y; | ||
276 | |||
277 | 1702202 | count = bi->block_height * bi->block_width; | |
278 | |||
279 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1702202 times.
|
1702202 | if (count < 2) |
280 | ✗ | return -1; | |
281 | |||
282 |
2/2✓ Branch 0 taken 6807008 times.
✓ Branch 1 taken 1702202 times.
|
8509210 | for (i = 0; i < bi->block_height; i++) { |
283 |
2/2✓ Branch 0 taken 27221232 times.
✓ Branch 1 taken 6807008 times.
|
34028240 | for (j = 0; j < bi->block_width; j++) { |
284 | 27221232 | x = GET_CHAN(block_ptr[j], xchannel); | |
285 | 27221232 | y = GET_CHAN(block_ptr[j], ychannel); | |
286 | 27221232 | sumx += x; | |
287 | 27221232 | sumy += y; | |
288 | 27221232 | sumx2 += x * x; | |
289 | 27221232 | sumy2 += y * y; | |
290 | 27221232 | sumxy += x * y; | |
291 | } | ||
292 | 6807008 | block_ptr += bi->rowstride; | |
293 | } | ||
294 | |||
295 | 1702202 | sumx_sq = sumx * sumx; | |
296 | 1702202 | tmp = (count * sumx2 - sumx_sq); | |
297 | |||
298 | // guard against div/0 | ||
299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1702202 times.
|
1702202 | if (tmp == 0) |
300 | ✗ | return -2; | |
301 | |||
302 | 1702202 | sumy_sq = sumy * sumy; | |
303 | |||
304 | 1702202 | *slope = (sumx * sumy - sumxy) / tmp; | |
305 | 1702202 | *y_intercept = (sumy - (*slope) * sumx) / count; | |
306 | |||
307 | 1702202 | tmp2 = count * sumy2 - sumy_sq; | |
308 |
2/2✓ Branch 0 taken 42945 times.
✓ Branch 1 taken 1659257 times.
|
1702202 | if (tmp2 == 0) { |
309 | 42945 | *correlation_coef = 0; | |
310 | } else { | ||
311 | 1659257 | *correlation_coef = (count * sumxy - sumx * sumy) / | |
312 | 1659257 | ff_sqrt((unsigned)tmp * tmp2); | |
313 | } | ||
314 | |||
315 | 1702202 | return 0; // success | |
316 | } | ||
317 | |||
318 | /* | ||
319 | * Determine the amount of error in the leastsquares fit. | ||
320 | */ | ||
321 | 3030648 | 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 | { | ||
325 | int i, j, x, y; | ||
326 | int err; | ||
327 | 3030648 | int max_err = 0; | |
328 | |||
329 |
2/2✓ Branch 0 taken 12119460 times.
✓ Branch 1 taken 3030648 times.
|
15150108 | for (i = 0; i < bi->block_height; i++) { |
330 |
2/2✓ Branch 0 taken 48465784 times.
✓ Branch 1 taken 12119460 times.
|
60585244 | for (j = 0; j < bi->block_width; j++) { |
331 | int x_inc, lin_y, lin_x; | ||
332 | 48465784 | x = GET_CHAN(block_ptr[j], xchannel); | |
333 | 48465784 | y = GET_CHAN(block_ptr[j], ychannel); | |
334 | |||
335 | /* calculate x_inc as the 4-color index (0..3) */ | ||
336 | 48465784 | x_inc = (x - min) * 3 / (max - min) + 1; | |
337 | 48465784 | x_inc = FFMAX(FFMIN(3, x_inc), 0); | |
338 | |||
339 | /* calculate lin_y corresponding to x_inc */ | ||
340 | 48465784 | lin_y = tmp_min + (tmp_max - tmp_min) * x_inc / 3 + 1; | |
341 | |||
342 | 48465784 | err = FFABS(lin_y - y); | |
343 |
2/2✓ Branch 0 taken 8561490 times.
✓ Branch 1 taken 39904294 times.
|
48465784 | if (err > max_err) |
344 | 8561490 | max_err = err; | |
345 | |||
346 | /* calculate lin_x corresponding to x_inc */ | ||
347 | 48465784 | lin_x = min + (max - min) * x_inc / 3 + 1; | |
348 | |||
349 | 48465784 | err = FFABS(lin_x - x); | |
350 |
2/2✓ Branch 0 taken 238381 times.
✓ Branch 1 taken 48227403 times.
|
48465784 | if (err > max_err) |
351 | 238381 | max_err += err; | |
352 | } | ||
353 | 12119460 | block_ptr += bi->rowstride; | |
354 | } | ||
355 | |||
356 | 3030648 | return max_err; | |
357 | } | ||
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 | { | ||
364 | ✗ | int ret = 0; | |
365 | ✗ | int smallest_variance = INT_MAX; | |
366 | uint8_t dithered_color[3]; | ||
367 | |||
368 | ✗ | for (int channel = 0; channel < 3; channel++) { | |
369 | ✗ | dithered_color[channel] = GET_CHAN(color[0], channel); | |
370 | } | ||
371 | |||
372 | ✗ | for (int palette_entry = 0; palette_entry < 4; palette_entry++) { | |
373 | ✗ | int variance = diff_colors(dithered_color, colors[palette_entry]); | |
374 | |||
375 | ✗ | if (variance < smallest_variance) { | |
376 | ✗ | smallest_variance = variance; | |
377 | ✗ | ret = palette_entry; | |
378 | } | ||
379 | } | ||
380 | |||
381 | ✗ | return ret; | |
382 | } | ||
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 | { | ||
392 | ✗ | const int y_size = FFMIN(4, bi->image_height - bi->row * 4); | |
393 | ✗ | const int x_size = FFMIN(4, bi->image_width - bi->col * 4); | |
394 | uint8_t color4[4][3]; | ||
395 | uint16_t rounded_max, rounded_min; | ||
396 | int idx; | ||
397 | |||
398 | // round min and max wider | ||
399 | ✗ | rounded_min = rgb24_to_rgb555(min_color); | |
400 | ✗ | 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 | ✗ | put_bits(pb, 16, rounded_max & ~0x8000); | |
405 | // ...second 16 bit color with MSB on. | ||
406 | ✗ | put_bits(pb, 16, rounded_min | 0x8000); | |
407 | |||
408 | ✗ | get_colors(min_color, max_color, color4); | |
409 | |||
410 | ✗ | for (int y = 0; y < y_size; y++) { | |
411 | ✗ | for (int x = 0; x < x_size; x++) { | |
412 | ✗ | idx = match_color(&block_ptr[x], color4); | |
413 | ✗ | put_bits(pb, 2, idx); | |
414 | } | ||
415 | |||
416 | ✗ | for (int x = x_size; x < 4; x++) | |
417 | ✗ | put_bits(pb, 2, idx); | |
418 | ✗ | block_ptr += bi->rowstride; | |
419 | } | ||
420 | |||
421 | ✗ | for (int y = y_size; y < 4; y++) { | |
422 | ✗ | for (int x = 0; x < 4; x++) | |
423 | ✗ | put_bits(pb, 2, 0); | |
424 | } | ||
425 | ✗ | return 1; // num blocks encoded | |
426 | } | ||
427 | |||
428 | /* | ||
429 | * Copy a 4x4 block from the current frame buffer to the previous frame buffer. | ||
430 | */ | ||
431 | 950768 | 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 | { | ||
435 | 950768 | const int y_size = FFMIN(4, bi->image_height - bi->row * 4); | |
436 | 950768 | const int x_size = FFMIN(4, bi->image_width - bi->col * 4) * 2; | |
437 | |||
438 |
2/2✓ Branch 0 taken 3802172 times.
✓ Branch 1 taken 950768 times.
|
4752940 | for (int y = 0; y < y_size; y++) { |
439 | 3802172 | memcpy(dest_pixels, src_pixels, x_size); | |
440 | 3802172 | dest_pixels += bi->prev_rowstride; | |
441 | 3802172 | src_pixels += bi->rowstride; | |
442 | } | ||
443 | 950768 | } | |
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 | 1048672 | 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 | { | ||
465 | int x, y; | ||
466 | int is_in_range; | ||
467 | int total_pixels_blk; | ||
468 | int threshold; | ||
469 | |||
470 | uint8_t min_color_blk[3], max_color_blk[3]; | ||
471 | int total_rgb_blk[3]; | ||
472 | uint8_t avg_color_blk[3]; | ||
473 | |||
474 |
2/2✓ Branch 0 taken 949944 times.
✓ Branch 1 taken 98728 times.
|
1048672 | if (first_block) { |
475 | 949944 | min_color[0] = UINT8_MAX; | |
476 | 949944 | min_color[1] = UINT8_MAX; | |
477 | 949944 | min_color[2] = UINT8_MAX; | |
478 | 949944 | max_color[0] = 0; | |
479 | 949944 | max_color[1] = 0; | |
480 | 949944 | max_color[2] = 0; | |
481 | 949944 | total_rgb[0] = 0; | |
482 | 949944 | total_rgb[1] = 0; | |
483 | 949944 | total_rgb[2] = 0; | |
484 | 949944 | *total_pixels = 0; | |
485 | 949944 | threshold = s->start_one_color_thresh; | |
486 | } else { | ||
487 | 98728 | threshold = s->continue_one_color_thresh; | |
488 | } | ||
489 | |||
490 | /* | ||
491 | The *_blk variables will include the current block. | ||
492 | Initialize them based on the blocks so far. | ||
493 | */ | ||
494 | 1048672 | min_color_blk[0] = min_color[0]; | |
495 | 1048672 | min_color_blk[1] = min_color[1]; | |
496 | 1048672 | min_color_blk[2] = min_color[2]; | |
497 | 1048672 | max_color_blk[0] = max_color[0]; | |
498 | 1048672 | max_color_blk[1] = max_color[1]; | |
499 | 1048672 | max_color_blk[2] = max_color[2]; | |
500 | 1048672 | total_rgb_blk[0] = total_rgb[0]; | |
501 | 1048672 | total_rgb_blk[1] = total_rgb[1]; | |
502 | 1048672 | total_rgb_blk[2] = total_rgb[2]; | |
503 | 1048672 | total_pixels_blk = *total_pixels + bi->block_height * bi->block_width; | |
504 | |||
505 | /* | ||
506 | Update stats for this block's pixels | ||
507 | */ | ||
508 |
2/2✓ Branch 0 taken 4193788 times.
✓ Branch 1 taken 1048672 times.
|
5242460 | for (y = 0; y < bi->block_height; y++) { |
509 |
2/2✓ Branch 0 taken 16771752 times.
✓ Branch 1 taken 4193788 times.
|
20965540 | for (x = 0; x < bi->block_width; x++) { |
510 | 16771752 | total_rgb_blk[0] += R(block[x]); | |
511 | 16771752 | total_rgb_blk[1] += G(block[x]); | |
512 | 16771752 | total_rgb_blk[2] += B(block[x]); | |
513 | |||
514 | 16771752 | min_color_blk[0] = FFMIN(R(block[x]), min_color_blk[0]); | |
515 | 16771752 | min_color_blk[1] = FFMIN(G(block[x]), min_color_blk[1]); | |
516 | 16771752 | min_color_blk[2] = FFMIN(B(block[x]), min_color_blk[2]); | |
517 | |||
518 | 16771752 | max_color_blk[0] = FFMAX(R(block[x]), max_color_blk[0]); | |
519 | 16771752 | max_color_blk[1] = FFMAX(G(block[x]), max_color_blk[1]); | |
520 | 16771752 | max_color_blk[2] = FFMAX(B(block[x]), max_color_blk[2]); | |
521 | } | ||
522 | 4193788 | block += bi->rowstride; | |
523 | } | ||
524 | |||
525 | /* | ||
526 | Calculate average color including current block. | ||
527 | */ | ||
528 | 1048672 | avg_color_blk[0] = total_rgb_blk[0] / total_pixels_blk; | |
529 | 1048672 | avg_color_blk[1] = total_rgb_blk[1] / total_pixels_blk; | |
530 | 1048672 | 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 | 2358988 | is_in_range = (max_color_blk[0] - avg_color_blk[0] <= threshold && | |
536 |
2/2✓ Branch 0 taken 149748 times.
✓ Branch 1 taken 111896 times.
|
261644 | max_color_blk[1] - avg_color_blk[1] <= threshold && |
537 |
2/2✓ Branch 0 taken 104996 times.
✓ Branch 1 taken 44752 times.
|
149748 | max_color_blk[2] - avg_color_blk[2] <= threshold && |
538 |
2/2✓ Branch 0 taken 103897 times.
✓ Branch 1 taken 1099 times.
|
104996 | avg_color_blk[0] - min_color_blk[0] <= threshold && |
539 |
4/4✓ Branch 0 taken 261644 times.
✓ Branch 1 taken 787028 times.
✓ Branch 2 taken 102084 times.
✓ Branch 3 taken 1813 times.
|
1412400 | avg_color_blk[1] - min_color_blk[1] <= threshold && |
540 |
2/2✓ Branch 0 taken 99667 times.
✓ Branch 1 taken 2417 times.
|
102084 | avg_color_blk[2] - min_color_blk[2] <= threshold); |
541 | |||
542 |
2/2✓ Branch 0 taken 99667 times.
✓ Branch 1 taken 949005 times.
|
1048672 | if (is_in_range) { |
543 | /* | ||
544 | Set the output variables to include this block. | ||
545 | */ | ||
546 | 99667 | min_color[0] = min_color_blk[0]; | |
547 | 99667 | min_color[1] = min_color_blk[1]; | |
548 | 99667 | min_color[2] = min_color_blk[2]; | |
549 | 99667 | max_color[0] = max_color_blk[0]; | |
550 | 99667 | max_color[1] = max_color_blk[1]; | |
551 | 99667 | max_color[2] = max_color_blk[2]; | |
552 | 99667 | total_rgb[0] = total_rgb_blk[0]; | |
553 | 99667 | total_rgb[1] = total_rgb_blk[1]; | |
554 | 99667 | total_rgb[2] = total_rgb_blk[2]; | |
555 | 99667 | *total_pixels = total_pixels_blk; | |
556 | 99667 | avg_color[0] = avg_color_blk[0]; | |
557 | 99667 | avg_color[1] = avg_color_blk[1]; | |
558 | 99667 | avg_color[2] = avg_color_blk[2]; | |
559 | } | ||
560 | |||
561 | 1048672 | return is_in_range; | |
562 | } | ||
563 | |||
564 | 200 | static void rpza_encode_stream(RpzaContext *s, const AVFrame *pict) | |
565 | { | ||
566 | BlockInfo bi; | ||
567 | 200 | int block_counter = 0; | |
568 | int n_blocks; | ||
569 | int total_blocks; | ||
570 | int prev_block_offset; | ||
571 | 200 | int block_offset = 0; | |
572 | 200 | int pblock_offset = 0; | |
573 | 200 | uint8_t min = 0, max = 0; | |
574 | channel_offset chan; | ||
575 | int i; | ||
576 | int tmp_min, tmp_max; | ||
577 | int total_rgb[3]; | ||
578 | uint8_t avg_color[3]; | ||
579 | int pixel_count; | ||
580 | uint8_t min_color[3], max_color[3]; | ||
581 | int slope, y_intercept, correlation_coef; | ||
582 | 200 | const uint16_t *src_pixels = (const uint16_t *)pict->data[0]; | |
583 | 200 | uint16_t *prev_pixels = (uint16_t *)s->prev_frame->data[0]; | |
584 | |||
585 | /* Number of 4x4 blocks in frame. */ | ||
586 | 200 | total_blocks = ((s->frame_width + 3) / 4) * ((s->frame_height + 3) / 4); | |
587 | |||
588 | 200 | bi.image_width = s->frame_width; | |
589 | 200 | bi.image_height = s->frame_height; | |
590 | 200 | bi.rowstride = pict->linesize[0] / 2; | |
591 | 200 | bi.prev_rowstride = s->prev_frame->linesize[0] / 2; | |
592 | |||
593 | 200 | bi.blocks_per_row = (s->frame_width + 3) / 4; | |
594 | |||
595 |
2/2✓ Branch 0 taken 949947 times.
✓ Branch 1 taken 200 times.
|
950147 | 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 |
2/2✓ Branch 0 taken 930954 times.
✓ Branch 1 taken 18993 times.
|
949947 | if (!s->first_frame) { |
600 | 930954 | n_blocks = 0; | |
601 | 930954 | prev_block_offset = 0; | |
602 | |||
603 |
2/4✓ Branch 0 taken 934636 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 934636 times.
✗ Branch 3 not taken.
|
934636 | while (n_blocks < 32 && block_counter + n_blocks < total_blocks) { |
604 | 934636 | block_offset = get_block_info(&bi, block_counter + n_blocks, 0); | |
605 | 934636 | 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 |
4/4✓ Branch 0 taken 3682 times.
✓ Branch 1 taken 930954 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3679 times.
|
934636 | if (prev_block_offset && block_offset - prev_block_offset > 12) { |
615 | 3 | break; | |
616 | } | ||
617 | |||
618 | 934633 | prev_block_offset = block_offset; | |
619 | |||
620 |
2/2✓ Branch 0 taken 930951 times.
✓ Branch 1 taken 3682 times.
|
934633 | if (compare_blocks(&prev_pixels[pblock_offset], |
621 | 934633 | &src_pixels[block_offset], &bi, s->skip_frame_thresh) != 0) { | |
622 | // write out skipable blocks | ||
623 |
2/2✓ Branch 0 taken 2314 times.
✓ Branch 1 taken 928637 times.
|
930951 | if (n_blocks) { |
624 | |||
625 | // write skip opcode | ||
626 | 2314 | put_bits(&s->pb, 8, 0x80 | (n_blocks - 1)); | |
627 | 2314 | block_counter += n_blocks; | |
628 | |||
629 | 2314 | goto post_skip; | |
630 | } | ||
631 | 928637 | break; | |
632 | } | ||
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 | 3682 | n_blocks++; | |
644 | } | ||
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 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 928637 times.
|
928640 | if (n_blocks) { |
649 | // write skip opcode | ||
650 | 3 | put_bits(&s->pb, 8, 0x80 | (n_blocks - 1)); | |
651 | 3 | block_counter += n_blocks; | |
652 | |||
653 | 3 | continue; | |
654 | } | ||
655 | |||
656 | } else { | ||
657 | 18993 | block_offset = get_block_info(&bi, block_counter, 0); | |
658 | 18993 | pblock_offset = get_block_info(&bi, block_counter, 1); | |
659 | } | ||
660 | 949944 | post_skip : | |
661 | |||
662 | // ONE COLOR CHECK | ||
663 |
2/2✓ Branch 1 taken 98843 times.
✓ Branch 2 taken 851101 times.
|
949944 | if (update_block_stats(s, &bi, &src_pixels[block_offset], |
664 | min_color, max_color, | ||
665 | total_rgb, &pixel_count, avg_color, 1)) { | ||
666 | 98843 | prev_block_offset = block_offset; | |
667 | |||
668 | 98843 | n_blocks = 1; | |
669 | |||
670 | /* update this block in the previous frame buffer */ | ||
671 | 98843 | update_block_in_prev_frame(&src_pixels[block_offset], | |
672 | 98843 | &prev_pixels[pblock_offset], &bi, block_counter + n_blocks); | |
673 | |||
674 | // check for subsequent blocks with the same color | ||
675 |
3/4✓ Branch 0 taken 99667 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99651 times.
✓ Branch 3 taken 16 times.
|
99667 | while (n_blocks < 32 && block_counter + n_blocks < total_blocks) { |
676 | 99651 | block_offset = get_block_info(&bi, block_counter + n_blocks, 0); | |
677 | 99651 | 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 |
2/2✓ Branch 0 taken 923 times.
✓ Branch 1 taken 98728 times.
|
99651 | if (block_offset - prev_block_offset > 12) { |
682 | 923 | break; | |
683 | } | ||
684 | |||
685 |
2/2✓ Branch 1 taken 97904 times.
✓ Branch 2 taken 824 times.
|
98728 | if (!update_block_stats(s, &bi, &src_pixels[block_offset], |
686 | min_color, max_color, | ||
687 | total_rgb, &pixel_count, avg_color, 0)) { | ||
688 | 97904 | break; | |
689 | } | ||
690 | |||
691 | 824 | prev_block_offset = block_offset; | |
692 | |||
693 | /* update this block in the previous frame buffer */ | ||
694 | 824 | update_block_in_prev_frame(&src_pixels[block_offset], | |
695 | 824 | &prev_pixels[pblock_offset], &bi, block_counter + n_blocks); | |
696 | |||
697 | 824 | n_blocks++; | |
698 | } | ||
699 | |||
700 | // write one color opcode. | ||
701 | 98843 | put_bits(&s->pb, 8, 0xa0 | (n_blocks - 1)); | |
702 | // write color to encode. | ||
703 | 98843 | put_bits(&s->pb, 16, rgb24_to_rgb555(avg_color)); | |
704 | // skip past the blocks we've just encoded. | ||
705 | 98843 | block_counter += n_blocks; | |
706 | } else { // FOUR COLOR CHECK | ||
707 | 851101 | int err = 0; | |
708 | |||
709 | // get max component diff for block | ||
710 | 851101 | get_max_component_diff(&bi, &src_pixels[block_offset], &min, &max, &chan); | |
711 | |||
712 | 851101 | min_color[0] = 0; | |
713 | 851101 | max_color[0] = 0; | |
714 | 851101 | min_color[1] = 0; | |
715 | 851101 | max_color[1] = 0; | |
716 | 851101 | min_color[2] = 0; | |
717 | 851101 | max_color[2] = 0; | |
718 | |||
719 | // run least squares against other two components | ||
720 |
2/2✓ Branch 0 taken 2553303 times.
✓ Branch 1 taken 851101 times.
|
3404404 | for (i = 0; i < 3; i++) { |
721 |
2/2✓ Branch 0 taken 851101 times.
✓ Branch 1 taken 1702202 times.
|
2553303 | if (i == chan) { |
722 | 851101 | min_color[i] = min; | |
723 | 851101 | max_color[i] = max; | |
724 | 851101 | continue; | |
725 | } | ||
726 | |||
727 | 1702202 | slope = y_intercept = correlation_coef = 0; | |
728 | |||
729 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1702202 times.
|
1702202 | if (leastsquares(&src_pixels[block_offset], &bi, chan, i, |
730 | &slope, &y_intercept, &correlation_coef)) { | ||
731 | ✗ | min_color[i] = GET_CHAN(src_pixels[block_offset], i); | |
732 | ✗ | max_color[i] = GET_CHAN(src_pixels[block_offset], i); | |
733 | } else { | ||
734 | 1702202 | tmp_min = 1 + min * slope + y_intercept; | |
735 | 1702202 | tmp_max = 1 + max * slope + y_intercept; | |
736 | |||
737 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1702202 times.
|
1702202 | av_assert0(tmp_min <= tmp_max); |
738 | // clamp min and max color values | ||
739 | 1702202 | tmp_min = av_clip_uint8(tmp_min); | |
740 | 1702202 | tmp_max = av_clip_uint8(tmp_max); | |
741 | |||
742 |
2/2✓ Branch 1 taken 1328446 times.
✓ Branch 2 taken 373756 times.
|
1702202 | err = FFMAX(calc_lsq_max_fit_error(&src_pixels[block_offset], &bi, |
743 | min, max, tmp_min, tmp_max, chan, i), err); | ||
744 | |||
745 | 1702202 | min_color[i] = tmp_min; | |
746 | 1702202 | max_color[i] = tmp_max; | |
747 | } | ||
748 | } | ||
749 | |||
750 |
1/2✓ Branch 0 taken 851101 times.
✗ Branch 1 not taken.
|
851101 | if (err > s->sixteen_color_thresh) { // DO SIXTEEN COLOR BLOCK |
751 | const uint16_t *row_ptr; | ||
752 | int y_size, x_size, rgb555; | ||
753 | |||
754 | 851101 | block_offset = get_block_info(&bi, block_counter, 0); | |
755 | 851101 | pblock_offset = get_block_info(&bi, block_counter, 1); | |
756 | |||
757 | 851101 | row_ptr = &src_pixels[block_offset]; | |
758 | 851101 | y_size = FFMIN(4, bi.image_height - bi.row * 4); | |
759 | 851101 | x_size = FFMIN(4, bi.image_width - bi.col * 4); | |
760 | |||
761 |
2/2✓ Branch 0 taken 3403504 times.
✓ Branch 1 taken 851101 times.
|
4254605 | for (int y = 0; y < y_size; y++) { |
762 |
2/2✓ Branch 0 taken 13610616 times.
✓ Branch 1 taken 3403504 times.
|
17014120 | for (int x = 0; x < x_size; x++) { |
763 | 13610616 | rgb555 = row_ptr[x] & ~0x8000; | |
764 | |||
765 | 13610616 | put_bits(&s->pb, 16, rgb555); | |
766 | } | ||
767 |
2/2✓ Branch 0 taken 3400 times.
✓ Branch 1 taken 3403504 times.
|
3406904 | for (int x = x_size; x < 4; x++) |
768 | 3400 | put_bits(&s->pb, 16, 0); | |
769 | |||
770 | 3403504 | row_ptr += bi.rowstride; | |
771 | } | ||
772 | |||
773 |
2/2✓ Branch 0 taken 900 times.
✓ Branch 1 taken 851101 times.
|
852001 | for (int y = y_size; y < 4; y++) { |
774 |
2/2✓ Branch 0 taken 3600 times.
✓ Branch 1 taken 900 times.
|
4500 | for (int x = 0; x < 4; x++) |
775 | 3600 | put_bits(&s->pb, 16, 0); | |
776 | } | ||
777 | |||
778 | 851101 | block_counter++; | |
779 | } else { // FOUR COLOR BLOCK | ||
780 | ✗ | block_counter += encode_four_color_block(min_color, max_color, | |
781 | ✗ | &s->pb, &src_pixels[block_offset], &bi); | |
782 | } | ||
783 | |||
784 | /* update this block in the previous frame buffer */ | ||
785 | 851101 | update_block_in_prev_frame(&src_pixels[block_offset], | |
786 | 851101 | &prev_pixels[pblock_offset], &bi, block_counter); | |
787 | } | ||
788 | } | ||
789 | 200 | } | |
790 | |||
791 | 4 | static int rpza_encode_init(AVCodecContext *avctx) | |
792 | { | ||
793 | 4 | RpzaContext *s = avctx->priv_data; | |
794 | |||
795 | 4 | s->frame_width = avctx->width; | |
796 | 4 | s->frame_height = avctx->height; | |
797 | |||
798 | 4 | s->prev_frame = av_frame_alloc(); | |
799 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!s->prev_frame) |
800 | ✗ | return AVERROR(ENOMEM); | |
801 | |||
802 | 4 | return 0; | |
803 | } | ||
804 | |||
805 | 200 | static int rpza_encode_frame(AVCodecContext *avctx, AVPacket *pkt, | |
806 | const AVFrame *pict, int *got_packet) | ||
807 | { | ||
808 | 200 | RpzaContext *s = avctx->priv_data; | |
809 | uint8_t *buf; | ||
810 | 200 | int ret = ff_alloc_packet(avctx, pkt, 4LL + 6LL * FFMAX(avctx->height, 4) * FFMAX(avctx->width, 4)); | |
811 | |||
812 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
|
200 | if (ret < 0) |
813 | ✗ | return ret; | |
814 | |||
815 | 200 | 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 | 200 | put_bits32(&s->pb, 0x00); | |
819 | |||
820 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 196 times.
|
200 | if (!s->prev_frame->data[0]) { |
821 | 4 | s->first_frame = 1; | |
822 | 4 | s->prev_frame->format = pict->format; | |
823 | 4 | s->prev_frame->width = pict->width; | |
824 | 4 | s->prev_frame->height = pict->height; | |
825 | 4 | ret = av_frame_get_buffer(s->prev_frame, 0); | |
826 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (ret < 0) |
827 | ✗ | return ret; | |
828 | } else { | ||
829 | 196 | s->first_frame = 0; | |
830 | } | ||
831 | |||
832 | 200 | rpza_encode_stream(s, pict); | |
833 | |||
834 | 200 | flush_put_bits(&s->pb); | |
835 | |||
836 | 200 | av_shrink_packet(pkt, put_bytes_output(&s->pb)); | |
837 | 200 | buf = pkt->data; | |
838 | |||
839 | // write header opcode | ||
840 | 200 | buf[0] = 0xe1; // chunk opcode | |
841 | |||
842 | // write chunk length | ||
843 | 200 | AV_WB24(buf + 1, pkt->size); | |
844 | |||
845 | 200 | *got_packet = 1; | |
846 | |||
847 | 200 | return 0; | |
848 | } | ||
849 | |||
850 | 4 | static int rpza_encode_end(AVCodecContext *avctx) | |
851 | { | ||
852 | 4 | RpzaContext *s = (RpzaContext *)avctx->priv_data; | |
853 | |||
854 | 4 | av_frame_free(&s->prev_frame); | |
855 | |||
856 | 4 | return 0; | |
857 | } | ||
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 | .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB555, | ||
888 | AV_PIX_FMT_NONE}, | ||
889 | }; | ||
890 |