1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Wing Commander III Movie (.mve) File Demuxer 3cabdff1aSopenharmony_ci * Copyright (c) 2003 The FFmpeg project 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 * Wing Commander III Movie file demuxer 25cabdff1aSopenharmony_ci * by Mike Melanson (melanson@pcisys.net) 26cabdff1aSopenharmony_ci * for more information on the WC3 .mve file format, visit: 27cabdff1aSopenharmony_ci * http://www.pcisys.net/~melanson/codecs/ 28cabdff1aSopenharmony_ci */ 29cabdff1aSopenharmony_ci 30cabdff1aSopenharmony_ci#include "libavutil/avstring.h" 31cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h" 32cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 33cabdff1aSopenharmony_ci#include "libavutil/dict.h" 34cabdff1aSopenharmony_ci#include "avformat.h" 35cabdff1aSopenharmony_ci#include "internal.h" 36cabdff1aSopenharmony_ci 37cabdff1aSopenharmony_ci#define FORM_TAG MKTAG('F', 'O', 'R', 'M') 38cabdff1aSopenharmony_ci#define MOVE_TAG MKTAG('M', 'O', 'V', 'E') 39cabdff1aSopenharmony_ci#define PC__TAG MKTAG('_', 'P', 'C', '_') 40cabdff1aSopenharmony_ci#define SOND_TAG MKTAG('S', 'O', 'N', 'D') 41cabdff1aSopenharmony_ci#define BNAM_TAG MKTAG('B', 'N', 'A', 'M') 42cabdff1aSopenharmony_ci#define SIZE_TAG MKTAG('S', 'I', 'Z', 'E') 43cabdff1aSopenharmony_ci#define PALT_TAG MKTAG('P', 'A', 'L', 'T') 44cabdff1aSopenharmony_ci#define INDX_TAG MKTAG('I', 'N', 'D', 'X') 45cabdff1aSopenharmony_ci#define BRCH_TAG MKTAG('B', 'R', 'C', 'H') 46cabdff1aSopenharmony_ci#define SHOT_TAG MKTAG('S', 'H', 'O', 'T') 47cabdff1aSopenharmony_ci#define VGA__TAG MKTAG('V', 'G', 'A', ' ') 48cabdff1aSopenharmony_ci#define TEXT_TAG MKTAG('T', 'E', 'X', 'T') 49cabdff1aSopenharmony_ci#define AUDI_TAG MKTAG('A', 'U', 'D', 'I') 50cabdff1aSopenharmony_ci 51cabdff1aSopenharmony_ci/* video resolution unless otherwise specified */ 52cabdff1aSopenharmony_ci#define WC3_DEFAULT_WIDTH 320 53cabdff1aSopenharmony_ci#define WC3_DEFAULT_HEIGHT 165 54cabdff1aSopenharmony_ci 55cabdff1aSopenharmony_ci/* always use the same PCM audio parameters */ 56cabdff1aSopenharmony_ci#define WC3_SAMPLE_RATE 22050 57cabdff1aSopenharmony_ci#define WC3_AUDIO_BITS 16 58cabdff1aSopenharmony_ci 59cabdff1aSopenharmony_ci/* nice, constant framerate */ 60cabdff1aSopenharmony_ci#define WC3_FRAME_FPS 15 61cabdff1aSopenharmony_ci 62cabdff1aSopenharmony_ci#define PALETTE_SIZE (256 * 3) 63cabdff1aSopenharmony_ci 64cabdff1aSopenharmony_citypedef struct Wc3DemuxContext { 65cabdff1aSopenharmony_ci int width; 66cabdff1aSopenharmony_ci int height; 67cabdff1aSopenharmony_ci int64_t pts; 68cabdff1aSopenharmony_ci int video_stream_index; 69cabdff1aSopenharmony_ci int audio_stream_index; 70cabdff1aSopenharmony_ci 71cabdff1aSopenharmony_ci AVPacket *vpkt; 72cabdff1aSopenharmony_ci 73cabdff1aSopenharmony_ci} Wc3DemuxContext; 74cabdff1aSopenharmony_ci 75cabdff1aSopenharmony_cistatic int wc3_read_close(AVFormatContext *s) 76cabdff1aSopenharmony_ci{ 77cabdff1aSopenharmony_ci Wc3DemuxContext *wc3 = s->priv_data; 78cabdff1aSopenharmony_ci 79cabdff1aSopenharmony_ci av_packet_free(&wc3->vpkt); 80cabdff1aSopenharmony_ci 81cabdff1aSopenharmony_ci return 0; 82cabdff1aSopenharmony_ci} 83cabdff1aSopenharmony_ci 84cabdff1aSopenharmony_cistatic int wc3_probe(const AVProbeData *p) 85cabdff1aSopenharmony_ci{ 86cabdff1aSopenharmony_ci if (p->buf_size < 12) 87cabdff1aSopenharmony_ci return 0; 88cabdff1aSopenharmony_ci 89cabdff1aSopenharmony_ci if ((AV_RL32(&p->buf[0]) != FORM_TAG) || 90cabdff1aSopenharmony_ci (AV_RL32(&p->buf[8]) != MOVE_TAG)) 91cabdff1aSopenharmony_ci return 0; 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX; 94cabdff1aSopenharmony_ci} 95cabdff1aSopenharmony_ci 96cabdff1aSopenharmony_cistatic int wc3_read_header(AVFormatContext *s) 97cabdff1aSopenharmony_ci{ 98cabdff1aSopenharmony_ci Wc3DemuxContext *wc3 = s->priv_data; 99cabdff1aSopenharmony_ci AVIOContext *pb = s->pb; 100cabdff1aSopenharmony_ci unsigned int fourcc_tag; 101cabdff1aSopenharmony_ci unsigned int size; 102cabdff1aSopenharmony_ci AVStream *st; 103cabdff1aSopenharmony_ci int ret = 0; 104cabdff1aSopenharmony_ci char *buffer; 105cabdff1aSopenharmony_ci 106cabdff1aSopenharmony_ci /* default context members */ 107cabdff1aSopenharmony_ci wc3->width = WC3_DEFAULT_WIDTH; 108cabdff1aSopenharmony_ci wc3->height = WC3_DEFAULT_HEIGHT; 109cabdff1aSopenharmony_ci wc3->pts = 0; 110cabdff1aSopenharmony_ci wc3->video_stream_index = wc3->audio_stream_index = 0; 111cabdff1aSopenharmony_ci wc3->vpkt = av_packet_alloc(); 112cabdff1aSopenharmony_ci if (!wc3->vpkt) 113cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci /* skip the first 3 32-bit numbers */ 116cabdff1aSopenharmony_ci avio_skip(pb, 12); 117cabdff1aSopenharmony_ci 118cabdff1aSopenharmony_ci /* traverse through the chunks and load the header information before 119cabdff1aSopenharmony_ci * the first BRCH tag */ 120cabdff1aSopenharmony_ci fourcc_tag = avio_rl32(pb); 121cabdff1aSopenharmony_ci size = (avio_rb32(pb) + 1) & (~1); 122cabdff1aSopenharmony_ci 123cabdff1aSopenharmony_ci do { 124cabdff1aSopenharmony_ci switch (fourcc_tag) { 125cabdff1aSopenharmony_ci 126cabdff1aSopenharmony_ci case SOND_TAG: 127cabdff1aSopenharmony_ci case INDX_TAG: 128cabdff1aSopenharmony_ci /* SOND unknown, INDX unnecessary; ignore both */ 129cabdff1aSopenharmony_ci avio_skip(pb, size); 130cabdff1aSopenharmony_ci break; 131cabdff1aSopenharmony_ci 132cabdff1aSopenharmony_ci case PC__TAG: 133cabdff1aSopenharmony_ci /* number of palettes, unneeded */ 134cabdff1aSopenharmony_ci avio_skip(pb, 12); 135cabdff1aSopenharmony_ci break; 136cabdff1aSopenharmony_ci 137cabdff1aSopenharmony_ci case BNAM_TAG: 138cabdff1aSopenharmony_ci /* load up the name */ 139cabdff1aSopenharmony_ci buffer = av_malloc(size+1); 140cabdff1aSopenharmony_ci if (!buffer) 141cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 142cabdff1aSopenharmony_ci if ((ret = avio_read(pb, buffer, size)) != size) { 143cabdff1aSopenharmony_ci av_freep(&buffer); 144cabdff1aSopenharmony_ci return AVERROR(EIO); 145cabdff1aSopenharmony_ci } 146cabdff1aSopenharmony_ci buffer[size] = 0; 147cabdff1aSopenharmony_ci av_dict_set(&s->metadata, "title", buffer, 148cabdff1aSopenharmony_ci AV_DICT_DONT_STRDUP_VAL); 149cabdff1aSopenharmony_ci break; 150cabdff1aSopenharmony_ci 151cabdff1aSopenharmony_ci case SIZE_TAG: 152cabdff1aSopenharmony_ci /* video resolution override */ 153cabdff1aSopenharmony_ci wc3->width = avio_rl32(pb); 154cabdff1aSopenharmony_ci wc3->height = avio_rl32(pb); 155cabdff1aSopenharmony_ci break; 156cabdff1aSopenharmony_ci 157cabdff1aSopenharmony_ci case PALT_TAG: 158cabdff1aSopenharmony_ci /* one of several palettes */ 159cabdff1aSopenharmony_ci avio_seek(pb, -8, SEEK_CUR); 160cabdff1aSopenharmony_ci av_append_packet(pb, wc3->vpkt, 8 + PALETTE_SIZE); 161cabdff1aSopenharmony_ci break; 162cabdff1aSopenharmony_ci 163cabdff1aSopenharmony_ci default: 164cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "unrecognized WC3 chunk: %s\n", 165cabdff1aSopenharmony_ci av_fourcc2str(fourcc_tag)); 166cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 167cabdff1aSopenharmony_ci } 168cabdff1aSopenharmony_ci 169cabdff1aSopenharmony_ci fourcc_tag = avio_rl32(pb); 170cabdff1aSopenharmony_ci /* chunk sizes are 16-bit aligned */ 171cabdff1aSopenharmony_ci size = (avio_rb32(pb) + 1) & (~1); 172cabdff1aSopenharmony_ci if (avio_feof(pb)) 173cabdff1aSopenharmony_ci return AVERROR(EIO); 174cabdff1aSopenharmony_ci 175cabdff1aSopenharmony_ci } while (fourcc_tag != BRCH_TAG); 176cabdff1aSopenharmony_ci 177cabdff1aSopenharmony_ci /* initialize the decoder streams */ 178cabdff1aSopenharmony_ci st = avformat_new_stream(s, NULL); 179cabdff1aSopenharmony_ci if (!st) 180cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 181cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 33, 1, WC3_FRAME_FPS); 182cabdff1aSopenharmony_ci wc3->video_stream_index = st->index; 183cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; 184cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_XAN_WC3; 185cabdff1aSopenharmony_ci st->codecpar->codec_tag = 0; /* no fourcc */ 186cabdff1aSopenharmony_ci st->codecpar->width = wc3->width; 187cabdff1aSopenharmony_ci st->codecpar->height = wc3->height; 188cabdff1aSopenharmony_ci 189cabdff1aSopenharmony_ci st = avformat_new_stream(s, NULL); 190cabdff1aSopenharmony_ci if (!st) 191cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 192cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 33, 1, WC3_FRAME_FPS); 193cabdff1aSopenharmony_ci wc3->audio_stream_index = st->index; 194cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; 195cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; 196cabdff1aSopenharmony_ci st->codecpar->codec_tag = 1; 197cabdff1aSopenharmony_ci st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; 198cabdff1aSopenharmony_ci st->codecpar->bits_per_coded_sample = WC3_AUDIO_BITS; 199cabdff1aSopenharmony_ci st->codecpar->sample_rate = WC3_SAMPLE_RATE; 200cabdff1aSopenharmony_ci st->codecpar->bit_rate = st->codecpar->ch_layout.nb_channels * st->codecpar->sample_rate * 201cabdff1aSopenharmony_ci st->codecpar->bits_per_coded_sample; 202cabdff1aSopenharmony_ci st->codecpar->block_align = WC3_AUDIO_BITS * st->codecpar->ch_layout.nb_channels; 203cabdff1aSopenharmony_ci 204cabdff1aSopenharmony_ci return 0; 205cabdff1aSopenharmony_ci} 206cabdff1aSopenharmony_ci 207cabdff1aSopenharmony_cistatic int wc3_read_packet(AVFormatContext *s, 208cabdff1aSopenharmony_ci AVPacket *pkt) 209cabdff1aSopenharmony_ci{ 210cabdff1aSopenharmony_ci Wc3DemuxContext *wc3 = s->priv_data; 211cabdff1aSopenharmony_ci AVIOContext *pb = s->pb; 212cabdff1aSopenharmony_ci unsigned int fourcc_tag; 213cabdff1aSopenharmony_ci unsigned int size; 214cabdff1aSopenharmony_ci int packet_read = 0; 215cabdff1aSopenharmony_ci int ret = 0; 216cabdff1aSopenharmony_ci unsigned char text[1024]; 217cabdff1aSopenharmony_ci 218cabdff1aSopenharmony_ci while (!packet_read) { 219cabdff1aSopenharmony_ci 220cabdff1aSopenharmony_ci fourcc_tag = avio_rl32(pb); 221cabdff1aSopenharmony_ci /* chunk sizes are 16-bit aligned */ 222cabdff1aSopenharmony_ci size = (avio_rb32(pb) + 1) & (~1); 223cabdff1aSopenharmony_ci if (avio_feof(pb)) 224cabdff1aSopenharmony_ci return AVERROR(EIO); 225cabdff1aSopenharmony_ci 226cabdff1aSopenharmony_ci switch (fourcc_tag) { 227cabdff1aSopenharmony_ci 228cabdff1aSopenharmony_ci case BRCH_TAG: 229cabdff1aSopenharmony_ci /* no-op */ 230cabdff1aSopenharmony_ci break; 231cabdff1aSopenharmony_ci 232cabdff1aSopenharmony_ci case SHOT_TAG: 233cabdff1aSopenharmony_ci /* load up new palette */ 234cabdff1aSopenharmony_ci avio_seek(pb, -8, SEEK_CUR); 235cabdff1aSopenharmony_ci av_append_packet(pb, wc3->vpkt, 8 + 4); 236cabdff1aSopenharmony_ci break; 237cabdff1aSopenharmony_ci 238cabdff1aSopenharmony_ci case VGA__TAG: 239cabdff1aSopenharmony_ci /* send out video chunk */ 240cabdff1aSopenharmony_ci avio_seek(pb, -8, SEEK_CUR); 241cabdff1aSopenharmony_ci ret= av_append_packet(pb, wc3->vpkt, 8 + size); 242cabdff1aSopenharmony_ci // ignore error if we have some data 243cabdff1aSopenharmony_ci if (wc3->vpkt->size > 0) 244cabdff1aSopenharmony_ci ret = 0; 245cabdff1aSopenharmony_ci av_packet_move_ref(pkt, wc3->vpkt); 246cabdff1aSopenharmony_ci pkt->stream_index = wc3->video_stream_index; 247cabdff1aSopenharmony_ci pkt->pts = wc3->pts; 248cabdff1aSopenharmony_ci packet_read = 1; 249cabdff1aSopenharmony_ci break; 250cabdff1aSopenharmony_ci 251cabdff1aSopenharmony_ci case TEXT_TAG: 252cabdff1aSopenharmony_ci /* subtitle chunk */ 253cabdff1aSopenharmony_ci if ((unsigned)size > sizeof(text) || (ret = avio_read(pb, text, size)) != size) 254cabdff1aSopenharmony_ci ret = AVERROR(EIO); 255cabdff1aSopenharmony_ci else { 256cabdff1aSopenharmony_ci int i = 0; 257cabdff1aSopenharmony_ci av_log (s, AV_LOG_DEBUG, "Subtitle time!\n"); 258cabdff1aSopenharmony_ci if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1) 259cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 260cabdff1aSopenharmony_ci av_log (s, AV_LOG_DEBUG, " inglish: %s\n", &text[i + 1]); 261cabdff1aSopenharmony_ci i += text[i] + 1; 262cabdff1aSopenharmony_ci if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1) 263cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 264cabdff1aSopenharmony_ci av_log (s, AV_LOG_DEBUG, " doytsch: %s\n", &text[i + 1]); 265cabdff1aSopenharmony_ci i += text[i] + 1; 266cabdff1aSopenharmony_ci if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1) 267cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 268cabdff1aSopenharmony_ci av_log (s, AV_LOG_DEBUG, " fronsay: %s\n", &text[i + 1]); 269cabdff1aSopenharmony_ci } 270cabdff1aSopenharmony_ci break; 271cabdff1aSopenharmony_ci 272cabdff1aSopenharmony_ci case AUDI_TAG: 273cabdff1aSopenharmony_ci /* send out audio chunk */ 274cabdff1aSopenharmony_ci ret= av_get_packet(pb, pkt, size); 275cabdff1aSopenharmony_ci pkt->stream_index = wc3->audio_stream_index; 276cabdff1aSopenharmony_ci pkt->pts = wc3->pts; 277cabdff1aSopenharmony_ci 278cabdff1aSopenharmony_ci /* time to advance pts */ 279cabdff1aSopenharmony_ci wc3->pts++; 280cabdff1aSopenharmony_ci 281cabdff1aSopenharmony_ci packet_read = 1; 282cabdff1aSopenharmony_ci break; 283cabdff1aSopenharmony_ci 284cabdff1aSopenharmony_ci default: 285cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "unrecognized WC3 chunk: %s\n", 286cabdff1aSopenharmony_ci av_fourcc2str(fourcc_tag)); 287cabdff1aSopenharmony_ci ret = AVERROR_INVALIDDATA; 288cabdff1aSopenharmony_ci packet_read = 1; 289cabdff1aSopenharmony_ci break; 290cabdff1aSopenharmony_ci } 291cabdff1aSopenharmony_ci } 292cabdff1aSopenharmony_ci 293cabdff1aSopenharmony_ci return ret; 294cabdff1aSopenharmony_ci} 295cabdff1aSopenharmony_ci 296cabdff1aSopenharmony_ciconst AVInputFormat ff_wc3_demuxer = { 297cabdff1aSopenharmony_ci .name = "wc3movie", 298cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Wing Commander III movie"), 299cabdff1aSopenharmony_ci .priv_data_size = sizeof(Wc3DemuxContext), 300cabdff1aSopenharmony_ci .flags_internal = FF_FMT_INIT_CLEANUP, 301cabdff1aSopenharmony_ci .read_probe = wc3_probe, 302cabdff1aSopenharmony_ci .read_header = wc3_read_header, 303cabdff1aSopenharmony_ci .read_packet = wc3_read_packet, 304cabdff1aSopenharmony_ci .read_close = wc3_read_close, 305cabdff1aSopenharmony_ci}; 306