Subversion Repositories shark

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <ctype.h>
#include <stdlib.h>
#include "mpg123.h"
#include "tables.h"

/* max = 1728 */
#define MAXFRAMESIZE 1792

#define SKIP_JUNK 1

int tabsel_123[2][3][16] = {
   { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},
     {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},
     {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} },

   { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,},
     {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,},
     {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} }
};

long freqs[7] = { 44100, 48000, 32000, 22050, 24000, 16000 , 11025 };

#ifdef I386_ASSEM
int bitindex;
unsigned char *wordpointer;
#else
static int bitindex;
static unsigned char *wordpointer;
#endif

static int fsize=0,fsizeold=0,ssize;
static unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */
static unsigned char *bsbuf=bsspace[1],*bsbufold;
static int bsnum=0;

struct ibuf {
        struct ibuf *next;
        struct ibuf *prev;
        unsigned char *buf;
        unsigned char *pnt;
        int len;
        /* skip,time stamp */
};

struct ibuf ibufs[2];
struct ibuf *cibuf;
int ibufnum=0;

unsigned char *pcm_sample;
int pcm_point = 0;
int audiobufsize = AUDIOBUFSIZE;

#ifdef VARMODESUPPORT
        /*
         *   This is a dirty hack!  It might burn your PC and kill your cat!
         *   When in "varmode", specially formatted layer-3 mpeg files are
         *   expected as input -- it will NOT work with standard mpeg files.
         *   The reason for this:
         *   Varmode mpeg files enable my own GUI player to perform fast
         *   forward and backward functions, and to jump to an arbitrary
         *   timestamp position within the file.  This would be possible
         *   with standard mpeg files, too, but it would be a lot harder to
         *   implement.
         *   A filter for converting standard mpeg to varmode mpeg is
         *   available on request, but it's really not useful on its own.
         *
         *   Oliver Fromme  <oliver.fromme@heim3.tu-clausthal.de>
         *   Mon Mar 24 00:04:24 MET 1997
         */

int varmode = FALSE;
int playlimit;
#endif

static FILE *filept;
static int filept_opened;

static void get_II_stuff(struct frame *fr)
{
  static int translate[3][2][16] =
   { { { 0,2,2,2,2,2,2,0,0,0,1,1,1,1,1,0 } ,
       { 0,2,2,0,0,0,1,1,1,1,1,1,1,1,1,0 } } ,
     { { 0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0 } ,
       { 0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0 } } ,
     { { 0,3,3,3,3,3,3,0,0,0,1,1,1,1,1,0 } ,
       { 0,3,3,0,0,0,1,1,1,1,1,1,1,1,1,0 } } };

  int table,sblim;
  static struct al_table *tables[5] =
       { alloc_0, alloc_1, alloc_2, alloc_3 , alloc_4 };
  static int sblims[5] = { 27 , 30 , 8, 12 , 30 };

  if(fr->lsf)
    table = 4;
  else
    table = translate[fr->sampling_frequency][2-fr->stereo][fr->bitrate_index];
  sblim = sblims[table];

  fr->alloc = tables[table];
  fr->II_sblimit = sblim;
}

void audio_flush(int outmode, struct audio_info_struct *ai)
{
  if (pcm_point) {
    switch (outmode) {
      case DECODE_STDOUT:
        write (1, pcm_sample, pcm_point);
        break;
      case DECODE_AUDIO:
        audio_play_samples (ai, pcm_sample, pcm_point);
        break;
      case DECODE_BUFFER:
        write (buffer_fd[1], pcm_sample, pcm_point);
        break;
    }
    pcm_point = 0;
  }
}

void (*catchsignal(int signum, void(*handler)()))()
{
  struct sigaction new_sa;
  struct sigaction old_sa;

  new_sa.sa_handler = handler;
  sigemptyset(&new_sa.sa_mask);
  new_sa.sa_flags = 0;
  if (sigaction(signum, &new_sa, &old_sa) == -1)
    return ((void (*)()) -1);
  return (old_sa.sa_handler);
}

