FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/smcenc.c
Date: 2022-11-28 23:49:43
Exec Total Coverage
Lines: 0 310 0.0%
Branches: 0 431 0.0%

Line Branch Exec Source
1 /*
2 * QuickTime Graphics (SMC) Video Encoder
3 * Copyright (c) 2021 The FFmpeg project
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file smcenc.c
24 * QT SMC Video Encoder by Paul B. Mahol
25 */
26
27 #include "libavutil/common.h"
28
29 #include "avcodec.h"
30 #include "codec_internal.h"
31 #include "encode.h"
32 #include "bytestream.h"
33
34 #define CPAIR 2
35 #define CQUAD 4
36 #define COCTET 8
37
38 #define COLORS_PER_TABLE 256
39
40 typedef struct SMCContext {
41 AVFrame *prev_frame; // buffer for previous source frame
42
43 uint8_t mono_value;
44 int nb_distinct;
45 int next_nb_distinct;
46 uint8_t distinct_values[16];
47 uint8_t next_distinct_values[16];
48
49 uint8_t color_pairs[COLORS_PER_TABLE][CPAIR];
50 uint8_t color_quads[COLORS_PER_TABLE][CQUAD];
51 uint8_t color_octets[COLORS_PER_TABLE][COCTET];
52
53 int key_frame;
54 } SMCContext;
55
56 #define ADVANCE_BLOCK(pixel_ptr, row_ptr, nb_blocks) \
57 { \
58 for (int block = 0; block < nb_blocks && pixel_ptr && row_ptr; block++) { \
59 pixel_ptr += 4; \
60 if (pixel_ptr - row_ptr >= width) \
61 { \
62 row_ptr += stride * 4; \
63 pixel_ptr = row_ptr; \
64 cur_y += 4; \
65 } \
66 } \
67 }
68
69 static int smc_cmp_values(const void *a, const void *b)
70 {
71 const uint8_t *aa = a, *bb = b;
72
73 return FFDIFFSIGN(aa[0], bb[0]);
74 }
75
76 static int count_distinct_items(const uint8_t *block_values,
77 uint8_t *distinct_values,
78 int size)
79 {
80 int n = 1;
81
82 distinct_values[0] = block_values[0];
83 for (int i = 1; i < size; i++) {
84 if (block_values[i] != block_values[i-1]) {
85 distinct_values[n] = block_values[i];
86 n++;
87 }
88 }
89
90 return n;
91 }
92
93 #define CACHE_PAIR(x) \
94 (s->color_pairs[i][0] == distinct_values[x] || \
95 s->color_pairs[i][1] == distinct_values[x])
96
97 #define CACHE_QUAD(x) \
98 (s->color_quads[i][0] == distinct_values[x] || \
99 s->color_quads[i][1] == distinct_values[x] || \
100 s->color_quads[i][2] == distinct_values[x] || \
101 s->color_quads[i][3] == distinct_values[x])
102
103 #define CACHE_OCTET(x) \
104 (s->color_octets[i][0] == distinct_values[x] || \
105 s->color_octets[i][1] == distinct_values[x] || \
106 s->color_octets[i][2] == distinct_values[x] || \
107 s->color_octets[i][3] == distinct_values[x] || \
108 s->color_octets[i][4] == distinct_values[x] || \
109 s->color_octets[i][5] == distinct_values[x] || \
110 s->color_octets[i][6] == distinct_values[x] || \
111 s->color_octets[i][7] == distinct_values[x])
112
113 static void smc_encode_stream(SMCContext *s, const AVFrame *frame,
114 PutByteContext *pb)
115 {
116 const uint8_t *src_pixels = (const uint8_t *)frame->data[0];
117 const int stride = frame->linesize[0];
118 const uint8_t *prev_pixels = (const uint8_t *)s->prev_frame->data[0];
119 const int prev_stride = s->prev_frame->linesize[0];
120 uint8_t *distinct_values = s->distinct_values;
121 const uint8_t *pixel_ptr, *row_ptr;
122 const int height = frame->height;
123 const int width = frame->width;
124 uint8_t block_values[16];
125 int block_counter = 0;
126 int color_pair_index = 0;
127 int color_quad_index = 0;
128 int color_octet_index = 0;
129 int color_table_index; /* indexes to color pair, quad, or octet tables */
130 int total_blocks;
131 int cur_y = 0;
132
133 memset(s->color_pairs, 0, sizeof(s->color_pairs));
134 memset(s->color_quads, 0, sizeof(s->color_quads));
135 memset(s->color_octets, 0, sizeof(s->color_octets));
136
137 /* Number of 4x4 blocks in frame. */
138 total_blocks = ((width + 3) / 4) * ((height + 3) / 4);
139
140 pixel_ptr = row_ptr = src_pixels;
141
142 while (block_counter < total_blocks) {
143 const uint8_t *xpixel_ptr = pixel_ptr;
144 const uint8_t *xrow_ptr = row_ptr;
145 int intra_skip_blocks = 0;
146 int inter_skip_blocks = 0;
147 int coded_distinct = 0;
148 int coded_blocks = 0;
149 int cache_index;
150 int distinct = 0;
151 int blocks = 0;
152 int frame_y = cur_y;
153
154 while (prev_pixels && s->key_frame == 0 && block_counter + inter_skip_blocks < total_blocks) {
155 const int y_size = FFMIN(4, height - cur_y);
156 int compare = 0;
157
158 for (int y = 0; y < y_size; y++) {
159 const ptrdiff_t offset = pixel_ptr - row_ptr;
160 const uint8_t *prev_pixel_ptr = prev_pixels + cur_y * prev_stride + offset;
161
162 compare |= memcmp(prev_pixel_ptr + y * prev_stride, pixel_ptr + y * stride, 4);
163 if (compare)
164 break;
165 }
166
167 if (compare)
168 break;
169
170 if (inter_skip_blocks >= 256)
171 break;
172 inter_skip_blocks++;
173
174 ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
175 }
176
177 pixel_ptr = xpixel_ptr;
178 row_ptr = xrow_ptr;
179 cur_y = frame_y;
180
181 while (block_counter > 0 && block_counter + intra_skip_blocks < total_blocks) {
182 const int y_size = FFMIN(4, height - cur_y);
183 const ptrdiff_t offset = pixel_ptr - src_pixels;
184 const int sy = offset / stride;
185 const int sx = offset % stride;
186 const int ny = sx < 4 ? sy - 4 : sy;
187 const int nx = sx < 4 ? width - 4 : sx - 4;
188 const uint8_t *old_pixel_ptr = src_pixels + nx + ny * stride;
189 int compare = 0;
190
191 for (int y = 0; y < y_size; y++) {
192 compare |= memcmp(old_pixel_ptr + y * stride, pixel_ptr + y * stride, 4);
193 if (compare)
194 break;
195 }
196
197 if (compare)
198 break;
199
200 if (intra_skip_blocks >= 256)
201 break;
202 intra_skip_blocks++;
203 ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
204 }
205
206 pixel_ptr = xpixel_ptr;
207 row_ptr = xrow_ptr;
208 cur_y = frame_y;
209
210 while (block_counter + coded_blocks < total_blocks && coded_blocks < 256) {
211 const int y_size = FFMIN(4, height - cur_y);
212 for (int y = 0; y < y_size; y++)
213 memcpy(block_values + y * 4, pixel_ptr + y * stride, 4);
214
215 qsort(block_values, 16, sizeof(block_values[0]), smc_cmp_values);
216 s->next_nb_distinct = count_distinct_items(block_values, s->next_distinct_values, 16);
217 if (coded_blocks == 0) {
218 memcpy(distinct_values, s->next_distinct_values, sizeof(s->distinct_values));
219 s->nb_distinct = s->next_nb_distinct;
220 } else {
221 if (s->next_nb_distinct != s->nb_distinct ||
222 memcmp(distinct_values, s->next_distinct_values, s->nb_distinct)) {
223 break;
224 }
225 }
226 s->mono_value = block_values[0];
227
228 coded_distinct = s->nb_distinct;
229 ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
230 coded_blocks++;
231 if (coded_distinct > 1 && coded_blocks >= 16)
232 break;
233 }
234
235 pixel_ptr = xpixel_ptr;
236 row_ptr = xrow_ptr;
237 cur_y = frame_y;
238
239 blocks = coded_blocks;
240 distinct = coded_distinct;
241
242 if (intra_skip_blocks > 0 && intra_skip_blocks >= inter_skip_blocks &&
243 intra_skip_blocks > 0) {
244 distinct = 17;
245 blocks = intra_skip_blocks;
246 }
247
248 if (intra_skip_blocks > 16 && intra_skip_blocks >= inter_skip_blocks &&
249 intra_skip_blocks > 0) {
250 distinct = 18;
251 blocks = intra_skip_blocks;
252 }
253
254 if (inter_skip_blocks > 0 && inter_skip_blocks > intra_skip_blocks &&
255 inter_skip_blocks > 0) {
256 distinct = 19;
257 blocks = inter_skip_blocks;
258 }
259
260 if (inter_skip_blocks > 16 && inter_skip_blocks > intra_skip_blocks &&
261 inter_skip_blocks > 0) {
262 distinct = 20;
263 blocks = inter_skip_blocks;
264 }
265
266 switch (distinct) {
267 case 1:
268 if (blocks <= 16) {
269 bytestream2_put_byte(pb, 0x60 | (blocks - 1));
270 } else {
271 bytestream2_put_byte(pb, 0x70);
272 bytestream2_put_byte(pb, blocks - 1);
273 }
274 bytestream2_put_byte(pb, s->mono_value);
275 ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
276 break;
277 case 2:
278 cache_index = -1;
279 for (int i = 0; i < COLORS_PER_TABLE; i++) {
280 if (CACHE_PAIR(0) &&
281 CACHE_PAIR(1)) {
282 cache_index = i;
283 break;
284 }
285 }
286
287 if (cache_index >= 0) {
288 bytestream2_put_byte(pb, 0x90 | (blocks - 1));
289 bytestream2_put_byte(pb, cache_index);
290 color_table_index = cache_index;
291 } else {
292 bytestream2_put_byte(pb, 0x80 | (blocks - 1));
293
294 color_table_index = color_pair_index;
295 for (int i = 0; i < CPAIR; i++) {
296 s->color_pairs[color_table_index][i] = distinct_values[i];
297 bytestream2_put_byte(pb, distinct_values[i]);
298 }
299
300 color_pair_index++;
301 if (color_pair_index == COLORS_PER_TABLE)
302 color_pair_index = 0;
303 }
304
305 for (int i = 0; i < blocks; i++) {
306 const int y_size = FFMIN(4, height - cur_y);
307 uint8_t value = s->color_pairs[color_table_index][1];
308 uint16_t flags = 0;
309 int shift = 15;
310
311 for (int y = 0; y < y_size; y++) {
312 for (int x = 0; x < 4; x++) {
313 flags |= (value == pixel_ptr[x + y * stride]) << shift;
314 shift--;
315 }
316 }
317
318 bytestream2_put_be16(pb, flags);
319
320 ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
321 }
322 break;
323 case 3:
324 case 4:
325 cache_index = -1;
326 for (int i = 0; i < COLORS_PER_TABLE; i++) {
327 if (CACHE_QUAD(0) &&
328 CACHE_QUAD(1) &&
329 CACHE_QUAD(2) &&
330 CACHE_QUAD(3)) {
331 cache_index = i;
332 break;
333 }
334 }
335
336 if (cache_index >= 0) {
337 bytestream2_put_byte(pb, 0xB0 | (blocks - 1));
338 bytestream2_put_byte(pb, cache_index);
339 color_table_index = cache_index;
340 } else {
341 bytestream2_put_byte(pb, 0xA0 | (blocks - 1));
342
343 color_table_index = color_quad_index;
344 for (int i = 0; i < CQUAD; i++) {
345 s->color_quads[color_table_index][i] = distinct_values[i];
346 bytestream2_put_byte(pb, distinct_values[i]);
347 }
348
349 color_quad_index++;
350 if (color_quad_index == COLORS_PER_TABLE)
351 color_quad_index = 0;
352 }
353
354 for (int i = 0; i < blocks; i++) {
355 const int y_size = FFMIN(4, height - cur_y);
356 uint32_t flags = 0;
357 uint8_t quad[4];
358 int shift = 30;
359
360 for (int k = 0; k < 4; k++)
361 quad[k] = s->color_quads[color_table_index][k];
362
363 for (int y = 0; y < y_size; y++) {
364 for (int x = 0; x < 4; x++) {
365 int pixel = pixel_ptr[x + y * stride];
366 uint32_t idx = 0;
367
368 for (int w = 0; w < CQUAD; w++) {
369 if (quad[w] == pixel) {
370 idx = w;
371 break;
372 }
373 }
374
375 flags |= idx << shift;
376 shift -= 2;
377 }
378 }
379
380 bytestream2_put_be32(pb, flags);
381
382 ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
383 }
384 break;
385 case 5:
386 case 6:
387 case 7:
388 case 8:
389 cache_index = -1;
390 for (int i = 0; i < COLORS_PER_TABLE; i++) {
391 if (CACHE_OCTET(0) &&
392 CACHE_OCTET(1) &&
393 CACHE_OCTET(2) &&
394 CACHE_OCTET(3) &&
395 CACHE_OCTET(4) &&
396 CACHE_OCTET(5) &&
397 CACHE_OCTET(6) &&
398 CACHE_OCTET(7)) {
399 cache_index = i;
400 break;
401 }
402 }
403
404 if (cache_index >= 0) {
405 bytestream2_put_byte(pb, 0xD0 | (blocks - 1));
406 bytestream2_put_byte(pb, cache_index);
407 color_table_index = cache_index;
408 } else {
409 bytestream2_put_byte(pb, 0xC0 | (blocks - 1));
410
411 color_table_index = color_octet_index;
412 for (int i = 0; i < COCTET; i++) {
413 s->color_octets[color_table_index][i] = distinct_values[i];
414 bytestream2_put_byte(pb, distinct_values[i]);
415 }
416
417 color_octet_index++;
418 if (color_octet_index == COLORS_PER_TABLE)
419 color_octet_index = 0;
420 }
421
422 for (int i = 0; i < blocks; i++) {
423 const int y_size = FFMIN(4, height - cur_y);
424 uint64_t flags = 0;
425 uint8_t octet[8];
426 int shift = 45;
427
428 for (int k = 0; k < 8; k++)
429 octet[k] = s->color_octets[color_table_index][k];
430
431 for (int y = 0; y < y_size; y++) {
432 for (int x = 0; x < 4; x++) {
433 int pixel = pixel_ptr[x + y * stride];
434 uint64_t idx = 0;
435
436 for (int w = 0; w < COCTET; w++) {
437 if (octet[w] == pixel) {
438 idx = w;
439 break;
440 }
441 }
442
443 flags |= idx << shift;
444 shift -= 3;
445 }
446 }
447
448 bytestream2_put_be16(pb, ((flags >> 32) & 0xFFF0) | ((flags >> 8) & 0xF));
449 bytestream2_put_be16(pb, ((flags >> 20) & 0xFFF0) | ((flags >> 4) & 0xF));
450 bytestream2_put_be16(pb, ((flags >> 8) & 0xFFF0) | ((flags >> 0) & 0xF));
451
452 ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
453 }
454 break;
455 default:
456 bytestream2_put_byte(pb, 0xE0 | (blocks - 1));
457 for (int i = 0; i < blocks; i++) {
458 const int y_size = FFMIN(4, height - cur_y);
459 for (int y = 0; y < y_size; y++) {
460 for (int x = 0; x < 4; x++)
461 bytestream2_put_byte(pb, pixel_ptr[x + y * stride]);
462 }
463
464 for (int y = y_size; y < 4; y++) {
465 for (int x = 0; x < 4; x++)
466 bytestream2_put_byte(pb, 0);
467 }
468
469 ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
470 }
471 break;
472 case 17:
473 bytestream2_put_byte(pb, 0x20 | (blocks - 1));
474 ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
475 break;
476 case 18:
477 bytestream2_put_byte(pb, 0x30);
478 bytestream2_put_byte(pb, blocks - 1);
479 ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
480 break;
481 case 19:
482 bytestream2_put_byte(pb, 0x00 | (blocks - 1));
483 ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
484 break;
485 case 20:
486 bytestream2_put_byte(pb, 0x10);
487 bytestream2_put_byte(pb, blocks - 1);
488 ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
489 break;
490 }
491
492 block_counter += blocks;
493 }
494 }
495
496 static int smc_encode_init(AVCodecContext *avctx)
497 {
498 SMCContext *s = avctx->priv_data;
499
500 avctx->bits_per_coded_sample = 8;
501
502 s->prev_frame = av_frame_alloc();
503 if (!s->prev_frame)
504 return AVERROR(ENOMEM);
505
506 return 0;
507 }
508
509 static int smc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
510 const AVFrame *frame, int *got_packet)
511 {
512 SMCContext *s = avctx->priv_data;
513 const AVFrame *pict = frame;
514 PutByteContext pb;
515 uint8_t *pal;
516 int ret;
517
518 ret = ff_alloc_packet(avctx, pkt, 8LL * avctx->height * avctx->width);
519 if (ret < 0)
520 return ret;
521
522 if (avctx->gop_size == 0 || !s->prev_frame->data[0] ||
523 (avctx->frame_number % avctx->gop_size) == 0) {
524 s->key_frame = 1;
525 } else {
526 s->key_frame = 0;
527 }
528
529 bytestream2_init_writer(&pb, pkt->data, pkt->size);
530
531 bytestream2_put_be32(&pb, 0x00);
532
533 pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
534 if (!pal)
535 return AVERROR(ENOMEM);
536 memcpy(pal, frame->data[1], AVPALETTE_SIZE);
537
538 smc_encode_stream(s, pict, &pb);
539
540 av_shrink_packet(pkt, bytestream2_tell_p(&pb));
541
542 pkt->data[0] = 0x0;
543
544 // write chunk length
545 AV_WB24(pkt->data + 1, pkt->size);
546
547 av_frame_unref(s->prev_frame);
548 ret = av_frame_ref(s->prev_frame, frame);
549 if (ret < 0) {
550 av_log(avctx, AV_LOG_ERROR, "cannot add reference\n");
551 return ret;
552 }
553
554 if (s->key_frame)
555 pkt->flags |= AV_PKT_FLAG_KEY;
556
557 *got_packet = 1;
558
559 return 0;
560 }
561
562 static int smc_encode_end(AVCodecContext *avctx)
563 {
564 SMCContext *s = (SMCContext *)avctx->priv_data;
565
566 av_frame_free(&s->prev_frame);
567
568 return 0;
569 }
570
571 const FFCodec ff_smc_encoder = {
572 .p.name = "smc",
573 CODEC_LONG_NAME("QuickTime Graphics (SMC)"),
574 .p.type = AVMEDIA_TYPE_VIDEO,
575 .p.id = AV_CODEC_ID_SMC,
576 .p.capabilities = AV_CODEC_CAP_DR1,
577 .priv_data_size = sizeof(SMCContext),
578 .init = smc_encode_init,
579 FF_CODEC_ENCODE_CB(smc_encode_frame),
580 .close = smc_encode_end,
581 .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_PAL8,
582 AV_PIX_FMT_NONE},
583 };
584