4189
|
1 /*
|
5413
|
2 FILM file parser for the MPlayer program
|
|
3 by Mike Melanson
|
4628
|
4
|
5413
|
5 This demuxer handles FILM (a.k.a. CPK) files commonly found on
|
|
6 Sega Saturn CD-ROM games. FILM files have also been found on 3DO
|
|
7 games.
|
|
8
|
|
9 Details of the FILM file format can be found at:
|
|
10 http://www.pcisys.net/~melanson/codecs/
|
4189
|
11 */
|
|
12
|
|
13 #include <stdio.h>
|
|
14 #include <stdlib.h>
|
|
15 #include <unistd.h>
|
|
16
|
|
17 #include "config.h"
|
|
18 #include "mp_msg.h"
|
|
19 #include "help_mp.h"
|
|
20
|
22605
|
21 #include "stream/stream.h"
|
4189
|
22 #include "demuxer.h"
|
|
23 #include "stheader.h"
|
|
24
|
|
25 // chunk types found in a FILM file
|
|
26 #define CHUNK_FILM mmioFOURCC('F', 'I', 'L', 'M')
|
|
27 #define CHUNK_FDSC mmioFOURCC('F', 'D', 'S', 'C')
|
|
28 #define CHUNK_STAB mmioFOURCC('S', 'T', 'A', 'B')
|
|
29
|
25607
|
30 typedef struct film_chunk_t
|
4564
|
31 {
|
|
32 off_t chunk_offset;
|
|
33 int chunk_size;
|
5143
|
34 unsigned int syncinfo1;
|
|
35 unsigned int syncinfo2;
|
5181
|
36
|
|
37 float pts;
|
4564
|
38 } film_chunk_t;
|
4189
|
39
|
25607
|
40 typedef struct film_data_t
|
4564
|
41 {
|
5157
|
42 unsigned int total_chunks;
|
|
43 unsigned int current_chunk;
|
4564
|
44 film_chunk_t *chunks;
|
5157
|
45 unsigned int chunks_per_second;
|
5413
|
46 unsigned int film_version;
|
4564
|
47 } film_data_t;
|
|
48
|
17636
|
49 static void demux_seek_film(demuxer_t *demuxer, float rel_seek_secs, float audio_delay, int flags)
|
4564
|
50 {
|
5157
|
51 film_data_t *film_data = (film_data_t *)demuxer->priv;
|
25883
|
52 int new_current_chunk=(flags&SEEK_ABSOLUTE)?0:film_data->current_chunk;
|
5157
|
53
|
25883
|
54 if(flags&SEEK_FACTOR)
|
5438
|
55 new_current_chunk += rel_seek_secs * film_data->total_chunks; // 0..1
|
5157
|
56 else
|
5438
|
57 new_current_chunk += rel_seek_secs * film_data->chunks_per_second; // secs
|
|
58
|
5157
|
59
|
18176
f72bc5754209
Part3 of Otvos Attila's oattila AT chello-hu mp_msg changes, with lots of modifications as usual
reynaldo
diff
changeset
|
60 mp_msg(MSGT_DECVIDEO, MSGL_INFO,"current, total chunks = %d, %d; seek %5.3f sec, new chunk guess = %d\n",
|
5157
|
61 film_data->current_chunk, film_data->total_chunks,
|
|
62 rel_seek_secs, new_current_chunk);
|
|
63
|
|
64 // check if the new chunk number is valid
|
|
65 if (new_current_chunk < 0)
|
|
66 new_current_chunk = 0;
|
|
67 if ((unsigned int)new_current_chunk > film_data->total_chunks)
|
5181
|
68 new_current_chunk = film_data->total_chunks - 1;
|
5157
|
69
|
|
70 while (((film_data->chunks[new_current_chunk].syncinfo1 == 0xFFFFFFFF) ||
|
|
71 (film_data->chunks[new_current_chunk].syncinfo1 & 0x80000000)) &&
|
|
72 (new_current_chunk > 0))
|
|
73 new_current_chunk--;
|
|
74
|
|
75 film_data->current_chunk = new_current_chunk;
|
5181
|
76
|
18176
f72bc5754209
Part3 of Otvos Attila's oattila AT chello-hu mp_msg changes, with lots of modifications as usual
reynaldo
diff
changeset
|
77 mp_msg(MSGT_DECVIDEO, MSGL_INFO," (flags = %X) actual new chunk = %d (syncinfo1 = %08X)\n",
|
5181
|
78 flags, film_data->current_chunk, film_data->chunks[film_data->current_chunk].syncinfo1);
|
5439
|
79 demuxer->video->pts=film_data->chunks[film_data->current_chunk].pts;
|
|
80
|
4189
|
81 }
|
|
82
|
|
83 // return value:
|
|
84 // 0 = EOF or no stream found
|
|
85 // 1 = successfully read a packet
|
16175
|
86 static int demux_film_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds)
|
4564
|
87 {
|
4628
|
88 int i;
|
|
89 unsigned char byte_swap;
|
|
90 int cvid_size;
|
4189
|
91 sh_video_t *sh_video = demuxer->video->sh;
|
4628
|
92 sh_audio_t *sh_audio = demuxer->audio->sh;
|
4564
|
93 film_data_t *film_data = (film_data_t *)demuxer->priv;
|
|
94 film_chunk_t film_chunk;
|
5413
|
95 int length_fix_bytes;
|
|
96 demux_packet_t* dp;
|
4189
|
97
|
|
98 // see if the end has been reached
|
4564
|
99 if (film_data->current_chunk >= film_data->total_chunks)
|
4189
|
100 return 0;
|
|
101
|
4564
|
102 film_chunk = film_data->chunks[film_data->current_chunk];
|
4189
|
103
|
4564
|
104 // position stream and fetch chunk
|
|
105 stream_seek(demuxer->stream, film_chunk.chunk_offset);
|
4628
|
106
|
|
107 // load the chunks manually (instead of using ds_read_packet()), since
|
|
108 // they require some adjustment
|
5143
|
109 // (all ones in syncinfo1 indicates an audio chunk)
|
|
110 if (film_chunk.syncinfo1 == 0xFFFFFFFF)
|
4628
|
111 {
|
5438
|
112 if(demuxer->audio->id>=-1){ // audio not disabled
|
5413
|
113 dp = new_demux_packet(film_chunk.chunk_size);
|
5157
|
114 if (stream_read(demuxer->stream, dp->buffer, film_chunk.chunk_size) !=
|
|
115 film_chunk.chunk_size)
|
|
116 return 0;
|
5181
|
117 dp->pts = film_chunk.pts;
|
4628
|
118 dp->pos = film_chunk.chunk_offset;
|
|
119 dp->flags = 0;
|
|
120
|
|
121 // adjust the data before queuing it:
|
|
122 // 8-bit: signed -> unsigned
|
|
123 // 16-bit: big-endian -> little-endian
|
|
124 if (sh_audio->wf->wBitsPerSample == 8)
|
|
125 for (i = 0; i < film_chunk.chunk_size; i++)
|
|
126 dp->buffer[i] += 128;
|
|
127 else
|
|
128 for (i = 0; i < film_chunk.chunk_size; i += 2)
|
|
129 {
|
|
130 byte_swap = dp->buffer[i];
|
|
131 dp->buffer[i] = dp->buffer[i + 1];
|
|
132 dp->buffer[i + 1] = byte_swap;
|
|
133 }
|
|
134
|
15468
|
135 /* for SegaSaturn .cpk file, translate audio data if stereo */
|
|
136 if (sh_audio->wf->nChannels == 2) {
|
|
137 if (sh_audio->wf->wBitsPerSample == 8) {
|
|
138 unsigned char* tmp = dp->buffer;
|
|
139 unsigned char buf[film_chunk.chunk_size];
|
|
140 for(i = 0; i < film_chunk.chunk_size/2; i++) {
|
|
141 buf[i*2] = tmp[i];
|
|
142 buf[i*2+1] = tmp[film_chunk.chunk_size/2+i];
|
|
143 }
|
|
144 memcpy( tmp, buf, film_chunk.chunk_size );
|
|
145 }
|
|
146 else {/* for 16bit */
|
|
147 unsigned short* tmp = dp->buffer;
|
|
148 unsigned short buf[film_chunk.chunk_size/2];
|
|
149 for(i = 0; i < film_chunk.chunk_size/4; i++) {
|
|
150 buf[i*2] = tmp[i];
|
|
151 buf[i*2+1] = tmp[film_chunk.chunk_size/4+i];
|
|
152 }
|
|
153 memcpy( tmp, buf, film_chunk.chunk_size );
|
|
154 }
|
|
155 }
|
|
156
|
4628
|
157 // append packet to DS stream
|
|
158 ds_add_packet(demuxer->audio, dp);
|
5438
|
159 }
|
4628
|
160 }
|
4564
|
161 else
|
4628
|
162 {
|
|
163 // if the demuxer is dealing with CVID data, deal with it a special way
|
5143
|
164 if (sh_video->format == mmioFOURCC('c', 'v', 'i', 'd'))
|
4628
|
165 {
|
5413
|
166 if (film_data->film_version)
|
|
167 length_fix_bytes = 2;
|
|
168 else
|
|
169 length_fix_bytes = 6;
|
4189
|
170
|
5413
|
171 // account for the fix bytes when allocating the buffer
|
|
172 dp = new_demux_packet(film_chunk.chunk_size - length_fix_bytes);
|
|
173
|
|
174 // these CVID data chunks have a few extra bytes; skip them
|
5157
|
175 if (stream_read(demuxer->stream, dp->buffer, 10) != 10)
|
|
176 return 0;
|
5413
|
177 stream_skip(demuxer->stream, length_fix_bytes);
|
|
178
|
5157
|
179 if (stream_read(demuxer->stream, dp->buffer + 10,
|
5413
|
180 film_chunk.chunk_size - (10 + length_fix_bytes)) !=
|
|
181 (film_chunk.chunk_size - (10 + length_fix_bytes)))
|
5157
|
182 return 0;
|
5413
|
183
|
5181
|
184 dp->pts = film_chunk.pts;
|
4628
|
185 dp->pos = film_chunk.chunk_offset;
|
5143
|
186 dp->flags = (film_chunk.syncinfo1 & 0x80000000) ? 1 : 0;
|
4628
|
187
|
5413
|
188 // fix the CVID chunk size
|
|
189 cvid_size = film_chunk.chunk_size - length_fix_bytes;
|
4628
|
190 dp->buffer[1] = (cvid_size >> 16) & 0xFF;
|
|
191 dp->buffer[2] = (cvid_size >> 8) & 0xFF;
|
|
192 dp->buffer[3] = (cvid_size >> 0) & 0xFF;
|
|
193
|
|
194 // append packet to DS stream
|
|
195 ds_add_packet(demuxer->video, dp);
|
|
196 }
|
|
197 else
|
|
198 {
|
|
199 ds_read_packet(demuxer->video, demuxer->stream, film_chunk.chunk_size,
|
5181
|
200 film_chunk.pts,
|
5143
|
201 film_chunk.chunk_offset, (film_chunk.syncinfo1 & 0x80000000) ? 1 : 0);
|
4628
|
202 }
|
|
203 }
|
5438
|
204 film_data->current_chunk++;
|
4628
|
205
|
4189
|
206 return 1;
|
|
207 }
|
|
208
|
16175
|
209 static demuxer_t* demux_open_film(demuxer_t* demuxer)
|
4564
|
210 {
|
4189
|
211 sh_video_t *sh_video = NULL;
|
4564
|
212 sh_audio_t *sh_audio = NULL;
|
|
213 film_data_t *film_data;
|
|
214 film_chunk_t film_chunk;
|
4189
|
215 int header_size;
|
|
216 unsigned int chunk_type;
|
|
217 unsigned int chunk_size;
|
5157
|
218 unsigned int i;
|
4564
|
219 unsigned int video_format;
|
4226
|
220 int audio_channels;
|
5157
|
221 int counting_chunks;
|
5181
|
222 unsigned int total_audio_bytes = 0;
|
4564
|
223
|
18885
|
224 film_data = malloc(sizeof(film_data_t));
|
4564
|
225 film_data->total_chunks = 0;
|
|
226 film_data->current_chunk = 0;
|
|
227 film_data->chunks = NULL;
|
5157
|
228 film_data->chunks_per_second = 0;
|
4189
|
229
|
|
230 // go back to the beginning
|
|
231 stream_reset(demuxer->stream);
|
|
232 stream_seek(demuxer->stream, 0);
|
|
233
|
|
234 // read the master chunk type
|
|
235 chunk_type = stream_read_fourcc(demuxer->stream);
|
|
236 // validate the chunk type
|
|
237 if (chunk_type != CHUNK_FILM)
|
|
238 {
|
|
239 mp_msg(MSGT_DEMUX, MSGL_ERR, "Not a FILM file\n");
|
17805
|
240 free(film_data);
|
26758
|
241 return NULL;
|
4189
|
242 }
|
|
243
|
|
244 // get the header size, which implicitly points past the header and
|
|
245 // to the start of the data
|
|
246 header_size = stream_read_dword(demuxer->stream);
|
5413
|
247 film_data->film_version = stream_read_fourcc(demuxer->stream);
|
4189
|
248 demuxer->movi_start = header_size;
|
|
249 demuxer->movi_end = demuxer->stream->end_pos;
|
|
250 header_size -= 16;
|
|
251
|
5413
|
252 mp_msg(MSGT_DEMUX, MSGL_HINT, "FILM version %.4s\n",
|
17366
|
253 (char *)&film_data->film_version);
|
5157
|
254
|
4189
|
255 // skip to where the next chunk should be
|
4628
|
256 stream_skip(demuxer->stream, 4);
|
4189
|
257
|
|
258 // traverse through the header
|
|
259 while (header_size > 0)
|
|
260 {
|
|
261 // fetch the chunk type and size
|
|
262 chunk_type = stream_read_fourcc(demuxer->stream);
|
|
263 chunk_size = stream_read_dword(demuxer->stream);
|
|
264 header_size -= chunk_size;
|
|
265
|
|
266 switch (chunk_type)
|
|
267 {
|
|
268 case CHUNK_FDSC:
|
4564
|
269 mp_msg(MSGT_DECVIDEO, MSGL_V, "parsing FDSC chunk\n");
|
5157
|
270
|
4564
|
271 // fetch the video codec fourcc to see if there's any video
|
|
272 video_format = stream_read_fourcc(demuxer->stream);
|
|
273 if (video_format)
|
|
274 {
|
|
275 // create and initialize the video stream header
|
|
276 sh_video = new_sh_video(demuxer, 0);
|
|
277 demuxer->video->sh = sh_video;
|
|
278 sh_video->ds = demuxer->video;
|
|
279
|
|
280 sh_video->format = video_format;
|
|
281 sh_video->disp_h = stream_read_dword(demuxer->stream);
|
|
282 sh_video->disp_w = stream_read_dword(demuxer->stream);
|
|
283 mp_msg(MSGT_DECVIDEO, MSGL_V,
|
4628
|
284 " FILM video: %d x %d\n", sh_video->disp_w,
|
|
285 sh_video->disp_h);
|
4564
|
286 }
|
|
287 else
|
5413
|
288 // skip height and width if no video
|
|
289 stream_skip(demuxer->stream, 8);
|
|
290
|
5438
|
291 if(demuxer->audio->id<-1){
|
18176
f72bc5754209
Part3 of Otvos Attila's oattila AT chello-hu mp_msg changes, with lots of modifications as usual
reynaldo
diff
changeset
|
292 mp_msg(MSGT_DECVIDEO, MSGL_INFO,"chunk size = 0x%X \n",chunk_size);
|
5438
|
293 stream_skip(demuxer->stream, chunk_size-12-8);
|
|
294 break; // audio disabled (or no soundcard)
|
|
295 }
|
|
296
|
5413
|
297 // skip over unknown byte, but only if file had non-NULL version
|
|
298 if (film_data->film_version)
|
|
299 stream_skip(demuxer->stream, 1);
|
4226
|
300
|
4564
|
301 // fetch the audio channels to see if there's any audio
|
5413
|
302 // don't do this if the file is a quirky file with NULL version
|
|
303 if (film_data->film_version)
|
4564
|
304 {
|
5413
|
305 audio_channels = stream_read_char(demuxer->stream);
|
|
306 if (audio_channels > 0)
|
|
307 {
|
|
308 // create and initialize the audio stream header
|
|
309 sh_audio = new_sh_audio(demuxer, 0);
|
26299
|
310 demuxer->audio->id = 0;
|
5413
|
311 demuxer->audio->sh = sh_audio;
|
|
312 sh_audio->ds = demuxer->audio;
|
|
313
|
18885
|
314 sh_audio->wf = malloc(sizeof(WAVEFORMATEX));
|
5413
|
315
|
|
316 // uncompressed PCM format
|
|
317 sh_audio->wf->wFormatTag = 1;
|
|
318 sh_audio->format = 1;
|
|
319 sh_audio->wf->nChannels = audio_channels;
|
|
320 sh_audio->wf->wBitsPerSample = stream_read_char(demuxer->stream);
|
|
321 stream_skip(demuxer->stream, 1); // skip unknown byte
|
|
322 sh_audio->wf->nSamplesPerSec = stream_read_word(demuxer->stream);
|
|
323 sh_audio->wf->nAvgBytesPerSec =
|
|
324 sh_audio->wf->nSamplesPerSec * sh_audio->wf->wBitsPerSample
|
|
325 * sh_audio->wf->nChannels / 8;
|
|
326 stream_skip(demuxer->stream, 6); // skip the rest of the unknown
|
|
327
|
|
328 mp_msg(MSGT_DECVIDEO, MSGL_V,
|
|
329 " FILM audio: %d channels, %d bits, %d Hz\n",
|
|
330 sh_audio->wf->nChannels, 8 * sh_audio->wf->wBitsPerSample,
|
|
331 sh_audio->wf->nSamplesPerSec);
|
|
332 }
|
|
333 else
|
|
334 stream_skip(demuxer->stream, 10);
|
|
335 }
|
|
336 else
|
|
337 {
|
|
338 // otherwise, make some assumptions about the audio
|
|
339
|
4564
|
340 // create and initialize the audio stream header
|
|
341 sh_audio = new_sh_audio(demuxer, 0);
|
|
342 demuxer->audio->sh = sh_audio;
|
|
343 sh_audio->ds = demuxer->audio;
|
|
344
|
18885
|
345 sh_audio->wf = malloc(sizeof(WAVEFORMATEX));
|
4226
|
346
|
4564
|
347 // uncompressed PCM format
|
|
348 sh_audio->wf->wFormatTag = 1;
|
|
349 sh_audio->format = 1;
|
5413
|
350 sh_audio->wf->nChannels = 1;
|
|
351 sh_audio->wf->wBitsPerSample = 8;
|
|
352 sh_audio->wf->nSamplesPerSec = 22050;
|
4564
|
353 sh_audio->wf->nAvgBytesPerSec =
|
4628
|
354 sh_audio->wf->nSamplesPerSec * sh_audio->wf->wBitsPerSample
|
|
355 * sh_audio->wf->nChannels / 8;
|
4564
|
356
|
|
357 mp_msg(MSGT_DECVIDEO, MSGL_V,
|
|
358 " FILM audio: %d channels, %d bits, %d Hz\n",
|
5413
|
359 sh_audio->wf->nChannels, sh_audio->wf->wBitsPerSample,
|
4564
|
360 sh_audio->wf->nSamplesPerSec);
|
|
361 }
|
4189
|
362 break;
|
|
363
|
|
364 case CHUNK_STAB:
|
4564
|
365 mp_msg(MSGT_DECVIDEO, MSGL_V, "parsing STAB chunk\n");
|
4628
|
366
|
|
367 if (sh_video)
|
|
368 {
|
|
369 sh_video->fps = stream_read_dword(demuxer->stream);
|
5143
|
370 sh_video->frametime = 1.0 / sh_video->fps;
|
4628
|
371 }
|
4189
|
372
|
4564
|
373 // fetch the number of chunks
|
|
374 film_data->total_chunks = stream_read_dword(demuxer->stream);
|
|
375 film_data->current_chunk = 0;
|
|
376 mp_msg(MSGT_DECVIDEO, MSGL_V,
|
|
377 " STAB chunk contains %d chunks\n", film_data->total_chunks);
|
4189
|
378
|
4564
|
379 // allocate enough entries for the chunk
|
|
380 film_data->chunks =
|
18885
|
381 calloc(film_data->total_chunks, sizeof(film_chunk_t));
|
4189
|
382
|
4564
|
383 // build the chunk index
|
5157
|
384 counting_chunks = 1;
|
4564
|
385 for (i = 0; i < film_data->total_chunks; i++)
|
4189
|
386 {
|
4564
|
387 film_chunk = film_data->chunks[i];
|
|
388 film_chunk.chunk_offset =
|
|
389 demuxer->movi_start + stream_read_dword(demuxer->stream);
|
4628
|
390 film_chunk.chunk_size = stream_read_dword(demuxer->stream);
|
5143
|
391 film_chunk.syncinfo1 = stream_read_dword(demuxer->stream);
|
|
392 film_chunk.syncinfo2 = stream_read_dword(demuxer->stream);
|
4564
|
393
|
5157
|
394 // count chunks for the purposes of seeking
|
|
395 if (counting_chunks)
|
4628
|
396 {
|
5157
|
397 // if we're counting chunks, always count an audio chunk
|
|
398 if (film_chunk.syncinfo1 == 0xFFFFFFFF)
|
|
399 film_data->chunks_per_second++;
|
|
400 // if it's a video chunk, check if it's time to stop counting
|
|
401 else if ((film_chunk.syncinfo1 & 0x7FFFFFFF) >= sh_video->fps)
|
|
402 counting_chunks = 0;
|
|
403 else
|
|
404 film_data->chunks_per_second++;
|
4628
|
405 }
|
5181
|
406
|
|
407 // precalculate PTS
|
|
408 if (film_chunk.syncinfo1 == 0xFFFFFFFF)
|
|
409 {
|
5438
|
410 if(demuxer->audio->id>=-1)
|
5181
|
411 film_chunk.pts =
|
|
412 (float)total_audio_bytes / (float)sh_audio->wf->nAvgBytesPerSec;
|
|
413 total_audio_bytes += film_chunk.chunk_size;
|
|
414 }
|
|
415 else
|
|
416 film_chunk.pts =
|
|
417 (film_chunk.syncinfo1 & 0x7FFFFFFF) / sh_video->fps;
|
|
418
|
|
419 film_data->chunks[i] = film_chunk;
|
4189
|
420 }
|
5157
|
421
|
5181
|
422 // in some FILM files (notably '1.09'), the length of the FDSC chunk
|
5157
|
423 // follows different rules
|
|
424 if (chunk_size == (film_data->total_chunks * 16))
|
|
425 header_size -= 16;
|
4189
|
426 break;
|
|
427
|
|
428 default:
|
|
429 mp_msg(MSGT_DEMUX, MSGL_ERR, "Unrecognized FILM header chunk: %08X\n",
|
|
430 chunk_type);
|
26758
|
431 return NULL;
|
4189
|
432 break;
|
|
433 }
|
|
434 }
|
|
435
|
4564
|
436 demuxer->priv = film_data;
|
4189
|
437
|
|
438 return demuxer;
|
|
439 }
|
5810
|
440
|
16175
|
441 static void demux_close_film(demuxer_t* demuxer) {
|
5810
|
442 film_data_t *film_data = demuxer->priv;
|
|
443
|
|
444 if(!film_data)
|
|
445 return;
|
|
446 if(film_data->chunks)
|
|
447 free(film_data->chunks);
|
|
448 free(film_data);
|
|
449
|
|
450 }
|
16175
|
451
|
|
452 static int film_check_file(demuxer_t* demuxer)
|
|
453 {
|
|
454 int signature=stream_read_fourcc(demuxer->stream);
|
|
455
|
|
456 // check for the FILM file magic number
|
|
457 if(signature==mmioFOURCC('F', 'I', 'L', 'M'))
|
|
458 return DEMUXER_TYPE_FILM;
|
|
459
|
|
460 return 0;
|
|
461 }
|
|
462
|
|
463
|
25707
|
464 const demuxer_desc_t demuxer_desc_film = {
|
16175
|
465 "FILM/CPK demuxer for Sega Saturn CD-ROM games",
|
|
466 "film",
|
|
467 "FILM",
|
|
468 "Mike Melanson",
|
|
469 "",
|
|
470 DEMUXER_TYPE_FILM,
|
|
471 0, // unsafe autodetect (short signature)
|
|
472 film_check_file,
|
|
473 demux_film_fill_buffer,
|
|
474 demux_open_film,
|
|
475 demux_close_film,
|
|
476 demux_seek_film,
|
|
477 NULL
|
|
478 };
|