| 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 | 1 | ff_copy_palette(ctx->palette, avpkt, avctx); | |
| 351 | 1 | memcpy(frame->data[1], ctx->palette, AVPALETTE_SIZE); | |
| 352 | } | ||
| 353 | // We only return a picture when enough of it is undamaged, this avoids copying nearly broken frames around | ||
| 354 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 25 times.
|
30 | if (ctx->valid_pixels < ctx->inflated_size) |
| 355 | 5 | ctx->valid_pixels += pixel_size; | |
| 356 |
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) |
| 357 | 30 | *got_frame = 1; | |
| 358 | |||
| 359 | 30 | ret = avpkt->size; | |
| 360 | 32 | end: | |
| 361 | 32 | av_free(inflated_tiles); | |
| 362 | 32 | return ret; | |
| 363 | } | ||
| 364 | |||
| 365 | const FFCodec ff_rscc_decoder = { | ||
| 366 | .p.name = "rscc", | ||
| 367 | CODEC_LONG_NAME("innoHeim/Rsupport Screen Capture Codec"), | ||
| 368 | .p.type = AVMEDIA_TYPE_VIDEO, | ||
| 369 | .p.id = AV_CODEC_ID_RSCC, | ||
| 370 | .init = rscc_init, | ||
| 371 | FF_CODEC_DECODE_CB(rscc_decode_frame), | ||
| 372 | .close = rscc_close, | ||
| 373 | .priv_data_size = sizeof(RsccContext), | ||
| 374 | .p.capabilities = AV_CODEC_CAP_DR1, | ||
| 375 | .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, | ||
| 376 | }; | ||
| 377 |