/* this file originally came from the mpg123 plugin, although I've carved it up quite a bit. It's under GPL. */ #include #include #include #include "mple.h" #include "common.h" #define MAX_SKIP_LENGTH (256 * 1024) #define MPG_MD_STEREO 0 #define MPG_MD_JOINT_STEREO 1 #define MPG_MD_DUAL_CHANNEL 2 #define MPG_MD_MONO 3 #define ID3_GET_SIZE28(a, b, c, d) \ (((a & 0x7f) << (24 - 3)) | \ ((b & 0x7f) << (16 - 2)) | \ ((c & 0x7f) << ( 8 - 1)) | \ ((d & 0x7f))) /* a bit silly, but... */ int framesize( struct frame *frm ) { return frm->framesize; } int bitrate_index( struct frame *frm ) { return frm->bitrate_index; } int lsf( struct frame *frm ) { return frm->lsf; } guint8 *header( struct frame *frm ) { return frm->header; } typedef struct { int frames; /* total bit stream frames from Xing header data */ int bytes; /* total bit stream bytes from Xing header data */ unsigned char toc[100]; /* "table of contents" */ } xing_header_t; struct bitstream_info { int bitindex; unsigned char *wordpointer; }; typedef struct { int going, num_frames, eof, jump_to_time, eq_active; int songtime; double tpf; float eq_mul[576]; gboolean output_audio, first_frame, network_stream; guint32 filesize; /* Filesize without junk */ } PlayerInfo; PlayerInfo mpg123_info_static; PlayerInfo *mpg123_info = &mpg123_info_static; /* prototypes */ int mpg123_read_frame(mp3file *,struct frame **); static int mpg123_head_check(unsigned long); static int mpg123_decode_header(struct frame *, unsigned long); /* max = 1728 */ #define MAXFRAMESIZE 1792 const 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,}} }; const int mpg123_freqs[9] = {44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000}; struct bitstream_info bsi; static int ssize; static int fullread(FILE * fd, unsigned char *buf, int count) { int ret, cnt = 0; while (cnt < count) { ret = fread(buf + cnt, 1, count - cnt, fd); if (ret < 0) return ret; if (ret == 0) break; cnt += ret; } return cnt; } static int stream_head_read(mp3file *mp3, unsigned long *newhead) { unsigned char hbuf[4]; /* allow for reading from a buffer as well as a file */ if ( mp3->fp != NULL ) { if (fullread(mp3->fp, hbuf, 4) != 4) return FALSE; } else if ( mp3->fb != NULL ) { /* slightly wasteful; we could just glom things straight into newhead */ if ( mp3->fb_ptr + 4 <= mp3->fb_size ) { memcpy( hbuf, &mp3->fb[mp3->fb_ptr], 4 ); mp3->fb_ptr += 4; } else { return FALSE; } } else { /* wtf? we need a SOURCE, dude */ return FALSE; } *newhead = ((unsigned long) hbuf[0] << 24) | ((unsigned long) hbuf[1] << 16) | ((unsigned long) hbuf[2] << 8) | (unsigned long) hbuf[3]; return TRUE; } static int stream_head_shift( mp3file *mp3, unsigned long *head) { unsigned char hbuf; if ( mp3->fp != NULL ) { if (fullread( mp3->fp, &hbuf, 1) != 1) return 0; } else if ( mp3->fb != NULL ) { if ( mp3->fb_ptr + 1 <= mp3->fb_size ) { hbuf = mp3->fb[mp3->fb_ptr]; mp3->fb_ptr++; } } else { return 0; } *head <<= 8; *head |= hbuf; *head &= 0xffffffff; return 1; } static int stream_mpg123_read_frame_body(mp3file *mp3, unsigned char *buf, int size) { long l; if ( mp3->fp != NULL ) { if ((l = fullread( mp3->fp, buf, size )) != size) { /* The original code: if the read is short, pad out with zeros. This is probably sensible enough for reading from a network stream, but is a bit dumb for reading from a file. */ #ifdef PAD_SHORT_READ if (l <= 0) return 0; memset( buf + l, 0, size - l ); #else return 0; #endif } } else if ( mp3->fb != NULL ) { if ( mp3->fb_ptr + size <= mp3->fb_size ) { l = size; } else { #ifdef PAD_SHORT_READ l = mp3->fb_size - mp3->fb_ptr; memset( buf + l, 0, size -l ); #else /* this should cascade back a failure */ return 0; #endif } memcpy( buf, &mp3->fb[mp3->fb_ptr], l ); mp3->fb_ptr += l; } else { /* you forgot to set either data pointer */ return 0; } return 1; } int mpg123_head_check(unsigned long head) { if ((head & 0xffe00000) != 0xffe00000) return FALSE; if (!((head >> 17) & 3)) return FALSE; if (((head >> 12) & 0xf) == 0xf) return FALSE; if (!((head >> 12) & 0xf)) return FALSE; if (((head >> 10) & 0x3) == 0x3) return FALSE; if (((head >> 19) & 1) == 1 && ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1) return FALSE; if ((head & 0xffff0000) == 0xfffe0000) return FALSE; return TRUE; } static int skip_id3v2( mp3file *f, guint32 head) { guint32 id3v2size; guint8 tmp[6]; if (!((head & 0xffffff00) == (('I' << 24) | ('D' << 16) | ('3' << 8)))) return TRUE; if ( f->fp != NULL ) { if (fread(tmp, 1, 6, f->fp) != 6) return FALSE; } else if ( f->fb != NULL ) { if ( f->fb_ptr + 6 <= f->fb_size ) { memcpy( tmp, &f->fb[f->fb_ptr], 6 ); f->fb_ptr += 6; } else { return FALSE; } } else { return FALSE; /* return value isn't actually checked */ } id3v2size = ID3_GET_SIZE28(tmp[2], tmp[3], tmp[4], tmp[5]); if (tmp[1] & 0x10) /* Footer */ id3v2size += 10; /* Let this fseek fail silently and continue parsing the file */ if ( f->fp != NULL ) { fseek( f->fp, id3v2size, SEEK_CUR); } else if ( f->fb != NULL ) { if ( f->fb_ptr + id3v2size <= f->fb_size ) { f->fb_ptr += id3v2size; } else { return FALSE; } } return TRUE; } /***************************************************************** * read next frame */ static struct frame frm; gboolean mpg123_read_frame( mp3file *mp3, struct frame **fr) { unsigned long newhead; long bookmark = -1; *fr = &frm; mp3->fsizeold = frm.framesize; /* for Layer3 */ if ( mp3->fp != NULL ) { bookmark = ftell( mp3->fp ); } else if ( mp3->fb != NULL ) { bookmark = mp3->fb_ptr; } else { goto bailout; } if (!stream_head_read(mp3, &newhead)) goto bailout; if (!mpg123_head_check(newhead) || !mpg123_decode_header(&frm, newhead)) { int try = 0; do { try++; if ((newhead & 0xffffff00) == ('I' << 24) + ('D' << 16) + ('3' << 8)) { skip_id3v2( mp3, newhead); if (!stream_head_read(mp3, &newhead)) goto bailout; } else if (!stream_head_shift(mp3, &newhead)) goto bailout; if (try > MAX_SKIP_LENGTH) goto bailout; } while (!mpg123_head_check(newhead) || !mpg123_decode_header(&frm,newhead)); mpg123_info->filesize -= try; } /* flip/init buffer for Layer 3 */ mp3->bsbufold = mp3->bsbuf; mp3->bsbuf = mp3->bsspace[mp3->bsnum] + 512; mp3->bsnum = (mp3->bsnum + 1) & 1; if (!stream_mpg123_read_frame_body(mp3, mp3->bsbuf, frm.framesize)) { goto bailout; } bsi.bitindex = 0; bsi.wordpointer = (unsigned char *) mp3->bsbuf; /* Save the header so we can write it out */ memcpy( frm.header, (guint8 *)&newhead, 4 ); return TRUE; bailout: if ( bookmark != -1 ) { if ( mp3->fp != NULL ) { fseek( mp3->fp, bookmark, SEEK_SET ); } else if ( mp3->fb != NULL ) { mp3->fb_ptr = bookmark; } } return FALSE; } /* * decode a header and write the information * into the frame structure */ int mpg123_decode_header(struct frame *fr, unsigned long newhead) { if (newhead & (1 << 20)) { fr->lsf = (newhead & (1 << 19)) ? 0x0 : 0x1; fr->mpeg25 = 0; } else { fr->lsf = 1; fr->mpeg25 = 1; } fr->lay = 4 - ((newhead >> 17) & 3); if (fr->mpeg25) { fr->sampling_frequency = 6 + ((newhead >> 10) & 0x3); } else fr->sampling_frequency = ((newhead >> 10) & 0x3) + (fr->lsf * 3); fr->error_protection = ((newhead >> 16) & 0x1) ^ 0x1; fr->bitrate_index = ((newhead >> 12) & 0xf); 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; ssize = 0; if (!fr->bitrate_index) return (0); switch (fr->lay) { /* we only support layer 3 */ case 3: if (fr->lsf) ssize = (fr->stereo == 1) ? 9 : 17; else ssize = (fr->stereo == 1) ? 17 : 32; if (fr->error_protection) ssize += 2; fr->framesize = tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000; fr->framesize /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf); fr->framesize = fr->framesize + fr->padding - 4; break; default: return (0); } if(fr->framesize > MAXFRAMESIZE) return 0; return 1; }