static unsigned long oldhead = 0;
static unsigned long firsthead=0;

void read_frame_init (void)
{
    oldhead = 0;
    firsthead = 0;
}

#define HDRCMPMASK 0xfffffd00
#if 0
#define HDRCMPMASK 0xfffffdft
#endif

/*
 * HACK,HACK,HACK
 * step back <num> frames
 */

int back_frame(struct frame *fr,int num)
{
        long bytes;
        unsigned char buf[4];
        unsigned long newhead;

        if(!firsthead)
                return 0;

        bytes = (fsize+8)*(num+2);

        if(fseek(filept,-bytes,SEEK_CUR) < 0)
                return -1;

        if(fread(buf,1,4,filept) != 4)
                return -1;

        newhead = (buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + buf[3];
       
        while( (newhead & HDRCMPMASK) != (firsthead & HDRCMPMASK) ) {
                if(fread(buf,1,1,filept) != 1)
                        return -1;
                newhead <<= 8;
                newhead |= buf[0];
                newhead &= 0xffffffff;
        }

        if( fseek(filept,-4,SEEK_CUR) < 0)
                return -1;
       
        read_frame(fr);
        read_frame(fr);

        if(fr->lay == 3) {
                set_pointer(512);
        }

        return 0;
}

int read_frame(struct frame *fr)
{
  static unsigned long newhead;

  static unsigned char ssave[34];
  unsigned char hbuf[8];
  static int framesize;
  static int halfphase = 0;
  int l;
  int try = 0;

  if (halfspeed)
    if (halfphase--) {
      bitindex = 0;
      wordpointer = (unsigned char *) bsbuf;
      if (fr->lay == 3)
        memcpy (bsbuf, ssave, ssize);
      return 1;
    }
    else
      halfphase = halfspeed - 1;

#ifdef VARMODESUPPORT
  if (varmode) {
    if(fread(hbuf,1,8,filept) != 8)
      return 0;
  }
  else
#endif
    read_again:
  if(fread(hbuf,1,4,filept) != 4)
    return 0;

  newhead = ((unsigned long) hbuf[0] << 24) | ((unsigned long) hbuf[1] << 16) |
    ((unsigned long) hbuf[2] << 8) | (unsigned long) hbuf[3];

  if(oldhead != newhead || !oldhead)
    {
      fr->header_change = 1;
#if 0
      fprintf(stderr,"Major headerchange %08lx->%08lx.\n",oldhead,newhead);
#endif

    init_resync:
      if( (newhead & 0xffe00000) != 0xffe00000) {
#ifdef SKIP_JUNK
        if(!firsthead) {
          int i;
          /* I even saw RIFF headers at the beginning of MPEG streams ;( */
          if(newhead == ('R'<<24)+('I'<<16)+('F'<<8)+'F') {
            char buf[40];
            fprintf(stderr,"Skipped RIFF header\n");
            fread(buf,1,68,filept);
            goto read_again;
          }
          /* give up after 1024 bytes */
          for(i=0;i<1024;i++) {
            memmove (&hbuf[0], &hbuf[1], 3);
            if(fread(hbuf+3,1,1,filept) != 1)
              return 0;
            newhead <<= 8;
            newhead |= hbuf[3];
            newhead &= 0xffffffff;
            goto init_resync;
          }
          fprintf(stderr,"Giving up searching valid MPEG header\n");
          return 0;
        }
#endif
        if (!quiet)
          fprintf(stderr,"Illegal Audio-MPEG-Header 0x%08lx at offset 0x%lx.\n",
                  newhead,ftell(filept)-4);
        if (tryresync) {
          /* Read more bytes until we find something that looks
             reasonably like a valid header.  This is not a
             perfect strategy, but it should get us back on the
             track within a short time (and hopefully without
             too much distortion in the audio output).  */

          do {
            try++;
            memmove (&hbuf[0], &hbuf[1], 7);
#ifdef VARMODESUPPORT
            if (fread(&hbuf[varmode?7:3],1,1,filept) != 1)
#else
              if (fread(&hbuf[3],1,1,filept) != 1)
#endif
                return 0;

            /* This is faster than combining newhead from scratch */
            newhead = ((newhead << 8) | hbuf[3]) & 0xffffffff;

            if (!oldhead)
              goto init_resync;       /* "considered harmful", eh? */

          } while ((newhead & HDRCMPMASK) != (oldhead & HDRCMPMASK)
                   && (newhead & HDRCMPMASK) != (firsthead & HDRCMPMASK));
          if (!quiet)
            fprintf (stderr, "Skipped %d bytes in input.\n", try);
        }
        else
          return (0);
      }
      if (!firsthead)
        firsthead = newhead;

      if( newhead & (1<<20) ) {
        fr->lsf = (newhead & (1<<19)) ? 0x0 : 0x1;
        fr->mpeg25 = 0;
      }
      else {
        fr->lsf = 1;
        fr->mpeg25 = 1;
      }
   
      if (!tryresync || !oldhead) {
        /* If "tryresync" is true, assume that certain
           parameters do not change within the stream! */

        fr->lay = 4-((newhead>>17)&3);
        fr->bitrate_index = ((newhead>>12)&0xf);
        if( ((newhead>>10)&0x3) == 0x3) {
          fprintf(stderr,"Stream error\n");
          exit(1);
        }
        if(fr->mpeg25)
          fr->sampling_frequency = 6;
        else
          fr->sampling_frequency = ((newhead>>10)&0x3) + (fr->lsf*3);
        fr->error_protection = ((newhead>>16)&0x1)^0x1;
      }

      fr->padding   = ((newhead>>9)&0x1);
      fr->extension = ((newhead>>8)&0x1);
      fr->mode      = ((newhead>>6)&0x3);
      fr->mode_ext  = ((newhead>>4)&0x3);
      fr->copyright = ((newhead>>3)&0x1);
      fr->original  = ((newhead>>2)&0x1);
      fr->emphasis  = newhead & 0x3;

      fr->stereo    = (fr->mode == MPG_MD_MONO) ? 1 : 2;

      oldhead = newhead;

      if(!fr->bitrate_index)
        {
          fprintf(stderr,"Free format not supported.\n");
          return (0);
        }

      switch(fr->lay)
        {
          case 1:
            fr->do_layer = do_layer1;
#ifdef VARMODESUPPORT
            if (varmode) {
              fprintf(stderr,"Sorry, layer-1 not supported in varmode.\n");
              return (0);
            }
#endif
            fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ?
              (fr->mode_ext<<2)+4 : 32;
            framesize  = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
            framesize /= freqs[fr->sampling_frequency];
            framesize  = ((framesize+fr->padding)<<2)-4;
            break;
          case 2:
            fr->do_layer = do_layer2;
#ifdef VARMODESUPPORT
            if (varmode) {
              fprintf(stderr,"Sorry, layer-2 not supported in varmode.\n");
              return (0);
            }
#endif
            get_II_stuff(fr);
            fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ?
              (fr->mode_ext<<2)+4 : fr->II_sblimit;
            framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
            framesize /= freqs[fr->sampling_frequency];
            framesize += fr->padding - 4;
            break;
          case 3:
            fr->do_layer = do_layer3;
            if(fr->lsf)
              ssize = (fr->stereo == 1) ? 9 : 17;
            else
              ssize = (fr->stereo == 1) ? 17 : 32;
            if(fr->error_protection)
              ssize += 2;
#ifdef VARMODESUPPORT
            if (varmode)
              playlimit = ((unsigned int) hbuf[6] << 8) | (unsigned int) hbuf[7];
            framesize = ssize +
              (((unsigned int) hbuf[4] << 8) | (unsigned int) hbuf[5]);
            else {
#endif
              framesize  = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
              framesize /= freqs[fr->sampling_frequency]<<(fr->lsf);
              framesize = framesize + fr->padding - 4;
#ifdef VARMODESUPPORT
            }
#endif
            break;
          default:
            fprintf(stderr,"Sorry, unknown layer type.\n");
            return (0);
        }
    }
  else
    fr->header_change = 0;

  fsizeold=fsize;       /* for Layer3 */
  bsbufold = bsbuf;    
  bsbuf = bsspace[bsnum]+512;
  bsnum = (bsnum + 1) & 1;

  fsize = framesize;
 
  if( (l=fread(bsbuf,1,fsize,filept)) != fsize)
    {
      if(l <= 0)
        return 0;
      memset(bsbuf+l,0,fsize-l);
    }

  if (halfspeed && fr->lay == 3)
    memcpy (ssave, bsbuf, ssize);

  bitindex = 0;
  wordpointer = (unsigned char *) bsbuf;

  return 1;
}

#ifdef MPG123_REMOTE
void print_rheader(struct frame *fr)
{
        static char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" };
        static char *layers[4] = { "Unknown" , "I", "II", "III" };
        static char *mpeg_type[2] = { "1.0" , "2.0" };

        /* version, layer, freq, mode, channels, bitrate, BPF */
        fprintf(stderr,"@I %s %s %ld %s %d %d %d\n",
                        mpeg_type[fr->lsf],layers[fr->lay],freqs[fr->sampling_frequency],
                        modes[fr->mode],fr->stereo,
                        tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],
                        fsize+4);
}
#endif

