mirror of
				https://github.com/owntone/owntone-server.git
				synced 2025-10-29 15:55:02 -04:00 
			
		
		
		
	add scan_type directive for doing brute-force scans
This commit is contained in:
		
							parent
							
								
									7f1ca1f552
								
							
						
					
					
						commit
						27d8a51309
					
				| @ -64,6 +64,10 @@ take effect. | ||||
| 	  <TD>Rescan Interval</TD> | ||||
| 	  <TD><INPUT TYPE="TEXT" @READONLY@ NAME="RESCAN_INTERVAL" VALUE="@RESCAN_INTERVAL@"></TD> | ||||
| 	</TR> | ||||
| 	<TR> | ||||
| 	  <TD>Scan Type</TD> | ||||
| 	  <TD><INPUT TYPE="TEXT" @READONLY@ NAME="SCAN_TYPE" VALUE="@SCAN_TYPE@"></TD> | ||||
| 	</TR> | ||||
| 	<TR> | ||||
| 	  <TD>Always Scan</TD> | ||||
| 	  <TD><INPUT TYPE="TEXT" @READONLY@ NAME="ALWAYS_SCAN" VALUE="@ALWAYS_SCAN@"></TD> | ||||
|  | ||||
| @ -2,6 +2,14 @@ | ||||
| # | ||||
| # This is the mt-daapd config file. | ||||
| # | ||||
| # If you have problems or questions with the format of this file, | ||||
| # direct your questions to rpedde@users.sourceforge.net. | ||||
| # | ||||
| # You can also check the website at http://mt-daapd.sourceforge.net, | ||||
| # as there is a growing documentation library there, peer-supported | ||||
| # forums and possibly more. | ||||
| # | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # web_root (required) | ||||
| @ -39,7 +47,9 @@ db_dir		/var/cache/mt-daapd | ||||
| # | ||||
| # mp3_dir (required) | ||||
| # | ||||
| # Location of the mp3 files to share | ||||
| # Location of the mp3 files to share.  Note that because the | ||||
| # files are stored in the database by inode, these must be | ||||
| # in the same physical filesystem. | ||||
| # | ||||
| 
 | ||||
| mp3_dir		/mnt/mp3 | ||||
| @ -49,7 +59,7 @@ mp3_dir		/mnt/mp3 | ||||
| # | ||||
| # This is both the name of the server as advertised | ||||
| # via rendezvous, and the name of the database | ||||
| # exported via DAAP | ||||
| # exported via DAAP.  Also know as "What shows up in iTunes". | ||||
| # | ||||
| 
 | ||||
| servername	mt-daapd | ||||
| @ -74,9 +84,9 @@ runas	nobody | ||||
| # See the mt-daapd.playlist file in the  | ||||
| # contrib directory for syntax and examples | ||||
| # | ||||
| # Note that static playlists will still | ||||
| # show up, even if this directive is not | ||||
| # specified | ||||
| # This doesn't control static playlists... these | ||||
| # are controlled with the "process_m3u" directive | ||||
| # below. | ||||
| # | ||||
| 
 | ||||
| playlist	/etc/mt-daapd.playlist | ||||
| @ -101,6 +111,7 @@ playlist	/etc/mt-daapd.playlist | ||||
| # play them.  Perhaps this would be useful on Linux with | ||||
| # Rhythmbox, once it understands daap.  (hurry up!) | ||||
| # | ||||
| # | ||||
| 
 | ||||
| extensions .mp3,.m4a,.m4p | ||||
| 
 | ||||
| @ -145,28 +156,66 @@ extensions .mp3,.m4a,.m4p | ||||
| # If background rescanning is disabled, a scan can still be forced from the  | ||||
| # "status" page of the administrative web interface | ||||
| # | ||||
| # Note that right now this is considered EXPERIMENTAL! | ||||
| # | ||||
| # Setting a rescan_interval lower than the time it takes to rescan | ||||
| # won't hurt anything, it will just waste CPU, and make connect times | ||||
| # to the daap server longer. | ||||
| # | ||||
| # There may be memory leaks here.  If you see evidence of leaks, please | ||||
| # let me (rpedde@users.sourceforge.net) know. | ||||
| # | ||||
| 
 | ||||
| #rescan_interval 300 | ||||
| 
 | ||||
