Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * innoHeim/Rsupport Screen Capture Codec | ||
3 | * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com> | ||
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 | ||
24 | * innoHeim/Rsupport Screen Capture Codec decoder | ||
25 | * | ||
26 | * Fourcc: ISCC, RSCC | ||
27 | * | ||
28 | * Lossless codec, data stored in tiles, with optional deflate compression. | ||
29 | * | ||
30 | * Header contains the number of tiles in a frame with the tile coordinates, | ||
31 | * and it can be deflated or not. Similarly, pixel data comes after the header | ||
32 | * and a variable size value, and it can be deflated or just raw. | ||
33 | * | ||
34 | * Supports: PAL8, BGRA, BGR24, RGB555 | ||
35 | */ | ||
36 | |||
37 | #include <stdint.h> | ||
38 | #include <string.h> | ||
39 | #include <zlib.h> | ||
40 | |||
41 | #include "libavutil/imgutils.h" | ||
42 | #include "libavutil/internal.h" | ||
43 | #include "libavutil/mem.h" | ||
44 | |||
45 | #include "avcodec.h" | ||
46 | #include "bytestream.h" | ||
47 | #include "codec_internal.h" | ||
48 | #include "decode.h" | ||
49 | |||
50 | #define TILE_SIZE 8 | ||
51 | |||
52 | typedef struct Tile { | ||
53 | int x, y; | ||
54 | int w, h; | ||
55 | } Tile; | ||
56 | |||
57 | typedef struct RsccContext { | ||
58 | GetByteContext gbc; | ||
59 | AVFrame *reference; | ||
60 | Tile *tiles; | ||
61 | unsigned int tiles_size; | ||
62 | int component_size; | ||
63 | |||
64 | uint8_t palette[AVPALETTE_SIZE]; | ||
65 | |||
66 | /* zlib interaction */ | ||
67 | uint8_t *inflated_buf; | ||
68 | uLongf inflated_size; | ||
69 | int valid_pixels; | ||
70 | } RsccContext; | ||
71 | |||
72 | 10 | static av_cold int rscc_init(AVCodecContext *avctx) | |
73 | { | ||
74 | 10 | RsccContext *ctx = avctx->priv_data; | |
75 | |||
76 | /* These needs to be set to estimate uncompressed buffer */ | ||
77 | 10 | int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); | |
78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (ret < 0) { |
79 | ✗ | av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n", | |
80 | avctx->width, avctx->height); | ||
81 | ✗ | return ret; | |
82 | } | ||
83 | |||
84 | /* Allocate reference frame */ | ||
85 | 10 | ctx->reference = av_frame_alloc(); | |
86 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (!ctx->reference) |
87 | ✗ | return AVERROR(ENOMEM); | |
88 | |||
89 | /* Get pixel format and the size of the pixel */ | ||
90 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
|
10 | if (avctx->codec_tag == MKTAG('I', 'S', 'C', 'C')) { |
91 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | if (avctx->extradata && avctx->extradata_size == 4) { |
92 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if ((avctx->extradata[0] >> 1) & 1) { |
93 | 2 | avctx->pix_fmt = AV_PIX_FMT_BGRA; | |
94 | 2 | ctx->component_size = 4; | |
95 | } else { | ||
96 | ✗ | avctx->pix_fmt = AV_PIX_FMT_BGR24; | |
97 | ✗ | ctx->component_size = 3; | |
98 | } | ||
99 | } else { | ||
100 | ✗ | avctx->pix_fmt = AV_PIX_FMT_BGRA; | |
101 | ✗ | ctx->component_size = 4; | |
102 | } | ||
103 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | } else if (avctx->codec_tag == MKTAG('R', 'S', 'C', 'C')) { |
104 | 8 | ctx->component_size = avctx->bits_per_coded_sample / 8; | |
105 |
4/5✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
8 | switch (avctx->bits_per_coded_sample) { |
106 | 2 | case 8: | |
107 | 2 | avctx->pix_fmt = AV_PIX_FMT_PAL8; | |
108 | 2 | break; | |
109 | 2 | case 16: | |
110 | 2 | avctx->pix_fmt = AV_PIX_FMT_RGB555LE; | |
111 | 2 | break; | |
112 | 2 | case 24: | |
113 | 2 | avctx->pix_fmt = AV_PIX_FMT_BGR24; | |
114 | 2 | break; | |
115 | 2 | case 32: | |
116 | 2 | avctx->pix_fmt = AV_PIX_FMT_BGR0; | |
117 | 2 | break; | |
118 | ✗ | default: | |
119 | ✗ | av_log(avctx, AV_LOG_ERROR, "Invalid bits per pixel value (%d)\n", | |
120 | avctx->bits_per_coded_sample); | ||
121 | ✗ | return AVERROR_INVALIDDATA; | |
122 | } | ||
123 | } else { | ||
124 | ✗ | avctx->pix_fmt = AV_PIX_FMT_BGR0; | |
125 | ✗ | ctx->component_size = 4; | |
126 | ✗ | av_log(avctx, AV_LOG_WARNING, "Invalid codec tag\n"); | |
127 | } | ||
128 | |||
129 | /* Store the value to check for keyframes */ | ||
130 | 10 | ctx->inflated_size = avctx->width * avctx->height * ctx->component_size; | |
131 | |||
132 | /* Allocate maximum size possible, a full frame */ | ||
133 | 10 | ctx->inflated_buf = av_malloc(ctx->inflated_size); | |
134 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (!ctx->inflated_buf) |
135 | ✗ | return AVERROR(ENOMEM); | |
136 | |||
137 | 10 | return 0; | |
138 | } | ||
139 | |||
140 | 10 | static av_cold int rscc_close(AVCodecContext *avctx) | |
141 | { | ||
142 | 10 | RsccContext *ctx = avctx->priv_data; | |
143 | |||
144 | 10 | av_freep(&ctx->tiles); | |
145 | 10 | av_freep(&ctx->inflated_buf); | |
146 | 10 | av_frame_free(&ctx->reference); | |
147 | |||
148 | 10 | return 0; | |
149 | } | ||
150 | |||
151 | 32 | static int rscc_decode_frame(AVCodecContext *avctx, AVFrame *frame, | |
152 | int *got_frame, AVPacket *avpkt) | ||
153 | { | ||
154 | 32 | RsccContext *ctx = avctx->priv_data; | |
155 | 32 | GetByteContext *gbc = &ctx->gbc; | |
156 | GetByteContext tiles_gbc; | ||
157 | const uint8_t *pixels, *raw; | ||
158 | 32 | uint8_t *inflated_tiles = NULL; | |
159 | 32 | int tiles_nb, packed_size, pixel_size = 0; | |
160 | 32 | int i, ret = 0; | |
161 | |||
162 | 32 | bytestream2_init(gbc, avpkt->data, avpkt->size); | |
163 | |||
164 | /* Size check */ | ||
165 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
|
32 | if (bytestream2_get_bytes_left(gbc) < 12) { |
166 | ✗ | av_log(avctx, AV_LOG_ERROR, "Packet too small (%d)\n", avpkt->size); | |
167 | ✗ | return AVERROR_INVALIDDATA; | |
168 | } | ||
169 | |||
170 | /* Read number of tiles, and allocate the array */ | ||
171 | 32 | tiles_nb = bytestream2_get_le16(gbc); | |
172 | |||
173 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (tiles_nb == 0) { |
174 | ✗ | av_log(avctx, AV_LOG_DEBUG, "no tiles\n"); | |
175 | ✗ | return avpkt->size; | |
176 | } | ||
177 | |||
178 | 32 | av_fast_malloc(&ctx->tiles, &ctx->tiles_size, | |
179 | tiles_nb * sizeof(*ctx->tiles)); | ||
180 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (!ctx->tiles) { |
181 | ✗ | ret = AVERROR(ENOMEM); | |
182 | ✗ | goto end; | |
183 | } | ||
184 | |||
185 | 32 | av_log(avctx, AV_LOG_DEBUG, "Frame with %d tiles.\n", tiles_nb); | |
186 | |||
187 | /* When there are more than 5 tiles, they are packed together with | ||
188 | * a size header. When that size does not match the number of tiles | ||
189 | * times the tile size, it means it needs to be inflated as well */ | ||
190 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
|
32 | if (tiles_nb > 5) { |
191 | uLongf packed_tiles_size; | ||
192 | |||
193 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 7 times.
|
24 | if (tiles_nb < 32) |
194 | 17 | packed_tiles_size = bytestream2_get_byte(gbc); | |
195 | else | ||
196 | 7 | packed_tiles_size = bytestream2_get_le16(gbc); | |
197 | |||
198 | ff_dlog(avctx, "packed tiles of size %lu.\n", packed_tiles_size); | ||
199 | |||
200 | /* If necessary, uncompress tiles, and hijack the bytestream reader */ | ||
201 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | if (packed_tiles_size != tiles_nb * TILE_SIZE) { |
202 | 24 | uLongf length = tiles_nb * TILE_SIZE; | |
203 | |||
204 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if (bytestream2_get_bytes_left(gbc) < packed_tiles_size) { |
205 | ✗ | ret = AVERROR_INVALIDDATA; | |
206 | ✗ | goto end; | |
207 | } | ||
208 | |||
209 | 24 | inflated_tiles = av_malloc(length); | |
210 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (!inflated_tiles) { |
211 | ✗ | ret = AVERROR(ENOMEM); | |
212 | ✗ | goto end; | |
213 | } | ||
214 | |||
215 | 24 | ret = uncompress(inflated_tiles, &length, | |
216 | 24 | gbc->buffer, packed_tiles_size); | |
217 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (ret) { |
218 | ✗ | av_log(avctx, AV_LOG_ERROR, "Tile deflate error %d.\n", ret); | |
219 | ✗ | ret = AVERROR_UNKNOWN; | |
220 | ✗ | goto end; | |
221 | } | ||
222 | |||
223 | /* Skip the compressed tile section in the main byte reader, | ||
224 | * and point it to read the newly uncompressed data */ | ||
225 | 24 | bytestream2_skip(gbc, packed_tiles_size); | |
226 | 24 | bytestream2_init(&tiles_gbc, inflated_tiles, length); | |
227 | 24 | gbc = &tiles_gbc; | |
228 | } | ||
229 | } | ||
230 | |||
231 | /* Fill in array of tiles, keeping track of how many pixels are updated */ | ||
232 |
2/2✓ Branch 0 taken 680 times.
✓ Branch 1 taken 32 times.
|
712 | for (i = 0; i < tiles_nb; i++) { |
233 | 680 | ctx->tiles[i].x = bytestream2_get_le16(gbc); | |
234 | 680 | ctx->tiles[i].w = bytestream2_get_le16(gbc); | |
235 | 680 | ctx->tiles[i].y = bytestream2_get_le16(gbc); | |
236 | 680 | ctx->tiles[i].h = bytestream2_get_le16(gbc); | |
237 | |||
238 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 680 times.
|
680 | if (pixel_size + ctx->tiles[i].w * (int64_t)ctx->tiles[i].h * ctx->component_size > INT_MAX) { |
239 | ✗ | av_log(avctx, AV_LOG_ERROR, "Invalid tile dimensions\n"); | |
240 | ✗ | ret = AVERROR_INVALIDDATA; | |
241 | ✗ | goto end; | |
242 | } | ||
243 | |||
244 | 680 | pixel_size += ctx->tiles[i].w * ctx->tiles[i].h * ctx->component_size; | |
245 | |||
246 | ff_dlog(avctx, "tile %d orig(%d,%d) %dx%d.\n", i, | ||
247 | ctx->tiles[i].x, ctx->tiles[i].y, | ||
248 | ctx->tiles[i].w, ctx->tiles[i].h); | ||
249 | |||
250 |
2/4✓ Branch 0 taken 680 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 680 times.
|
680 | if (ctx->tiles[i].w == 0 || ctx->tiles[i].h == 0) { |
251 | ✗ | av_log(avctx, AV_LOG_ERROR, | |
252 | "invalid tile %d at (%d.%d) with size %dx%d.\n", i, | ||
253 | ✗ | ctx->tiles[i].x, ctx->tiles[i].y, | |
254 | ✗ | ctx->tiles[i].w, ctx->tiles[i].h); | |
255 | ✗ | ret = AVERROR_INVALIDDATA; | |
256 | ✗ | goto end; | |
257 |
1/2✓ Branch 0 taken 680 times.
✗ Branch 1 not taken.
|
680 | } else if (ctx->tiles[i].x + ctx->tiles[i].w > avctx->width || |
258 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 680 times.
|
680 | ctx->tiles[i].y + ctx->tiles[i].h > avctx->height) { |
259 | ✗ | av_log(avctx, AV_LOG_ERROR, | |
260 | "out of bounds tile %d at (%d.%d) with size %dx%d.\n", i, | ||
261 | ✗ | ctx->tiles[i].x, ctx->tiles[i].y, | |
262 | ✗ | ctx->tiles[i].w, ctx->tiles[i].h); | |
263 | ✗ | ret = AVERROR_INVALIDDATA; | |
264 | ✗ | goto end; | |
265 | } | ||
266 | } | ||
267 | |||
268 | /* Reset the reader in case it had been modified before */ | ||
269 | 32 | gbc = &ctx->gbc; | |
270 | |||
271 | /* Extract how much pixel data the tiles contain */ | ||
272 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (pixel_size < 0x100) |
273 | ✗ | packed_size = bytestream2_get_byte(gbc); | |
274 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | else if (pixel_size < 0x10000) |
275 | ✗ | packed_size = bytestream2_get_le16(gbc); | |
276 |
1/2✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
|
32 | else if (pixel_size < 0x1000000) |
277 | 32 | packed_size = bytestream2_get_le24(gbc); | |
278 | else | ||
279 | ✗ | packed_size = bytestream2_get_le32(gbc); | |
280 | |||
281 | ff_dlog(avctx, "pixel_size %d packed_size %d.\n", pixel_size, packed_size); | ||
282 | |||
283 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (packed_size < 0) { |
284 | ✗ | av_log(avctx, AV_LOG_ERROR, "Invalid tile size %d\n", packed_size); | |
285 | ✗ | ret = AVERROR_INVALIDDATA; | |
286 | ✗ | goto end; | |
287 | } | ||
288 | |||
289 | /* Get pixels buffer, it may be deflated or just raw */ | ||
290 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (pixel_size == packed_size) { |
291 | ✗ | if (bytestream2_get_bytes_left(gbc) < pixel_size) { | |
292 | ✗ | av_log(avctx, AV_LOG_ERROR, "Insufficient input for %d\n", pixel_size); | |
293 | ✗ | ret = AVERROR_INVALIDDATA; | |
294 | ✗ | goto end; | |
295 | } | ||
296 | ✗ | pixels = gbc->buffer; | |
297 | } else { | ||
298 | 32 | uLongf len = ctx->inflated_size; | |
299 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 30 times.
|
32 | if (bytestream2_get_bytes_left(gbc) < packed_size) { |
300 | 2 | av_log(avctx, AV_LOG_ERROR, "Insufficient input for %d\n", packed_size); | |
301 | 2 | ret = AVERROR_INVALIDDATA; | |
302 | 2 | goto end; | |
303 | } | ||
304 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | if (ctx->inflated_size < pixel_size) { |
305 | ✗ | ret = AVERROR_INVALIDDATA; | |
306 | ✗ | goto end; | |
307 | } | ||
308 | 30 | ret = uncompress(ctx->inflated_buf, &len, gbc->buffer, packed_size); | |
309 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | if (ret) { |
310 | ✗ | av_log(avctx, AV_LOG_ERROR, "Pixel deflate error %d.\n", ret); | |
311 | ✗ | ret = AVERROR_UNKNOWN; | |
312 | ✗ | goto end; | |
313 | } | ||
314 | 30 | pixels = ctx->inflated_buf; | |
315 | } | ||
316 | |||
317 | /* Allocate when needed */ | ||
318 | 30 | ret = ff_reget_buffer(avctx, ctx->reference, 0); | |
319 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | if (ret < 0) |
320 | ✗ | goto end; | |
321 | |||
322 | /* Pointer to actual pixels, will be updated when data is consumed */ | ||
323 | 30 | raw = pixels; | |
324 |
2/2✓ Branch 0 taken 664 times.
✓ Branch 1 taken 30 times.
|
694 | for (i = 0; i < tiles_nb; i++) { |
325 | 664 | uint8_t *dst = ctx->reference->data[0] + ctx->reference->linesize[0] * | |
326 | 664 | (avctx->height - ctx->tiles[i].y - 1) + | |
327 | 664 | ctx->tiles[i].x * ctx->component_size; | |
328 | 664 | av_image_copy_plane(dst, -1 * ctx->reference->linesize[0], | |
329 | 664 | raw, ctx->tiles[i].w * ctx->component_size, | |
330 | 664 | ctx->tiles[i].w * ctx->component_size, | |
331 | 664 | ctx->tiles[i].h); | |
332 | 664 | raw += ctx->tiles[i].w * ctx->component_size * ctx->tiles[i].h; | |
333 | } | ||
334 | |||
335 | /* Frame is ready to be output */ | ||
336 | 30 | ret = av_frame_ref(frame, ctx->reference); | |
337 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | if (ret < 0) |
338 | ✗ | goto end; | |
339 | |||
340 | /* Keyframe when the number of pixels updated matches the whole surface */ | ||
341 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 25 times.
|
30 | if (pixel_size == ctx->inflated_size) { |
342 | 5 | frame->pict_type = AV_PICTURE_TYPE_I; | |
343 | 5 | frame->flags |= AV_FRAME_FLAG_KEY; | |
344 | } else { | ||
345 | 25 | frame->pict_type = AV_PICTURE_TYPE_P; | |
346 | } | ||
347 | |||
348 | /* Palette handling */ | ||
349 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29 times.
|
30 | if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { |
350 | #if FF_API_PALETTE_HAS_CHANGED | ||
351 | FF_DISABLE_DEPRECATION_WARNINGS | ||
352 | 1 | frame->palette_has_changed = | |
353 | #endif | ||
354 | 1 | ff_copy_palette(ctx->palette, avpkt, avctx); | |
355 | #if FF_API_PALETTE_HAS_CHANGED | ||
356 | FF_ENABLE_DEPRECATION_WARNINGS | ||
357 | #endif | ||
358 | 1 | memcpy(frame->data[1], ctx->palette, AVPALETTE_SIZE); | |
359 | } | ||
360 | // We only return a picture when enough of it is undamaged, this avoids copying nearly broken frames around | ||
361 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 25 times.
|
30 | if (ctx->valid_pixels < ctx->inflated_size) |
362 | 5 | ctx->valid_pixels += pixel_size; | |
363 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30 | if (ctx->valid_pixels >= ctx->inflated_size * (100 - avctx->discard_damaged_percentage) / 100) |
364 | 30 | *got_frame = 1; | |
365 | |||
366 | 30 | ret = avpkt->size; | |
367 | 32 | end: | |
368 | 32 | av_free(inflated_tiles); | |
369 | 32 | return ret; | |
370 | } | ||
371 | |||
372 | const FFCodec ff_rscc_decoder = { | ||
373 | .p.name = "rscc", | ||
374 | CODEC_LONG_NAME("innoHeim/Rsupport Screen Capture Codec"), | ||
375 | .p.type = AVMEDIA_TYPE_VIDEO, | ||
376 | .p.id = AV_CODEC_ID_RSCC, | ||
377 | .init = rscc_init, | ||
378 | FF_CODEC_DECODE_CB(rscc_decode_frame), | ||
379 | .close = rscc_close, | ||
380 | .priv_data_size = sizeof(RsccContext), | ||
381 | .p.capabilities = AV_CODEC_CAP_DR1, | ||
382 | .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, | ||
383 | }; | ||
384 |