demux_wc3movie.c (27466B)
1 /* 2 * Copyright Copyright (C) 2000-2002 the xine project 3 * 4 * This file is part of xine, a free video player. 5 * 6 * xine is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * xine is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * 20 * File Demuxer for Wing Commander III MVE movie files 21 * by Mike Melanson (melanson@pcisys.net) 22 * For more information on the MVE file format, visit: 23 * http://www.pcisys.net/~melanson/codecs/ 24 * 25 * $Id: demux_wc3movie.c,v 1.1 2003/04/03 14:51:25 grothoff Exp $ 26 */ 27 28 #ifdef HAVE_CONFIG_H 29 #include "config.h" 30 #endif 31 32 #include <stdio.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <stdlib.h> 37 38 #include "xine_internal.h" 39 #include "xineutils.h" 40 #include "compat.h" 41 #include "demux.h" 42 #include "bswap.h" 43 44 #define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \ 45 ( (long)(unsigned char)(ch3) | \ 46 ( (long)(unsigned char)(ch2) << 8 ) | \ 47 ( (long)(unsigned char)(ch1) << 16 ) | \ 48 ( (long)(unsigned char)(ch0) << 24 ) ) 49 50 #define FORM_TAG FOURCC_TAG('F', 'O', 'R', 'M') 51 #define MOVE_TAG FOURCC_TAG('M', 'O', 'V', 'E') 52 #define PC_TAG FOURCC_TAG('_', 'P', 'C', '_') 53 #define SOND_TAG FOURCC_TAG('S', 'O', 'N', 'D') 54 #define PALT_TAG FOURCC_TAG('P', 'A', 'L', 'T') 55 #define INDX_TAG FOURCC_TAG('I', 'N', 'D', 'X') 56 #define BNAM_TAG FOURCC_TAG('B', 'N', 'A', 'M') 57 #define SIZE_TAG FOURCC_TAG('S', 'I', 'Z', 'E') 58 #define BRCH_TAG FOURCC_TAG('B', 'R', 'C', 'H') 59 #define SHOT_TAG FOURCC_TAG('S', 'H', 'O', 'T') 60 #define VGA_TAG FOURCC_TAG('V', 'G', 'A', ' ') 61 #define AUDI_TAG FOURCC_TAG('A', 'U', 'D', 'I') 62 #define TEXT_TAG FOURCC_TAG('T', 'E', 'X', 'T') 63 64 #define PALETTE_SIZE 256 65 #define PALETTE_CHUNK_SIZE (PALETTE_SIZE * 3) 66 #define WC3_FRAMERATE 15 67 #define WC3_PTS_INC (90000 / 15) 68 #define WC3_USUAL_WIDTH 320 69 #define WC3_USUAL_HEIGHT 165 70 #define WC3_HEADER_SIZE 16 71 #define PREAMBLE_SIZE 8 72 73 typedef struct 74 { 75 76 demux_plugin_t demux_plugin; 77 78 xine_stream_t *stream; 79 80 config_values_t *config; 81 82 fifo_buffer_t *video_fifo; 83 fifo_buffer_t *audio_fifo; 84 85 input_plugin_t *input; 86 87 int status; 88 89 unsigned int fps; 90 unsigned int frame_pts_inc; 91 unsigned int video_width; 92 unsigned int video_height; 93 94 xine_waveformatex wave; 95 96 palette_entry_t *palettes; 97 unsigned int number_of_shots; 98 unsigned int current_shot; 99 off_t *shot_offsets; 100 int seek_flag; /* this is set when a seek occurs */ 101 102 off_t data_start; 103 off_t data_size; 104 105 int64_t video_pts; 106 107 char last_mrl[1024]; 108 } demux_mve_t; 109 110 typedef struct 111 { 112 113 demux_class_t demux_class; 114 115 /* class-wide, global variables here */ 116 117 xine_t *xine; 118 config_values_t *config; 119 } demux_mve_class_t; 120 121 /* bizarre palette lookup table */ 122 const unsigned char wc3_pal_lookup[] = { 123 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0E, 0x10, 0x12, 0x13, 0x15, 124 0x16, 125 0x18, 0x19, 0x1A, 126 0x1C, 0x1D, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2A, 127 0x2C, 128 0x2D, 0x2E, 0x2F, 129 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 130 0x3F, 131 0x40, 0x41, 0x42, 132 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 133 0x50, 134 0x51, 0x52, 0x53, 135 0x54, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 136 0x61, 137 0x62, 0x63, 0x64, 138 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 139 0x71, 140 0x72, 0x73, 0x74, 141 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 142 0x80, 143 0x81, 0x82, 0x83, 144 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8D, 0x8E, 145 0x8F, 146 0x90, 0x91, 0x92, 147 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 148 0x9E, 149 0x9F, 0xA0, 0xA1, 150 0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAA, 0xAB, 151 0xAC, 152 0xAD, 0xAE, 0xAF, 153 0xB0, 0xB1, 0xB2, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xB9, 154 0xBA, 155 0xBB, 0xBC, 0xBD, 156 0xBE, 0xBF, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC5, 0xC6, 0xC7, 157 0xC8, 158 0xC9, 0xCA, 0xCB, 159 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 160 0xD5, 161 0xD6, 0xD7, 0xD8, 162 0xD9, 0xDA, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xDF, 0xE0, 0xE1, 0xE2, 163 0xE3, 164 0xE4, 0xE4, 0xE5, 165 0xE6, 0xE7, 0xE8, 0xE9, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xED, 0xEE, 0xEF, 166 0xF0, 167 0xF1, 0xF1, 0xF2, 168 0xF3, 0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC, 169 0xFD, 170 0xFD, 0xFD, 0xFD 171 }; 172 173 static int 174 demux_mve_send_chunk (demux_plugin_t * this_gen) 175 { 176 177 demux_mve_t *this = (demux_mve_t *) this_gen; 178 buf_element_t *buf = NULL; 179 int64_t text_pts = 0; 180 int64_t audio_pts = 0; 181 unsigned char preamble[PREAMBLE_SIZE]; 182 unsigned int chunk_tag; 183 unsigned int chunk_size; 184 off_t current_file_pos; 185 unsigned int palette_number; 186 187 /* compensate for the initial data in the file */ 188 current_file_pos = this->input->get_current_pos (this->input) - 189 this->data_start; 190 191 if (this->input->read (this->input, preamble, PREAMBLE_SIZE) != 192 PREAMBLE_SIZE) 193 this->status = DEMUX_FINISHED; 194 else 195 { 196 chunk_tag = BE_32 (&preamble[0]); 197 /* round up to the nearest even size */ 198 chunk_size = (BE_32 (&preamble[4]) + 1) & (~1); 199 200 if (chunk_tag == BRCH_TAG) 201 { 202 203 /* empty chunk; do nothing */ 204 205 } 206 else if (chunk_tag == SHOT_TAG) 207 { 208 209 if (this->seek_flag) 210 { 211 212 /* reset pts */ 213 this->video_pts = 0; 214 xine_demux_control_newpts (this->stream, 0, BUF_FLAG_SEEK); 215 this->seek_flag = 0; 216 217 } 218 else 219 { 220 221 /* record the offset of the SHOT chunk */ 222 this->shot_offsets[this->current_shot] = 223 this->input->get_current_pos (this->input) - PREAMBLE_SIZE; 224 } 225 226 this->current_shot++; 227 228 /* this is the start of a new shot; send a new palette */ 229 if (this->input->read (this->input, preamble, 4) != 4) 230 { 231 this->status = DEMUX_FINISHED; 232 return this->status; 233 } 234 palette_number = LE_32 (&preamble[0]); 235 236 if (palette_number >= this->number_of_shots) 237 { 238 xine_log (this->stream->xine, XINE_LOG_MSG, 239 _ 240 ("demux_wc3movie: SHOT chunk referenced invalid palette (%d >= %d)\n"), 241 palette_number, this->number_of_shots); 242 this->status = DEMUX_FINISHED; 243 return this->status; 244 } 245 246 buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); 247 buf->decoder_flags = BUF_FLAG_SPECIAL; 248 buf->decoder_info[1] = BUF_SPECIAL_PALETTE; 249 buf->decoder_info[2] = PALETTE_SIZE; 250 buf->decoder_info_ptr[2] = 251 &this->palettes[PALETTE_SIZE * palette_number]; 252 buf->size = 0; 253 buf->type = BUF_VIDEO_WC3; 254 this->video_fifo->put (this->video_fifo, buf); 255 256 } 257 else if (chunk_tag == AUDI_TAG) 258 { 259 260 if (this->audio_fifo) 261 { 262 263 audio_pts = this->video_pts - WC3_PTS_INC; 264 265 while (chunk_size) 266 { 267 buf = 268 this->audio_fifo->buffer_pool_alloc (this->audio_fifo); 269 buf->type = BUF_AUDIO_LPCM_LE; 270 buf->extra_info->input_pos = current_file_pos; 271 buf->extra_info->input_length = this->data_size; 272 buf->extra_info->input_time = audio_pts / 90; 273 buf->pts = audio_pts; 274 275 if (chunk_size > buf->max_size) 276 buf->size = buf->max_size; 277 else 278 buf->size = chunk_size; 279 chunk_size -= buf->size; 280 281 if (this->input-> 282 read (this->input, buf->content, 283 buf->size) != buf->size) 284 { 285 buf->free_buffer (buf); 286 this->status = DEMUX_FINISHED; 287 break; 288 } 289 290 if (!chunk_size) 291 buf->decoder_flags |= BUF_FLAG_FRAME_END; 292 293 this->audio_fifo->put (this->audio_fifo, buf); 294 } 295 } 296 else 297 { 298 this->input->seek (this->input, chunk_size, SEEK_CUR); 299 } 300 } 301 else if (chunk_tag == VGA_TAG) 302 { 303 304 while (chunk_size) 305 { 306 buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); 307 buf->type = BUF_VIDEO_WC3; 308 buf->extra_info->input_pos = current_file_pos; 309 buf->extra_info->input_length = this->data_size; 310 buf->extra_info->input_time = this->video_pts / 90; 311 buf->pts = this->video_pts; 312 313 if (chunk_size > buf->max_size) 314 buf->size = buf->max_size; 315 else 316 buf->size = chunk_size; 317 chunk_size -= buf->size; 318 319 if (this->input->read (this->input, buf->content, buf->size) != 320 buf->size) 321 { 322 buf->free_buffer (buf); 323 this->status = DEMUX_FINISHED; 324 break; 325 } 326 327 if (!chunk_size) 328 buf->decoder_flags |= BUF_FLAG_FRAME_END; 329 330 this->video_fifo->put (this->video_fifo, buf); 331 } 332 this->video_pts += WC3_PTS_INC; 333 334 } 335 else if (chunk_tag == TEXT_TAG) 336 { 337 338 text_pts = this->video_pts - WC3_PTS_INC; 339 340 /* unhandled thus far */ 341 this->input->seek (this->input, chunk_size, SEEK_CUR); 342 343 } 344 else 345 { 346 347 /* report an unknown chunk and skip it */ 348 printf (_("demux_wc3movie: encountered unknown chunk: %c%c%c%c\n"), 349 (chunk_tag >> 24) & 0xFF, 350 (chunk_tag >> 16) & 0xFF, 351 (chunk_tag >> 8) & 0xFF, (chunk_tag >> 0) & 0xFF); 352 this->input->seek (this->input, chunk_size, SEEK_CUR); 353 } 354 } 355 356 return this->status; 357 } 358 359 static void 360 demux_mve_send_headers (demux_plugin_t * this_gen) 361 { 362 363 demux_mve_t *this = (demux_mve_t *) this_gen; 364 buf_element_t *buf; 365 366 this->video_fifo = this->stream->video_fifo; 367 this->audio_fifo = this->stream->audio_fifo; 368 369 this->status = DEMUX_OK; 370 371 /* load stream information */ 372 this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1; 373 /* this is not strictly correct-- some WC3 MVE files do not contain 374 * audio, but I'm too lazy to check if that is the case */ 375 this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1; 376 this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] = this->video_width; 377 this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] = 378 this->video_height; 379 this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = 380 this->wave.nChannels; 381 this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] = 382 this->wave.nSamplesPerSec; 383 this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = 384 this->wave.wBitsPerSample; 385 386 /* send start buffers */ 387 xine_demux_control_start (this->stream); 388 389 /* send init info to decoders */ 390 buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); 391 buf->decoder_flags = BUF_FLAG_HEADER; 392 buf->decoder_info[0] = 0; 393 buf->decoder_info[1] = WC3_PTS_INC; /* initial video_step */ 394 /* really be a rebel: No structure at all, just put the video width 395 * and height straight into the buffer, BE_16 format */ 396 buf->content[0] = (this->video_width >> 8) & 0xFF; 397 buf->content[1] = (this->video_width >> 0) & 0xFF; 398 buf->content[2] = (this->video_height >> 8) & 0xFF; 399 buf->content[3] = (this->video_height >> 0) & 0xFF; 400 buf->size = 4; 401 buf->type = BUF_VIDEO_WC3; 402 this->video_fifo->put (this->video_fifo, buf); 403 404 if (this->audio_fifo) 405 { 406 this->wave.wFormatTag = 1; 407 this->wave.nChannels = 1; 408 this->wave.nSamplesPerSec = 22050; 409 this->wave.wBitsPerSample = 16; 410 this->wave.nBlockAlign = 411 (this->wave.wBitsPerSample / 8) * this->wave.nChannels; 412 this->wave.nAvgBytesPerSec = 413 this->wave.nBlockAlign * this->wave.nSamplesPerSec; 414 415 buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); 416 buf->type = BUF_AUDIO_LPCM_LE; 417 buf->decoder_flags = BUF_FLAG_HEADER; 418 buf->decoder_info[0] = 0; 419 buf->decoder_info[1] = this->wave.nSamplesPerSec; 420 buf->decoder_info[2] = this->wave.wBitsPerSample; 421 buf->decoder_info[3] = this->wave.nChannels; 422 buf->content = (void *) &this->wave; 423 buf->size = sizeof (this->wave); 424 this->audio_fifo->put (this->audio_fifo, buf); 425 } 426 } 427 428 /* returns 1 if the MVE file was opened successfully, 0 otherwise */ 429 static int 430 open_mve_file (demux_mve_t * this) 431 { 432 433 unsigned char preamble[PREAMBLE_SIZE]; 434 unsigned int chunk_tag; 435 unsigned int chunk_size; 436 unsigned char disk_palette[PALETTE_CHUNK_SIZE]; 437 int i, j; 438 unsigned char r, g, b; 439 int temp; 440 unsigned char header[WC3_HEADER_SIZE]; 441 unsigned char preview[MAX_PREVIEW_SIZE]; 442 void *title; 443 444 /* these are the frame dimensions unless others are found */ 445 this->video_width = WC3_USUAL_WIDTH; 446 this->video_height = WC3_USUAL_HEIGHT; 447 448 if (this->input->get_capabilities (this->input) & INPUT_CAP_SEEKABLE) 449 { 450 this->input->seek (this->input, 0, SEEK_SET); 451 if (this->input->read (this->input, header, WC3_HEADER_SIZE) != 452 WC3_HEADER_SIZE) 453 return 0; 454 } 455 else 456 { 457 this->input->get_optional_data (this->input, preview, 458 INPUT_OPTIONAL_DATA_PREVIEW); 459 460 /* copy over the header bytes for processing */ 461 memcpy (header, preview, WC3_HEADER_SIZE); 462 } 463 464 if ((BE_32 (&header[0]) != FORM_TAG) || 465 (BE_32 (&header[8]) != MOVE_TAG) || (BE_32 (&header[12]) != PC_TAG)) 466 return 0; 467 468 /* file is qualified; if the input was not seekable, skip over the header 469 * bytes in the stream */ 470 if ((this->input->get_capabilities (this->input) & INPUT_CAP_SEEKABLE) == 0) 471 { 472 this->input->seek (this->input, WC3_HEADER_SIZE, SEEK_SET); 473 } 474 475 /* load the number of palettes, the only interesting piece of information 476 * in the _PC_ chunk; take it for granted that it will always appear at 477 * position 0x1C */ 478 this->input->seek (this->input, 0x1C, SEEK_SET); 479 if (this->input->read (this->input, preamble, 4) != 4) 480 return 0; 481 this->number_of_shots = LE_32 (&preamble[0]); 482 483 /* allocate space for the shot offset index and set offsets to 0 */ 484 this->shot_offsets = xine_xmalloc (this->number_of_shots * sizeof (off_t)); 485 this->current_shot = 0; 486 for (i = 0; i < this->number_of_shots; i++) 487 this->shot_offsets[i] = 0; 488 489 /* skip the SOND chunk */ 490 this->input->seek (this->input, 12, SEEK_CUR); 491 492 /* load the palette chunks */ 493 this->palettes = xine_xmalloc (this->number_of_shots * PALETTE_SIZE * 494 sizeof (palette_entry_t)); 495 for (i = 0; i < this->number_of_shots; i++) 496 { 497 /* make sure there was a valid palette chunk preamble */ 498 if (this->input->read (this->input, preamble, PREAMBLE_SIZE) != 499 PREAMBLE_SIZE) 500 { 501 free (this->palettes); 502 free (this->shot_offsets); 503 return 0; 504 } 505 506 if ((BE_32 (&preamble[0]) != PALT_TAG) || 507 (BE_32 (&preamble[4]) != PALETTE_CHUNK_SIZE)) 508 { 509 xine_log (this->stream->xine, XINE_LOG_MSG, 510 _ 511 ("demux_wc3movie: There was a problem while loading palette chunks\n")); 512 free (this->palettes); 513 free (this->shot_offsets); 514 return 0; 515 } 516 517 /* load the palette chunk */ 518 if (this->input->read (this->input, disk_palette, PALETTE_CHUNK_SIZE) != 519 PALETTE_CHUNK_SIZE) 520 { 521 free (this->palettes); 522 free (this->shot_offsets); 523 return 0; 524 } 525 526 /* convert and store the palette */ 527 for (j = 0; j < PALETTE_SIZE; j++) 528 { 529 r = disk_palette[j * 3 + 0]; 530 g = disk_palette[j * 3 + 1]; 531 b = disk_palette[j * 3 + 2]; 532 /* rotate each component left by 2 */ 533 temp = r << 2; 534 r = (temp & 0xff) | (temp >> 8); 535 r = wc3_pal_lookup[r]; 536 temp = g << 2; 537 g = (temp & 0xff) | (temp >> 8); 538 g = wc3_pal_lookup[g]; 539 temp = b << 2; 540 b = (temp & 0xff) | (temp >> 8); 541 b = wc3_pal_lookup[b]; 542 this->palettes[i * 256 + j].r = r; 543 this->palettes[i * 256 + j].g = g; 544 this->palettes[i * 256 + j].b = b; 545 } 546 } 547 548 /* after the palette chunks comes any number of chunks such as INDX, 549 * BNAM, SIZE and perhaps others; traverse chunks until first BRCH 550 * chunk is found */ 551 chunk_tag = 0; 552 title = NULL; 553 while (chunk_tag != BRCH_TAG) 554 { 555 556 if (this->input->read (this->input, preamble, PREAMBLE_SIZE) != 557 PREAMBLE_SIZE) 558 { 559 free (title); 560 free (this->palettes); 561 free (this->shot_offsets); 562 return 0; 563 } 564 565 chunk_tag = BE_32 (&preamble[0]); 566 /* round up to the nearest even size */ 567 chunk_size = (BE_32 (&preamble[4]) + 1) & (~1); 568 569 switch (chunk_tag) 570 { 571 572 case BRCH_TAG: 573 /* time to start demuxing */ 574 break; 575 576 case BNAM_TAG: 577 /* load the name into the stream attributes */ 578 title = realloc (title, chunk_size); 579 if (this->input->read (this->input, title, chunk_size) != 580 chunk_size) 581 { 582 free (title); 583 free (this->palettes); 584 free (this->shot_offsets); 585 return 0; 586 } 587 break; 588 589 case SIZE_TAG: 590 /* override the default width and height */ 591 /* reuse the preamble bytes */ 592 if (this->input->read (this->input, preamble, PREAMBLE_SIZE) != 593 PREAMBLE_SIZE) 594 { 595 free (title); 596 free (this->palettes); 597 free (this->shot_offsets); 598 return 0; 599 } 600 this->video_width = BE_32 (&preamble[0]); 601 this->video_height = BE_32 (&preamble[4]); 602 break; 603 604 case INDX_TAG: 605 /* index is not useful for this demuxer */ 606 this->input->seek (this->input, chunk_size, SEEK_CUR); 607 break; 608 609 default: 610 /* report an unknown chunk and skip it */ 611 printf (_("demux_wc3movie: encountered unknown chunk: %c%c%c%c\n"), 612 (chunk_tag >> 24) & 0xFF, 613 (chunk_tag >> 16) & 0xFF, 614 (chunk_tag >> 8) & 0xFF, (chunk_tag >> 0) & 0xFF); 615 this->input->seek (this->input, chunk_size, SEEK_CUR); 616 break; 617 } 618 619 } 620 621 /* note the data start offset */ 622 this->data_start = this->input->get_current_pos (this->input); 623 624 this->data_size = this->input->get_length (this->input) - this->data_start; 625 626 this->video_pts = 0; 627 628 this->stream->meta_info[XINE_META_INFO_TITLE] = title; 629 630 return 1; 631 } 632 633 static int 634 demux_mve_seek (demux_plugin_t * this_gen, off_t start_pos, int start_time) 635 { 636 637 /* 638 * MVE files are comprised of a series of SHOTs. A SHOT begins when the 639 * camera angle changes. The first frame of a SHOT is effectively a 640 * keyframe so it is safe to seek to the start of a SHOT. A/V sync is 641 * not a concern since each video or audio chunk represents exactly 642 * 1/15 sec. 643 * 644 * When a seek is requested, traverse the list of SHOT offsets and find 645 * the best match. If not enough SHOT boundaries have been crossed while 646 * demuxing the file, traverse the file until enough SHOTs are found. 647 */ 648 649 demux_mve_t *this = (demux_mve_t *) this_gen; 650 int i; 651 unsigned char preamble[PREAMBLE_SIZE]; 652 unsigned int chunk_tag; 653 unsigned int chunk_size; 654 int new_shot = -1; 655 656 this->status = DEMUX_OK; 657 xine_demux_flush_engine (this->stream); 658 this->seek_flag = 1; 659 660 /* if input is non-seekable, do not proceed with the rest of this 661 * seek function */ 662 if ((this->input->get_capabilities (this->input) & INPUT_CAP_SEEKABLE) == 0) 663 return this->status; 664 665 /* make sure the first shot has been recorded */ 666 if (this->shot_offsets[0] == 0) 667 { 668 669 while (1) 670 { 671 672 if (this->input->read (this->input, preamble, PREAMBLE_SIZE) != 673 PREAMBLE_SIZE) 674 { 675 this->status = DEMUX_FINISHED; 676 return this->status; 677 } 678 679 chunk_tag = BE_32 (&preamble[0]); 680 /* round up to the nearest even size */ 681 chunk_size = (BE_32 (&preamble[4]) + 1) & (~1); 682 683 if (chunk_tag == SHOT_TAG) 684 { 685 this->shot_offsets[0] = 686 this->input->get_current_pos (this->input) - PREAMBLE_SIZE; 687 /* skip the four SHOT data bytes (palette index) */ 688 this->input->seek (this->input, 4, SEEK_CUR); 689 break; /* get out of the infinite while loop */ 690 } 691 else 692 { 693 this->input->seek (this->input, chunk_size, SEEK_CUR); 694 } 695 } 696 } 697 698 /* compensate for data at start of file */ 699 start_pos += this->data_start; 700 for (i = 0; i < this->number_of_shots - 1; i++) 701 { 702 703 /* if the next shot offset has not been recorded, traverse through the 704 * file until it is found */ 705 if (this->shot_offsets[i + 1] == 0) 706 { 707 708 while (1) 709 { 710 711 if (this->input->read (this->input, preamble, PREAMBLE_SIZE) != 712 PREAMBLE_SIZE) 713 { 714 this->status = DEMUX_FINISHED; 715 return this->status; 716 } 717 718 chunk_tag = BE_32 (&preamble[0]); 719 /* round up to the nearest even size */ 720 chunk_size = (BE_32 (&preamble[4]) + 1) & (~1); 721 722 if (chunk_tag == SHOT_TAG) 723 { 724 this->shot_offsets[i + 1] = 725 this->input->get_current_pos (this->input) - 726 PREAMBLE_SIZE; 727 /* skip the four SHOT data bytes (palette index) */ 728 this->input->seek (this->input, 4, SEEK_CUR); 729 break; /* get out of the infinite while loop */ 730 } 731 else 732 { 733 this->input->seek (this->input, chunk_size, SEEK_CUR); 734 } 735 } 736 } 737 738 /* check if the seek-to offset falls in between this shot offset and 739 * the next one */ 740 if ((start_pos >= this->shot_offsets[i]) && 741 (start_pos < this->shot_offsets[i + 1])) 742 { 743 744 new_shot = i; 745 break; 746 } 747 } 748 749 /* if no new shot was found in the loop, the new shot must be the last 750 * shot */ 751 if (new_shot == -1) 752 new_shot = this->number_of_shots - 1; 753 this->current_shot = new_shot; 754 755 /* reposition the stream at new shot */ 756 this->input->seek (this->input, this->shot_offsets[new_shot], SEEK_SET); 757 758 return this->status; 759 } 760 761 static void 762 demux_mve_dispose (demux_plugin_t * this_gen) 763 { 764 demux_mve_t *this = (demux_mve_t *) this_gen; 765 766 free (this->palettes); 767 free (this->shot_offsets); 768 free (this); 769 } 770 771 static int 772 demux_mve_get_status (demux_plugin_t * this_gen) 773 { 774 demux_mve_t *this = (demux_mve_t *) this_gen; 775 776 return this->status; 777 } 778 779 static int 780 demux_mve_get_stream_length (demux_plugin_t * this_gen) 781 { 782 783 return 0; 784 } 785 786 static uint32_t 787 demux_mve_get_capabilities (demux_plugin_t * this_gen) 788 { 789 return DEMUX_CAP_NOCAP; 790 } 791 792 static int 793 demux_mve_get_optional_data (demux_plugin_t * this_gen, 794 void *data, int data_type) 795 { 796 return DEMUX_OPTIONAL_UNSUPPORTED; 797 } 798 799 static demux_plugin_t * 800 open_plugin (demux_class_t * class_gen, xine_stream_t * stream, 801 input_plugin_t * input_gen) 802 { 803 804 input_plugin_t *input = (input_plugin_t *) input_gen; 805 demux_mve_t *this; 806 807 this = xine_xmalloc (sizeof (demux_mve_t)); 808 this->stream = stream; 809 this->input = input; 810 811 this->demux_plugin.send_headers = demux_mve_send_headers; 812 this->demux_plugin.send_chunk = demux_mve_send_chunk; 813 this->demux_plugin.seek = demux_mve_seek; 814 this->demux_plugin.dispose = demux_mve_dispose; 815 this->demux_plugin.get_status = demux_mve_get_status; 816 this->demux_plugin.get_stream_length = demux_mve_get_stream_length; 817 this->demux_plugin.get_video_frame = NULL; 818 this->demux_plugin.got_video_frame_cb = NULL; 819 this->demux_plugin.get_capabilities = demux_mve_get_capabilities; 820 this->demux_plugin.get_optional_data = demux_mve_get_optional_data; 821 this->demux_plugin.demux_class = class_gen; 822 823 this->status = DEMUX_FINISHED; 824 825 switch (stream->content_detection_method) 826 { 827 828 case METHOD_BY_CONTENT: 829 case METHOD_EXPLICIT: 830 831 if (!open_mve_file (this)) 832 { 833 free (this); 834 return NULL; 835 } 836 837 break; 838 839 case METHOD_BY_EXTENSION: 840 { 841 char *ending, *mrl; 842 843 mrl = input->get_mrl (input); 844 845 ending = strrchr (mrl, '.'); 846 847 if (!ending) 848 { 849 free (this); 850 return NULL; 851 } 852 853 if (strncasecmp (ending, ".mve", 4)) 854 { 855 free (this); 856 return NULL; 857 } 858 859 if (!open_mve_file (this)) 860 { 861 free (this); 862 return NULL; 863 } 864 865 } 866 867 break; 868 869 default: 870 free (this); 871 return NULL; 872 } 873 874 strncpy (this->last_mrl, input->get_mrl (input), 1024); 875 876 return &this->demux_plugin; 877 } 878 879 static char * 880 get_description (demux_class_t * this_gen) 881 { 882 return "Wing Commander III Movie (MVE) demux plugin"; 883 } 884 885 static char * 886 get_identifier (demux_class_t * this_gen) 887 { 888 return "WC3 Movie"; 889 } 890 891 static char * 892 get_extensions (demux_class_t * this_gen) 893 { 894 return "mve"; 895 } 896 897 static char * 898 get_mimetypes (demux_class_t * this_gen) 899 { 900 return NULL; 901 } 902 903 static void 904 class_dispose (demux_class_t * this_gen) 905 { 906 907 demux_mve_class_t *this = (demux_mve_class_t *) this_gen; 908 909 free (this); 910 } 911 912 void * 913 demux_wc3movie_init_plugin (xine_t * xine, void *data) 914 { 915 916 demux_mve_class_t *this; 917 918 this = xine_xmalloc (sizeof (demux_mve_class_t)); 919 this->config = xine->config; 920 this->xine = xine; 921 922 this->demux_class.open_plugin = open_plugin; 923 this->demux_class.get_description = get_description; 924 this->demux_class.get_identifier = get_identifier; 925 this->demux_class.get_mimetypes = get_mimetypes; 926 this->demux_class.get_extensions = get_extensions; 927 this->demux_class.dispose = class_dispose; 928 929 return this; 930 } 931 932 /* 933 * exported plugin catalog entry 934 */ 935 936 #if 0 937 plugin_info_t xine_plugin_info[] = { 938 /* type, API, "name", version, special_info, init_function */ 939 {PLUGIN_DEMUX, 20, "wc3movie", XINE_VERSION_CODE, NULL, 940 demux_wc3movie_init_plugin} 941 , 942 {PLUGIN_NONE, 0, "", 0, NULL, NULL} 943 }; 944 #endif