void print_header(struct frame *fr)
{
        static char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" };
        static char *layers[4] = { "Unknown" , "I", "II", "III" };
 
        fprintf(stderr,"MPEG %s, Layer: %s, Freq: %ld, mode: %s, modext: %d, BPF: %d\n",
                fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
                layers[fr->lay],freqs[fr->sampling_frequency],
                modes[fr->mode],fr->mode_ext,fsize+4);
        fprintf(stderr,"Channels: %d, copyright: %s, original: %s, CRC: %s, emphasis: %d.\n",
                fr->stereo,fr->copyright?"Yes":"No",
                fr->original?"Yes":"No",fr->error_protection?"Yes":"No",
                fr->emphasis);
        fprintf(stderr,"Bitrate: %d Kbits/s, Extension value: %d\n",
                tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],fr->extension);
}

void print_header_compact(struct frame *fr)
{
        static char *modes[4] = { "stereo", "joint-stereo", "dual-channel", "mono" };
        static char *layers[4] = { "Unknown" , "I", "II", "III" };
 
        fprintf(stderr,"MPEG %s layer %s, %d kbit/s, %ld Hz %s\n",
                fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
                layers[fr->lay],
                tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],
                freqs[fr->sampling_frequency], modes[fr->mode]);
}

/*
 *   Allocate space for a new string containing the first
 *   "num" characters of "src".  The resulting string is
 *   always zero-terminated.  Returns NULL if malloc fails.
 */


