1/* 2 * RL2 Video Decoder 3 * Copyright (C) 2008 Sascha Sommer (saschasommer@freenet.de) 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 * RL2 Video Decoder 25 * @author Sascha Sommer (saschasommer@freenet.de) 26 * @see http://wiki.multimedia.cx/index.php?title=RL2 27 */ 28 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32 33#include "libavutil/intreadwrite.h" 34#include "avcodec.h" 35 36 37#define EXTRADATA1_SIZE (6 + 256 * 3) ///< video base, clr count, palette 38 39typedef struct Rl2Context { 40 AVCodecContext *avctx; 41 AVFrame frame; 42 43 unsigned short video_base; ///< initial drawing offset 44 unsigned int clr_count; ///< number of used colors (currently unused) 45 unsigned char* back_frame; ///< background frame 46 unsigned int palette[AVPALETTE_COUNT]; 47} Rl2Context; 48 49/** 50 * Run Length Decode a single 320x200 frame 51 * @param s rl2 context 52 * @param in input buffer 53 * @param size input buffer size 54 * @param out ouput buffer 55 * @param stride stride of the output buffer 56 * @param video_base offset of the rle data inside the frame 57 */ 58static void rl2_rle_decode(Rl2Context *s,const unsigned char* in,int size, 59 unsigned char* out,int stride,int video_base){ 60 int base_x = video_base % s->avctx->width; 61 int base_y = video_base / s->avctx->width; 62 int stride_adj = stride - s->avctx->width; 63 int i; 64 const unsigned char* back_frame = s->back_frame; 65 const unsigned char* in_end = in + size; 66 const unsigned char* out_end = out + stride * s->avctx->height; 67 unsigned char* line_end = out + s->avctx->width; 68 69 /** copy start of the background frame */ 70 for(i=0;i<=base_y;i++){ 71 if(s->back_frame) 72 memcpy(out,back_frame,s->avctx->width); 73 out += stride; 74 back_frame += s->avctx->width; 75 } 76 back_frame += base_x - s->avctx->width; 77 line_end = out - stride_adj; 78 out += base_x - stride; 79 80 /** decode the variable part of the frame */ 81 while(in < in_end){ 82 unsigned char val = *in++; 83 int len = 1; 84 if(val >= 0x80){ 85 if(in >= in_end) 86 break; 87 len = *in++; 88 if(!len) 89 break; 90 } 91 92 if(len >= out_end - out) 93 break; 94 95 if(s->back_frame) 96 val |= 0x80; 97 else 98 val &= ~0x80; 99 100 while(len--){ 101 *out++ = (val == 0x80)? *back_frame:val; 102 back_frame++; 103 if(out == line_end){ 104 out += stride_adj; 105 line_end += stride; 106 if(len >= out_end - out) 107 break; 108 } 109 } 110 } 111 112 /** copy the rest from the background frame */ 113 if(s->back_frame){ 114 while(out < out_end){ 115 memcpy(out, back_frame, line_end - out); 116 back_frame += line_end - out; 117 out = line_end + stride_adj; 118 line_end += stride; 119 } 120 } 121} 122 123 124/** 125 * Initialize the decoder 126 * @param avctx decoder context 127 * @return 0 success, -1 on error 128 */ 129static av_cold int rl2_decode_init(AVCodecContext *avctx) 130{ 131 Rl2Context *s = avctx->priv_data; 132 int back_size; 133 int i; 134 s->avctx = avctx; 135 avctx->pix_fmt = PIX_FMT_PAL8; 136 137 /** parse extra data */ 138 if(!avctx->extradata || avctx->extradata_size < EXTRADATA1_SIZE){ 139 av_log(avctx, AV_LOG_ERROR, "invalid extradata size\n"); 140 return -1; 141 } 142 143 /** get frame_offset */ 144 s->video_base = AV_RL16(&avctx->extradata[0]); 145 s->clr_count = AV_RL32(&avctx->extradata[2]); 146 147 if(s->video_base >= avctx->width * avctx->height){ 148 av_log(avctx, AV_LOG_ERROR, "invalid video_base\n"); 149 return -1; 150 } 151 152 /** initialize palette */ 153 for(i=0;i<AVPALETTE_COUNT;i++) 154 s->palette[i] = AV_RB24(&avctx->extradata[6 + i * 3]); 155 156 /** decode background frame if present */ 157 back_size = avctx->extradata_size - EXTRADATA1_SIZE; 158 159 if(back_size > 0){ 160 unsigned char* back_frame = av_mallocz(avctx->width*avctx->height); 161 if(!back_frame) 162 return -1; 163 rl2_rle_decode(s,avctx->extradata + EXTRADATA1_SIZE,back_size, 164 back_frame,avctx->width,0); 165 s->back_frame = back_frame; 166 } 167 return 0; 168} 169 170 171static int rl2_decode_frame(AVCodecContext *avctx, 172 void *data, int *data_size, 173 AVPacket *avpkt) 174{ 175 const uint8_t *buf = avpkt->data; 176 int buf_size = avpkt->size; 177 Rl2Context *s = avctx->priv_data; 178 179 if(s->frame.data[0]) 180 avctx->release_buffer(avctx, &s->frame); 181 182 /** get buffer */ 183 s->frame.reference= 0; 184 if(avctx->get_buffer(avctx, &s->frame)) { 185 av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n"); 186 return -1; 187 } 188 189 /** run length decode */ 190 rl2_rle_decode(s,buf,buf_size,s->frame.data[0],s->frame.linesize[0],s->video_base); 191 192 /** make the palette available on the way out */ 193 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); 194 195 *data_size = sizeof(AVFrame); 196 *(AVFrame*)data = s->frame; 197 198 /** report that the buffer was completely consumed */ 199 return buf_size; 200} 201 202 203/** 204 * Uninit decoder 205 * @param avctx decoder context 206 * @return 0 success, -1 on error 207 */ 208static av_cold int rl2_decode_end(AVCodecContext *avctx) 209{ 210 Rl2Context *s = avctx->priv_data; 211 212 if(s->frame.data[0]) 213 avctx->release_buffer(avctx, &s->frame); 214 215 av_free(s->back_frame); 216 217 return 0; 218} 219 220 221AVCodec ff_rl2_decoder = { 222 .name = "rl2", 223 .type = AVMEDIA_TYPE_VIDEO, 224 .id = CODEC_ID_RL2, 225 .priv_data_size = sizeof(Rl2Context), 226 .init = rl2_decode_init, 227 .close = rl2_decode_end, 228 .decode = rl2_decode_frame, 229 .capabilities = CODEC_CAP_DR1, 230 .long_name = NULL_IF_CONFIG_SMALL("RL2 video"), 231}; 232 233