ffmpeg / libavformat / wc3movie.c @ 2874c81c
History | View | Annotate | Download (12.5 KB)
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 libavformat/wc3movie.c
|
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/intreadwrite.h" |
31 |
#include "avformat.h" |
32 |
|
33 |
#define FORM_TAG MKTAG('F', 'O', 'R', 'M') |
34 |
#define MOVE_TAG MKTAG('M', 'O', 'V', 'E') |
35 |
#define PC__TAG MKTAG('_', 'P', 'C', '_') |
36 |
#define SOND_TAG MKTAG('S', 'O', 'N', 'D') |
37 |
#define BNAM_TAG MKTAG('B', 'N', 'A', 'M') |
38 |
#define SIZE_TAG MKTAG('S', 'I', 'Z', 'E') |
39 |
#define PALT_TAG MKTAG('P', 'A', 'L', 'T') |
40 |
#define INDX_TAG MKTAG('I', 'N', 'D', 'X') |
41 |
#define BRCH_TAG MKTAG('B', 'R', 'C', 'H') |
42 |
#define SHOT_TAG MKTAG('S', 'H', 'O', 'T') |
43 |
#define VGA__TAG MKTAG('V', 'G', 'A', ' ') |
44 |
#define TEXT_TAG MKTAG('T', 'E', 'X', 'T') |
45 |
#define AUDI_TAG MKTAG('A', 'U', 'D', 'I') |
46 |
|
47 |
/* video resolution unless otherwise specified */
|
48 |
#define WC3_DEFAULT_WIDTH 320 |
49 |
#define WC3_DEFAULT_HEIGHT 165 |
50 |
|
51 |
/* always use the same PCM audio parameters */
|
52 |
#define WC3_SAMPLE_RATE 22050 |
53 |
#define WC3_AUDIO_CHANNELS 1 |
54 |
#define WC3_AUDIO_BITS 16 |
55 |
|
56 |
/* nice, constant framerate */
|
57 |
#define WC3_FRAME_FPS 15 |
58 |
|
59 |
#define PALETTE_SIZE (256 * 3) |
60 |
#define PALETTE_COUNT 256 |
61 |
|
62 |
typedef struct Wc3DemuxContext { |
63 |
int width;
|
64 |
int height;
|
65 |
unsigned char *palettes; |
66 |
int palette_count;
|
67 |
int64_t pts; |
68 |
int video_stream_index;
|
69 |
int audio_stream_index;
|
70 |
|
71 |
AVPaletteControl palette_control; |
72 |
|
73 |
} Wc3DemuxContext; |
74 |
|
75 |
/**
|
76 |
* palette lookup table that does gamma correction
|
77 |
*
|
78 |
* can be calculated by this formula:
|
79 |
* for i between 0 and 251 inclusive:
|
80 |
* wc3_pal_lookup[i] = round(pow(i / 256.0, 0.8) * 256);
|
81 |
* values 252, 253, 254 and 255 are all 0xFD
|
82 |
* calculating this at runtime should not cause any
|
83 |
* rounding issues, the maximum difference between
|
84 |
* the table values and the calculated doubles is
|
85 |
* about 0.497527
|
86 |
*/
|
87 |
static const unsigned char wc3_pal_lookup[] = { |
88 |
0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0E, |
89 |
0x10, 0x12, 0x13, 0x15, 0x16, 0x18, 0x19, 0x1A, |
90 |
0x1C, 0x1D, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x25, |
91 |
0x27, 0x28, 0x29, 0x2A, 0x2C, 0x2D, 0x2E, 0x2F, |
92 |
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x39, |
93 |
0x3A, 0x3B, 0x3C, 0x3D, 0x3F, 0x40, 0x41, 0x42, |
94 |
0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, |
95 |
0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, |
96 |
0x54, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, |
97 |
0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, |
98 |
0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, |
99 |
0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, |
100 |
0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, |
101 |
0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, |
102 |
0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, |
103 |
0x8C, 0x8D, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, |
104 |
0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x99, |
105 |
0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, |
106 |
0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, |
107 |
0xA9, 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, |
108 |
0xB0, 0xB1, 0xB2, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, |
109 |
0xB7, 0xB8, 0xB9, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, |
110 |
0xBE, 0xBF, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, |
111 |
0xC5, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, |
112 |
0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD0, 0xD1, |
113 |
0xD2, 0xD3, 0xD4, 0xD5, 0xD5, 0xD6, 0xD7, 0xD8, |
114 |
0xD9, 0xDA, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, |
115 |
0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE4, 0xE5, |
116 |
0xE6, 0xE7, 0xE8, 0xE9, 0xE9, 0xEA, 0xEB, 0xEC, |
117 |
0xED, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, |
118 |
0xF3, 0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF9, |
119 |
0xFA, 0xFA, 0xFB, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD |
120 |
}; |
121 |
|
122 |
|
123 |
static int wc3_probe(AVProbeData *p) |
124 |
{ |
125 |
if (p->buf_size < 12) |
126 |
return 0; |
127 |
|
128 |
if ((AV_RL32(&p->buf[0]) != FORM_TAG) || |
129 |
(AV_RL32(&p->buf[8]) != MOVE_TAG))
|
130 |
return 0; |
131 |
|
132 |
return AVPROBE_SCORE_MAX;
|
133 |
} |
134 |
|
135 |
static int wc3_read_header(AVFormatContext *s, |
136 |
AVFormatParameters *ap) |
137 |
{ |
138 |
Wc3DemuxContext *wc3 = s->priv_data; |
139 |
ByteIOContext *pb = s->pb; |
140 |
unsigned int fourcc_tag; |
141 |
unsigned int size; |
142 |
AVStream *st; |
143 |
int ret = 0; |
144 |
int current_palette = 0; |
145 |
char *buffer;
|
146 |
int i;
|
147 |
unsigned char rotate; |
148 |
|
149 |
/* default context members */
|
150 |
wc3->width = WC3_DEFAULT_WIDTH; |
151 |
wc3->height = WC3_DEFAULT_HEIGHT; |
152 |
wc3->palettes = NULL;
|
153 |
wc3->palette_count = 0;
|
154 |
wc3->pts = 0;
|
155 |
wc3->video_stream_index = wc3->audio_stream_index = 0;
|
156 |
|
157 |
/* skip the first 3 32-bit numbers */
|
158 |
url_fseek(pb, 12, SEEK_CUR);
|
159 |
|
160 |
/* traverse through the chunks and load the header information before
|
161 |
* the first BRCH tag */
|
162 |
fourcc_tag = get_le32(pb); |
163 |
size = (get_be32(pb) + 1) & (~1); |
164 |
|
165 |
do {
|
166 |
switch (fourcc_tag) {
|
167 |
|
168 |
case SOND_TAG:
|
169 |
case INDX_TAG:
|
170 |
/* SOND unknown, INDX unnecessary; ignore both */
|
171 |
url_fseek(pb, size, SEEK_CUR); |
172 |
break;
|
173 |
|
174 |
case PC__TAG:
|
175 |
/* need the number of palettes */
|
176 |
url_fseek(pb, 8, SEEK_CUR);
|
177 |
wc3->palette_count = get_le32(pb); |
178 |
if((unsigned)wc3->palette_count >= UINT_MAX / PALETTE_SIZE){ |
179 |
wc3->palette_count= 0;
|
180 |
return -1; |
181 |
} |
182 |
wc3->palettes = av_malloc(wc3->palette_count * PALETTE_SIZE); |
183 |
break;
|
184 |
|
185 |
case BNAM_TAG:
|
186 |
/* load up the name */
|
187 |
buffer = av_malloc(size+1);
|
188 |
if (!buffer)
|
189 |
return AVERROR(ENOMEM);
|
190 |
if ((ret = get_buffer(pb, buffer, size)) != size)
|
191 |
return AVERROR(EIO);
|
192 |
buffer[size] = 0;
|
193 |
av_metadata_set2(&s->metadata, "title", buffer,
|
194 |
AV_METADATA_DONT_STRDUP_VAL); |
195 |
break;
|
196 |
|
197 |
case SIZE_TAG:
|
198 |
/* video resolution override */
|
199 |
wc3->width = get_le32(pb); |
200 |
wc3->height = get_le32(pb); |
201 |
break;
|
202 |
|
203 |
case PALT_TAG:
|
204 |
/* one of several palettes */
|
205 |
if ((unsigned)current_palette >= wc3->palette_count) |
206 |
return AVERROR_INVALIDDATA;
|
207 |
if ((ret = get_buffer(pb,
|
208 |
&wc3->palettes[current_palette * PALETTE_SIZE], |
209 |
PALETTE_SIZE)) != PALETTE_SIZE) |
210 |
return AVERROR(EIO);
|
211 |
|
212 |
/* transform the current palette in place */
|
213 |
for (i = current_palette * PALETTE_SIZE;
|
214 |
i < (current_palette + 1) * PALETTE_SIZE; i++) {
|
215 |
/* rotate each palette component left by 2 and use the result
|
216 |
* as an index into the color component table */
|
217 |
rotate = ((wc3->palettes[i] << 2) & 0xFF) | |
218 |
((wc3->palettes[i] >> 6) & 0xFF); |
219 |
wc3->palettes[i] = wc3_pal_lookup[rotate]; |
220 |
} |
221 |
current_palette++; |
222 |
break;
|
223 |
|
224 |
default:
|
225 |
av_log(s, AV_LOG_ERROR, " unrecognized WC3 chunk: %c%c%c%c (0x%02X%02X%02X%02X)\n",
|
226 |
(uint8_t)fourcc_tag, (uint8_t)(fourcc_tag >> 8), (uint8_t)(fourcc_tag >> 16), (uint8_t)(fourcc_tag >> 24), |
227 |
(uint8_t)fourcc_tag, (uint8_t)(fourcc_tag >> 8), (uint8_t)(fourcc_tag >> 16), (uint8_t)(fourcc_tag >> 24)); |
228 |
return AVERROR_INVALIDDATA;
|
229 |
break;
|
230 |
} |
231 |
|
232 |
fourcc_tag = get_le32(pb); |
233 |
/* chunk sizes are 16-bit aligned */
|
234 |
size = (get_be32(pb) + 1) & (~1); |
235 |
if (url_feof(pb))
|
236 |
return AVERROR(EIO);
|
237 |
|
238 |
} while (fourcc_tag != BRCH_TAG);
|
239 |
|
240 |
/* initialize the decoder streams */
|
241 |
st = av_new_stream(s, 0);
|
242 |
if (!st)
|
243 |
return AVERROR(ENOMEM);
|
244 |
av_set_pts_info(st, 33, 1, WC3_FRAME_FPS); |
245 |
wc3->video_stream_index = st->index; |
246 |
st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
247 |
st->codec->codec_id = CODEC_ID_XAN_WC3; |
248 |
st->codec->codec_tag = 0; /* no fourcc */ |
249 |
st->codec->width = wc3->width; |
250 |
st->codec->height = wc3->height; |
251 |
|
252 |
/* palette considerations */
|
253 |
st->codec->palctrl = &wc3->palette_control; |
254 |
|
255 |
st = av_new_stream(s, 0);
|
256 |
if (!st)
|
257 |
return AVERROR(ENOMEM);
|
258 |
av_set_pts_info(st, 33, 1, WC3_FRAME_FPS); |
259 |
wc3->audio_stream_index = st->index; |
260 |
st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
261 |
st->codec->codec_id = CODEC_ID_PCM_S16LE; |
262 |
st->codec->codec_tag = 1;
|
263 |
st->codec->channels = WC3_AUDIO_CHANNELS; |
264 |
st->codec->bits_per_coded_sample = WC3_AUDIO_BITS; |
265 |
st->codec->sample_rate = WC3_SAMPLE_RATE; |
266 |
st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * |
267 |
st->codec->bits_per_coded_sample; |
268 |
st->codec->block_align = WC3_AUDIO_BITS * WC3_AUDIO_CHANNELS; |
269 |
|
270 |
return 0; |
271 |
} |
272 |
|
273 |
static int wc3_read_packet(AVFormatContext *s, |
274 |
AVPacket *pkt) |
275 |
{ |
276 |
Wc3DemuxContext *wc3 = s->priv_data; |
277 |
ByteIOContext *pb = s->pb; |
278 |
unsigned int fourcc_tag; |
279 |
unsigned int size; |
280 |
int packet_read = 0; |
281 |
int ret = 0; |
282 |
unsigned char text[1024]; |
283 |
unsigned int palette_number; |
284 |
int i;
|
285 |
unsigned char r, g, b; |
286 |
int base_palette_index;
|
287 |
|
288 |
while (!packet_read) {
|
289 |
|
290 |
fourcc_tag = get_le32(pb); |
291 |
/* chunk sizes are 16-bit aligned */
|
292 |
size = (get_be32(pb) + 1) & (~1); |
293 |
if (url_feof(pb))
|
294 |
return AVERROR(EIO);
|
295 |
|
296 |
switch (fourcc_tag) {
|
297 |
|
298 |
case BRCH_TAG:
|
299 |
/* no-op */
|
300 |
break;
|
301 |
|
302 |
case SHOT_TAG:
|
303 |
/* load up new palette */
|
304 |
palette_number = get_le32(pb); |
305 |
if (palette_number >= wc3->palette_count)
|
306 |
return AVERROR_INVALIDDATA;
|
307 |
base_palette_index = palette_number * PALETTE_COUNT * 3;
|
308 |
for (i = 0; i < PALETTE_COUNT; i++) { |
309 |
r = wc3->palettes[base_palette_index + i * 3 + 0]; |
310 |
g = wc3->palettes[base_palette_index + i * 3 + 1]; |
311 |
b = wc3->palettes[base_palette_index + i * 3 + 2]; |
312 |
wc3->palette_control.palette[i] = (r << 16) | (g << 8) | (b); |
313 |
} |
314 |
wc3->palette_control.palette_changed = 1;
|
315 |
break;
|
316 |
|
317 |
case VGA__TAG:
|
318 |
/* send out video chunk */
|
319 |
ret= av_get_packet(pb, pkt, size); |
320 |
pkt->stream_index = wc3->video_stream_index; |
321 |
pkt->pts = wc3->pts; |
322 |
packet_read = 1;
|
323 |
break;
|
324 |
|
325 |
case TEXT_TAG:
|
326 |
/* subtitle chunk */
|
327 |
#if 0
|
328 |
url_fseek(pb, size, SEEK_CUR);
|
329 |
#else
|
330 |
if ((unsigned)size > sizeof(text) || (ret = get_buffer(pb, text, size)) != size) |
331 |
ret = AVERROR(EIO); |
332 |
else {
|
333 |
int i = 0; |
334 |
av_log (s, AV_LOG_DEBUG, "Subtitle time!\n");
|
335 |
av_log (s, AV_LOG_DEBUG, " inglish: %s\n", &text[i + 1]); |
336 |
i += text[i] + 1;
|
337 |
av_log (s, AV_LOG_DEBUG, " doytsch: %s\n", &text[i + 1]); |
338 |
i += text[i] + 1;
|
339 |
av_log (s, AV_LOG_DEBUG, " fronsay: %s\n", &text[i + 1]); |
340 |
} |
341 |
#endif
|
342 |
break;
|
343 |
|
344 |
case AUDI_TAG:
|
345 |
/* send out audio chunk */
|
346 |
ret= av_get_packet(pb, pkt, size); |
347 |
pkt->stream_index = wc3->audio_stream_index; |
348 |
pkt->pts = wc3->pts; |
349 |
|
350 |
/* time to advance pts */
|
351 |
wc3->pts++; |
352 |
|
353 |
packet_read = 1;
|
354 |
break;
|
355 |
|
356 |
default:
|
357 |
av_log (s, AV_LOG_ERROR, " unrecognized WC3 chunk: %c%c%c%c (0x%02X%02X%02X%02X)\n",
|
358 |
(uint8_t)fourcc_tag, (uint8_t)(fourcc_tag >> 8), (uint8_t)(fourcc_tag >> 16), (uint8_t)(fourcc_tag >> 24), |
359 |
(uint8_t)fourcc_tag, (uint8_t)(fourcc_tag >> 8), (uint8_t)(fourcc_tag >> 16), (uint8_t)(fourcc_tag >> 24)); |
360 |
ret = AVERROR_INVALIDDATA; |
361 |
packet_read = 1;
|
362 |
break;
|
363 |
} |
364 |
} |
365 |
|
366 |
return ret;
|
367 |
} |
368 |
|
369 |
static int wc3_read_close(AVFormatContext *s) |
370 |
{ |
371 |
Wc3DemuxContext *wc3 = s->priv_data; |
372 |
|
373 |
av_free(wc3->palettes); |
374 |
|
375 |
return 0; |
376 |
} |
377 |
|
378 |
AVInputFormat wc3_demuxer = { |
379 |
"wc3movie",
|
380 |
NULL_IF_CONFIG_SMALL("Wing Commander III movie format"),
|
381 |
sizeof(Wc3DemuxContext),
|
382 |
wc3_probe, |
383 |
wc3_read_header, |
384 |
wc3_read_packet, |
385 |
wc3_read_close, |
386 |
}; |