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