1/* 2 * Quicktime Video (RPZA) Video Decoder 3 * Copyright (C) 2003 the ffmpeg project 4 * 5 * This file is part of Libav. 6 * 7 * Libav 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 * Libav 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 Libav; 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 <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40 41#include "libavutil/intreadwrite.h" 42#include "avcodec.h" 43 44typedef struct RpzaContext { 45 46 AVCodecContext *avctx; 47 AVFrame frame; 48 49 const unsigned char *buf; 50 int size; 51 52} RpzaContext; 53 54#define ADVANCE_BLOCK() \ 55{ \ 56 pixel_ptr += 4; \ 57 if (pixel_ptr >= width) \ 58 { \ 59 pixel_ptr = 0; \ 60 row_ptr += stride * 4; \ 61 } \ 62 total_blocks--; \ 63 if (total_blocks < 0) \ 64 { \ 65 av_log(s->avctx, AV_LOG_ERROR, "warning: block counter just went negative (this should not happen)\n"); \ 66 return; \ 67 } \ 68} 69 70static void rpza_decode_stream(RpzaContext *s) 71{ 72 int width = s->avctx->width; 73 int stride = s->frame.linesize[0] / 2; 74 int row_inc = stride - 4; 75 int stream_ptr = 0; 76 int chunk_size; 77 unsigned char opcode; 78 int n_blocks; 79 unsigned short colorA = 0, colorB; 80 unsigned short color4[4]; 81 unsigned char index, idx; 82 unsigned short ta, tb; 83 unsigned short *pixels = (unsigned short *)s->frame.data[0]; 84 85 int row_ptr = 0; 86 int pixel_ptr = 0; 87 int block_ptr; 88 int pixel_x, pixel_y; 89 int total_blocks; 90 91 /* First byte is always 0xe1. Warn if it's different */ 92 if (s->buf[stream_ptr] != 0xe1) 93 av_log(s->avctx, AV_LOG_ERROR, "First chunk byte is 0x%02x instead of 0xe1\n", 94 s->buf[stream_ptr]); 95 96 /* Get chunk size, ingnoring first byte */ 97 chunk_size = AV_RB32(&s->buf[stream_ptr]) & 0x00FFFFFF; 98 stream_ptr += 4; 99 100 /* If length mismatch use size from MOV file and try to decode anyway */ 101 if (chunk_size != s->size) 102 av_log(s->avctx, AV_LOG_ERROR, "MOV chunk size != encoded chunk size; using MOV chunk size\n"); 103 104 chunk_size = s->size; 105 106 /* Number of 4x4 blocks in frame. */ 107 total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); 108 109 /* Process chunk data */ 110 while (stream_ptr < chunk_size) { 111 opcode = s->buf[stream_ptr++]; /* Get opcode */ 112 113 n_blocks = (opcode & 0x1f) + 1; /* Extract block counter from opcode */ 114 115 /* If opcode MSbit is 0, we need more data to decide what to do */ 116 if ((opcode & 0x80) == 0) { 117 colorA = (opcode << 8) | (s->buf[stream_ptr++]); 118 opcode = 0; 119 if ((s->buf[stream_ptr] & 0x80) != 0) { 120 /* Must behave as opcode 110xxxxx, using colorA computed 121 * above. Use fake opcode 0x20 to enter switch block at 122 * the right place */ 123 opcode = 0x20; 124 n_blocks = 1; 125 } 126 } 127 128 switch (opcode & 0xe0) { 129 130 /* Skip blocks */ 131 case 0x80: 132 while (n_blocks--) { 133 ADVANCE_BLOCK(); 134 } 135 break; 136 137 /* Fill blocks with one color */ 138 case 0xa0: 139 colorA = AV_RB16 (&s->buf[stream_ptr]); 140 stream_ptr += 2; 141 while (n_blocks--) { 142 block_ptr = row_ptr + pixel_ptr; 143 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 144 for (pixel_x = 0; pixel_x < 4; pixel_x++){ 145 pixels[block_ptr] = colorA; 146 block_ptr++; 147 } 148 block_ptr += row_inc; 149 } 150 ADVANCE_BLOCK(); 151 } 152 break; 153 154 /* Fill blocks with 4 colors */ 155 case 0xc0: 156 colorA = AV_RB16 (&s->buf[stream_ptr]); 157 stream_ptr += 2; 158 case 0x20: 159 colorB = AV_RB16 (&s->buf[stream_ptr]); 160 stream_ptr += 2; 161 162 /* sort out the colors */ 163 color4[0] = colorB; 164 color4[1] = 0; 165 color4[2] = 0; 166 color4[3] = colorA; 167 168 /* red components */ 169 ta = (colorA >> 10) & 0x1F; 170 tb = (colorB >> 10) & 0x1F; 171 color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10; 172 color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10; 173 174 /* green components */ 175 ta = (colorA >> 5) & 0x1F; 176 tb = (colorB >> 5) & 0x1F; 177 color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5; 178 color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5; 179 180 /* blue components */ 181 ta = colorA & 0x1F; 182 tb = colorB & 0x1F; 183 color4[1] |= ((11 * ta + 21 * tb) >> 5); 184 color4[2] |= ((21 * ta + 11 * tb) >> 5); 185 186 if (s->size - stream_ptr < n_blocks * 4) 187 return; 188 while (n_blocks--) { 189 block_ptr = row_ptr + pixel_ptr; 190 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 191 index = s->buf[stream_ptr++]; 192 for (pixel_x = 0; pixel_x < 4; pixel_x++){ 193 idx = (index >> (2 * (3 - pixel_x))) & 0x03; 194 pixels[block_ptr] = color4[idx]; 195 block_ptr++; 196 } 197 block_ptr += row_inc; 198 } 199 ADVANCE_BLOCK(); 200 } 201 break; 202 203 /* Fill block with 16 colors */ 204 case 0x00: 205 if (s->size - stream_ptr < 16) 206 return; 207 block_ptr = row_ptr + pixel_ptr; 208 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 209 for (pixel_x = 0; pixel_x < 4; pixel_x++){ 210 /* We already have color of upper left pixel */ 211 if ((pixel_y != 0) || (pixel_x !=0)) { 212 colorA = AV_RB16 (&s->buf[stream_ptr]); 213 stream_ptr += 2; 214 } 215 pixels[block_ptr] = colorA; 216 block_ptr++; 217 } 218 block_ptr += row_inc; 219 } 220 ADVANCE_BLOCK(); 221 break; 222 223 /* Unknown opcode */ 224 default: 225 av_log(s->avctx, AV_LOG_ERROR, "Unknown opcode %d in rpza chunk." 226 " Skip remaining %d bytes of chunk data.\n", opcode, 227 chunk_size - stream_ptr); 228 return; 229 } /* Opcode switch */ 230 } 231} 232 233static av_cold int rpza_decode_init(AVCodecContext *avctx) 234{ 235 RpzaContext *s = avctx->priv_data; 236 237 s->avctx = avctx; 238 avctx->pix_fmt = PIX_FMT_RGB555; 239 240 s->frame.data[0] = NULL; 241 242 return 0; 243} 244 245static int rpza_decode_frame(AVCodecContext *avctx, 246 void *data, int *data_size, 247 AVPacket *avpkt) 248{ 249 const uint8_t *buf = avpkt->data; 250 int buf_size = avpkt->size; 251 RpzaContext *s = avctx->priv_data; 252 253 s->buf = buf; 254 s->size = buf_size; 255 256 s->frame.reference = 1; 257 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE; 258 if (avctx->reget_buffer(avctx, &s->frame)) { 259 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); 260 return -1; 261 } 262 263 rpza_decode_stream(s); 264 265 *data_size = sizeof(AVFrame); 266 *(AVFrame*)data = s->frame; 267 268 /* always report that the buffer was completely consumed */ 269 return buf_size; 270} 271 272static av_cold int rpza_decode_end(AVCodecContext *avctx) 273{ 274 RpzaContext *s = avctx->priv_data; 275 276 if (s->frame.data[0]) 277 avctx->release_buffer(avctx, &s->frame); 278 279 return 0; 280} 281 282AVCodec ff_rpza_decoder = { 283 .name = "rpza", 284 .type = AVMEDIA_TYPE_VIDEO, 285 .id = CODEC_ID_RPZA, 286 .priv_data_size = sizeof(RpzaContext), 287 .init = rpza_decode_init, 288 .close = rpza_decode_end, 289 .decode = rpza_decode_frame, 290 .capabilities = CODEC_CAP_DR1, 291 .long_name = NULL_IF_CONFIG_SMALL("QuickTime video (RPZA)"), 292}; 293