| Line | Branch | Exec | Source | 
|---|---|---|---|
| 1 | /* | ||
| 2 | * Quicktime Video (RPZA) Video Decoder | ||
| 3 | * Copyright (C) 2003 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 | ||
| 24 | * QT RPZA Video Decoder by Roberto Togni | ||
| 25 | * For more information about the RPZA format, visit: | ||
| 26 | * http://www.pcisys.net/~melanson/codecs/ | ||
| 27 | * | ||
| 28 | * The RPZA decoder outputs RGB555 colorspace data. | ||
| 29 | * | ||
| 30 | * Note that this decoder reads big endian RGB555 pixel values from the | ||
| 31 | * bytestream, arranges them in the host's endian order, and outputs | ||
| 32 | * them to the final rendered map in the same host endian order. This is | ||
| 33 | * intended behavior as the libavcodec documentation states that RGB555 | ||
| 34 | * pixels shall be stored in native CPU endianness. | ||
| 35 | */ | ||
| 36 | |||
| 37 | #include <stdint.h> | ||
| 38 | |||
| 39 | #include "libavutil/internal.h" | ||
| 40 | #include "avcodec.h" | ||
| 41 | #include "bytestream.h" | ||
| 42 | #include "codec_internal.h" | ||
| 43 | #include "decode.h" | ||
| 44 | |||
| 45 | typedef struct RpzaContext { | ||
| 46 | |||
| 47 | AVCodecContext *avctx; | ||
| 48 | AVFrame *frame; | ||
| 49 | |||
| 50 | GetByteContext gb; | ||
| 51 | } RpzaContext; | ||
| 52 | |||
| 53 | #define CHECK_BLOCK() \ | ||
| 54 | if (total_blocks < 1) { \ | ||
| 55 | av_log(s->avctx, AV_LOG_ERROR, \ | ||
| 56 | "Block counter just went negative (this should not happen)\n"); \ | ||
| 57 | return AVERROR_INVALIDDATA; \ | ||
| 58 | } \ | ||
| 59 | |||
| 60 | #define ADVANCE_BLOCK() \ | ||
| 61 | { \ | ||
| 62 | pixel_ptr += 4; \ | ||
| 63 | if (pixel_ptr >= width) \ | ||
| 64 | { \ | ||
| 65 | pixel_ptr = 0; \ | ||
| 66 | row_ptr += stride * 4; \ | ||
| 67 | } \ | ||
| 68 | total_blocks--; \ | ||
| 69 | } | ||
| 70 | |||
| 71 | 234 | static int rpza_decode_stream(RpzaContext *s) | |
| 72 | { | ||
| 73 | 234 | int width = s->avctx->width; | |
| 74 | int stride, row_inc, ret; | ||
| 75 | int chunk_size; | ||
| 76 | 234 | uint16_t colorA = 0, colorB; | |
| 77 | uint16_t color4[4]; | ||
| 78 | uint16_t ta, tb; | ||
| 79 | uint16_t *pixels; | ||
| 80 | |||
| 81 | 234 | int row_ptr = 0; | |
| 82 | 234 | int pixel_ptr = 0; | |
| 83 | int block_ptr; | ||
| 84 | int pixel_x, pixel_y; | ||
| 85 | int total_blocks; | ||
| 86 | |||
| 87 | /* First byte is always 0xe1. Warn if it's different */ | ||
| 88 | 
        1/2✗ Branch 1 not taken. 
          ✓ Branch 2 taken 234 times. 
         | 
      234 | if (bytestream2_peek_byte(&s->gb) != 0xe1) | 
| 89 | ✗ | av_log(s->avctx, AV_LOG_ERROR, "First chunk byte is 0x%02x instead of 0xe1\n", | |
| 90 | ✗ | bytestream2_peek_byte(&s->gb)); | |
| 91 | |||
| 92 | /* Get chunk size, ignoring first byte */ | ||
| 93 | 234 | chunk_size = bytestream2_get_be32(&s->gb) & 0x00FFFFFF; | |
| 94 | |||
| 95 | /* If length mismatch use size from MOV file and try to decode anyway */ | ||
| 96 | 
        1/2✗ Branch 1 not taken. 
          ✓ Branch 2 taken 234 times. 
         | 
      234 | if (chunk_size != bytestream2_get_bytes_left(&s->gb) + 4) | 
| 97 | ✗ | av_log(s->avctx, AV_LOG_WARNING, | |
| 98 | "MOV chunk size %d != encoded chunk size %d\n", | ||
| 99 | chunk_size, | ||
| 100 | ✗ | bytestream2_get_bytes_left(&s->gb) + 4 | |
| 101 | ); | ||
| 102 | |||
| 103 | /* Number of 4x4 blocks in frame. */ | ||
| 104 | 234 | total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); | |
| 105 | |||
| 106 | 
        1/2✗ Branch 1 not taken. 
          ✓ Branch 2 taken 234 times. 
         | 
      234 | if (total_blocks / 32 > bytestream2_get_bytes_left(&s->gb)) | 
