Libav
|
00001 /* 00002 * QPEG codec 00003 * Copyright (c) 2004 Konstantin Shishkov 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00027 #include "avcodec.h" 00028 00029 typedef struct QpegContext{ 00030 AVCodecContext *avctx; 00031 AVFrame pic; 00032 uint8_t *refdata; 00033 } QpegContext; 00034 00035 static void qpeg_decode_intra(const uint8_t *src, uint8_t *dst, int size, 00036 int stride, int width, int height) 00037 { 00038 int i; 00039 int code; 00040 int c0, c1; 00041 int run, copy; 00042 int filled = 0; 00043 int rows_to_go; 00044 00045 rows_to_go = height; 00046 height--; 00047 dst = dst + height * stride; 00048 00049 while((size > 0) && (rows_to_go > 0)) { 00050 code = *src++; 00051 size--; 00052 run = copy = 0; 00053 if(code == 0xFC) /* end-of-picture code */ 00054 break; 00055 if(code >= 0xF8) { /* very long run */ 00056 c0 = *src++; 00057 c1 = *src++; 00058 size -= 2; 00059 run = ((code & 0x7) << 16) + (c0 << 8) + c1 + 2; 00060 } else if (code >= 0xF0) { /* long run */ 00061 c0 = *src++; 00062 size--; 00063 run = ((code & 0xF) << 8) + c0 + 2; 00064 } else if (code >= 0xE0) { /* short run */ 00065 run = (code & 0x1F) + 2; 00066 } else if (code >= 0xC0) { /* very long copy */ 00067 c0 = *src++; 00068 c1 = *src++; 00069 size -= 2; 00070 copy = ((code & 0x3F) << 16) + (c0 << 8) + c1 + 1; 00071 } else if (code >= 0x80) { /* long copy */ 00072 c0 = *src++; 00073 size--; 00074 copy = ((code & 0x7F) << 8) + c0 + 1; 00075 } else { /* short copy */ 00076 copy = code + 1; 00077 } 00078 00079 /* perform actual run or copy */ 00080 if(run) { 00081 int p; 00082 00083 p = *src++; 00084 size--; 00085 for(i = 0; i < run; i++) { 00086 dst[filled++] = p; 00087 if (filled >= width) { 00088 filled = 0; 00089 dst -= stride; 00090 rows_to_go--; 00091 if(rows_to_go <= 0) 00092 break; 00093 } 00094 } 00095 } else { 00096 size -= copy; 00097 for(i = 0; i < copy; i++) { 00098 dst[filled++] = *src++; 00099 if (filled >= width) { 00100 filled = 0; 00101 dst -= stride; 00102 rows_to_go--; 00103 if(rows_to_go <= 0) 00104 break; 00105 } 00106 } 00107 } 00108 } 00109 } 00110 00111 static const int qpeg_table_h[16] = 00112 { 0x00, 0x20, 0x20, 0x20, 0x18, 0x10, 0x10, 0x20, 0x10, 0x08, 0x18, 0x08, 0x08, 0x18, 0x10, 0x04}; 00113 static const int qpeg_table_w[16] = 00114 { 0x00, 0x20, 0x18, 0x08, 0x18, 0x10, 0x20, 0x10, 0x08, 0x10, 0x20, 0x20, 0x08, 0x10, 0x18, 0x04}; 00115 00116 /* Decodes delta frames */ 00117 static void qpeg_decode_inter(const uint8_t *src, uint8_t *dst, int size, 00118 int stride, int width, int height, 00119 int delta, const uint8_t *ctable, uint8_t *refdata) 00120 { 00121 int i, j; 00122 int code; 00123 int filled = 0; 00124 int orig_height; 00125 00126 /* copy prev frame */ 00127 for(i = 0; i < height; i++) 00128 memcpy(refdata + (i * width), dst + (i * stride), width); 00129 00130 orig_height = height; 00131 height--; 00132 dst = dst + height * stride; 00133 00134 while((size > 0) && (height >= 0)) { 00135 code = *src++; 00136 size--; 00137 00138 if(delta) { 00139 /* motion compensation */ 00140 while((code & 0xF0) == 0xF0) { 00141 if(delta == 1) { 00142 int me_idx; 00143 int me_w, me_h, me_x, me_y; 00144 uint8_t *me_plane; 00145 int corr, val; 00146 00147 /* get block size by index */ 00148 me_idx = code & 0xF; 00149 me_w = qpeg_table_w[me_idx]; 00150 me_h = qpeg_table_h[me_idx]; 00151 00152 /* extract motion vector */ 00153 corr = *src++; 00154 size--; 00155 00156 val = corr >> 4; 00157 if(val > 7) 00158 val -= 16; 00159 me_x = val; 00160 00161 val = corr & 0xF; 00162 if(val > 7) 00163 val -= 16; 00164 me_y = val; 00165 00166 /* check motion vector */ 00167 if ((me_x + filled < 0) || (me_x + me_w + filled > width) || 00168 (height - me_y - me_h < 0) || (height - me_y > orig_height) || 00169 (filled + me_w > width) || (height - me_h < 0)) 00170 av_log(NULL, AV_LOG_ERROR, "Bogus motion vector (%i,%i), block size %ix%i at %i,%i\n", 00171 me_x, me_y, me_w, me_h, filled, height); 00172 else { 00173 /* do motion compensation */ 00174 me_plane = refdata + (filled + me_x) + (height - me_y) * width; 00175 for(j = 0; j < me_h; j++) { 00176 for(i = 0; i < me_w; i++) 00177 dst[filled + i - (j * stride)] = me_plane[i - (j * width)]; 00178 } 00179 } 00180 } 00181 code = *src++; 00182 size--; 00183 } 00184 } 00185 00186 if(code == 0xE0) /* end-of-picture code */ 00187 break; 00188 if(code > 0xE0) { /* run code: 0xE1..0xFF */ 00189 int p; 00190 00191 code &= 0x1F; 00192 p = *src++; 00193 size--; 00194 for(i = 0; i <= code; i++) { 00195 dst[filled++] = p; 00196 if(filled >= width) { 00197 filled = 0; 00198 dst -= stride; 00199 height--; 00200 } 00201 } 00202 } else if(code >= 0xC0) { /* copy code: 0xC0..0xDF */ 00203 code &= 0x1F; 00204 00205 for(i = 0; i <= code; i++) { 00206 dst[filled++] = *src++; 00207 if(filled >= width) { 00208 filled = 0; 00209 dst -= stride; 00210 height--; 00211 } 00212 } 00213 size -= code + 1; 00214 } else if(code >= 0x80) { /* skip code: 0x80..0xBF */ 00215 int skip; 00216 00217 code &= 0x3F; 00218 /* codes 0x80 and 0x81 are actually escape codes, 00219 skip value minus constant is in the next byte */ 00220 if(!code) 00221 skip = (*src++) + 64; 00222 else if(code == 1) 00223 skip = (*src++) + 320; 00224 else 00225 skip = code; 00226 filled += skip; 00227 while( filled >= width) { 00228 filled -= width; 00229 dst -= stride; 00230 height--; 00231 if(height < 0) 00232 break; 00233 } 00234 } else { 00235 /* zero code treated as one-pixel skip */ 00236 if(code) 00237 dst[filled++] = ctable[code & 0x7F]; 00238 else 00239 filled++; 00240 if(filled >= width) { 00241 filled = 0; 00242 dst -= stride; 00243 height--; 00244 } 00245 } 00246 } 00247 } 00248 00249 static int decode_frame(AVCodecContext *avctx, 00250 void *data, int *data_size, 00251 AVPacket *avpkt) 00252 { 00253 const uint8_t *buf = avpkt->data; 00254 int buf_size = avpkt->size; 00255 QpegContext * const a = avctx->priv_data; 00256 AVFrame * const p= (AVFrame*)&a->pic; 00257 uint8_t* outdata; 00258 int delta; 00259 00260 if(p->data[0]) 00261 avctx->release_buffer(avctx, p); 00262 00263 p->reference= 0; 00264 if(avctx->get_buffer(avctx, p) < 0){ 00265 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); 00266 return -1; 00267 } 00268 outdata = a->pic.data[0]; 00269 if(buf[0x85] == 0x10) { 00270 qpeg_decode_intra(buf+0x86, outdata, buf_size - 0x86, a->pic.linesize[0], avctx->width, avctx->height); 00271 } else { 00272 delta = buf[0x85]; 00273 qpeg_decode_inter(buf+0x86, outdata, buf_size - 0x86, a->pic.linesize[0], avctx->width, avctx->height, delta, buf + 4, a->refdata); 00274 } 00275 00276 /* make the palette available on the way out */ 00277 memcpy(a->pic.data[1], a->avctx->palctrl->palette, AVPALETTE_SIZE); 00278 if (a->avctx->palctrl->palette_changed) { 00279 a->pic.palette_has_changed = 1; 00280 a->avctx->palctrl->palette_changed = 0; 00281 } 00282 00283 *data_size = sizeof(AVFrame); 00284 *(AVFrame*)data = a->pic; 00285 00286 return buf_size; 00287 } 00288 00289 static av_cold int decode_init(AVCodecContext *avctx){ 00290 QpegContext * const a = avctx->priv_data; 00291 00292 if (!avctx->palctrl) { 00293 av_log(avctx, AV_LOG_FATAL, "Missing required palette via palctrl\n"); 00294 return -1; 00295 } 00296 a->avctx = avctx; 00297 avctx->pix_fmt= PIX_FMT_PAL8; 00298 a->refdata = av_malloc(avctx->width * avctx->height); 00299 00300 return 0; 00301 } 00302 00303 static av_cold int decode_end(AVCodecContext *avctx){ 00304 QpegContext * const a = avctx->priv_data; 00305 AVFrame * const p= (AVFrame*)&a->pic; 00306 00307 if(p->data[0]) 00308 avctx->release_buffer(avctx, p); 00309 00310 av_free(a->refdata); 00311 return 0; 00312 } 00313 00314 AVCodec qpeg_decoder = { 00315 "qpeg", 00316 AVMEDIA_TYPE_VIDEO, 00317 CODEC_ID_QPEG, 00318 sizeof(QpegContext), 00319 decode_init, 00320 NULL, 00321 decode_end, 00322 decode_frame, 00323 CODEC_CAP_DR1, 00324 .long_name = NULL_IF_CONFIG_SMALL("Q-team QPEG"), 00325 };