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