libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

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