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