yading@10: /* yading@10: * IBM Ultimotion Video Decoder yading@10: * Copyright (C) 2004 Konstantin Shishkov yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /** yading@10: * @file yading@10: * IBM Ultimotion Video Decoder. yading@10: */ yading@10: yading@10: #include yading@10: #include yading@10: #include yading@10: yading@10: #include "avcodec.h" yading@10: #include "bytestream.h" yading@10: #include "internal.h" yading@10: yading@10: #include "ulti_cb.h" yading@10: yading@10: typedef struct UltimotionDecodeContext { yading@10: AVCodecContext *avctx; yading@10: int width, height, blocks; yading@10: AVFrame frame; yading@10: const uint8_t *ulti_codebook; yading@10: GetByteContext gb; yading@10: } UltimotionDecodeContext; yading@10: yading@10: static av_cold int ulti_decode_init(AVCodecContext *avctx) yading@10: { yading@10: UltimotionDecodeContext *s = avctx->priv_data; yading@10: yading@10: s->avctx = avctx; yading@10: s->width = avctx->width; yading@10: s->height = avctx->height; yading@10: s->blocks = (s->width / 8) * (s->height / 8); yading@10: avctx->pix_fmt = AV_PIX_FMT_YUV410P; yading@10: avctx->coded_frame = &s->frame; yading@10: avctx->coded_frame = (AVFrame*) &s->frame; yading@10: s->ulti_codebook = ulti_codebook; yading@10: avcodec_get_frame_defaults(&s->frame); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold int ulti_decode_end(AVCodecContext *avctx){ yading@10: UltimotionDecodeContext *s = avctx->priv_data; yading@10: AVFrame *pic = &s->frame; yading@10: yading@10: av_frame_unref(pic); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static const int block_coords[8] = // 4x4 block coords in 8x8 superblock yading@10: { 0, 0, 0, 4, 4, 4, 4, 0}; yading@10: yading@10: static const int angle_by_index[4] = { 0, 2, 6, 12}; yading@10: yading@10: /* Lookup tables for luma and chroma - used by ulti_convert_yuv() */ yading@10: static const uint8_t ulti_lumas[64] = yading@10: { 0x10, 0x13, 0x17, 0x1A, 0x1E, 0x21, 0x25, 0x28, yading@10: 0x2C, 0x2F, 0x33, 0x36, 0x3A, 0x3D, 0x41, 0x44, yading@10: 0x48, 0x4B, 0x4F, 0x52, 0x56, 0x59, 0x5C, 0x60, yading@10: 0x63, 0x67, 0x6A, 0x6E, 0x71, 0x75, 0x78, 0x7C, yading@10: 0x7F, 0x83, 0x86, 0x8A, 0x8D, 0x91, 0x94, 0x98, yading@10: 0x9B, 0x9F, 0xA2, 0xA5, 0xA9, 0xAC, 0xB0, 0xB3, yading@10: 0xB7, 0xBA, 0xBE, 0xC1, 0xC5, 0xC8, 0xCC, 0xCF, yading@10: 0xD3, 0xD6, 0xDA, 0xDD, 0xE1, 0xE4, 0xE8, 0xEB}; yading@10: yading@10: static const uint8_t ulti_chromas[16] = yading@10: { 0x60, 0x67, 0x6D, 0x73, 0x7A, 0x80, 0x86, 0x8D, yading@10: 0x93, 0x99, 0xA0, 0xA6, 0xAC, 0xB3, 0xB9, 0xC0}; yading@10: yading@10: /* convert Ultimotion YUV block (sixteen 6-bit Y samples and yading@10: two 4-bit chroma samples) into standard YUV and put it into frame */ yading@10: static void ulti_convert_yuv(AVFrame *frame, int x, int y, yading@10: uint8_t *luma,int chroma) yading@10: { yading@10: uint8_t *y_plane, *cr_plane, *cb_plane; yading@10: int i; yading@10: yading@10: y_plane = frame->data[0] + x + y * frame->linesize[0]; yading@10: cr_plane = frame->data[1] + (x / 4) + (y / 4) * frame->linesize[1]; yading@10: cb_plane = frame->data[2] + (x / 4) + (y / 4) * frame->linesize[2]; yading@10: yading@10: cr_plane[0] = ulti_chromas[chroma >> 4]; yading@10: yading@10: cb_plane[0] = ulti_chromas[chroma & 0xF]; yading@10: yading@10: yading@10: for(i = 0; i < 16; i++){ yading@10: y_plane[i & 3] = ulti_lumas[luma[i]]; yading@10: if((i & 3) == 3) { //next row yading@10: y_plane += frame->linesize[0]; yading@10: } yading@10: } yading@10: } yading@10: yading@10: /* generate block like in MS Video1 */ yading@10: static void ulti_pattern(AVFrame *frame, int x, int y, yading@10: int f0, int f1, int Y0, int Y1, int chroma) yading@10: { yading@10: uint8_t Luma[16]; yading@10: int mask, i; yading@10: for(mask = 0x80, i = 0; mask; mask >>= 1, i++) { yading@10: if(f0 & mask) yading@10: Luma[i] = Y1; yading@10: else yading@10: Luma[i] = Y0; yading@10: } yading@10: yading@10: for(mask = 0x80, i = 8; mask; mask >>= 1, i++) { yading@10: if(f1 & mask) yading@10: Luma[i] = Y1; yading@10: else yading@10: Luma[i] = Y0; yading@10: } yading@10: yading@10: ulti_convert_yuv(frame, x, y, Luma, chroma); yading@10: } yading@10: yading@10: /* fill block with some gradient */ yading@10: static void ulti_grad(AVFrame *frame, int x, int y, uint8_t *Y, int chroma, int angle) yading@10: { yading@10: uint8_t Luma[16]; yading@10: if(angle & 8) { //reverse order yading@10: int t; yading@10: angle &= 0x7; yading@10: t = Y[0]; yading@10: Y[0] = Y[3]; yading@10: Y[3] = t; yading@10: t = Y[1]; yading@10: Y[1] = Y[2]; yading@10: Y[2] = t; yading@10: } yading@10: switch(angle){ yading@10: case 0: yading@10: Luma[0] = Y[0]; Luma[1] = Y[1]; Luma[2] = Y[2]; Luma[3] = Y[3]; yading@10: Luma[4] = Y[0]; Luma[5] = Y[1]; Luma[6] = Y[2]; Luma[7] = Y[3]; yading@10: Luma[8] = Y[0]; Luma[9] = Y[1]; Luma[10] = Y[2]; Luma[11] = Y[3]; yading@10: Luma[12] = Y[0]; Luma[13] = Y[1]; Luma[14] = Y[2]; Luma[15] = Y[3]; yading@10: break; yading@10: case 1: yading@10: Luma[0] = Y[1]; Luma[1] = Y[2]; Luma[2] = Y[3]; Luma[3] = Y[3]; yading@10: Luma[4] = Y[0]; Luma[5] = Y[1]; Luma[6] = Y[2]; Luma[7] = Y[3]; yading@10: Luma[8] = Y[0]; Luma[9] = Y[1]; Luma[10] = Y[2]; Luma[11] = Y[3]; yading@10: Luma[12] = Y[0]; Luma[13] = Y[0]; Luma[14] = Y[1]; Luma[15] = Y[2]; yading@10: break; yading@10: case 2: yading@10: Luma[0] = Y[1]; Luma[1] = Y[2]; Luma[2] = Y[3]; Luma[3] = Y[3]; yading@10: Luma[4] = Y[1]; Luma[5] = Y[2]; Luma[6] = Y[2]; Luma[7] = Y[3]; yading@10: Luma[8] = Y[0]; Luma[9] = Y[1]; Luma[10] = Y[1]; Luma[11] = Y[2]; yading@10: Luma[12] = Y[0]; Luma[13] = Y[0]; Luma[14] = Y[1]; Luma[15] = Y[2]; yading@10: break; yading@10: case 3: yading@10: Luma[0] = Y[2]; Luma[1] = Y[3]; Luma[2] = Y[3]; Luma[3] = Y[3]; yading@10: Luma[4] = Y[1]; Luma[5] = Y[2]; Luma[6] = Y[2]; Luma[7] = Y[3]; yading@10: Luma[8] = Y[0]; Luma[9] = Y[1]; Luma[10] = Y[1]; Luma[11] = Y[2]; yading@10: Luma[12] = Y[0]; Luma[13] = Y[0]; Luma[14] = Y[0]; Luma[15] = Y[1]; yading@10: break; yading@10: case 4: yading@10: Luma[0] = Y[3]; Luma[1] = Y[3]; Luma[2] = Y[3]; Luma[3] = Y[3]; yading@10: Luma[4] = Y[2]; Luma[5] = Y[2]; Luma[6] = Y[2]; Luma[7] = Y[2]; yading@10: Luma[8] = Y[1]; Luma[9] = Y[1]; Luma[10] = Y[1]; Luma[11] = Y[1]; yading@10: Luma[12] = Y[0]; Luma[13] = Y[0]; Luma[14] = Y[0]; Luma[15] = Y[0]; yading@10: break; yading@10: case 5: yading@10: Luma[0] = Y[3]; Luma[1] = Y[3]; Luma[2] = Y[3]; Luma[3] = Y[2]; yading@10: Luma[4] = Y[3]; Luma[5] = Y[2]; Luma[6] = Y[2]; Luma[7] = Y[1]; yading@10: Luma[8] = Y[2]; Luma[9] = Y[1]; Luma[10] = Y[1]; Luma[11] = Y[0]; yading@10: Luma[12] = Y[1]; Luma[13] = Y[0]; Luma[14] = Y[0]; Luma[15] = Y[0]; yading@10: break; yading@10: case 6: yading@10: Luma[0] = Y[3]; Luma[1] = Y[3]; Luma[2] = Y[2]; Luma[3] = Y[2]; yading@10: Luma[4] = Y[3]; Luma[5] = Y[2]; Luma[6] = Y[1]; Luma[7] = Y[1]; yading@10: Luma[8] = Y[2]; Luma[9] = Y[2]; Luma[10] = Y[1]; Luma[11] = Y[0]; yading@10: Luma[12] = Y[1]; Luma[13] = Y[1]; Luma[14] = Y[0]; Luma[15] = Y[0]; yading@10: break; yading@10: case 7: yading@10: Luma[0] = Y[3]; Luma[1] = Y[3]; Luma[2] = Y[2]; Luma[3] = Y[1]; yading@10: Luma[4] = Y[3]; Luma[5] = Y[2]; Luma[6] = Y[1]; Luma[7] = Y[0]; yading@10: Luma[8] = Y[3]; Luma[9] = Y[2]; Luma[10] = Y[1]; Luma[11] = Y[0]; yading@10: Luma[12] = Y[2]; Luma[13] = Y[1]; Luma[14] = Y[0]; Luma[15] = Y[0]; yading@10: break; yading@10: default: yading@10: Luma[0] = Y[0]; Luma[1] = Y[0]; Luma[2] = Y[1]; Luma[3] = Y[1]; yading@10: Luma[4] = Y[0]; Luma[5] = Y[0]; Luma[6] = Y[1]; Luma[7] = Y[1]; yading@10: Luma[8] = Y[2]; Luma[9] = Y[2]; Luma[10] = Y[3]; Luma[11] = Y[3]; yading@10: Luma[12] = Y[2]; Luma[13] = Y[2]; Luma[14] = Y[3]; Luma[15] = Y[3]; yading@10: break; yading@10: } yading@10: yading@10: ulti_convert_yuv(frame, x, y, Luma, chroma); yading@10: } yading@10: yading@10: static int ulti_decode_frame(AVCodecContext *avctx, yading@10: void *data, int *got_frame, yading@10: AVPacket *avpkt) yading@10: { yading@10: const uint8_t *buf = avpkt->data; yading@10: int buf_size = avpkt->size; yading@10: UltimotionDecodeContext *s=avctx->priv_data; yading@10: int modifier = 0; yading@10: int uniq = 0; yading@10: int mode = 0; yading@10: int blocks = 0; yading@10: int done = 0; yading@10: int x = 0, y = 0; yading@10: int i, ret; yading@10: int skip; yading@10: int tmp; yading@10: yading@10: if ((ret = ff_reget_buffer(avctx, &s->frame)) < 0) yading@10: return ret; yading@10: yading@10: bytestream2_init(&s->gb, buf, buf_size); yading@10: yading@10: while(!done) { yading@10: int idx; yading@10: if(blocks >= s->blocks || y >= s->height) yading@10: break;//all blocks decoded yading@10: yading@10: if (bytestream2_get_bytes_left(&s->gb) < 1) yading@10: goto err; yading@10: idx = bytestream2_get_byteu(&s->gb); yading@10: if((idx & 0xF8) == 0x70) { yading@10: switch(idx) { yading@10: case 0x70: //change modifier yading@10: modifier = bytestream2_get_byte(&s->gb); yading@10: if(modifier>1) yading@10: av_log(avctx, AV_LOG_INFO, "warning: modifier must be 0 or 1, got %i\n", modifier); yading@10: break; yading@10: case 0x71: // set uniq flag yading@10: uniq = 1; yading@10: break; yading@10: case 0x72: //toggle mode yading@10: mode = !mode; yading@10: break; yading@10: case 0x73: //end-of-frame yading@10: done = 1; yading@10: break; yading@10: case 0x74: //skip some blocks yading@10: skip = bytestream2_get_byte(&s->gb); yading@10: if ((blocks + skip) >= s->blocks) yading@10: break; yading@10: blocks += skip; yading@10: x += skip * 8; yading@10: while(x >= s->width) { yading@10: x -= s->width; yading@10: y += 8; yading@10: } yading@10: break; yading@10: default: yading@10: av_log(avctx, AV_LOG_INFO, "warning: unknown escape 0x%02X\n", idx); yading@10: } yading@10: } else { //handle one block yading@10: int code; yading@10: int cf; yading@10: int angle = 0; yading@10: uint8_t Y[4]; // luma samples of block yading@10: int tx = 0, ty = 0; //coords of subblock yading@10: int chroma = 0; yading@10: if (mode || uniq) { yading@10: uniq = 0; yading@10: cf = 1; yading@10: chroma = 0; yading@10: } else { yading@10: cf = 0; yading@10: if (idx) { yading@10: chroma = bytestream2_get_byte(&s->gb); yading@10: } yading@10: } yading@10: for (i = 0; i < 4; i++) { // for every subblock yading@10: code = (idx >> (6 - i*2)) & 3; //extract 2 bits yading@10: if(!code) //skip subblock yading@10: continue; yading@10: if(cf) { yading@10: chroma = bytestream2_get_byte(&s->gb); yading@10: } yading@10: tx = x + block_coords[i * 2]; yading@10: ty = y + block_coords[(i * 2) + 1]; yading@10: switch(code) { yading@10: case 1: yading@10: tmp = bytestream2_get_byte(&s->gb); yading@10: yading@10: angle = angle_by_index[(tmp >> 6) & 0x3]; yading@10: yading@10: Y[0] = tmp & 0x3F; yading@10: Y[1] = Y[0]; yading@10: yading@10: if (angle) { yading@10: Y[2] = Y[0]+1; yading@10: if (Y[2] > 0x3F) yading@10: Y[2] = 0x3F; yading@10: Y[3] = Y[2]; yading@10: } else { yading@10: Y[2] = Y[0]; yading@10: Y[3] = Y[0]; yading@10: } yading@10: break; yading@10: yading@10: case 2: yading@10: if (modifier) { // unpack four luma samples yading@10: tmp = bytestream2_get_be24(&s->gb); yading@10: yading@10: Y[0] = (tmp >> 18) & 0x3F; yading@10: Y[1] = (tmp >> 12) & 0x3F; yading@10: Y[2] = (tmp >> 6) & 0x3F; yading@10: Y[3] = tmp & 0x3F; yading@10: angle = 16; yading@10: } else { // retrieve luma samples from codebook yading@10: tmp = bytestream2_get_be16(&s->gb); yading@10: yading@10: angle = (tmp >> 12) & 0xF; yading@10: tmp &= 0xFFF; yading@10: tmp <<= 2; yading@10: Y[0] = s->ulti_codebook[tmp]; yading@10: Y[1] = s->ulti_codebook[tmp + 1]; yading@10: Y[2] = s->ulti_codebook[tmp + 2]; yading@10: Y[3] = s->ulti_codebook[tmp + 3]; yading@10: } yading@10: break; yading@10: yading@10: case 3: yading@10: if (modifier) { // all 16 luma samples yading@10: uint8_t Luma[16]; yading@10: yading@10: if (bytestream2_get_bytes_left(&s->gb) < 12) yading@10: goto err; yading@10: tmp = bytestream2_get_be24u(&s->gb); yading@10: Luma[0] = (tmp >> 18) & 0x3F; yading@10: Luma[1] = (tmp >> 12) & 0x3F; yading@10: Luma[2] = (tmp >> 6) & 0x3F; yading@10: Luma[3] = tmp & 0x3F; yading@10: yading@10: tmp = bytestream2_get_be24u(&s->gb); yading@10: Luma[4] = (tmp >> 18) & 0x3F; yading@10: Luma[5] = (tmp >> 12) & 0x3F; yading@10: Luma[6] = (tmp >> 6) & 0x3F; yading@10: Luma[7] = tmp & 0x3F; yading@10: yading@10: tmp = bytestream2_get_be24u(&s->gb); yading@10: Luma[8] = (tmp >> 18) & 0x3F; yading@10: Luma[9] = (tmp >> 12) & 0x3F; yading@10: Luma[10] = (tmp >> 6) & 0x3F; yading@10: Luma[11] = tmp & 0x3F; yading@10: yading@10: tmp = bytestream2_get_be24u(&s->gb); yading@10: Luma[12] = (tmp >> 18) & 0x3F; yading@10: Luma[13] = (tmp >> 12) & 0x3F; yading@10: Luma[14] = (tmp >> 6) & 0x3F; yading@10: Luma[15] = tmp & 0x3F; yading@10: yading@10: ulti_convert_yuv(&s->frame, tx, ty, Luma, chroma); yading@10: } else { yading@10: if (bytestream2_get_bytes_left(&s->gb) < 4) yading@10: goto err; yading@10: tmp = bytestream2_get_byteu(&s->gb); yading@10: if(tmp & 0x80) { yading@10: angle = (tmp >> 4) & 0x7; yading@10: tmp = (tmp << 8) + bytestream2_get_byteu(&s->gb); yading@10: Y[0] = (tmp >> 6) & 0x3F; yading@10: Y[1] = tmp & 0x3F; yading@10: Y[2] = bytestream2_get_byteu(&s->gb) & 0x3F; yading@10: Y[3] = bytestream2_get_byteu(&s->gb) & 0x3F; yading@10: ulti_grad(&s->frame, tx, ty, Y, chroma, angle); //draw block yading@10: } else { // some patterns yading@10: int f0, f1; yading@10: f0 = bytestream2_get_byteu(&s->gb); yading@10: f1 = tmp; yading@10: Y[0] = bytestream2_get_byteu(&s->gb) & 0x3F; yading@10: Y[1] = bytestream2_get_byteu(&s->gb) & 0x3F; yading@10: ulti_pattern(&s->frame, tx, ty, f1, f0, Y[0], Y[1], chroma); yading@10: } yading@10: } yading@10: break; yading@10: } yading@10: if(code != 3) yading@10: ulti_grad(&s->frame, tx, ty, Y, chroma, angle); // draw block yading@10: } yading@10: blocks++; yading@10: x += 8; yading@10: if(x >= s->width) { yading@10: x = 0; yading@10: y += 8; yading@10: } yading@10: } yading@10: } yading@10: yading@10: *got_frame = 1; yading@10: if ((ret = av_frame_ref(data, &s->frame)) < 0) yading@10: return ret; yading@10: yading@10: return buf_size; yading@10: yading@10: err: yading@10: av_log(avctx, AV_LOG_ERROR, yading@10: "Insufficient data\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: AVCodec ff_ulti_decoder = { yading@10: .name = "ultimotion", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .id = AV_CODEC_ID_ULTI, yading@10: .priv_data_size = sizeof(UltimotionDecodeContext), yading@10: .init = ulti_decode_init, yading@10: .close = ulti_decode_end, yading@10: .decode = ulti_decode_frame, yading@10: .capabilities = CODEC_CAP_DR1, yading@10: .long_name = NULL_IF_CONFIG_SMALL("IBM UltiMotion"), yading@10: };