mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-24 22:25:56 -05: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>Rescan Interval</TD>
|
||||||
<TD><INPUT TYPE="TEXT" @READONLY@ NAME="RESCAN_INTERVAL" VALUE="@RESCAN_INTERVAL@"></TD>
|
<TD><INPUT TYPE="TEXT" @READONLY@ NAME="RESCAN_INTERVAL" VALUE="@RESCAN_INTERVAL@"></TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
<TR>
|
||||||
|
<TD>Scan Type</TD>
|
||||||
|
<TD><INPUT TYPE="TEXT" @READONLY@ NAME="SCAN_TYPE" VALUE="@SCAN_TYPE@"></TD>
|
||||||
|
</TR>
|
||||||
<TR>
|
<TR>
|
||||||
<TD>Always Scan</TD>
|
<TD>Always Scan</TD>
|
||||||
<TD><INPUT TYPE="TEXT" @READONLY@ NAME="ALWAYS_SCAN" VALUE="@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.
|
# 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)
|
# web_root (required)
|
||||||
@ -39,7 +47,9 @@ db_dir /var/cache/mt-daapd
|
|||||||
#
|
#
|
||||||
# mp3_dir (required)
|
# 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
|
mp3_dir /mnt/mp3
|
||||||
@ -49,7 +59,7 @@ mp3_dir /mnt/mp3
|
|||||||
#
|
#
|
||||||
# This is both the name of the server as advertised
|
# This is both the name of the server as advertised
|
||||||
# via rendezvous, and the name of the database
|
# 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
|
servername mt-daapd
|
||||||
@ -74,9 +84,9 @@ runas nobody
|
|||||||
# See the mt-daapd.playlist file in the
|
# See the mt-daapd.playlist file in the
|
||||||
# contrib directory for syntax and examples
|
# contrib directory for syntax and examples
|
||||||
#
|
#
|
||||||
# Note that static playlists will still
|
# This doesn't control static playlists... these
|
||||||
# show up, even if this directive is not
|
# are controlled with the "process_m3u" directive
|
||||||
# specified
|
# below.
|
||||||
#
|
#
|
||||||
|
|
||||||
playlist /etc/mt-daapd.playlist
|
playlist /etc/mt-daapd.playlist
|
||||||
@ -101,6 +111,7 @@ playlist /etc/mt-daapd.playlist
|
|||||||
# play them. Perhaps this would be useful on Linux with
|
# play them. Perhaps this would be useful on Linux with
|
||||||
# Rhythmbox, once it understands daap. (hurry up!)
|
# Rhythmbox, once it understands daap. (hurry up!)
|
||||||
#
|
#
|
||||||
|
#
|
||||||
|
|
||||||
extensions .mp3,.m4a,.m4p
|
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
|
# If background rescanning is disabled, a scan can still be forced from the
|
||||||
# "status" page of the administrative web interface
|
# "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
|
# 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
|
# won't hurt anything, it will just waste CPU, and make connect times
|
||||||
# to the daap server longer.
|
# 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
|
#rescan_interval 300
|
||||||
|
|
||||||
#
|
|
||||||
# always_scan
|
# always_scan
|
||||||
#
|
#
|
||||||
# The default behavior is not not do background rescans of the filesystem
|
# The default behavior is not not do background rescans of the
|
||||||
# unless there are clients connected. The thought is to allow the drives
|
# filesystem unless there are clients connected. The thought is to
|
||||||
# to spin down unless they are in use. This might be of more importance
|
# allow the drives to spin down unless they are in use. This might be
|
||||||
# in IDE drives that aren't designed to be run 24x7.
|
# of more importance in IDE drives that aren't designed to be run
|
||||||
#
|
# 24x7. Forcing a scan through the web interface will always work
|
||||||
# Forcing a scan will always work though, even if no users are connected.
|
# though, even if no users are connected.
|
||||||
#
|
|
||||||
#
|
|
||||||
# always_scan 0
|
# 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,"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,"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,"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,"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,"extensions",(void*)&config.extensions,config_emit_string },
|
||||||
{ 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, 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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef NSLU2
|
||||||
config.always_scan=0;
|
config.always_scan=0;
|
||||||
|
#else
|
||||||
|
config.always_scan=1;
|
||||||
|
#endif
|
||||||
|
|
||||||
config.configfile=strdup(file);
|
config.configfile=strdup(file);
|
||||||
config.web_root=NULL;
|
config.web_root=NULL;
|
||||||
config.adminpassword=NULL;
|
config.adminpassword=NULL;
|
||||||
@ -185,6 +191,7 @@ int config_read(char *file) {
|
|||||||
config.logfile=NULL;
|
config.logfile=NULL;
|
||||||
config.rescan_interval=0;
|
config.rescan_interval=0;
|
||||||
config.process_m3u=0;
|
config.process_m3u=0;
|
||||||
|
config.scan_type=0;
|
||||||
|
|
||||||
/* DWB: use alloced space so it can be freed without errors */
|
/* DWB: use alloced space so it can be freed without errors */
|
||||||
config.extensions=strdup(".mp3");
|
config.extensions=strdup(".mp3");
|
||||||
@ -336,6 +343,8 @@ int config_write(WS_CONNINFO *pwsc) {
|
|||||||
fprintf(configfile,"extensions\t%s\n",ws_getvar(pwsc,"extensions"));
|
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,"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,"rescan_interval\t%s\n",ws_getvar(pwsc,"rescan_interval"));
|
||||||
|
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"));
|
fprintf(configfile,"always_scan\t%s\n",ws_getvar(pwsc,"always_scan"));
|
||||||
if(ws_getvar(pwsc,"art_filename") && strlen(ws_getvar(pwsc,"art_filename")))
|
if(ws_getvar(pwsc,"art_filename") && strlen(ws_getvar(pwsc,"art_filename")))
|
||||||
fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename"));
|
fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename"));
|
||||||
|
@ -53,6 +53,7 @@ typedef struct tag_config {
|
|||||||
int rescan_interval; /**< How often to do a background rescan of the file system */
|
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 always_scan; /**< 0 to minimize disk usage (embedded devices) */
|
||||||
int process_m3u; /**< Should we process m3u files? */
|
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 *adminpassword; /**< Password to web management pages */
|
||||||
char *readpassword; /**< iTunes password */
|
char *readpassword; /**< iTunes password */
|
||||||
char *mp3dir; /**< root directory of the mp3 files */
|
char *mp3dir; /**< root directory of the mp3 files */
|
||||||
|
@ -59,21 +59,27 @@
|
|||||||
* Typedefs
|
* Typedefs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct to keep info about the information gleaned from
|
||||||
|
* the mp3 frame header.
|
||||||
|
*/
|
||||||
typedef struct tag_scan_frameinfo {
|
typedef struct tag_scan_frameinfo {
|
||||||
int layer;
|
int layer; /**< 1, 2, or 3, representing Layer I, II, and III */
|
||||||
int bitrate;
|
int bitrate; /**< Bitrate in kbps (128, 64, etc) */
|
||||||
int samplerate;
|
int samplerate; /**< Samplerate (e.g. 44100) */
|
||||||
int stereo;
|
int stereo; /**< Any kind of stereo.. joint, dual mono, etc */
|
||||||
|
|
||||||
int frame_length;
|
int frame_length; /**< Frame length in bytes - calculated */
|
||||||
int crc_protected;
|
int crc_protected; /**< Is the frame crc protected? */
|
||||||
int samples_per_frame;
|
int samples_per_frame; /**< Samples per frame - calculated field */
|
||||||
int padding;
|
int padding; /**< Whether or not there is a padding sample */
|
||||||
int xing_offset;
|
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;
|
int is_valid;
|
||||||
} SCAN_FRAMEINFO;
|
} SCAN_FRAMEINFO;
|
||||||
|
|
||||||
@ -266,22 +272,28 @@ char *scan_winamp_genre[] = {
|
|||||||
/*
|
/*
|
||||||
* Forwards
|
* Forwards
|
||||||
*/
|
*/
|
||||||
int scan_path(char *path);
|
static int scan_path(char *path);
|
||||||
int scan_gettags(char *file, MP3FILE *pmp3);
|
static int scan_gettags(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_mp3tags(char *file, MP3FILE *pmp3);
|
static int scan_get_mp3tags(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_aactags(char *file, MP3FILE *pmp3);
|
static int scan_get_aactags(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_nultags(char *file, MP3FILE *pmp3) { return 0; };
|
static int scan_get_nultags(char *file, MP3FILE *pmp3) { return 0; };
|
||||||
int scan_get_fileinfo(char *file, MP3FILE *pmp3);
|
static int scan_get_fileinfo(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3);
|
static int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_aacfileinfo(char *file, MP3FILE *pmp3);
|
static int scan_get_aacfileinfo(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_nulfileinfo(char *file, MP3FILE *pmp3) { return 0; };
|
static int scan_get_nulfileinfo(char *file, MP3FILE *pmp3) { return 0; };
|
||||||
int scan_get_urlfileinfo(char *file, MP3FILE *pmp3);
|
static int scan_get_urlfileinfo(char *file, MP3FILE *pmp3);
|
||||||
|
|
||||||
int scan_freetags(MP3FILE *pmp3);
|
static int scan_freetags(MP3FILE *pmp3);
|
||||||
void scan_static_playlist(char *path, struct dirent *pde, struct stat *psb);
|
static 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 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 {
|
typedef struct {
|
||||||
char* suffix;
|
char* suffix;
|
||||||
@ -299,6 +311,12 @@ static taghandler taghandlers[] = {
|
|||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert mac time to unix time (different epochs)
|
||||||
|
*
|
||||||
|
* param t time since mac epoch
|
||||||
|
*/
|
||||||
time_t mac_to_unix_time(int t) {
|
time_t mac_to_unix_time(int t) {
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
struct timezone tz;
|
struct timezone tz;
|
||||||
@ -1233,7 +1251,7 @@ int scan_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) {
|
|||||||
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) {
|
if((bitrate_index == 0xF) || (bitrate_index==0x0)) {
|
||||||
pfi->is_valid=0;
|
pfi->is_valid=0;
|
||||||
return -1;
|
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;
|
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;
|
pfi->is_valid=1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
/*
|
* Scan 10 frames from the middle of the file and determine an
|
||||||
* scan_get_mp3fileinfo
|
* 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
|
* Get information from the file headers itself -- like
|
||||||
* song length, bit rate, etc.
|
* 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) {
|
int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
||||||
FILE *infile;
|
FILE *infile;
|
||||||
@ -1300,15 +1471,19 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
unsigned char buffer[1024];
|
unsigned char buffer[1024];
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
int number_of_frames=0;
|
|
||||||
int xing_flags;
|
int xing_flags;
|
||||||
int found;
|
int found;
|
||||||
|
|
||||||
|
int first_check;
|
||||||
|
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 -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset((void*)&fi,0x00,sizeof(fi));
|
||||||
|
|
||||||
fseek(infile,0,SEEK_END);
|
fseek(infile,0,SEEK_END);
|
||||||
file_size=ftell(infile);
|
file_size=ftell(infile);
|
||||||
fseek(infile,0,SEEK_SET);
|
fseek(infile,0,SEEK_SET);
|
||||||
@ -1327,87 +1502,105 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
|
|
||||||
pid3=(SCAN_ID3HEADER*)buffer;
|
pid3=(SCAN_ID3HEADER*)buffer;
|
||||||
|
|
||||||
|
found=0;
|
||||||
|
fp_size=0;
|
||||||
|
|
||||||
if(strncmp(pid3->id,"ID3",3)==0) {
|
if(strncmp(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;
|
||||||
DPRINTF(E_DBG,L_SCAN,"Header length: %d\n",size);
|
DPRINTF(E_DBG,L_SCAN,"Header length: %d\n",size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
/* Here we start the brute-force header seeking. Sure wish there
|
||||||
|
* weren't so many crappy mp3 files out there
|
||||||
|
*/
|
||||||
|
|
||||||
|
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);
|
||||||
if(fread(buffer,1,sizeof(buffer),infile) < sizeof(buffer)) {
|
if(fread(buffer,1,sizeof(buffer),infile) < sizeof(buffer)) {
|
||||||
DPRINTF(E_LOG,L_SCAN,"Short file: %s\n",file);
|
DPRINTF(E_LOG,L_SCAN,"Short read: %s\n",file);
|
||||||
fclose(infile);
|
fclose(infile);
|
||||||
return -1;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
index=0;
|
index=0;
|
||||||
found=0;
|
while(!found) {
|
||||||
|
while((buffer[index] != 0xFF) && (index < (sizeof(buffer)-50)))
|
||||||
if(scan_decode_mp3_frame(&buffer[index],&fi)) { /* Bad frame header */
|
index++;
|
||||||
DPRINTF(E_DBG,L_SCAN,"Starting brute-force search for frame header\n");
|
|
||||||
|
|
||||||
|
if((first_check) && (index)) {
|
||||||
fp_size=0;
|
fp_size=0;
|
||||||
while(!found) {
|
DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n");
|
||||||
DPRINTF(E_DBG,L_SCAN,"Seeking to %d\n",(int)fp_size);
|
first_check=0;
|
||||||
fseek(infile,fp_size,SEEK_SET);
|
break;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* look for header */
|
if(index > sizeof(buffer) - 50) {
|
||||||
index=0;
|
|
||||||
while(!found) {
|
|
||||||
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; /* read in the next block */
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!scan_decode_mp3_frame(&buffer[index],&fi)) {
|
if(!scan_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,
|
||||||
|
* this is definately a valid frame */
|
||||||
found=1;
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* should check next frame... */
|
if(!found) {
|
||||||
found=1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!found)
|
|
||||||
index++;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file_size -= fp_size;
|
file_size -= fp_size;
|
||||||
|
fi.frame_offset=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);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if(scan_decode_mp3_frame(&buffer[index],&fi)) {
|
if(scan_decode_mp3_frame(&buffer[index],&fi)) {
|
||||||
fclose(infile);
|
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;
|
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," 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);
|
||||||
@ -1423,21 +1616,39 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
|
|
||||||
if(xing_flags & 0x1) {
|
if(xing_flags & 0x1) {
|
||||||
/* Frames field is valid... */
|
/* Frames field is valid... */
|
||||||
number_of_frames=*((int*)&buffer[index+fi.xing_offset+4+8]);
|
fi.number_of_frames=*((int*)&buffer[index+fi.xing_offset+4+8]);
|
||||||
number_of_frames=ntohs(number_of_frames);
|
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 */
|
/* 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(!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)(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1448,6 +1659,13 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
return 0;
|
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)
|
void make_composite_tags(MP3FILE *song)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
@ -70,9 +70,9 @@ typedef struct tag_mp3file {
|
|||||||
} MP3FILE;
|
} MP3FILE;
|
||||||
|
|
||||||
extern int scan_init(char *path);
|
extern int scan_init(char *path);
|
||||||
|
|
||||||
extern void make_composite_tags(MP3FILE *song);
|
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);
|
extern off_t aac_drilltoatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length);
|
||||||
|
|
||||||
#endif /* _MP3_SCANNER_H_ */
|
#endif /* _MP3_SCANNER_H_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user