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 ffmpeg documentation states that RGB555 pixels 34 * 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 while (n_blocks--) { 187 block_ptr = row_ptr + pixel_ptr; 188 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 189 index = s->buf[stream_ptr++]; 190 for (pixel_x = 0; pixel_x < 4; pixel_x++){ 191 idx = (index >> (2 * (3 - pixel_x))) & 0x03; 192 pixels[block_ptr] = color4[idx]; 193 block_ptr++; 194 } 195 block_ptr += row_inc; 196 } 197 ADVANCE_BLOCK(); 198 } 199 break; 200 201 /* Fill block with 16 colors */ 202 case 0x00: 203 block_ptr = row_ptr + pixel_ptr; 204 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 205 for (pixel_x = 0; pixel_x < 4; pixel_x++){ 206 /* We already have color of upper left pixel */ 207 if ((pixel_y != 0) || (pixel_x !=0)) { 208 colorA = AV_RB16 (&s->buf[stream_ptr]); 209 stream_ptr += 2; 210 } 211 pixels[block_ptr] = colorA; 212 block_ptr++; 213 } 214 block_ptr += row_inc; 215 } 216 ADVANCE_BLOCK(); 217 break; 218 219 /* Unknown opcode */ 220 default: 221 av_log(s->avctx, AV_LOG_ERROR, "Unknown opcode %d in rpza chunk." 222 " Skip remaining %d bytes of chunk data.\n", opcode, 223 chunk_size - stream_ptr); 224 return; 225 } /* Opcode switch */ 226 } 227} 228 229static av_cold int rpza_decode_init(AVCodecContext *avctx) 230{ 231 RpzaContext *s = avctx->priv_data; 232 233 s->avctx = avctx; 234 avctx->pix_fmt = PIX_FMT_RGB555; 235 236 s->frame.data[0] = NULL; 237 238 return 0; 239} 240 241static int rpza_decode_frame(AVCodecContext *avctx, 242 void *data, int *data_size, 243 AVPacket *avpkt) 244{ 245 const uint8_t *buf = avpkt->data; 246 int buf_size = avpkt->size; 247 RpzaContext *s = avctx->priv_data; 248 249 s->buf = buf; 250 s->size = buf_size; 251 252 s->frame.reference = 1; 253 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE; 254 if (avctx->reget_buffer(avctx, &s->frame)) { 255 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); 256 return -1; 257 } 258 259 rpza_decode_stream(s); 260 261 *data_size = sizeof(AVFrame); 262 *(AVFrame*)data = s->frame; 263 264 /* always report that the buffer was completely consumed */ 265 return buf_size; 266} 267 268static av_cold int rpza_decode_end(AVCodecContext *avctx) 269{ 270 RpzaContext *s = avctx->priv_data; 271 272 if (s->frame.data[0]) 273 avctx->release_buffer(avctx, &s->frame); 274 275 return 0; 276} 277 278AVCodec rpza_decoder = { 279 "rpza", 280 AVMEDIA_TYPE_VIDEO, 281 CODEC_ID_RPZA, 282 sizeof(RpzaContext), 283 rpza_decode_init, 284 NULL, 285 rpza_decode_end, 286 rpza_decode_frame, 287 CODEC_CAP_DR1, 288 .long_name = NULL_IF_CONFIG_SMALL("QuickTime video (RPZA)"), 289}; 290