char *strndup (const char *src, int num)
{
        char *dst;

        if (!(dst = (char *) malloc(num+1)))
                return (NULL);
        dst[num] = '\0';
        return (strncpy(dst, src, num));
}

/*
 *   Split "path" into directory and filename components.
 *
 *   Return value is 0 if no directory was specified (i.e.
 *   "path" does not contain a '/'), OR if the directory
 *   is the same as on the previous call to this function.
 *
 *   Return value is 1 if a directory was specified AND it
 *   is different from the previous one (if any).
 */


int split_dir_file (const char *path, char **dname, char **fname)
{
  static char *lastdir = NULL;
  char *slashpos;

  if ((slashpos = strrchr(path, '/'))) {
    *fname = slashpos + 1;
    *dname = strndup(path, 1 + slashpos - path);
    if (lastdir && !strcmp(lastdir, *dname)) {
      /***   same as previous directory   ***/
      free (*dname);
      *dname = lastdir;
      return 0;
    }
    else {
      /***   different directory   ***/
      if (lastdir)
        free (lastdir);
      lastdir = *dname;
      return 1;
    }
  }
  else {
    /***   no directory specified   ***/
    if (lastdir) {
      free (lastdir);
      lastdir = NULL;
    };
    *dname = NULL;
    *fname = (char *)path;
    return 0;
  }
}

