1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Bethsoft VID format Demuxer 3cabdff1aSopenharmony_ci * Copyright (c) 2007 Nicholas Tung 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 * @brief Bethesda Softworks VID (.vid) file demuxer 25cabdff1aSopenharmony_ci * @author Nicholas Tung [ntung (at. ntung com] (2007-03) 26cabdff1aSopenharmony_ci * @see http://wiki.multimedia.cx/index.php?title=Bethsoft_VID 27cabdff1aSopenharmony_ci * @see http://www.svatopluk.com/andux/docs/dfvid.html 28cabdff1aSopenharmony_ci */ 29cabdff1aSopenharmony_ci 30cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h" 31cabdff1aSopenharmony_ci#include "libavutil/imgutils.h" 32cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 33cabdff1aSopenharmony_ci#include "avformat.h" 34cabdff1aSopenharmony_ci#include "internal.h" 35cabdff1aSopenharmony_ci#include "libavcodec/bethsoftvideo.h" 36cabdff1aSopenharmony_ci 37cabdff1aSopenharmony_ci#define BVID_PALETTE_SIZE 3 * 256 38cabdff1aSopenharmony_ci 39cabdff1aSopenharmony_ci#define DEFAULT_SAMPLE_RATE 11111 40cabdff1aSopenharmony_ci 41cabdff1aSopenharmony_citypedef struct BVID_DemuxContext 42cabdff1aSopenharmony_ci{ 43cabdff1aSopenharmony_ci int nframes; 44cabdff1aSopenharmony_ci int sample_rate; /**< audio sample rate */ 45cabdff1aSopenharmony_ci int width; /**< video width */ 46cabdff1aSopenharmony_ci int height; /**< video height */ 47cabdff1aSopenharmony_ci /** delay value between frames, added to individual frame delay. 48cabdff1aSopenharmony_ci * custom units, which will be added to other custom units (~=16ms according 49cabdff1aSopenharmony_ci * to free, unofficial documentation) */ 50cabdff1aSopenharmony_ci int bethsoft_global_delay; 51cabdff1aSopenharmony_ci int video_index; /**< video stream index */ 52cabdff1aSopenharmony_ci int audio_index; /**< audio stream index */ 53cabdff1aSopenharmony_ci int has_palette; 54cabdff1aSopenharmony_ci uint8_t palette[BVID_PALETTE_SIZE]; 55cabdff1aSopenharmony_ci 56cabdff1aSopenharmony_ci int is_finished; 57cabdff1aSopenharmony_ci 58cabdff1aSopenharmony_ci} BVID_DemuxContext; 59cabdff1aSopenharmony_ci 60cabdff1aSopenharmony_cistatic int vid_probe(const AVProbeData *p) 61cabdff1aSopenharmony_ci{ 62cabdff1aSopenharmony_ci // little-endian VID tag, file starts with "VID\0" 63cabdff1aSopenharmony_ci if (AV_RL32(p->buf) != MKTAG('V', 'I', 'D', 0)) 64cabdff1aSopenharmony_ci return 0; 65cabdff1aSopenharmony_ci 66cabdff1aSopenharmony_ci if (p->buf[4] != 2) 67cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX / 4; 68cabdff1aSopenharmony_ci 69cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX; 70cabdff1aSopenharmony_ci} 71cabdff1aSopenharmony_ci 72cabdff1aSopenharmony_cistatic int vid_read_header(AVFormatContext *s) 73cabdff1aSopenharmony_ci{ 74cabdff1aSopenharmony_ci BVID_DemuxContext *vid = s->priv_data; 75cabdff1aSopenharmony_ci AVIOContext *pb = s->pb; 76cabdff1aSopenharmony_ci int ret; 77cabdff1aSopenharmony_ci 78cabdff1aSopenharmony_ci /* load main header. Contents: 79cabdff1aSopenharmony_ci * bytes: 'V' 'I' 'D' 80cabdff1aSopenharmony_ci * int16s: always_512, nframes, width, height, delay, always_14 81cabdff1aSopenharmony_ci */ 82cabdff1aSopenharmony_ci avio_skip(pb, 5); 83cabdff1aSopenharmony_ci vid->nframes = avio_rl16(pb); 84cabdff1aSopenharmony_ci vid->width = avio_rl16(pb); 85cabdff1aSopenharmony_ci vid->height = avio_rl16(pb); 86cabdff1aSopenharmony_ci vid->bethsoft_global_delay = avio_rl16(pb); 87cabdff1aSopenharmony_ci avio_rl16(pb); 88cabdff1aSopenharmony_ci 89cabdff1aSopenharmony_ci ret = av_image_check_size(vid->width, vid->height, 0, s); 90cabdff1aSopenharmony_ci if (ret < 0) 91cabdff1aSopenharmony_ci return ret; 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_ci // wait until the first packet to create each stream 94cabdff1aSopenharmony_ci vid->video_index = -1; 95cabdff1aSopenharmony_ci vid->audio_index = -1; 96cabdff1aSopenharmony_ci vid->sample_rate = DEFAULT_SAMPLE_RATE; 97cabdff1aSopenharmony_ci s->ctx_flags |= AVFMTCTX_NOHEADER; 98cabdff1aSopenharmony_ci 99cabdff1aSopenharmony_ci return 0; 100cabdff1aSopenharmony_ci} 101cabdff1aSopenharmony_ci 102cabdff1aSopenharmony_ci#define BUFFER_PADDING_SIZE 1000 103cabdff1aSopenharmony_cistatic int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt, 104cabdff1aSopenharmony_ci uint8_t block_type, AVFormatContext *s) 105cabdff1aSopenharmony_ci{ 106cabdff1aSopenharmony_ci uint8_t * vidbuf_start = NULL; 107cabdff1aSopenharmony_ci int vidbuf_nbytes = 0; 108cabdff1aSopenharmony_ci int code; 109cabdff1aSopenharmony_ci int bytes_copied = 0; 110cabdff1aSopenharmony_ci int position, duration, npixels; 111cabdff1aSopenharmony_ci unsigned int vidbuf_capacity; 112cabdff1aSopenharmony_ci int ret = 0; 113cabdff1aSopenharmony_ci AVStream *st; 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci if (vid->video_index < 0) { 116cabdff1aSopenharmony_ci st = avformat_new_stream(s, NULL); 117cabdff1aSopenharmony_ci if (!st) 118cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 119cabdff1aSopenharmony_ci vid->video_index = st->index; 120cabdff1aSopenharmony_ci if (vid->audio_index < 0) { 121cabdff1aSopenharmony_ci avpriv_request_sample(s, "Using default video time base since " 122cabdff1aSopenharmony_ci "having no audio packet before the first " 123cabdff1aSopenharmony_ci "video packet"); 124cabdff1aSopenharmony_ci } 125cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 185, vid->sample_rate); 126cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; 127cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_BETHSOFTVID; 128cabdff1aSopenharmony_ci st->codecpar->width = vid->width; 129cabdff1aSopenharmony_ci st->codecpar->height = vid->height; 130cabdff1aSopenharmony_ci } 131cabdff1aSopenharmony_ci st = s->streams[vid->video_index]; 132cabdff1aSopenharmony_ci npixels = st->codecpar->width * st->codecpar->height; 133cabdff1aSopenharmony_ci 134cabdff1aSopenharmony_ci vidbuf_start = av_malloc(vidbuf_capacity = BUFFER_PADDING_SIZE); 135cabdff1aSopenharmony_ci if(!vidbuf_start) 136cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 137cabdff1aSopenharmony_ci 138cabdff1aSopenharmony_ci // save the file position for the packet, include block type 139cabdff1aSopenharmony_ci position = avio_tell(pb) - 1; 140cabdff1aSopenharmony_ci 141cabdff1aSopenharmony_ci vidbuf_start[vidbuf_nbytes++] = block_type; 142cabdff1aSopenharmony_ci 143cabdff1aSopenharmony_ci // get the current packet duration 144cabdff1aSopenharmony_ci duration = vid->bethsoft_global_delay + avio_rl16(pb); 145cabdff1aSopenharmony_ci 146cabdff1aSopenharmony_ci // set the y offset if it exists (decoder header data should be in data section) 147cabdff1aSopenharmony_ci if(block_type == VIDEO_YOFF_P_FRAME){ 148cabdff1aSopenharmony_ci if (avio_read(pb, &vidbuf_start[vidbuf_nbytes], 2) != 2) { 149cabdff1aSopenharmony_ci ret = AVERROR(EIO); 150cabdff1aSopenharmony_ci goto fail; 151cabdff1aSopenharmony_ci } 152cabdff1aSopenharmony_ci vidbuf_nbytes += 2; 153cabdff1aSopenharmony_ci } 154cabdff1aSopenharmony_ci 155cabdff1aSopenharmony_ci do{ 156cabdff1aSopenharmony_ci uint8_t *tmp = av_fast_realloc(vidbuf_start, &vidbuf_capacity, 157cabdff1aSopenharmony_ci vidbuf_nbytes + BUFFER_PADDING_SIZE); 158cabdff1aSopenharmony_ci if (!tmp) { 159cabdff1aSopenharmony_ci ret = AVERROR(ENOMEM); 160cabdff1aSopenharmony_ci goto fail; 161cabdff1aSopenharmony_ci } 162cabdff1aSopenharmony_ci vidbuf_start = tmp; 163cabdff1aSopenharmony_ci 164cabdff1aSopenharmony_ci code = avio_r8(pb); 165cabdff1aSopenharmony_ci vidbuf_start[vidbuf_nbytes++] = code; 166cabdff1aSopenharmony_ci 167cabdff1aSopenharmony_ci if(code >= 0x80){ // rle sequence 168cabdff1aSopenharmony_ci if(block_type == VIDEO_I_FRAME) 169cabdff1aSopenharmony_ci vidbuf_start[vidbuf_nbytes++] = avio_r8(pb); 170cabdff1aSopenharmony_ci } else if(code){ // plain sequence 171cabdff1aSopenharmony_ci if (avio_read(pb, &vidbuf_start[vidbuf_nbytes], code) != code) { 172cabdff1aSopenharmony_ci ret = AVERROR(EIO); 173cabdff1aSopenharmony_ci goto fail; 174cabdff1aSopenharmony_ci } 175cabdff1aSopenharmony_ci vidbuf_nbytes += code; 176cabdff1aSopenharmony_ci } 177cabdff1aSopenharmony_ci bytes_copied += code & 0x7F; 178cabdff1aSopenharmony_ci if(bytes_copied == npixels){ // sometimes no stop character is given, need to keep track of bytes copied 179cabdff1aSopenharmony_ci // may contain a 0 byte even if read all pixels 180cabdff1aSopenharmony_ci if(avio_r8(pb)) 181cabdff1aSopenharmony_ci avio_seek(pb, -1, SEEK_CUR); 182cabdff1aSopenharmony_ci break; 183cabdff1aSopenharmony_ci } 184cabdff1aSopenharmony_ci if (bytes_copied > npixels) { 185cabdff1aSopenharmony_ci ret = AVERROR_INVALIDDATA; 186cabdff1aSopenharmony_ci goto fail; 187cabdff1aSopenharmony_ci } 188cabdff1aSopenharmony_ci } while(code); 189cabdff1aSopenharmony_ci 190cabdff1aSopenharmony_ci // copy data into packet 191cabdff1aSopenharmony_ci if ((ret = av_new_packet(pkt, vidbuf_nbytes)) < 0) 192cabdff1aSopenharmony_ci goto fail; 193cabdff1aSopenharmony_ci memcpy(pkt->data, vidbuf_start, vidbuf_nbytes); 194cabdff1aSopenharmony_ci 195cabdff1aSopenharmony_ci pkt->pos = position; 196cabdff1aSopenharmony_ci pkt->stream_index = vid->video_index; 197cabdff1aSopenharmony_ci pkt->duration = duration; 198cabdff1aSopenharmony_ci if (block_type == VIDEO_I_FRAME) 199cabdff1aSopenharmony_ci pkt->flags |= AV_PKT_FLAG_KEY; 200cabdff1aSopenharmony_ci 201cabdff1aSopenharmony_ci /* if there is a new palette available, add it to packet side data */ 202cabdff1aSopenharmony_ci if (vid->has_palette) { 203cabdff1aSopenharmony_ci uint8_t *pdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, 204cabdff1aSopenharmony_ci BVID_PALETTE_SIZE); 205cabdff1aSopenharmony_ci if (!pdata) { 206cabdff1aSopenharmony_ci ret = AVERROR(ENOMEM); 207cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Failed to allocate palette side data\n"); 208cabdff1aSopenharmony_ci goto fail; 209cabdff1aSopenharmony_ci } 210cabdff1aSopenharmony_ci memcpy(pdata, vid->palette, BVID_PALETTE_SIZE); 211cabdff1aSopenharmony_ci vid->has_palette = 0; 212cabdff1aSopenharmony_ci } 213cabdff1aSopenharmony_ci 214cabdff1aSopenharmony_ci vid->nframes--; // used to check if all the frames were read 215cabdff1aSopenharmony_cifail: 216cabdff1aSopenharmony_ci av_free(vidbuf_start); 217cabdff1aSopenharmony_ci return ret; 218cabdff1aSopenharmony_ci} 219cabdff1aSopenharmony_ci 220cabdff1aSopenharmony_cistatic int vid_read_packet(AVFormatContext *s, 221cabdff1aSopenharmony_ci AVPacket *pkt) 222cabdff1aSopenharmony_ci{ 223cabdff1aSopenharmony_ci BVID_DemuxContext *vid = s->priv_data; 224cabdff1aSopenharmony_ci AVIOContext *pb = s->pb; 225cabdff1aSopenharmony_ci unsigned char block_type; 226cabdff1aSopenharmony_ci int audio_length; 227cabdff1aSopenharmony_ci int ret_value; 228cabdff1aSopenharmony_ci 229cabdff1aSopenharmony_ci if(vid->is_finished || avio_feof(pb)) 230cabdff1aSopenharmony_ci return AVERROR_EOF; 231cabdff1aSopenharmony_ci 232cabdff1aSopenharmony_ci block_type = avio_r8(pb); 233cabdff1aSopenharmony_ci switch(block_type){ 234cabdff1aSopenharmony_ci case PALETTE_BLOCK: 235cabdff1aSopenharmony_ci if (vid->has_palette) { 236cabdff1aSopenharmony_ci av_log(s, AV_LOG_WARNING, "discarding unused palette\n"); 237cabdff1aSopenharmony_ci vid->has_palette = 0; 238cabdff1aSopenharmony_ci } 239cabdff1aSopenharmony_ci if (avio_read(pb, vid->palette, BVID_PALETTE_SIZE) != BVID_PALETTE_SIZE) { 240cabdff1aSopenharmony_ci return AVERROR(EIO); 241cabdff1aSopenharmony_ci } 242cabdff1aSopenharmony_ci vid->has_palette = 1; 243cabdff1aSopenharmony_ci return vid_read_packet(s, pkt); 244cabdff1aSopenharmony_ci 245cabdff1aSopenharmony_ci case FIRST_AUDIO_BLOCK: 246cabdff1aSopenharmony_ci avio_rl16(pb); 247cabdff1aSopenharmony_ci // soundblaster DAC used for sample rate, as on specification page (link above) 248cabdff1aSopenharmony_ci vid->sample_rate = 1000000 / (256 - avio_r8(pb)); 249cabdff1aSopenharmony_ci case AUDIO_BLOCK: 250cabdff1aSopenharmony_ci if (vid->audio_index < 0) { 251cabdff1aSopenharmony_ci AVStream *st = avformat_new_stream(s, NULL); 252cabdff1aSopenharmony_ci if (!st) 253cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 254cabdff1aSopenharmony_ci vid->audio_index = st->index; 255cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; 256cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_PCM_U8; 257cabdff1aSopenharmony_ci st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; 258cabdff1aSopenharmony_ci st->codecpar->bits_per_coded_sample = 8; 259cabdff1aSopenharmony_ci st->codecpar->sample_rate = vid->sample_rate; 260cabdff1aSopenharmony_ci st->codecpar->bit_rate = 8 * st->codecpar->sample_rate; 261cabdff1aSopenharmony_ci st->start_time = 0; 262cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, vid->sample_rate); 263cabdff1aSopenharmony_ci } 264cabdff1aSopenharmony_ci audio_length = avio_rl16(pb); 265cabdff1aSopenharmony_ci if ((ret_value = av_get_packet(pb, pkt, audio_length)) != audio_length) { 266cabdff1aSopenharmony_ci if (ret_value < 0) 267cabdff1aSopenharmony_ci return ret_value; 268cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "incomplete audio block\n"); 269cabdff1aSopenharmony_ci return AVERROR(EIO); 270cabdff1aSopenharmony_ci } 271cabdff1aSopenharmony_ci pkt->stream_index = vid->audio_index; 272cabdff1aSopenharmony_ci pkt->duration = audio_length; 273cabdff1aSopenharmony_ci pkt->flags |= AV_PKT_FLAG_KEY; 274cabdff1aSopenharmony_ci return 0; 275cabdff1aSopenharmony_ci 276cabdff1aSopenharmony_ci case VIDEO_P_FRAME: 277cabdff1aSopenharmony_ci case VIDEO_YOFF_P_FRAME: 278cabdff1aSopenharmony_ci case VIDEO_I_FRAME: 279cabdff1aSopenharmony_ci return read_frame(vid, pb, pkt, block_type, s); 280cabdff1aSopenharmony_ci 281cabdff1aSopenharmony_ci case EOF_BLOCK: 282cabdff1aSopenharmony_ci if(vid->nframes != 0) 283cabdff1aSopenharmony_ci av_log(s, AV_LOG_VERBOSE, "reached terminating character but not all frames read.\n"); 284cabdff1aSopenharmony_ci vid->is_finished = 1; 285cabdff1aSopenharmony_ci return AVERROR(EIO); 286cabdff1aSopenharmony_ci default: 287cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "unknown block (character = %c, decimal = %d, hex = %x)!!!\n", 288cabdff1aSopenharmony_ci block_type, block_type, block_type); 289cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 290cabdff1aSopenharmony_ci } 291cabdff1aSopenharmony_ci} 292cabdff1aSopenharmony_ci 293cabdff1aSopenharmony_ciconst AVInputFormat ff_bethsoftvid_demuxer = { 294cabdff1aSopenharmony_ci .name = "bethsoftvid", 295cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Bethesda Softworks VID"), 296cabdff1aSopenharmony_ci .priv_data_size = sizeof(BVID_DemuxContext), 297cabdff1aSopenharmony_ci .read_probe = vid_probe, 298cabdff1aSopenharmony_ci .read_header = vid_read_header, 299cabdff1aSopenharmony_ci .read_packet = vid_read_packet, 300cabdff1aSopenharmony_ci}; 301