| 107 | ✗ | return AVERROR_INVALIDDATA; | |
| 108 | |||
| 109 | 
        1/2✗ Branch 1 not taken. 
          ✓ Branch 2 taken 234 times. 
         | 
      234 | if ((ret = ff_reget_buffer(s->avctx, s->frame, 0)) < 0) | 
| 110 | ✗ | return ret; | |
| 111 | 234 | pixels = (uint16_t *)s->frame->data[0]; | |
| 112 | 234 | stride = s->frame->linesize[0] / 2; | |
| 113 | 234 | row_inc = stride - 4; | |
| 114 | |||
| 115 | /* Process chunk data */ | ||
| 116 | 
        2/2✓ Branch 1 taken 1018575 times. 
          ✓ Branch 2 taken 234 times. 
         | 
      1018809 | while (bytestream2_get_bytes_left(&s->gb)) { | 
| 117 | 1018575 | uint8_t opcode = bytestream2_get_byte(&s->gb); /* Get opcode */ | |
| 118 | |||
| 119 | 1018575 | int n_blocks = (opcode & 0x1f) + 1; /* Extract block counter from opcode */ | |
| 120 | |||
| 121 | /* If opcode MSbit is 0, we need more data to decide what to do */ | ||
| 122 | 
        2/2✓ Branch 0 taken 904267 times. 
          ✓ Branch 1 taken 114308 times. 
         | 
      1018575 | if ((opcode & 0x80) == 0) { | 
| 123 | 904267 | colorA = (opcode << 8) | bytestream2_get_byte(&s->gb); | |
| 124 | 904267 | opcode = 0; | |
| 125 | 
        2/2✓ Branch 1 taken 53166 times. 
          ✓ Branch 2 taken 851101 times. 
         | 
      904267 | if ((bytestream2_peek_byte(&s->gb) & 0x80) != 0) { | 
| 126 | /* Must behave as opcode 110xxxxx, using colorA computed | ||
| 127 | * above. Use fake opcode 0x20 to enter switch block at | ||
| 128 | * the right place */ | ||
| 129 | 53166 | opcode = 0x20; | |
| 130 | 53166 | n_blocks = 1; | |
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | 1018575 | n_blocks = FFMIN(n_blocks, total_blocks); | |
| 135 | |||
| 136 | 
        4/6✓ Branch 0 taken 13761 times. 
          ✓ Branch 1 taken 100547 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 53166 times. 
          ✓ Branch 4 taken 851101 times. 
          ✗ Branch 5 not taken. 
         | 
      1018575 | switch (opcode & 0xe0) { | 
| 137 | |||
| 138 | /* Skip blocks */ | ||
| 139 | 13761 | case 0x80: | |
| 140 | 
        2/2✓ Branch 0 taken 111297 times. 
          ✓ Branch 1 taken 13761 times. 
         | 
      125058 | while (n_blocks--) { | 
| 141 | 
        1/2✗ Branch 0 not taken. 
          ✓ Branch 1 taken 111297 times. 
         | 
      111297 | CHECK_BLOCK(); | 
| 142 | 
        2/2✓ Branch 0 taken 1786 times. 
          ✓ Branch 1 taken 109511 times. 
         | 
      111297 | ADVANCE_BLOCK(); | 
| 143 | } | ||
| 144 | 13761 | break; | |
| 145 | |||
| 146 | /* Fill blocks with one color */ | ||
| 147 | 100547 | case 0xa0: | |
| 148 | 100547 | colorA = bytestream2_get_be16(&s->gb); | |
| 149 | 
        2/2✓ Branch 0 taken 102086 times. 
          ✓ Branch 1 taken 100547 times. 
         | 
      202633 | while (n_blocks--) { | 
| 150 | 
        1/2✗ Branch 0 not taken. 
          ✓ Branch 1 taken 102086 times. 
         | 
      102086 | CHECK_BLOCK(); | 
| 151 | 102086 | block_ptr = row_ptr + pixel_ptr; | |
| 152 | 
        2/2✓ Branch 0 taken 408344 times. 
          ✓ Branch 1 taken 102086 times. 
         | 
      510430 | for (pixel_y = 0; pixel_y < 4; pixel_y++) { | 
| 153 | 
        2/2✓ Branch 0 taken 1633376 times. 
          ✓ Branch 1 taken 408344 times. 
         | 
      2041720 | for (pixel_x = 0; pixel_x < 4; pixel_x++){ | 
| 154 | 1633376 | pixels[block_ptr] = colorA; | |
| 155 | 1633376 | block_ptr++; | |
| 156 | } | ||
| 157 | 408344 | block_ptr += row_inc; | |
| 158 | } | ||
| 159 | 
        2/2✓ Branch 0 taken 1024 times. 
          ✓ Branch 1 taken 101062 times. 
         | 
      102086 | ADVANCE_BLOCK(); | 
| 160 | } | ||
| 161 | 100547 | break; | |
| 162 | |||
| 163 | /* Fill blocks with 4 colors */ | ||
| 164 | ✗ | case 0xc0: | |
| 165 | ✗ | colorA = bytestream2_get_be16(&s->gb); | |
| 166 | 53166 | case 0x20: | |
| 167 | 53166 | colorB = bytestream2_get_be16(&s->gb); | |
| 168 | |||
| 169 | /* sort out the colors */ | ||
| 170 | 53166 | color4[0] = colorB; | |
| 171 | 53166 | color4[1] = 0; | |
| 172 | 53166 | color4[2] = 0; | |
| 173 | 53166 | color4[3] = colorA; | |
| 174 | |||
| 175 | /* red components */ | ||
| 176 | 53166 | ta = (colorA >> 10) & 0x1F; | |
| 177 | 53166 | tb = (colorB >> 10) & 0x1F; | |
| 178 | 53166 | color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10; | |
| 179 | 53166 | color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10; | |
| 180 | |||
| 181 | /* green components */ | ||
| 182 | 53166 | ta = (colorA >> 5) & 0x1F; | |
| 183 | 53166 | tb = (colorB >> 5) & 0x1F; | |
| 184 | 53166 | color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5; | |
| 185 | 53166 | color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5; | |
| 186 | |||
| 187 | /* blue components */ | ||
| 188 | 53166 | ta = colorA & 0x1F; | |
| 189 | 53166 | tb = colorB & 0x1F; | |
| 190 | 53166 | color4[1] |= ((11 * ta + 21 * tb) >> 5); | |
| 191 | 53166 | color4[2] |= ((21 * ta + 11 * tb) >> 5); | |
| 192 | |||
| 193 | 
        1/2✗ Branch 1 not taken. 
          ✓ Branch 2 taken 53166 times. 
         | 
      53166 | if (bytestream2_get_bytes_left(&s->gb) < n_blocks * 4) | 
| 194 | ✗ | return AVERROR_INVALIDDATA; | |
| 195 | 
        2/2✓ Branch 0 taken 53166 times. 
          ✓ Branch 1 taken 53166 times. 
         | 
      106332 | while (n_blocks--) { | 
| 196 | 
        1/2✗ Branch 0 not taken. 
          ✓ Branch 1 taken 53166 times. 
         | 
      53166 | CHECK_BLOCK(); | 
| 197 | 53166 | block_ptr = row_ptr + pixel_ptr; | |
| 198 | 
        2/2✓ Branch 0 taken 212664 times. 
          ✓ Branch 1 taken 53166 times. 
         | 
      265830 | for (pixel_y = 0; pixel_y < 4; pixel_y++) { | 
| 199 | 212664 | uint8_t index = bytestream2_get_byteu(&s->gb); | |
| 200 | 
        2/2✓ Branch 0 taken 850656 times. 
          ✓ Branch 1 taken 212664 times. 
         | 
      1063320 | for (pixel_x = 0; pixel_x < 4; pixel_x++){ | 
| 201 | 850656 | uint8_t idx = (index >> (2 * (3 - pixel_x))) & 0x03; | |
| 202 | 850656 | pixels[block_ptr] = color4[idx]; | |
| 203 | 850656 | block_ptr++; | |
| 204 | } | ||
| 205 | 212664 | block_ptr += row_inc; | |
| 206 | } | ||
| 207 | 
        2/2✓ Branch 0 taken 172 times. 
          ✓ Branch 1 taken 52994 times. 
         | 
      53166 | ADVANCE_BLOCK(); | 
| 208 | } | ||
| 209 | 53166 | break; | |
| 210 | |||
| 211 | /* Fill block with 16 colors */ | ||
| 212 | 851101 | case 0x00: | |
| 213 | 
        1/2✗ Branch 1 not taken. 
          ✓ Branch 2 taken 851101 times. 
         | 
      851101 | if (bytestream2_get_bytes_left(&s->gb) < 30) | 
| 214 | ✗ | return AVERROR_INVALIDDATA; | |
| 215 | 
        1/2✗ Branch 0 not taken. 
          ✓ Branch 1 taken 851101 times. 
         | 
      851101 | CHECK_BLOCK(); | 
| 216 | 851101 | block_ptr = row_ptr + pixel_ptr; | |
| 217 | 
        2/2✓ Branch 0 taken 3404404 times. 
          ✓ Branch 1 taken 851101 times. 
         | 
      4255505 | for (pixel_y = 0; pixel_y < 4; pixel_y++) { | 
| 218 | 
        2/2✓ Branch 0 taken 13617616 times. 
          ✓ Branch 1 taken 3404404 times. 
         | 
      17022020 | for (pixel_x = 0; pixel_x < 4; pixel_x++){ | 
| 219 | /* We already have color of upper left pixel */ | ||
| 220 | 
        4/4✓ Branch 0 taken 3404404 times. 
          ✓ Branch 1 taken 10213212 times. 
          ✓ Branch 2 taken 2553303 times. 
          ✓ Branch 3 taken 851101 times. 
         | 
      13617616 | if ((pixel_y != 0) || (pixel_x != 0)) | 
| 221 | 12766515 | colorA = bytestream2_get_be16u(&s->gb); | |
| 222 | 13617616 | pixels[block_ptr] = colorA; | |
| 223 | 13617616 | block_ptr++; | |
| 224 | } | ||
| 225 | 3404404 | block_ptr += row_inc; | |
| 226 | } | ||
| 227 | 
        2/2✓ Branch 0 taken 10308 times. 
          ✓ Branch 1 taken 840793 times. 
         | 
      851101 | ADVANCE_BLOCK(); | 
| 228 | 851101 | break; | |
| 229 | |||
| 230 | /* Unknown opcode */ | ||
| 231 | ✗ | default: | |
| 232 | ✗ | av_log(s->avctx, AV_LOG_ERROR, "Unknown opcode %d in rpza chunk." | |
| 233 | " Skip remaining %d bytes of chunk data.\n", opcode, | ||
| 234 | ✗ | bytestream2_get_bytes_left(&s->gb)); | |
| 235 | ✗ | return AVERROR_INVALIDDATA; | |
| 236 | } /* Opcode switch */ | ||
| 237 | } | ||
| 238 | |||
| 239 | 234 | return 0; | |
| 240 | } | ||
| 241 | |||
| 242 | 10 | static av_cold int rpza_decode_init(AVCodecContext *avctx) | |
| 243 | { | ||
| 244 | 10 | RpzaContext *s = avctx->priv_data; | |
| 245 | |||
| 246 | 10 | s->avctx = avctx; | |
| 247 | 10 | avctx->pix_fmt = AV_PIX_FMT_RGB555; | |
| 248 | |||
| 249 | 10 | s->frame = av_frame_alloc(); | |
| 250 | 
        1/2✗ Branch 0 not taken. 
          ✓ Branch 1 taken 10 times. 
         | 
      10 | if (!s->frame) | 
| 251 | ✗ | return AVERROR(ENOMEM); | |
| 252 | |||
| 253 | 10 | return 0; | |
| 254 | } | ||
| 255 | |||
| 256 | 234 | static int rpza_decode_frame(AVCodecContext *avctx, AVFrame *rframe, | |
| 257 | int *got_frame, AVPacket *avpkt) | ||
| 258 | { | ||
| 259 | 234 | RpzaContext *s = avctx->priv_data; | |
| 260 | int ret; | ||
| 261 | |||
| 262 | 234 | bytestream2_init(&s->gb, avpkt->data, avpkt->size); | |
| 263 | |||
| 264 | 234 | ret = rpza_decode_stream(s); | |
| 265 | 
        1/2✗ Branch 0 not taken. 
          ✓ Branch 1 taken 234 times. 
         | 
      234 | if (ret < 0) | 
| 266 | ✗ | return ret; | |
| 267 | |||
| 268 | 
        1/2✗ Branch 1 not taken. 
          ✓ Branch 2 taken 234 times. 
         | 
      234 | if ((ret = av_frame_ref(rframe, s->frame)) < 0) | 
| 269 | ✗ | return ret; | |
| 270 | |||
| 271 | 234 | *got_frame = 1; | |
| 272 | |||
| 273 | /* always report that the buffer was completely consumed */ | ||
| 274 | 234 | return avpkt->size; | |
| 275 | } | ||
| 276 | |||
| 277 | 10 | static av_cold int rpza_decode_end(AVCodecContext *avctx) | |
| 278 | { | ||
| 279 | 10 | RpzaContext *s = avctx->priv_data; | |
| 280 | |||
| 281 | 10 | av_frame_free(&s->frame); | |
| 282 | |||
| 283 | 10 | return 0; | |
| 284 | } | ||
| 285 | |||
| 286 | const FFCodec ff_rpza_decoder = { | ||
| 287 | .p.name = "rpza", | ||
| 288 | CODEC_LONG_NAME("QuickTime video (RPZA)"), | ||
| 289 | .p.type = AVMEDIA_TYPE_VIDEO, | ||
| 290 | .p.id = AV_CODEC_ID_RPZA, | ||
| 291 | .priv_data_size = sizeof(RpzaContext), | ||
| 292 | .init = rpza_decode_init, | ||
| 293 | .close = rpza_decode_end, | ||
| 294 | FF_CODEC_DECODE_CB(rpza_decode_frame), | ||
| 295 | .p.capabilities = AV_CODEC_CAP_DR1, | ||
| 296 | }; | ||
| 297 |