/* open the device to read the bit stream from it */

void open_stream(char *bs_filenam,int fd)
{
  filept_opened = 1;
  if (!bs_filenam) {
    if(fd < 0) {
      filept = stdin;
      filept_opened = 0;
    }
    else
      filept = fdopen(fd,"r");
  }
  else if (!strncmp(bs_filenam, "http://", 7))
    filept = http_open(bs_filenam);
  else if (!(filept = fopen(bs_filenam, "rb"))) {
    perror (bs_filenam);
    exit(1);
  }
}

/*close the device containing the bit stream after a read process*/

void close_stream(void)
{
    if (filept_opened)
        fclose(filept);
}

long tell_stream(void)
{
        return ftell(filept);
}

#if !defined(I386_ASSEM) || defined(DEBUG_GETBITS)
#ifdef _gcc_
inline
#endif
unsigned int getbits(int number_of_bits)
{
  unsigned long rval;

#ifdef DEBUG_GETBITS
fprintf(stderr,"g%d",number_of_bits);
#endif

  if(!number_of_bits)
    return 0;

  {
    rval = wordpointer[0];
    rval <<= 8;
    rval |= wordpointer[1];
    rval <<= 8;
    rval |= wordpointer[2];
#if 0
    rval = ((unsigned int) wordpointer[0] << 16)+((unsigned int) wordpointer[1]<<8)+
                 (unsigned int) wordpointer[2];
#endif
    rval <<= bitindex;
    rval &= 0xffffff;

    bitindex += number_of_bits;

    rval >>= (24-number_of_bits);

    wordpointer += (bitindex>>3);
    bitindex &= 7;
  }

#ifdef DEBUG_GETBITS
fprintf(stderr,":%x ",rval);
#endif
  return rval;
}

#ifdef _gcc_
inline
#endif
unsigned int getbits_fast(int number_of_bits)
{
  unsigned long rval;

#ifdef DEBUG_GETBITS
fprintf(stderr,"g%d",number_of_bits);
#endif

  {
    rval = wordpointer[0];
    rval <<= 8;
    rval |= wordpointer[1];
    rval <<= bitindex;
    rval &= 0xffff;
#if 0
    rval = ((unsigned int) high << (8-bitindex) )+((unsigned int) (unsigned char) wordpointer[1]);
#endif
    bitindex += number_of_bits;

    rval >>= (16-number_of_bits);

    wordpointer += (bitindex>>3);
    bitindex &= 7;
  }


#ifdef DEBUG_GETBITS
fprintf(stderr,":%x ",rval);
#endif


  return rval;
}

#ifdef _gcc_
inline
#endif
unsigned int get1bit(void)
{
  unsigned char rval;

#ifdef DEBUG_GETBITS
fprintf(stderr,"g%d",1);
#endif

  rval = *wordpointer << bitindex;

  bitindex++;
  wordpointer += (bitindex>>3);
  bitindex &= 7;

#ifdef DEBUG_GETBITS
fprintf(stderr,":%d ",rval>>7);
#endif

  return rval>>7;
}
#endif

void set_pointer(long backstep)
{
  wordpointer = bsbuf + ssize - backstep;
  if (backstep)
    memcpy(wordpointer,bsbufold+fsizeold-backstep,backstep);
  bitindex = 0;
}