xing header alignment problems fix, hat tip to Paul Forgey

This commit is contained in:
Ron Pedde 2005-10-29 15:49:11 +00:00
parent fc2c656317
commit cb42785a36
2 changed files with 406 additions and 397 deletions

View File

@ -101,4 +101,9 @@ Phil Packer
Diego Penneno Diego Penneno
* Patches for multiple PID files to facilitate gentoo init scripts * Patches for multiple PID files to facilitate gentoo init scripts
Paul Forgey
* Fixes for alignment problems in XING header parsing

View File

@ -254,7 +254,7 @@ static void scan_mp3_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi);
*/ */
int scan_get_mp3info(char *filename, MP3FILE *pmp3) { int scan_get_mp3info(char *filename, MP3FILE *pmp3) {
if(scan_mp3_get_mp3tags(filename, pmp3)) if(scan_mp3_get_mp3tags(filename, pmp3))
return scan_mp3_get_mp3fileinfo(filename,pmp3); return scan_mp3_get_mp3fileinfo(filename,pmp3);
return FALSE; return FALSE;
} }
@ -268,9 +268,9 @@ int scan_mp3_is_numeric(char *str) {
char *ptr=str; char *ptr=str;
while(*ptr) { while(*ptr) {
if(!isdigit(*ptr)) if(!isdigit(*ptr))
return 0; return 0;
ptr++; ptr++;
} }
return 1; return 1;
} }
@ -292,187 +292,187 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
pid3file=id3_file_open(file,ID3_FILE_MODE_READONLY); pid3file=id3_file_open(file,ID3_FILE_MODE_READONLY);
if(!pid3file) { if(!pid3file) {
DPRINTF(E_WARN,L_SCAN,"Cannot open %s\n",file); DPRINTF(E_WARN,L_SCAN,"Cannot open %s\n",file);
return FALSE; return FALSE;
} }
pid3tag=id3_file_tag(pid3file); pid3tag=id3_file_tag(pid3file);
if(!pid3tag) { if(!pid3tag) {
err=errno; err=errno;
id3_file_close(pid3file); id3_file_close(pid3file);
errno=err; errno=err;
DPRINTF(E_WARN,L_SCAN,"Cannot get ID3 tag for %s\n",file); DPRINTF(E_WARN,L_SCAN,"Cannot get ID3 tag for %s\n",file);
return FALSE; return FALSE;
} }
DPRINTF(E_SPAM,L_SCAN,"Starting mp3 tag scan\n"); DPRINTF(E_SPAM,L_SCAN,"Starting mp3 tag scan\n");
index=0; index=0;
while((pid3frame=id3_tag_findframe(pid3tag,"",index))) { while((pid3frame=id3_tag_findframe(pid3tag,"",index))) {
used=0; used=0;
utf8_text=NULL; utf8_text=NULL;
native_text=NULL; native_text=NULL;
have_utf8=0; have_utf8=0;
have_text=0; have_text=0;
DPRINTF(E_SPAM,L_SCAN,"Found tag %s\n",pid3frame->id); DPRINTF(E_SPAM,L_SCAN,"Found tag %s\n",pid3frame->id);
if(!strcmp(pid3frame->id,"YTCP")) { /* for id3v2.2 */ if(!strcmp(pid3frame->id,"YTCP")) { /* for id3v2.2 */
pmp3->compilation = 1; pmp3->compilation = 1;
DPRINTF(E_DBG,L_SCAN,"Compilation: %d\n", pmp3->compilation); DPRINTF(E_DBG,L_SCAN,"Compilation: %d\n", pmp3->compilation);
} }
if(((pid3frame->id[0] == 'T')||(strcmp(pid3frame->id,"COMM")==0)) && if(((pid3frame->id[0] == 'T')||(strcmp(pid3frame->id,"COMM")==0)) &&
(id3_field_getnstrings(&pid3frame->fields[1]))) (id3_field_getnstrings(&pid3frame->fields[1])))
have_text=1; have_text=1;
if(have_text) { if(have_text) {
native_text=id3_field_getstrings(&pid3frame->fields[1],0); native_text=id3_field_getstrings(&pid3frame->fields[1],0);
if(native_text) { if(native_text) {
/* FIXME: I didn't understand what was happening here. /* FIXME: I didn't understand what was happening here.
* this should really be a switch to evaluate latin1 * this should really be a switch to evaluate latin1
* tags as native codepage. Not only is this hackish, * tags as native codepage. Not only is this hackish,
* it's just plain wrong. * it's just plain wrong.
*/ */
have_utf8=1; have_utf8=1;
if(config.latin1_tags) { if(config.latin1_tags) {
utf8_text=(char *)id3_ucs4_latin1duplicate(native_text); utf8_text=(char *)id3_ucs4_latin1duplicate(native_text);
} else { } else {
utf8_text=(char *)id3_ucs4_utf8duplicate(native_text); utf8_text=(char *)id3_ucs4_utf8duplicate(native_text);
} }
MEMNOTIFY(utf8_text); MEMNOTIFY(utf8_text);
if(!strcmp(pid3frame->id,"TIT2")) { /* Title */ if(!strcmp(pid3frame->id,"TIT2")) { /* Title */
used=1; used=1;
pmp3->title = utf8_text; pmp3->title = utf8_text;
DPRINTF(E_DBG,L_SCAN," Title: %s\n",utf8_text); DPRINTF(E_DBG,L_SCAN," Title: %s\n",utf8_text);
} else if(!strcmp(pid3frame->id,"TPE1")) { } else if(!strcmp(pid3frame->id,"TPE1")) {
used=1; used=1;
pmp3->artist = utf8_text; pmp3->artist = utf8_text;
DPRINTF(E_DBG,L_SCAN," Artist: %s\n",utf8_text); DPRINTF(E_DBG,L_SCAN," Artist: %s\n",utf8_text);
} else if(!strcmp(pid3frame->id,"TALB")) { } else if(!strcmp(pid3frame->id,"TALB")) {
used=1; used=1;
pmp3->album = utf8_text; pmp3->album = utf8_text;
DPRINTF(E_DBG,L_SCAN," Album: %s\n",utf8_text); DPRINTF(E_DBG,L_SCAN," Album: %s\n",utf8_text);
} else if(!strcmp(pid3frame->id,"TCOM")) { } else if(!strcmp(pid3frame->id,"TCOM")) {
used=1; used=1;
pmp3->composer = utf8_text; pmp3->composer = utf8_text;
DPRINTF(E_DBG,L_SCAN," Composer: %s\n",utf8_text); DPRINTF(E_DBG,L_SCAN," Composer: %s\n",utf8_text);
} else if(!strcmp(pid3frame->id,"TIT1")) { } else if(!strcmp(pid3frame->id,"TIT1")) {
used=1; used=1;
pmp3->grouping = utf8_text; pmp3->grouping = utf8_text;
DPRINTF(E_DBG,L_SCAN," Grouping: %s\n",utf8_text); DPRINTF(E_DBG,L_SCAN," Grouping: %s\n",utf8_text);
} else if(!strcmp(pid3frame->id,"TPE2")) { } else if(!strcmp(pid3frame->id,"TPE2")) {
used=1; used=1;
pmp3->orchestra = utf8_text; pmp3->orchestra = utf8_text;
DPRINTF(E_DBG,L_SCAN," Orchestra: %s\n",utf8_text); DPRINTF(E_DBG,L_SCAN," Orchestra: %s\n",utf8_text);
} else if(!strcmp(pid3frame->id,"TPE3")) { } else if(!strcmp(pid3frame->id,"TPE3")) {
used=1; used=1;
pmp3->conductor = utf8_text; pmp3->conductor = utf8_text;
DPRINTF(E_DBG,L_SCAN," Conductor: %s\n",utf8_text); DPRINTF(E_DBG,L_SCAN," Conductor: %s\n",utf8_text);
} else if(!strcmp(pid3frame->id,"TCON")) { } else if(!strcmp(pid3frame->id,"TCON")) {
used=1; used=1;
pmp3->genre = utf8_text; pmp3->genre = utf8_text;
got_numeric_genre=0; got_numeric_genre=0;
DPRINTF(E_DBG,L_SCAN," Genre: %s\n",utf8_text); DPRINTF(E_DBG,L_SCAN," Genre: %s\n",utf8_text);
if(pmp3->genre) { if(pmp3->genre) {
if(!strlen(pmp3->genre)) { if(!strlen(pmp3->genre)) {
genre=WINAMP_GENRE_UNKNOWN; genre=WINAMP_GENRE_UNKNOWN;
got_numeric_genre=1; got_numeric_genre=1;
} else if (scan_mp3_is_numeric(pmp3->genre)) { } else if (scan_mp3_is_numeric(pmp3->genre)) {
genre=atoi(pmp3->genre); genre=atoi(pmp3->genre);
got_numeric_genre=1; got_numeric_genre=1;
} else if ((pmp3->genre[0] == '(') && (isdigit(pmp3->genre[1]))) { } else if ((pmp3->genre[0] == '(') && (isdigit(pmp3->genre[1]))) {
genre=atoi((char*)&pmp3->genre[1]); genre=atoi((char*)&pmp3->genre[1]);
got_numeric_genre=1; got_numeric_genre=1;
} }
if(got_numeric_genre) { if(got_numeric_genre) {
if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN)) if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
genre=WINAMP_GENRE_UNKNOWN; genre=WINAMP_GENRE_UNKNOWN;
free(pmp3->genre); free(pmp3->genre);
pmp3->genre=strdup(scan_winamp_genre[genre]); pmp3->genre=strdup(scan_winamp_genre[genre]);
} }
} }
} else if(!strcmp(pid3frame->id,"COMM")) { } else if(!strcmp(pid3frame->id,"COMM")) {
used=1; used=1;
pmp3->comment = utf8_text; pmp3->comment = utf8_text;
DPRINTF(E_DBG,L_SCAN," Comment: %s\n",pmp3->comment); DPRINTF(E_DBG,L_SCAN," Comment: %s\n",pmp3->comment);
} else if(!strcmp(pid3frame->id,"TPOS")) { } else if(!strcmp(pid3frame->id,"TPOS")) {
tmp=utf8_text; tmp=utf8_text;
strsep(&tmp,"/"); strsep(&tmp,"/");
if(tmp) { if(tmp) {
pmp3->total_discs=atoi(tmp); pmp3->total_discs=atoi(tmp);
} }
pmp3->disc=atoi(utf8_text); pmp3->disc=atoi(utf8_text);
DPRINTF(E_DBG,L_SCAN," Disc %d of %d\n",pmp3->disc,pmp3->total_discs); DPRINTF(E_DBG,L_SCAN," Disc %d of %d\n",pmp3->disc,pmp3->total_discs);
} else if(!strcmp(pid3frame->id,"TRCK")) { } else if(!strcmp(pid3frame->id,"TRCK")) {
tmp=utf8_text; tmp=utf8_text;
strsep(&tmp,"/"); strsep(&tmp,"/");
if(tmp) { if(tmp) {
pmp3->total_tracks=atoi(tmp); pmp3->total_tracks=atoi(tmp);
} }
pmp3->track=atoi(utf8_text); pmp3->track=atoi(utf8_text);
DPRINTF(E_DBG,L_SCAN," Track %d of %d\n",pmp3->track,pmp3->total_tracks); DPRINTF(E_DBG,L_SCAN," Track %d of %d\n",pmp3->track,pmp3->total_tracks);
} else if(!strcmp(pid3frame->id,"TDRC")) { } else if(!strcmp(pid3frame->id,"TDRC")) {
pmp3->year = atoi(utf8_text); pmp3->year = atoi(utf8_text);
DPRINTF(E_DBG,L_SCAN," Year: %d\n",pmp3->year); DPRINTF(E_DBG,L_SCAN," Year: %d\n",pmp3->year);
} else if(!strcmp(pid3frame->id,"TLEN")) { } else if(!strcmp(pid3frame->id,"TLEN")) {
pmp3->song_length = atoi(utf8_text); /* now in ms */ pmp3->song_length = atoi(utf8_text); /* now in ms */
DPRINTF(E_DBG,L_SCAN," Length: %d\n", pmp3->song_length); DPRINTF(E_DBG,L_SCAN," Length: %d\n", pmp3->song_length);
} else if(!strcmp(pid3frame->id,"TBPM")) { } else if(!strcmp(pid3frame->id,"TBPM")) {
pmp3->bpm = atoi(utf8_text); pmp3->bpm = atoi(utf8_text);
DPRINTF(E_DBG,L_SCAN,"BPM: %d\n", pmp3->bpm); DPRINTF(E_DBG,L_SCAN,"BPM: %d\n", pmp3->bpm);
} else if(!strcmp(pid3frame->id,"TCMP")) { /* for id3v2.3 */ } else if(!strcmp(pid3frame->id,"TCMP")) { /* for id3v2.3 */
pmp3->compilation = (char)atoi(utf8_text); pmp3->compilation = (char)atoi(utf8_text);
DPRINTF(E_DBG,L_SCAN,"Compilation: %d\n", pmp3->compilation); DPRINTF(E_DBG,L_SCAN,"Compilation: %d\n", pmp3->compilation);
} }
} }
} }
/* can check for non-text tags here */ /* can check for non-text tags here */
if((!used) && (have_utf8) && (utf8_text)) if((!used) && (have_utf8) && (utf8_text))
free(utf8_text); free(utf8_text);
/* v2 COMM tags are a bit different than v1 */ /* v2 COMM tags are a bit different than v1 */
if((!strcmp(pid3frame->id,"COMM")) && (pid3frame->nfields == 4)) { if((!strcmp(pid3frame->id,"COMM")) && (pid3frame->nfields == 4)) {
/* Make sure it isn't a application-specific comment... /* Make sure it isn't a application-specific comment...
* This currently includes the following: * This currently includes the following:
* *
* iTunes_CDDB_IDs * iTunes_CDDB_IDs
* iTunNORM * iTunNORM
* *
* If other apps stuff crap into comment fields, then we'll ignore them * If other apps stuff crap into comment fields, then we'll ignore them
* here. * here.
*/ */
native_text=id3_field_getstring(&pid3frame->fields[2]); native_text=id3_field_getstring(&pid3frame->fields[2]);
if(native_text) { if(native_text) {
utf8_text=(char*)id3_ucs4_utf8duplicate(native_text); utf8_text=(char*)id3_ucs4_utf8duplicate(native_text);
if((utf8_text) && (strncasecmp(utf8_text,"iTun",4) != 0)) { if((utf8_text) && (strncasecmp(utf8_text,"iTun",4) != 0)) {
/* it's a real comment */ /* it's a real comment */
if(utf8_text) if(utf8_text)
free(utf8_text); free(utf8_text);
native_text=id3_field_getfullstring(&pid3frame->fields[3]); native_text=id3_field_getfullstring(&pid3frame->fields[3]);
if(native_text) { if(native_text) {
if(pmp3->comment) if(pmp3->comment)
free(pmp3->comment); free(pmp3->comment);
utf8_text=(char*)id3_ucs4_utf8duplicate(native_text); utf8_text=(char*)id3_ucs4_utf8duplicate(native_text);
if(utf8_text) { if(utf8_text) {
pmp3->comment=utf8_text; pmp3->comment=utf8_text;
MEMNOTIFY(pmp3->comment); MEMNOTIFY(pmp3->comment);
} }
} }
} else { } else {
if(utf8_text) if(utf8_text)
free(utf8_text); free(utf8_text);
} }
} }
} }
index++; index++;
} }
id3_file_close(pid3file); id3_file_close(pid3file);
@ -496,8 +496,8 @@ int scan_mp3_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) {
int samplerate_index; int samplerate_index;
if((frame[0] != 0xFF) || (frame[1] < 224)) { if((frame[0] != 0xFF) || (frame[1] < 224)) {
pfi->is_valid=0; pfi->is_valid=0;
return -1; return -1;
} }
ver=(frame[1] & 0x18) >> 3; ver=(frame[1] & 0x18) >> 3;
@ -508,105 +508,105 @@ int scan_mp3_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) {
switch(ver) { switch(ver) {
case 0: case 0:
pfi->version = 2.5; pfi->version = 2.5;
sample_index=2; sample_index=2;
if(pfi->layer == 1) if(pfi->layer == 1)
layer_index = 3; layer_index = 3;
if((pfi->layer == 2) || (pfi->layer == 3)) if((pfi->layer == 2) || (pfi->layer == 3))
layer_index = 4; layer_index = 4;
break; break;
case 2: case 2:
pfi->version = 2.0; pfi->version = 2.0;
sample_index=1; sample_index=1;
if(pfi->layer == 1) if(pfi->layer == 1)
layer_index=3; layer_index=3;
if((pfi->layer == 2) || (pfi->layer == 3)) if((pfi->layer == 2) || (pfi->layer == 3))
layer_index=4; layer_index=4;
break; break;
case 3: case 3:
pfi->version = 1.0; pfi->version = 1.0;
sample_index=0; sample_index=0;
if(pfi->layer == 1) if(pfi->layer == 1)
layer_index = 0; layer_index = 0;
if(pfi->layer == 2) if(pfi->layer == 2)
layer_index = 1; layer_index = 1;
if(pfi->layer == 3) if(pfi->layer == 3)
layer_index = 2; layer_index = 2;
break; break;
} }
if((layer_index < 0) || (layer_index > 4)) { if((layer_index < 0) || (layer_index > 4)) {
pfi->is_valid=0; pfi->is_valid=0;
return -1; return -1;
} }
if((sample_index < 0) || (sample_index > 2)) { if((sample_index < 0) || (sample_index > 2)) {
pfi->is_valid=0; pfi->is_valid=0;
return -1; return -1;
} }
if(pfi->layer==1) pfi->samples_per_frame=384; if(pfi->layer==1) pfi->samples_per_frame=384;
if(pfi->layer==2) pfi->samples_per_frame=1152; if(pfi->layer==2) pfi->samples_per_frame=1152;
if(pfi->layer==3) { if(pfi->layer==3) {
if(pfi->version == 1.0) { if(pfi->version == 1.0) {
pfi->samples_per_frame=1152; pfi->samples_per_frame=1152;
} else { } else {
pfi->samples_per_frame=576; pfi->samples_per_frame=576;
} }
} }
bitrate_index=(frame[2] & 0xF0) >> 4; bitrate_index=(frame[2] & 0xF0) >> 4;
samplerate_index=(frame[2] & 0x0C) >> 2; samplerate_index=(frame[2] & 0x0C) >> 2;
if((bitrate_index == 0xF) || (bitrate_index==0x0)) { if((bitrate_index == 0xF) || (bitrate_index==0x0)) {
pfi->is_valid=0; pfi->is_valid=0;
return -1; return -1;
} }
if(samplerate_index == 3) { if(samplerate_index == 3) {
pfi->is_valid=0; pfi->is_valid=0;
return -1; return -1;
} }
pfi->bitrate = scan_br_table[layer_index][bitrate_index]; pfi->bitrate = scan_br_table[layer_index][bitrate_index];
pfi->samplerate = scan_sample_table[sample_index][samplerate_index]; pfi->samplerate = scan_sample_table[sample_index][samplerate_index];
if((frame[3] & 0xC0 >> 6) == 3) if((frame[3] & 0xC0 >> 6) == 3)
pfi->stereo = 0; pfi->stereo = 0;
else else
pfi->stereo = 1; pfi->stereo = 1;
if(frame[2] & 0x02) { /* Padding bit set */ if(frame[2] & 0x02) { /* Padding bit set */
pfi->padding=1; pfi->padding=1;
} else { } else {
pfi->padding=0; pfi->padding=0;
} }
if(pfi->version == 1.0) { if(pfi->version == 1.0) {
if(pfi->stereo) { if(pfi->stereo) {
pfi->xing_offset=32; pfi->xing_offset=32;
} else { } else {
pfi->xing_offset=17; pfi->xing_offset=17;
} }
} else { } else {
if(pfi->stereo) { if(pfi->stereo) {
pfi->xing_offset=17; pfi->xing_offset=17;
} else { } else {
pfi->xing_offset=9; pfi->xing_offset=9;
} }
} }
pfi->crc_protected=(frame[1] & 0xFE); pfi->crc_protected=(frame[1] & 0xFE);
if(pfi->layer == 1) { if(pfi->layer == 1) {
pfi->frame_length = (12 * pfi->bitrate * 1000 / pfi->samplerate + pfi->padding) * 4; pfi->frame_length = (12 * pfi->bitrate * 1000 / pfi->samplerate + pfi->padding) * 4;
} else { } else {
pfi->frame_length = 144 * pfi->bitrate * 1000 / pfi->samplerate + pfi->padding; pfi->frame_length = 144 * pfi->bitrate * 1000 / pfi->samplerate + pfi->padding;
} }
if((pfi->frame_length > 2880) || (pfi->frame_length <= 0)) { if((pfi->frame_length > 2880) || (pfi->frame_length <= 0)) {
pfi->is_valid=0; pfi->is_valid=0;
return -1; return -1;
} }
pfi->is_valid=1; pfi->is_valid=1;
@ -642,50 +642,50 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
/* now, find the first frame */ /* now, find the first frame */
fseek(infile,pos,SEEK_SET); fseek(infile,pos,SEEK_SET);
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) != sizeof(frame_buffer)) if(fread(frame_buffer,1,sizeof(frame_buffer),infile) != sizeof(frame_buffer))
return; return;
while(!found) { while(!found) {
while((frame_buffer[index] != 0xFF) && (index < (sizeof(frame_buffer)-4))) while((frame_buffer[index] != 0xFF) && (index < (sizeof(frame_buffer)-4)))
index++; index++;
if(index >= (sizeof(frame_buffer)-4)) { /* largest mp3 frame is 2880 bytes */ if(index >= (sizeof(frame_buffer)-4)) { /* largest mp3 frame is 2880 bytes */
DPRINTF(E_DBG,L_SCAN,"Could not find frame... quitting\n"); DPRINTF(E_DBG,L_SCAN,"Could not find frame... quitting\n");
return; return;
} }
if(!scan_mp3_decode_mp3_frame(&frame_buffer[index],&fi)) { if(!scan_mp3_decode_mp3_frame(&frame_buffer[index],&fi)) {
/* see if next frame is valid */ /* see if next frame is valid */
fseek(infile,pos + index + fi.frame_length,SEEK_SET); fseek(infile,pos + index + fi.frame_length,SEEK_SET);
if(fread(header,1,sizeof(header),infile) != sizeof(header)) { if(fread(header,1,sizeof(header),infile) != sizeof(header)) {
DPRINTF(E_DBG,L_SCAN,"Could not read frame header\n"); DPRINTF(E_DBG,L_SCAN,"Could not read frame header\n");
return; return;
} }
if(!scan_mp3_decode_mp3_frame(header,&fi)) if(!scan_mp3_decode_mp3_frame(header,&fi))
found=1; found=1;
} }
if(!found) if(!found)
index++; index++;
} }
pos += index; pos += index;
/* found first frame. Let's move */ /* found first frame. Let's move */
while(frame_count < 10) { while(frame_count < 10) {
fseek(infile,pos,SEEK_SET); fseek(infile,pos,SEEK_SET);
if(fread(header,1,sizeof(header),infile) != sizeof(header)) { if(fread(header,1,sizeof(header),infile) != sizeof(header)) {
DPRINTF(E_DBG,L_SCAN,"Could not read frame header\n"); DPRINTF(E_DBG,L_SCAN,"Could not read frame header\n");
return; return;
} }
if(scan_mp3_decode_mp3_frame(header,&fi)) { if(scan_mp3_decode_mp3_frame(header,&fi)) {
DPRINTF(E_DBG,L_SCAN,"Invalid frame header while averaging\n"); DPRINTF(E_DBG,L_SCAN,"Invalid frame header while averaging\n");
return; return;
} }
bitrate_total += fi.bitrate; bitrate_total += fi.bitrate;
frame_count++; frame_count++;
pos += fi.frame_length; pos += fi.frame_length;
} }
DPRINTF(E_DBG,L_SCAN,"Old bitrate: %d\n",pfi->bitrate); DPRINTF(E_DBG,L_SCAN,"Old bitrate: %d\n",pfi->bitrate);
@ -723,40 +723,40 @@ void scan_mp3_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi) {
pos=pfi->frame_offset; pos=pfi->frame_offset;
while(1) { while(1) {
err=1; err=1;
DPRINTF(E_SPAM,L_SCAN,"Seeking to %d\n",pos); DPRINTF(E_SPAM,L_SCAN,"Seeking to %d\n",pos);
fseek(infile,pos,SEEK_SET); fseek(infile,pos,SEEK_SET);
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) == sizeof(frame_buffer)) { if(fread(frame_buffer,1,sizeof(frame_buffer),infile) == sizeof(frame_buffer)) {
/* check for valid frame */ /* check for valid frame */
if(!scan_mp3_decode_mp3_frame(frame_buffer,&fi)) { if(!scan_mp3_decode_mp3_frame(frame_buffer,&fi)) {
frames++; frames++;
pos += fi.frame_length; pos += fi.frame_length;
err=0; err=0;
if((last_bitrate) && (fi.bitrate != last_bitrate)) if((last_bitrate) && (fi.bitrate != last_bitrate))
cbr=0; cbr=0;
last_bitrate=fi.bitrate; last_bitrate=fi.bitrate;
/* no point in brute scan of a cbr file... */ /* no point in brute scan of a cbr file... */
if(cbr && (frames > 100)) { if(cbr && (frames > 100)) {
DPRINTF(E_DBG,L_SCAN,"File appears to be CBR... quitting frame count\n"); DPRINTF(E_DBG,L_SCAN,"File appears to be CBR... quitting frame count\n");
return; return;
} }
} }
} }
if(err) { if(err) {
if(pos > (file_size - 4096)) { /* probably good enough */ if(pos > (file_size - 4096)) { /* probably good enough */
pfi->number_of_frames=frames; pfi->number_of_frames=frames;
DPRINTF(E_DBG,L_SCAN,"Estimated frame count: %d\n",frames); DPRINTF(E_DBG,L_SCAN,"Estimated frame count: %d\n",frames);
return; return;
} else { } else {
DPRINTF(E_DBG,L_SCAN,"Frame count aborted on error. Pos=%d, Count=%d\n", DPRINTF(E_DBG,L_SCAN,"Frame count aborted on error. Pos=%d, Count=%d\n",
pos, frames); pos, frames);
return; return;
} }
} }
} }
} }
@ -785,8 +785,8 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
char frame_buffer[4]; char frame_buffer[4];
if(!(infile=fopen(file,"rb"))) { if(!(infile=fopen(file,"rb"))) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",file); DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",file);
return FALSE; return FALSE;
} }
memset((void*)&fi,0x00,sizeof(fi)); memset((void*)&fi,0x00,sizeof(fi));
@ -798,13 +798,13 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
pmp3->file_size=file_size; pmp3->file_size=file_size;
if(fread(buffer,1,sizeof(buffer),infile) != sizeof(buffer)) { if(fread(buffer,1,sizeof(buffer),infile) != sizeof(buffer)) {
if(ferror(infile)) { if(ferror(infile)) {
DPRINTF(E_LOG,L_SCAN,"Error reading: %s\n",strerror(errno)); DPRINTF(E_LOG,L_SCAN,"Error reading: %s\n",strerror(errno));
} else { } else {
DPRINTF(E_LOG,L_SCAN,"Short file: %s\n",file); DPRINTF(E_LOG,L_SCAN,"Short file: %s\n",file);
} }
fclose(infile); fclose(infile);
return FALSE; return FALSE;
} }
pid3=(SCAN_ID3HEADER*)buffer; pid3=(SCAN_ID3HEADER*)buffer;
@ -813,13 +813,13 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
fp_size=0; fp_size=0;
if(strncmp((char*)pid3->id,"ID3",3)==0) { if(strncmp((char*)pid3->id,"ID3",3)==0) {
/* found an ID3 header... */ /* found an ID3 header... */
DPRINTF(E_DBG,L_SCAN,"Found ID3 header\n"); DPRINTF(E_DBG,L_SCAN,"Found ID3 header\n");
size = (pid3->size[0] << 21 | pid3->size[1] << 14 | size = (pid3->size[0] << 21 | pid3->size[1] << 14 |
pid3->size[2] << 7 | pid3->size[3]); pid3->size[2] << 7 | pid3->size[3]);
fp_size=size + sizeof(SCAN_ID3HEADER); fp_size=size + sizeof(SCAN_ID3HEADER);
first_check=1; first_check=1;
DPRINTF(E_DBG,L_SCAN,"Header length: %d\n",size); DPRINTF(E_DBG,L_SCAN,"Header length: %d\n",size);
} }
index = 0; index = 0;
@ -829,123 +829,127 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
*/ */
while(!found) { while(!found) {
fseek(infile,fp_size,SEEK_SET); fseek(infile,fp_size,SEEK_SET);
DPRINTF(E_DBG,L_SCAN,"Reading in new block at %d\n",(int)fp_size); DPRINTF(E_DBG,L_SCAN,"Reading in new block at %d\n",(int)fp_size);
if(fread(buffer,1,sizeof(buffer),infile) < sizeof(buffer)) { if(fread(buffer,1,sizeof(buffer),infile) < sizeof(buffer)) {
DPRINTF(E_LOG,L_SCAN,"Short read: %s\n",file); DPRINTF(E_LOG,L_SCAN,"Short read: %s\n",file);
fclose(infile); fclose(infile);
return TRUE; return TRUE;
} }
index=0; index=0;
while(!found) { while(!found) {
while((buffer[index] != 0xFF) && (index < (sizeof(buffer)-50))) while((buffer[index] != 0xFF) && (index < (sizeof(buffer)-50)))
index++; index++;
if((first_check) && (index)) { if((first_check) && (index)) {
fp_size=0; fp_size=0;
DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n"); DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n");
first_check=0; first_check=0;
break; break;
} }
if(index > sizeof(buffer) - 50) { if(index > sizeof(buffer) - 50) {
fp_size += index; fp_size += index;
DPRINTF(E_DBG,L_SCAN,"Block exhausted\n"); DPRINTF(E_DBG,L_SCAN,"Block exhausted\n");
break; break;
} }
if(!scan_mp3_decode_mp3_frame(&buffer[index],&fi)) { if(!scan_mp3_decode_mp3_frame(&buffer[index],&fi)) {
DPRINTF(E_DBG,L_SCAN,"valid header at %d\n",index); DPRINTF(E_DBG,L_SCAN,"valid header at %d\n",index);
if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) { if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) {
/* no need to check further... if there is a xing header there, /* no need to check further... if there is a xing header there,
* this is definately a valid frame */ * this is definately a valid frame */
found=1; found=1;
fp_size += index; fp_size += index;
} else { } else {
/* No Xing... check for next frame */ /* No Xing... check for next frame */
DPRINTF(E_DBG,L_SCAN,"Found valid frame at %04x\n",(int)fp_size+index); DPRINTF(E_DBG,L_SCAN,"Found valid frame at %04x\n",(int)fp_size+index);
DPRINTF(E_DBG,L_SCAN,"Checking at %04x\n",(int)fp_size+index+fi.frame_length); DPRINTF(E_DBG,L_SCAN,"Checking at %04x\n",(int)fp_size+index+fi.frame_length);
fseek(infile,fp_size + index + fi.frame_length,SEEK_SET); fseek(infile,fp_size + index + fi.frame_length,SEEK_SET);
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) == sizeof(frame_buffer)) { if(fread(frame_buffer,1,sizeof(frame_buffer),infile) == sizeof(frame_buffer)) {
if(!scan_mp3_decode_mp3_frame((u_char*)frame_buffer,&fi)) { if(!scan_mp3_decode_mp3_frame((u_char*)frame_buffer,&fi)) {
found=1; found=1;
fp_size += index; fp_size += index;
} }
} else { } else {
DPRINTF(E_LOG,L_SCAN,"Could not read frame header: %s\n",file); DPRINTF(E_LOG,L_SCAN,"Could not read frame header: %s\n",file);
fclose(infile); fclose(infile);
return 0; return 0;
} }
if(!found) { if(!found) {
DPRINTF(E_DBG,L_SCAN,"Didn't pan out.\n"); DPRINTF(E_DBG,L_SCAN,"Didn't pan out.\n");
} }
} }
} }
if(!found) { if(!found) {
index++; index++;
if (first_check) { if (first_check) {
/* if the header info was wrong about where the data started, /* if the header info was wrong about where the data started,
* then start a brute-force scan from the beginning of the file. * then start a brute-force scan from the beginning of the file.
* don't want to just scan forward, because we might have already * don't want to just scan forward, because we might have already
* missed the xing header * missed the xing header
*/ */
DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n"); DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n");
first_check=0; first_check=0;
fp_size=0; fp_size=0;
break; break;
} }
} }
} }
} }
file_size -= fp_size; file_size -= fp_size;
fi.frame_offset=fp_size; fi.frame_offset=fp_size;
if(scan_mp3_decode_mp3_frame(&buffer[index],&fi)) { if(scan_mp3_decode_mp3_frame(&buffer[index],&fi)) {
fclose(infile); fclose(infile);
DPRINTF(E_LOG,L_SCAN,"Could not find sync frame: %s\n",file); DPRINTF(E_LOG,L_SCAN,"Could not find sync frame: %s\n",file);
DPRINTF(E_LOG,L_SCAN,"If this is a valid mp3 file that plays in " DPRINTF(E_LOG,L_SCAN,"If this is a valid mp3 file that plays in "
"other applications, please email me at rpedde@users.sourceforge.net " "other applications, please email me at rpedde@users.sourceforge.net "
"and tell me you got this error. Thanks"); "and tell me you got this error. Thanks");
return TRUE; return TRUE;
} }
DPRINTF(E_DBG,L_SCAN," MPEG Version: %0.1g\n",fi.version); DPRINTF(E_DBG,L_SCAN," MPEG Version: %0.1g\n",fi.version);
DPRINTF(E_DBG,L_SCAN," Layer: %d\n",fi.layer); DPRINTF(E_DBG,L_SCAN," Layer: %d\n",fi.layer);
DPRINTF(E_DBG,L_SCAN," Sample Rate: %d\n",fi.samplerate); DPRINTF(E_DBG,L_SCAN," Sample Rate: %d\n",fi.samplerate);
DPRINTF(E_DBG,L_SCAN," Bit Rate: %d\n",fi.bitrate); DPRINTF(E_DBG,L_SCAN," Bit Rate: %d\n",fi.bitrate);
/* now check for an XING header */ /* now check for an XING header */
if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) { if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) {
DPRINTF(E_DBG,L_SCAN,"Found Xing header\n"); DPRINTF(E_DBG,L_SCAN,"Found Xing header\n");
xing_flags=*((int*)&buffer[index+fi.xing_offset+4+4]); xing_flags = buffer[index+fi.xing_offset+4+4] << 24 |
xing_flags=ntohs(xing_flags); buffer[index+fi.xing_offset+4+5] << 16 |
buffer[index+fi.xing_offset+4+6] << 8 |
buffer[index+fi.xing_offset+4+7];
DPRINTF(E_DBG,L_SCAN,"Xing Flags: %02X\n",xing_flags);
DPRINTF(E_DBG,L_SCAN,"Xing Flags: %02X\n",xing_flags); if(xing_flags & 0x1) {
/* Frames field is valid... */
if(xing_flags & 0x1) { fi.number_of_frames=buffer[index+fi.xing_offset+4+8] << 24 |
/* Frames field is valid... */ buffer[index+fi.xing_offset+4+9] << 16 |
fi.number_of_frames=*((int*)&buffer[index+fi.xing_offset+4+8]); buffer[index+fi.xing_offset+4+10] << 8 |
fi.number_of_frames=ntohs(fi.number_of_frames); buffer[index+fi.xing_offset+4+11];
} }
} }
if((config.scan_type != 0) && if((config.scan_type != 0) &&
(fi.number_of_frames == 0) && (fi.number_of_frames == 0) &&
(!pmp3->song_length)) { (!pmp3->song_length)) {
/* We have no good estimate of song time, and we want more /* We have no good estimate of song time, and we want more
* aggressive scanning */ * aggressive scanning */
DPRINTF(E_DBG,L_SCAN,"Starting aggressive file length scan\n"); DPRINTF(E_DBG,L_SCAN,"Starting aggressive file length scan\n");
if(config.scan_type == 1) { if(config.scan_type == 1) {
/* get average bitrate */ /* get average bitrate */
scan_mp3_get_average_bitrate(infile, &fi); scan_mp3_get_average_bitrate(infile, &fi);
} else { } else {
/* get full frame count */ /* get full frame count */
scan_mp3_get_frame_count(infile, &fi); scan_mp3_get_frame_count(infile, &fi);
} }
} }
pmp3->bitrate=fi.bitrate; pmp3->bitrate=fi.bitrate;
@ -953,16 +957,16 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
/* guesstimate the file length */ /* guesstimate the file length */
if(!pmp3->song_length) { /* could have gotten it from the tag */ if(!pmp3->song_length) { /* could have gotten it from the tag */
/* DWB: use ms time instead of seconds, use doubles to /* DWB: use ms time instead of seconds, use doubles to
avoid overflow */ avoid overflow */
if(!fi.number_of_frames) { /* not vbr */ if(!fi.number_of_frames) { /* not vbr */
pmp3->song_length = (int) ((double) file_size * 8. / pmp3->song_length = (int) ((double) file_size * 8. /
(double) fi.bitrate); (double) fi.bitrate);
} else { } else {
pmp3->song_length = (int) ((double)(fi.number_of_frames*fi.samples_per_frame*1000.)/ pmp3->song_length = (int) ((double)(fi.number_of_frames*fi.samples_per_frame*1000.)/
(double) fi.samplerate); (double) fi.samplerate);
} }
} }
DPRINTF(E_DBG,L_SCAN," Song Length: %d\n",pmp3->song_length); DPRINTF(E_DBG,L_SCAN," Song Length: %d\n",pmp3->song_length);