]> git.sesse.net Git - ffmpeg/blob - libavformat/wc3movie.c
Add symbol versioning for shared libraries
[ffmpeg] / libavformat / wc3movie.c
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_NOMEM;
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 = CODEC_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 = CODEC_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 };