| # always_scan  | ||||
| # | ||||
| # always_scan | ||||
| # | ||||
| # The default behavior is not not do background rescans of the filesystem | ||||
| # unless there are clients connected.  The thought is to allow the drives | ||||
| # to spin down unless they are in use.  This might be of more importance | ||||
| # in IDE drives that aren't designed to be run 24x7. | ||||
| # | ||||
| # Forcing a scan will always work though, even if no users are connected. | ||||
| # | ||||
| # | ||||
| # The default behavior is not not do background rescans of the | ||||
| # filesystem unless there are clients connected.  The thought is to | ||||
| # allow the drives to spin down unless they are in use.  This might be | ||||
| # of more importance in IDE drives that aren't designed to be run | ||||
| # 24x7.  Forcing a scan through the web interface will always work | ||||
| # though, even if no users are connected. | ||||
| 
 | ||||
| # always_scan 0 | ||||
| 
 | ||||
| #  | ||||
| # process_m3u | ||||
| # | ||||
| # By default m3u processing is turned off, since most m3u files | ||||
| # sitting around in peoples mp3 directories have bad paths, and | ||||
| # I hear about it.  :) | ||||
| # | ||||
| # If you are sure your m3u files have good paths (i.e. unixly pathed, | ||||
| # with relative paths relative to the directory the m3u is in), then | ||||
| # you can turn on m3u processing by setting this directive to 1. | ||||
| # | ||||
| # I'm not sure "unixly" is a word, but you get the idea. | ||||
| # | ||||
| 
 | ||||
| # process_m3u 0 | ||||
| 
 | ||||
| # | ||||
| # scan_type | ||||
| #  | ||||
| # | ||||
| # This sets how aggressively mp3 files should be scanned to determine | ||||
| # file length.  There are three values: | ||||
| # | ||||
| # 0 (Normal)  | ||||
| #   Just scan the first mp3 frame to try and calculate size.  This will | ||||
| #   be accurate for most files, but VBR files without an Xing tag will | ||||
| #   probably have wildly inaccurate file times | ||||
| # | ||||
| # 1 (Aggressive) | ||||
| #   This checks the bitrates of 10 frames in the middle of the song.   | ||||
| #   This will still be inaccurate for VBR files without an Xing tag, | ||||
| #   but they probably won't be quite as inaccurate as 0.  This takes | ||||
| #   more time, obviously, although the time hit will only happen the | ||||
| #   first time you scan a particular file. | ||||
| # | ||||
| # 2 (Painfully aggressive) | ||||
| #   This walks through the entire song, counting the number of frames. | ||||
| #   This should result in accurate song times, but will take the most | ||||
| #   time.  Again, this will only have to be incurred the first time | ||||
| #   the file is indexed. | ||||
| #  | ||||
| 
 | ||||
| # scan_type  0 | ||||
| 
 | ||||
