1/* 2 * Bethesda VID video decoder 3 * Copyright (C) 2007 Nicholas Tung 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 * @brief Bethesda Softworks VID Video Decoder 25 * @author Nicholas Tung [ntung (at. ntung com] (2007-03) 26 * @see http://wiki.multimedia.cx/index.php?title=Bethsoft_VID 27 * @see http://www.svatopluk.com/andux/docs/dfvid.html 28 */ 29 30#include "libavutil/common.h" 31#include "avcodec.h" 32#include "bethsoftvideo.h" 33#include "bytestream.h" 34#include "codec_internal.h" 35#include "internal.h" 36 37typedef struct BethsoftvidContext { 38 AVFrame *frame; 39 GetByteContext g; 40} BethsoftvidContext; 41 42static av_cold int bethsoftvid_decode_init(AVCodecContext *avctx) 43{ 44 BethsoftvidContext *vid = avctx->priv_data; 45 avctx->pix_fmt = AV_PIX_FMT_PAL8; 46 47 vid->frame = av_frame_alloc(); 48 if (!vid->frame) 49 return AVERROR(ENOMEM); 50 51 return 0; 52} 53 54static int set_palette(BethsoftvidContext *ctx) 55{ 56 uint32_t *palette = (uint32_t *)ctx->frame->data[1]; 57 int a; 58 59 if (bytestream2_get_bytes_left(&ctx->g) < 256*3) 60 return AVERROR_INVALIDDATA; 61 62 for(a = 0; a < 256; a++){ 63 palette[a] = 0xFFU << 24 | bytestream2_get_be24u(&ctx->g) * 4; 64 palette[a] |= palette[a] >> 6 & 0x30303; 65 } 66 ctx->frame->palette_has_changed = 1; 67 return 0; 68} 69 70static int bethsoftvid_decode_frame(AVCodecContext *avctx, AVFrame *rframe, 71 int *got_frame, AVPacket *avpkt) 72{ 73 BethsoftvidContext * vid = avctx->priv_data; 74 char block_type; 75 uint8_t * dst; 76 uint8_t * frame_end; 77 int remaining = avctx->width; // number of bytes remaining on a line 78 int wrap_to_next_line; 79 int code, ret; 80 int yoffset; 81 82 if ((ret = ff_reget_buffer(avctx, vid->frame, 0)) < 0) 83 return ret; 84 wrap_to_next_line = vid->frame->linesize[0] - avctx->width; 85 86 if (avpkt->side_data_elems > 0 && 87 avpkt->side_data[0].type == AV_PKT_DATA_PALETTE) { 88 bytestream2_init(&vid->g, avpkt->side_data[0].data, 89 avpkt->side_data[0].size); 90 if ((ret = set_palette(vid)) < 0) 91 return ret; 92 } 93 94 bytestream2_init(&vid->g, avpkt->data, avpkt->size); 95 dst = vid->frame->data[0]; 96 frame_end = vid->frame->data[0] + vid->frame->linesize[0] * avctx->height; 97 98 switch(block_type = bytestream2_get_byte(&vid->g)){ 99 case PALETTE_BLOCK: { 100 *got_frame = 0; 101 if ((ret = set_palette(vid)) < 0) { 102 av_log(avctx, AV_LOG_ERROR, "error reading palette\n"); 103 return ret; 104 } 105 return bytestream2_tell(&vid->g); 106 } 107 case VIDEO_YOFF_P_FRAME: 108 yoffset = bytestream2_get_le16(&vid->g); 109 if(yoffset >= avctx->height) 110 return AVERROR_INVALIDDATA; 111 dst += vid->frame->linesize[0] * yoffset; 112 case VIDEO_P_FRAME: 113 case VIDEO_I_FRAME: 114 break; 115 default: 116 return AVERROR_INVALIDDATA; 117 } 118 119 // main code 120 while((code = bytestream2_get_byte(&vid->g))){ 121 int length = code & 0x7f; 122 123 // copy any bytes starting at the current position, and ending at the frame width 124 while(length > remaining){ 125 if(code < 0x80) 126 bytestream2_get_buffer(&vid->g, dst, remaining); 127 else if(block_type == VIDEO_I_FRAME) 128 memset(dst, bytestream2_peek_byte(&vid->g), remaining); 129 length -= remaining; // decrement the number of bytes to be copied 130 dst += remaining + wrap_to_next_line; // skip over extra bytes at end of frame 131 remaining = avctx->width; 132 if(dst == frame_end) 133 goto end; 134 } 135 136 // copy any remaining bytes after / if line overflows 137 if(code < 0x80) 138 bytestream2_get_buffer(&vid->g, dst, length); 139 else if(block_type == VIDEO_I_FRAME) 140 memset(dst, bytestream2_get_byte(&vid->g), length); 141 remaining -= length; 142 dst += length; 143 } 144 end: 145 146 if ((ret = av_frame_ref(rframe, vid->frame)) < 0) 147 return ret; 148 149 *got_frame = 1; 150 151 return avpkt->size; 152} 153 154static av_cold int bethsoftvid_decode_end(AVCodecContext *avctx) 155{ 156 BethsoftvidContext * vid = avctx->priv_data; 157 av_frame_free(&vid->frame); 158 return 0; 159} 160 161const FFCodec ff_bethsoftvid_decoder = { 162 .p.name = "bethsoftvid", 163 .p.long_name = NULL_IF_CONFIG_SMALL("Bethesda VID video"), 164 .p.type = AVMEDIA_TYPE_VIDEO, 165 .p.id = AV_CODEC_ID_BETHSOFTVID, 166 .priv_data_size = sizeof(BethsoftvidContext), 167 .init = bethsoftvid_decode_init, 168 .close = bethsoftvid_decode_end, 169 FF_CODEC_DECODE_CB(bethsoftvid_decode_frame), 170 .p.capabilities = AV_CODEC_CAP_DR1, 171 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 172}; 173