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