|  | ||||
| @ -109,6 +109,7 @@ CONFIGELEMENT config_elements[] = { | ||||
|     { 1,0,0,CONFIG_TYPE_INT,"rescan_interval",(void*)&config.rescan_interval,config_emit_int }, | ||||
|     { 1,0,0,CONFIG_TYPE_INT,"always_scan",(void*)&config.always_scan,config_emit_int }, | ||||
|     { 1,0,0,CONFIG_TYPE_INT,"process_m3u",(void*)&config.process_m3u,config_emit_int }, | ||||
|     { 1,0,0,CONFIG_TYPE_INT,"scan_type",(void*)&config.scan_type,config_emit_int }, | ||||
|     { 1,0,0,CONFIG_TYPE_STRING,"playlist",(void*)&config.playlist,config_emit_string }, | ||||
|     { 1,0,0,CONFIG_TYPE_STRING,"extensions",(void*)&config.extensions,config_emit_string }, | ||||
|     { 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, config_emit_string }, | ||||
| @ -173,7 +174,12 @@ int config_read(char *file) { | ||||
| 	return -1; | ||||
|     } | ||||
| 
 | ||||
| #ifdef NSLU2     | ||||
|     config.always_scan=0; | ||||
| #else | ||||
|     config.always_scan=1; | ||||
| #endif | ||||
| 
 | ||||
|     config.configfile=strdup(file); | ||||
|     config.web_root=NULL; | ||||
|     config.adminpassword=NULL; | ||||
| @ -185,6 +191,7 @@ int config_read(char *file) { | ||||
|     config.logfile=NULL; | ||||
|     config.rescan_interval=0; | ||||
|     config.process_m3u=0; | ||||
|     config.scan_type=0; | ||||
| 
 | ||||
|     /* DWB: use alloced space so it can be freed without errors */ | ||||
|     config.extensions=strdup(".mp3"); | ||||
| @ -336,7 +343,9 @@ int config_write(WS_CONNINFO *pwsc) { | ||||
|     fprintf(configfile,"extensions\t%s\n",ws_getvar(pwsc,"extensions")); | ||||
|     fprintf(configfile,"db_dir\t\t%s\n",ws_getvar(pwsc,"db_dir")); | ||||
|     fprintf(configfile,"rescan_interval\t%s\n",ws_getvar(pwsc,"rescan_interval")); | ||||
|     fprintf(configfile,"always_scan\t%s\n",ws_getvar(pwsc,"always_scan")); | ||||
|     fprintf(configfile,"scan_type\t%d\n",ws_getvar(pwsc,"scan_type")); | ||||
|     if(ws_getvar(pwsc,"always_scan") && strlen(ws_getvar(pwsc,"always_scan"))) | ||||
| 	fprintf(configfile,"always_scan\t%s\n",ws_getvar(pwsc,"always_scan")); | ||||
|     if(ws_getvar(pwsc,"art_filename") && strlen(ws_getvar(pwsc,"art_filename"))) | ||||
| 	fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename")); | ||||
|     if(ws_getvar(pwsc,"logfile") && strlen(ws_getvar(pwsc,"logfile"))) | ||||
|  | ||||
| @ -53,6 +53,7 @@ typedef struct tag_config { | ||||
|     int rescan_interval;  /**< How often to do a background rescan of the file system */ | ||||
|     int always_scan;      /**< 0 to minimize disk usage (embedded devices) */ | ||||
|     int process_m3u;      /**< Should we process m3u files? */ | ||||
|     int scan_type;        /**< How hard to search mp3 files. see scan_get_mp3fileinfo() */ | ||||
|     char *adminpassword;  /**< Password to web management pages */ | ||||
|     char *readpassword;   /**< iTunes password */ | ||||
|     char *mp3dir;         /**< root directory of the mp3 files */ | ||||
|  | ||||
| @ -59,21 +59,27 @@ | ||||
|  * Typedefs | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * Struct to keep info about the information gleaned from | ||||
|  * the mp3 frame header. | ||||
|  */ | ||||
| typedef struct tag_scan_frameinfo { | ||||
|     int layer; | ||||
|     int bitrate; | ||||
|     int samplerate; | ||||
|     int stereo; | ||||
|     int layer;               /**< 1, 2, or 3, representing Layer I, II, and III */ | ||||
|     int bitrate;             /**< Bitrate in kbps (128, 64, etc) */ | ||||
|     int samplerate;          /**< Samplerate (e.g. 44100) */ | ||||
|     int stereo;              /**< Any kind of stereo.. joint, dual mono, etc */ | ||||
| 
 | ||||
|     int frame_length; | ||||
|     int crc_protected; | ||||
|     int samples_per_frame; | ||||
|     int padding; | ||||
|     int xing_offset; | ||||
|     int frame_length;        /**< Frame length in bytes - calculated */ | ||||
|     int crc_protected;       /**< Is the frame crc protected? */ | ||||
|     int samples_per_frame;   /**< Samples per frame - calculated field */ | ||||
|     int padding;             /**< Whether or not there is a padding sample */ | ||||
|     int xing_offset;         /**< Where the xing header should be relative to end of hdr */ | ||||
|     int number_of_frames;    /**< Number of frames in the song */ | ||||
| 
 | ||||
|     double version; | ||||
|     int frame_offset;        /**< Where this frame was found */ | ||||
| 
 | ||||
|     double version;          /**< MPEG version (e.g. 2.0, 2.5, 1.0) */ | ||||
| 
 | ||||
|     int is_cbr; | ||||
|     int is_valid; | ||||
| } SCAN_FRAMEINFO; | ||||
| 
 | ||||
| @ -266,22 +272,28 @@ char *scan_winamp_genre[] = { | ||||
| /*
 | ||||
|  * Forwards | ||||
|  */ | ||||
| int scan_path(char *path); | ||||
| int scan_gettags(char *file, MP3FILE *pmp3); | ||||
| int scan_get_mp3tags(char *file, MP3FILE *pmp3); | ||||
| int scan_get_aactags(char *file, MP3FILE *pmp3); | ||||
| int scan_get_nultags(char *file, MP3FILE *pmp3) { return 0; }; | ||||
| int scan_get_fileinfo(char *file, MP3FILE *pmp3); | ||||
| int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3); | ||||
| int scan_get_aacfileinfo(char *file, MP3FILE *pmp3); | ||||
| int scan_get_nulfileinfo(char *file, MP3FILE *pmp3) { return 0; }; | ||||
| int scan_get_urlfileinfo(char *file, MP3FILE *pmp3); | ||||
| static int scan_path(char *path); | ||||
| static int scan_gettags(char *file, MP3FILE *pmp3); | ||||
| static int scan_get_mp3tags(char *file, MP3FILE *pmp3); | ||||
| static int scan_get_aactags(char *file, MP3FILE *pmp3); | ||||
| static int scan_get_nultags(char *file, MP3FILE *pmp3) { return 0; }; | ||||
| static int scan_get_fileinfo(char *file, MP3FILE *pmp3); | ||||
| static int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3); | ||||
| static int scan_get_aacfileinfo(char *file, MP3FILE *pmp3); | ||||
| static int scan_get_nulfileinfo(char *file, MP3FILE *pmp3) { return 0; }; | ||||
| static int scan_get_urlfileinfo(char *file, MP3FILE *pmp3); | ||||
| 
 | ||||
| int scan_freetags(MP3FILE *pmp3); | ||||
| void scan_static_playlist(char *path, struct dirent *pde, struct stat *psb); | ||||
| void scan_music_file(char *path, struct dirent *pde, struct stat *psb); | ||||
| static int scan_freetags(MP3FILE *pmp3); | ||||
| static void scan_static_playlist(char *path, struct dirent *pde, struct stat *psb); | ||||
| static void scan_music_file(char *path, struct dirent *pde, struct stat *psb); | ||||
| 
 | ||||
| void make_composite_tags(MP3FILE *pmp3); | ||||
| static int scan_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi); | ||||
| static time_t mac_to_unix_time(int t); | ||||
| 
 | ||||
| 
 | ||||
| /* 
 | ||||
|  * Typedefs | ||||
|  */ | ||||
| 
 | ||||
| typedef struct { | ||||
|     char*	suffix; | ||||
| @ -299,6 +311,12 @@ static taghandler taghandlers[] = { | ||||
|     { NULL, 0 } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Convert mac time to unix time (different epochs) | ||||
|  * | ||||
|  * param t time since mac epoch | ||||
|  */ | ||||
| time_t mac_to_unix_time(int t) { | ||||
|   struct timeval        tv; | ||||
|   struct timezone       tz; | ||||
| @ -1233,7 +1251,7 @@ int scan_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) { | ||||
|     bitrate_index=(frame[2] & 0xF0) >> 4; | ||||
|     samplerate_index=(frame[2] & 0x0C) >> 2; | ||||
| 
 | ||||
|     if(bitrate_index == 0xF) { | ||||
|     if((bitrate_index == 0xF) || (bitrate_index==0x0)) { | ||||
| 	pfi->is_valid=0; | ||||
| 	return -1; | ||||
|     } | ||||
| @ -1279,16 +1297,169 @@ int scan_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) { | ||||
| 	pfi->frame_length = 144 * pfi->bitrate * 1000 / pfi->samplerate + pfi->padding; | ||||
|     } | ||||
| 
 | ||||
|     if((pfi->frame_length > 2880) || (pfi->frame_length <= 0)) { | ||||
| 	pfi->is_valid=0; | ||||
| 	return -1; | ||||
|     } | ||||
| 
 | ||||
|     pfi->is_valid=1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * scan_get_mp3fileinfo | ||||
| /**
 | ||||
|  * Scan 10 frames from the middle of the file and determine an | ||||
|  * average bitrate from that.  It might not be as accurate as a full | ||||
|  * frame count, but it's probably Close Enough (tm) | ||||
|  * | ||||
|  * @param infile file to scan for average bitrate | ||||
|  * @param pfi pointer to frame info struct to put the bitrate into | ||||
|  */ | ||||
| void scan_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) { | ||||
|     fpos_t file_size; | ||||
|     unsigned char frame_buffer[2900]; | ||||
|     unsigned char header[4]; | ||||
|     int index=0; | ||||
|     int found=0; | ||||
|     fpos_t pos; | ||||
|     SCAN_FRAMEINFO fi; | ||||
|     int frame_count=0; | ||||
|     int bitrate_total=0; | ||||
| 
 | ||||
|     DPRINTF(E_DBG,L_SCAN,"Starting averaging bitrate\n"); | ||||
| 
 | ||||
|     fseek(infile,0,SEEK_END); | ||||
|     file_size=ftell(infile); | ||||
| 
 | ||||
|     pos=file_size/2; | ||||
| 
 | ||||
|     /* now, find the first frame */ | ||||
|     fseek(infile,pos,SEEK_SET); | ||||
|     if(fread(frame_buffer,1,sizeof(frame_buffer),infile) != sizeof(frame_buffer))  | ||||
| 	return; | ||||
| 
 | ||||
|     while(!found) { | ||||
| 	while((frame_buffer[index] != 0xFF) && (index < (sizeof(frame_buffer)-4))) | ||||
| 	    index++; | ||||
| 
 | ||||
| 	if(index >= (sizeof(frame_buffer)-4)) { /* largest mp3 frame is 2880 bytes */ | ||||
| 	    DPRINTF(E_DBG,L_SCAN,"Could not find frame... quitting\n"); | ||||
| 	    return; | ||||
| 	} | ||||
| 	     | ||||
| 	if(!scan_decode_mp3_frame(&frame_buffer[index],&fi)) {  | ||||
| 	    /* see if next frame is valid */ | ||||
| 	    fseek(infile,pos + index + fi.frame_length,SEEK_SET); | ||||
| 	    if(fread(header,1,sizeof(header),infile) != sizeof(header)) { | ||||
| 		DPRINTF(E_DBG,L_SCAN,"Could not read frame header\n"); | ||||
| 		return; | ||||
| 	    } | ||||
| 
 | ||||
| 	    if(!scan_decode_mp3_frame(header,&fi)) | ||||
| 		found=1; | ||||
| 	} | ||||
| 	 | ||||
| 	if(!found) | ||||
| 	    index++; | ||||
|     } | ||||
| 
 | ||||
|     pos += index; | ||||
| 
 | ||||
|     /* found first frame.  Let's move */ | ||||
|     while(frame_count < 10) { | ||||
| 	fseek(infile,pos,SEEK_SET); | ||||
| 	if(fread(header,1,sizeof(header),infile) != sizeof(header)) { | ||||
| 	    DPRINTF(E_DBG,L_SCAN,"Could not read frame header\n"); | ||||
| 	    return; | ||||
| 	} | ||||
| 	if(scan_decode_mp3_frame(header,&fi)) { | ||||
| 	    DPRINTF(E_DBG,L_SCAN,"Invalid frame header while averaging\n"); | ||||
| 	    return; | ||||
| 	} | ||||
|      | ||||
| 	bitrate_total += fi.bitrate; | ||||
| 	frame_count++; | ||||
| 	pos += fi.frame_length; | ||||
|     } | ||||
| 
 | ||||
|     DPRINTF(E_DBG,L_SCAN,"Old bitrate: %d\n",pfi->bitrate); | ||||
|     pfi->bitrate = bitrate_total/frame_count; | ||||
|     DPRINTF(E_DBG,L_SCAN,"New bitrate: %d\n",pfi->bitrate); | ||||
| 
 | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * do a full frame-by-frame scan of the file, counting frames | ||||
|  * as we go to try and get a more accurate song length estimate. | ||||
|  * If the song turns out to be CBR, then we'll not set the frame | ||||
|  * length.  Instead we'll use the file size estimate, since it is  | ||||
|  * more consistent with iTunes. | ||||
|  * | ||||
|  * @param infile file to scan for frame count | ||||
|  * @param pfi pointer to frame info struct to put framecount into | ||||
|  */ | ||||
| void scan_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi) { | ||||
|     int pos; | ||||
|     int frames=0; | ||||
|     unsigned char frame_buffer[4]; | ||||
|     SCAN_FRAMEINFO fi; | ||||
|     fpos_t file_size; | ||||
|     int err=0; | ||||
|     int cbr=1; | ||||
|     int last_bitrate=0; | ||||
| 
 | ||||
|     DPRINTF(E_DBG,L_SCAN,"Starting frame count\n"); | ||||
|      | ||||
|     fseek(infile,0,SEEK_END); | ||||
|     file_size=ftell(infile); | ||||
| 
 | ||||
|     pos=pfi->frame_offset; | ||||
| 
 | ||||
|     while(1) { | ||||
| 	err=1; | ||||
| 	DPRINTF(E_DBG,L_SCAN,"Seeking to %d\n",pos); | ||||
| 
 | ||||
| 	fseek(infile,pos,SEEK_SET); | ||||
| 	if(fread(frame_buffer,1,sizeof(frame_buffer),infile) == sizeof(frame_buffer)) { | ||||
| 	    /* check for valid frame */ | ||||
| 	    if(!scan_decode_mp3_frame(frame_buffer,&fi)) { | ||||
| 		frames++; | ||||
| 		pos += fi.frame_length; | ||||
| 		err=0; | ||||
| 
 | ||||
| 		if((last_bitrate) && (fi.bitrate != last_bitrate)) | ||||
| 		    cbr=0; | ||||
| 		last_bitrate=fi.bitrate; | ||||
| 
 | ||||
| 		/* no point in brute scan of a cbr file... */ | ||||
| 		if(cbr && (frames > 100)) { | ||||
| 		    DPRINTF(E_DBG,L_SCAN,"File appears to be CBR... quitting frame count\n"); | ||||
| 		    return; | ||||
| 		} | ||||
| 	    } | ||||
| 	} | ||||
| 
 | ||||
| 	if(err) { | ||||
| 	    if(pos > (file_size - 4096)) {  /* probably good enough */ | ||||
| 		pfi->number_of_frames=frames; | ||||
| 		DPRINTF(E_DBG,L_SCAN,"Estimated frame count: %d\n",frames); | ||||
| 		return; | ||||
| 	    } else { | ||||
| 		DPRINTF(E_DBG,L_SCAN,"Frame count aborted on error.  Pos=%d, Count=%d\n", | ||||
| 			pos, frames); | ||||
| 		return; | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Get information from the file headers itself -- like | ||||
|  * song length, bit rate, etc. | ||||
|  * | ||||
|  * @param file File to get info for | ||||
|  * @param pmp3 where to put the found information | ||||
|  */ | ||||
| int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) { | ||||
|     FILE *infile; | ||||
| @ -1300,15 +1471,19 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) { | ||||
|     unsigned char buffer[1024]; | ||||
|     int index; | ||||
| 
 | ||||
|     int number_of_frames=0; | ||||
|     int xing_flags; | ||||
|     int found; | ||||
| 
 | ||||
|     int first_check; | ||||
|     char frame_buffer[4]; | ||||
| 
 | ||||
|     if(!(infile=fopen(file,"rb"))) { | ||||
| 	DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",file); | ||||
| 	return -1; | ||||
|     } | ||||
| 
 | ||||
|     memset((void*)&fi,0x00,sizeof(fi)); | ||||
| 
 | ||||
|     fseek(infile,0,SEEK_END); | ||||
|     file_size=ftell(infile); | ||||
|     fseek(infile,0,SEEK_SET); | ||||
| @ -1326,88 +1501,106 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) { | ||||
|     } | ||||
| 
 | ||||
|     pid3=(SCAN_ID3HEADER*)buffer; | ||||
|      | ||||
| 
 | ||||
|     found=0; | ||||
|     fp_size=0; | ||||
| 
 | ||||
|     if(strncmp(pid3->id,"ID3",3)==0) { | ||||
| 	/* found an ID3 header... */ | ||||
| 	DPRINTF(E_DBG,L_SCAN,"Found ID3 header\n"); | ||||
| 	size = (pid3->size[0] << 21 | pid3->size[1] << 14 |  | ||||
| 		pid3->size[2] << 7 | pid3->size[3]); | ||||
| 	fp_size=size + sizeof(SCAN_ID3HEADER); | ||||
| 	first_check=1; | ||||
| 	DPRINTF(E_DBG,L_SCAN,"Header length: %d\n",size); | ||||
|     } | ||||
| 
 | ||||
|     fseek(infile,fp_size,SEEK_SET); | ||||
|     if(fread(buffer,1,sizeof(buffer),infile) < sizeof(buffer)) { | ||||
| 	DPRINTF(E_LOG,L_SCAN,"Short file: %s\n",file); | ||||
| 	fclose(infile); | ||||
| 	return -1; | ||||
|     } | ||||
|     index = 0; | ||||
| 
 | ||||
|     index=0; | ||||
|     found=0; | ||||
|      | ||||
|     if(scan_decode_mp3_frame(&buffer[index],&fi)) { /* Bad frame header */ | ||||
| 	DPRINTF(E_DBG,L_SCAN,"Starting brute-force search for frame header\n"); | ||||
|     /* Here we start the brute-force header seeking.  Sure wish there
 | ||||
|      * weren't so many crappy mp3 files out there | ||||
|      */ | ||||
| 
 | ||||
| 	fp_size=0; | ||||
|     while(!found) { | ||||
| 	fseek(infile,fp_size,SEEK_SET); | ||||
| 	DPRINTF(E_DBG,L_SCAN,"Reading in new block at %d\n",(int)fp_size); | ||||
| 	if(fread(buffer,1,sizeof(buffer),infile) < sizeof(buffer)) { | ||||
| 	    DPRINTF(E_LOG,L_SCAN,"Short read: %s\n",file); | ||||
| 	    fclose(infile); | ||||
| 	    break; | ||||
| 	} | ||||
| 
 | ||||
| 	index=0; | ||||
| 	while(!found) { | ||||
| 	    DPRINTF(E_DBG,L_SCAN,"Seeking to %d\n",(int)fp_size); | ||||
| 	    fseek(infile,fp_size,SEEK_SET); | ||||
| 	    while((buffer[index] != 0xFF) && (index < (sizeof(buffer)-50))) | ||||
| 		index++; | ||||
| 
 | ||||
| 	    if(fread(buffer,1,sizeof(buffer),infile) < sizeof(buffer)) { | ||||
| 		DPRINTF(E_LOG,L_SCAN,"Can't find valid MP3 frame for %s\n",file); | ||||
| 		fclose(infile); | ||||
| 		return -1; | ||||
| 	    if((first_check) && (index)) { | ||||
| 		fp_size=0; | ||||
| 		DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n"); | ||||
| 		first_check=0; | ||||
| 		break; | ||||
| 	    } | ||||
| 
 | ||||
| 	    /* look for header */ | ||||
| 	    index=0; | ||||
| 	    while(!found) { | ||||
| 		if(index > sizeof(buffer)-50) { | ||||
| 		    fp_size += index; | ||||
| 		    DPRINTF(E_DBG,L_SCAN,"Block exhausted\n"); | ||||
| 		    break;  /* read in the next block */ | ||||
| 		} | ||||
| 	    if(index > sizeof(buffer) - 50) { | ||||
| 		fp_size += index; | ||||
| 		DPRINTF(E_DBG,L_SCAN,"Block exhausted\n"); | ||||
| 		break; | ||||
| 	    } | ||||
| 
 | ||||
| 		if(!scan_decode_mp3_frame(&buffer[index],&fi)) { | ||||
| 		    DPRINTF(E_DBG,L_SCAN,"valid header at %d\n",index); | ||||
| 		    if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) { | ||||
| 			found=1; | ||||
| 		    } | ||||
| 
 | ||||
| 		    /* should check next frame... */ | ||||
| 	    if(!scan_decode_mp3_frame(&buffer[index],&fi)) { | ||||
| 		DPRINTF(E_DBG,L_SCAN,"valid header at %d\n",index); | ||||
| 		if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) { | ||||
| 		    /* no need to check further... if there is a xing header there,
 | ||||
| 		     * this is definately a valid frame */ | ||||
| 		    found=1; | ||||
| 		    fp_size += index; | ||||
| 		} else { | ||||
| 		    /* 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,"Checking at %04x\n",(int)fp_size+index+fi.frame_length); | ||||
| 		    fseek(infile,fp_size + index + fi.frame_length,SEEK_SET); | ||||
| 		    if(fread(frame_buffer,1,sizeof(frame_buffer),infile) == sizeof(frame_buffer)) { | ||||
| 			if(!scan_decode_mp3_frame(frame_buffer,&fi)) { | ||||
| 			    found=1; | ||||
| 			    fp_size += index; | ||||
| 			}  | ||||
| 		    } | ||||
| 		    if(!found) { | ||||
| 			DPRINTF(E_DBG,L_SCAN,"Didn't pan out.\n"); | ||||
| 		    } | ||||
| 		} | ||||
| 	    } | ||||
| 	     | ||||
| 	    if(!found) { | ||||
| 		index++; | ||||
| 		if (first_check) { | ||||
| 		    /* if the header info was wrong about where the data started,
 | ||||
| 		     * then start a brute-force scan from the beginning of the file. | ||||
| 		     * don't want to just scan forward, because we might have already  | ||||
| 		     * missed the xing header | ||||
| 		     */ | ||||
| 		    DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n"); | ||||
| 		    first_check=0; | ||||
| 		    fp_size=0; | ||||
| 		    break; | ||||
| 		} | ||||
| 
 | ||||
| 		if(!found) | ||||
| 		    index++; | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|     file_size -= fp_size; | ||||
| 
 | ||||
|     /*
 | ||||
| 
 | ||||
|     while(((buffer[index] != 0xFF) || (buffer[index+1] < 224)) && | ||||
| 	  (index < (sizeof(buffer)-(10 + 18 + 32)))) { | ||||
| 	index++; | ||||
|     } | ||||
| 
 | ||||
|     if(index) { | ||||
| 	DPRINTF(E_DBG,L_SCAN,"Scanned forward %d bytes to find frame header\n",index); | ||||
|     } | ||||
|     */ | ||||
|     fi.frame_offset=fp_size; | ||||
| 
 | ||||
|     if(scan_decode_mp3_frame(&buffer[index],&fi)) { | ||||
| 	fclose(infile); | ||||
| 	DPRINTF(E_DBG,L_SCAN,"Could not find sync frame\n"); | ||||
| 	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 " | ||||
| 		"other applications, please email me at rpedde@users.sourceforge.net " | ||||
| 		"and tell me you got this error.  Thanks"); | ||||
| 	return 0; | ||||
|     } | ||||
| 
 | ||||
|     pmp3->bitrate=fi.bitrate; | ||||
|     pmp3->samplerate=fi.samplerate; | ||||
|      | ||||
|     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," Sample Rate: %d\n",fi.samplerate); | ||||
| @ -1423,21 +1616,39 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) { | ||||
| 
 | ||||
| 	if(xing_flags & 0x1) { | ||||
| 	    /* Frames field is valid... */ | ||||
| 	    number_of_frames=*((int*)&buffer[index+fi.xing_offset+4+8]); | ||||
| 	    number_of_frames=ntohs(number_of_frames); | ||||
| 	    fi.number_of_frames=*((int*)&buffer[index+fi.xing_offset+4+8]); | ||||
| 	    fi.number_of_frames=ntohs(fi.number_of_frames); | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|     if((config.scan_type != 0) && | ||||
|        (fi.number_of_frames == 0) && | ||||
|        (!pmp3->song_length)) { | ||||
| 	/* We have no good estimate of song time, and we want more
 | ||||
| 	 * aggressive scanning */ | ||||
| 	DPRINTF(E_DBG,L_SCAN,"Starting aggressive file length scan\n"); | ||||
| 	if(config.scan_type == 1) { | ||||
| 	    /* get average bitrate */ | ||||
| 	    scan_get_average_bitrate(infile, &fi); | ||||
| 	} else { | ||||
| 	    /* get full frame count */ | ||||
| 	    scan_get_frame_count(infile, &fi); | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|     pmp3->bitrate=fi.bitrate; | ||||
|     pmp3->samplerate=fi.samplerate; | ||||
|      | ||||
|     /* guesstimate the file length */ | ||||
|     if(!pmp3->song_length) { /* could have gotten it from the tag */ | ||||
| 	/* DWB: use ms time instead of seconds, use doubles to
 | ||||
| 	   avoid overflow */ | ||||
| 	if(!number_of_frames) { /* not vbr */ | ||||
| 	if(!fi.number_of_frames) { /* not vbr */ | ||||
| 	    pmp3->song_length = (int) ((double) file_size * 8. / | ||||
| 				       (double) fi.bitrate); | ||||
| 
 | ||||
| 	} else { | ||||
| 	    pmp3->song_length = (int) ((double)(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); | ||||
| 	} | ||||
| 
 | ||||
| @ -1448,6 +1659,13 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) { | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Manually build tags.  Set artist to computer/orchestra | ||||
|  * if there is already no artist.  Perhaps this could be  | ||||
|  * done better, but I'm not sure what else to do here. | ||||
|  * | ||||
|  * @param song MP3FILE of the file to build composite tags for | ||||
|  */ | ||||
| void make_composite_tags(MP3FILE *song) | ||||
| { | ||||
|     int len; | ||||
|  | ||||
| @ -70,9 +70,9 @@ typedef struct tag_mp3file { | ||||
| } MP3FILE; | ||||
| 
 | ||||
| extern int scan_init(char *path); | ||||
| 
 | ||||
| extern void make_composite_tags(MP3FILE *song); | ||||
| 
 | ||||
| /* this should be refactored out of here... */ | ||||
| extern off_t aac_drilltoatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length); | ||||
| 
 | ||||
| #endif /* _MP3_SCANNER_H_ */ | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user