libextractor

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

demux_nsf.c (11885B)


      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  * NSF File "Demuxer" by Mike Melanson (melanson@pcisys.net)
     21  * This is really just a loader for NES Music File Format (extension NSF)
     22  * which loads an entire NSF file and passes it over to the NSF audio
     23  * decoder.
     24  *
     25  * After the file is sent over, the demuxer controls the playback by
     26  * sending empty buffers with incrementing pts values.
     27  *
     28  * For more information regarding the NSF format, visit:
     29  *   http://www.tripoint.org/kevtris/nes/nsfspec.txt
     30  *
     31  * $Id: demux_nsf.c,v 1.1 2003/04/03 14:51:25 grothoff Exp $
     32  */
     33 
     34 #ifdef HAVE_CONFIG_H
     35 #include "config.h"
     36 #endif
     37 
     38 #include <stdio.h>
     39 #include <fcntl.h>
     40 #include <unistd.h>
     41 #include <string.h>
     42 #include <stdlib.h>
     43 
     44 #include "xine_internal.h"
     45 #include "xineutils.h"
     46 #include "compat.h"
     47 #include "demux.h"
     48 #include "bswap.h"
     49 
     50 #define NSF_HEADER_SIZE 0x80
     51 #define NSF_SAMPLERATE 44100
     52 #define NSF_BITS 8
     53 #define NSF_CHANNELS 1
     54 #define NSF_REFRESH_RATE 60
     55 #define NSF_PTS_INC (90000 / NSF_REFRESH_RATE)
     56 
     57 typedef struct
     58 {
     59 
     60   demux_plugin_t demux_plugin;
     61 
     62   xine_stream_t *stream;
     63 
     64   config_values_t *config;
     65 
     66   fifo_buffer_t *video_fifo;
     67   fifo_buffer_t *audio_fifo;
     68 
     69   input_plugin_t *input;
     70 
     71   pthread_t thread;
     72   int thread_running;
     73   pthread_mutex_t mutex;
     74   int send_end_buffers;
     75 
     76   int status;
     77 
     78   char *title;
     79   char *artist;
     80   char *copyright;
     81   int total_songs;
     82   int current_song;
     83   int new_song;                 /* indicates song change */
     84   off_t filesize;
     85 
     86   int64_t current_pts;
     87   int file_sent;
     88 
     89   char last_mrl[1024];
     90 
     91 } demux_nsf_t;
     92 
     93 typedef struct
     94 {
     95 
     96   demux_class_t demux_class;
     97 
     98   /* class-wide, global variables here */
     99 
    100   xine_t *xine;
    101   config_values_t *config;
    102 } demux_nsf_class_t;
    103 
    104 
    105 /* returns 1 if the NSF file was opened successfully, 0 otherwise */
    106 static int
    107 open_nsf_file (demux_nsf_t * this)
    108 {
    109 
    110   unsigned char header[NSF_HEADER_SIZE];
    111 
    112   this->input->seek (this->input, 0, SEEK_SET);
    113   if (this->input->read (this->input, header, NSF_HEADER_SIZE) !=
    114       NSF_HEADER_SIZE)
    115     return 0;
    116 
    117   /* check for the signature */
    118   if ((header[0] != 'N') ||
    119       (header[1] != 'E') ||
    120       (header[2] != 'S') || (header[3] != 'M') || (header[4] != 0x1A))
    121     return 0;
    122 
    123   this->total_songs = header[6];
    124   this->current_song = header[7];
    125   this->title = strdup (&header[0x0E]);
    126   this->artist = strdup (&header[0x2E]);
    127   this->copyright = strdup (&header[0x4E]);
    128 
    129   this->filesize = this->input->get_length (this->input);
    130 
    131   return 1;
    132 }
    133 
    134 static int
    135 demux_nsf_send_chunk (demux_plugin_t * this_gen)
    136 {
    137 
    138   demux_nsf_t *this = (demux_nsf_t *) this_gen;
    139   buf_element_t *buf;
    140   int bytes_read;
    141   char title[100];
    142 
    143   /* send chunks of the file to the decoder until file is completely
    144    * loaded; then send control buffers */
    145   if (!this->file_sent)
    146     {
    147       buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
    148       buf->type = BUF_AUDIO_NSF;
    149       bytes_read =
    150         this->input->read (this->input, buf->content, buf->max_size);
    151 
    152       if (bytes_read == 0)
    153         {
    154           /* the file has been completely loaded, free the buffer and start
    155            * sending control buffers */
    156           buf->free_buffer (buf);
    157           this->file_sent = 1;
    158 
    159         }
    160       else
    161         {
    162 
    163           /* keep loading the file */
    164           if (bytes_read < buf->max_size)
    165             buf->size = bytes_read;
    166           else
    167             buf->size = buf->max_size;
    168 
    169           buf->extra_info->input_pos = 0;
    170           buf->extra_info->input_length = 0;
    171           buf->extra_info->input_time = 0;
    172           buf->pts = 0;
    173 
    174           this->audio_fifo->put (this->audio_fifo, buf);
    175         }
    176     }
    177 
    178   /* this is not an 'else' because control might fall through from above */
    179   if (this->file_sent)
    180     {
    181       /* send a control buffer */
    182       buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
    183 
    184       if (this->new_song)
    185         {
    186 
    187           buf->decoder_info[1] = this->current_song;
    188           this->new_song = 0;
    189           sprintf (title, "%s, song %d/%d",
    190                    this->title, this->current_song, this->total_songs);
    191           if (this->stream->meta_info[XINE_META_INFO_TITLE])
    192             free (this->stream->meta_info[XINE_META_INFO_TITLE]);
    193           this->stream->meta_info[XINE_META_INFO_TITLE] = strdup (title);
    194           xine_demux_control_newpts (this->stream, this->current_pts, 0);
    195 
    196         }
    197       else
    198         buf->decoder_info[1] = 0;
    199 
    200       buf->type = BUF_AUDIO_NSF;
    201       buf->extra_info->input_pos = this->current_song - 1;
    202       buf->extra_info->input_length = this->total_songs;
    203       buf->extra_info->input_time = this->current_pts / 90;
    204       buf->pts = this->current_pts;
    205       buf->size = 0;
    206       this->audio_fifo->put (this->audio_fifo, buf);
    207 
    208       this->current_pts += NSF_PTS_INC;
    209     }
    210 
    211   return this->status;
    212 }
    213 
    214 static void
    215 demux_nsf_send_headers (demux_plugin_t * this_gen)
    216 {
    217 
    218   demux_nsf_t *this = (demux_nsf_t *) this_gen;
    219   buf_element_t *buf;
    220   char copyright[100];
    221 
    222   this->video_fifo = this->stream->video_fifo;
    223   this->audio_fifo = this->stream->audio_fifo;
    224 
    225   this->status = DEMUX_OK;
    226 
    227   /* load stream information */
    228   this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 0;
    229   this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
    230   this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = NSF_CHANNELS;
    231   this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] =
    232     NSF_SAMPLERATE;
    233   this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = NSF_BITS;
    234 
    235   this->stream->meta_info[XINE_META_INFO_TITLE] = strdup (this->title);
    236   this->stream->meta_info[XINE_META_INFO_ARTIST] = strdup (this->artist);
    237   sprintf (copyright, "Copyright (C) %s", this->copyright);
    238   this->stream->meta_info[XINE_META_INFO_COMMENT] = strdup (copyright);
    239 
    240   /* send start buffers */
    241   xine_demux_control_start (this->stream);
    242 
    243   /* send init info to the audio decoder */
    244   if (this->audio_fifo)
    245     {
    246       buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
    247       buf->type = BUF_AUDIO_NSF;
    248       buf->decoder_flags = BUF_FLAG_HEADER;
    249       buf->decoder_info[0] = 5;
    250       buf->decoder_info[1] = NSF_SAMPLERATE;
    251       buf->decoder_info[2] = NSF_BITS;
    252       buf->decoder_info[3] = NSF_CHANNELS;
    253 
    254       /* send the NSF filesize in the body, big endian format */
    255       buf->content[0] = (this->filesize >> 24) & 0xFF;
    256       buf->content[1] = (this->filesize >> 16) & 0xFF;
    257       buf->content[2] = (this->filesize >> 8) & 0xFF;
    258       buf->content[3] = (this->filesize >> 0) & 0xFF;
    259       /* send the requested song */
    260       buf->content[4] = this->current_song + 5;
    261 
    262       this->audio_fifo->put (this->audio_fifo, buf);
    263     }
    264 }
    265 
    266 static int
    267 demux_nsf_seek (demux_plugin_t * this_gen, off_t start_pos, int start_time)
    268 {
    269 
    270   demux_nsf_t *this = (demux_nsf_t *) this_gen;
    271 
    272   /* if thread is not running, initialize demuxer */
    273   if (!this->stream->demux_thread_running)
    274     {
    275 
    276       /* send new pts */
    277       xine_demux_control_newpts (this->stream, 0, 0);
    278 
    279       this->status = DEMUX_OK;
    280 
    281       /* reposition stream at the start for loading */
    282       this->input->seek (this->input, 0, SEEK_SET);
    283 
    284       this->file_sent = 0;
    285       this->current_pts = 0;
    286       this->new_song = 1;
    287     }
    288   else
    289     {
    290       this->current_song = start_pos + 1;
    291       this->new_song = 1;
    292       this->current_pts = 0;
    293       xine_demux_flush_engine (this->stream);
    294     }
    295 
    296   return this->status;
    297 }
    298 
    299 static void
    300 demux_nsf_dispose (demux_plugin_t * this_gen)
    301 {
    302   demux_nsf_t *this = (demux_nsf_t *) this_gen;
    303 
    304   free (this->title);
    305   free (this->artist);
    306   free (this->copyright);
    307   free (this);
    308 }
    309 
    310 static int
    311 demux_nsf_get_status (demux_plugin_t * this_gen)
    312 {
    313   demux_nsf_t *this = (demux_nsf_t *) this_gen;
    314 
    315   return this->status;
    316 }
    317 
    318 /* return the approximate length in miliseconds */
    319 static int
    320 demux_nsf_get_stream_length (demux_plugin_t * this_gen)
    321 {
    322 
    323   return 0;
    324 }
    325 
    326 static uint32_t
    327 demux_nsf_get_capabilities (demux_plugin_t * this_gen)
    328 {
    329   return DEMUX_CAP_NOCAP;
    330 }
    331 
    332 static int
    333 demux_nsf_get_optional_data (demux_plugin_t * this_gen,
    334                              void *data, int data_type)
    335 {
    336   return DEMUX_OPTIONAL_UNSUPPORTED;
    337 }
    338 
    339 static demux_plugin_t *
    340 open_plugin (demux_class_t * class_gen, xine_stream_t * stream,
    341              input_plugin_t * input_gen)
    342 {
    343 
    344   input_plugin_t *input = (input_plugin_t *) input_gen;
    345   demux_nsf_t *this;
    346 
    347   if (!(input->get_capabilities (input) & INPUT_CAP_SEEKABLE))
    348     {
    349       if (stream->xine->verbosity >= XINE_VERBOSITY_DEBUG)
    350         printf (_("demux_nsf.c: input not seekable, can not handle!\n"));
    351       return NULL;
    352     }
    353 
    354   this = xine_xmalloc (sizeof (demux_nsf_t));
    355   this->stream = stream;
    356   this->input = input;
    357 
    358   this->demux_plugin.send_headers = demux_nsf_send_headers;
    359   this->demux_plugin.send_chunk = demux_nsf_send_chunk;
    360   this->demux_plugin.seek = demux_nsf_seek;
    361   this->demux_plugin.dispose = demux_nsf_dispose;
    362   this->demux_plugin.get_status = demux_nsf_get_status;
    363   this->demux_plugin.get_stream_length = demux_nsf_get_stream_length;
    364   this->demux_plugin.get_video_frame = NULL;
    365   this->demux_plugin.got_video_frame_cb = NULL;
    366   this->demux_plugin.get_capabilities = demux_nsf_get_capabilities;
    367   this->demux_plugin.get_optional_data = demux_nsf_get_optional_data;
    368   this->demux_plugin.demux_class = class_gen;
    369 
    370   this->status = DEMUX_FINISHED;
    371 
    372   switch (stream->content_detection_method)
    373     {
    374 
    375     case METHOD_BY_CONTENT:
    376     case METHOD_EXPLICIT:
    377 
    378       if (!open_nsf_file (this))
    379         {
    380           free (this);
    381           return NULL;
    382         }
    383 
    384       break;
    385 
    386     case METHOD_BY_EXTENSION:
    387       {
    388         char *ending, *mrl;
    389 
    390         mrl = input->get_mrl (input);
    391 
    392         ending = strrchr (mrl, '.');
    393 
    394         if (!ending)
    395           {
    396             free (this);
    397             return NULL;
    398           }
    399 
    400         if (strncasecmp (ending, ".nsf", 4))
    401           {
    402             free (this);
    403             return NULL;
    404           }
    405 
    406         if (!open_nsf_file (this))
    407           {
    408             free (this);
    409             return NULL;
    410           }
    411 
    412       }
    413 
    414       break;
    415 
    416     default:
    417       free (this);
    418       return NULL;
    419     }
    420 
    421   strncpy (this->last_mrl, input->get_mrl (input), 1024);
    422 
    423   return &this->demux_plugin;
    424 }
    425 
    426 static char *
    427 get_description (demux_class_t * this_gen)
    428 {
    429   return "NES Music file demux plugin";
    430 }
    431 
    432 static char *
    433 get_identifier (demux_class_t * this_gen)
    434 {
    435   return "NSF";
    436 }
    437 
    438 static char *
    439 get_extensions (demux_class_t * this_gen)
    440 {
    441   return "nsf";
    442 }
    443 
    444 static char *
    445 get_mimetypes (demux_class_t * this_gen)
    446 {
    447   return NULL;
    448 }
    449 
    450 static void
    451 class_dispose (demux_class_t * this_gen)
    452 {
    453 
    454   demux_nsf_class_t *this = (demux_nsf_class_t *) this_gen;
    455 
    456   free (this);
    457 }
    458 
    459 void *
    460 demux_nsf_init_plugin (xine_t * xine, void *data)
    461 {
    462 
    463   demux_nsf_class_t *this;
    464 
    465   this = xine_xmalloc (sizeof (demux_nsf_class_t));
    466   this->config = xine->config;
    467   this->xine = xine;
    468 
    469   this->demux_class.open_plugin = open_plugin;
    470   this->demux_class.get_description = get_description;
    471   this->demux_class.get_identifier = get_identifier;
    472   this->demux_class.get_mimetypes = get_mimetypes;
    473   this->demux_class.get_extensions = get_extensions;
    474   this->demux_class.dispose = class_dispose;
    475 
    476   return this;
    477 }