Dynamic art for AAC files from Paul Kim

This commit is contained in:
Ron Pedde 2004-10-15 20:10:39 +00:00
parent feda9d5a02
commit 7cc249142e
9 changed files with 566 additions and 91 deletions

View File

@ -36,4 +36,10 @@ Hiren Joshi (hirenj@mooh.org)
David W. Berry (dwb@greenwing.com) David W. Berry (dwb@greenwing.com)
* Tons of protocol compliance fixes. * Tons of protocol compliance fixes.
* Browse, query, and index support
Paul Kim (mr.pickles.esq@gmail.com)
* Dynamic art code for AAC
* Various AAC meta-info cleanups
* Compilation tag support (aac and mp3)

View File

@ -1,4 +1,9 @@
2204-09-08 Ron Pedde <ron@pedde.com> 2004-10-15 Ron Pedde <ron@pedde.com>
* src/dynamic-art.c: merged in patch from Paul Kim
(mr.pickles.esq@gmail.com) for dynamic cover art on aac
files (like the stuff from Hiren).
2004-09-08 Ron Pedde <ron@pedde.com>
* mp3-scanner.c: decode more aac tags. Feature request #990546 * mp3-scanner.c: decode more aac tags. Feature request #990546
* mp3-scanner.c: support for bitrate on playlsits. FR #996506 * mp3-scanner.c: support for bitrate on playlsits. FR #996506

View File

@ -511,7 +511,8 @@ DAAP_BLOCK* daap_add_song_entry(DAAP_BLOCK* mlcl, MP3FILE* song, MetaField_t met
if(song->comment && (wantsMeta(meta, metaSongComment))) if(song->comment && (wantsMeta(meta, metaSongComment)))
g = g && daap_add_string(mlit,"ascm",song->comment); /* comment */ g = g && daap_add_string(mlit,"ascm",song->comment); /* comment */
// g = g && daap_add_char(mlit,"asco",0x0); /* compilation */ if(song->compilation && (wantsMeta(meta, metaSongCompilation)))
g = g && daap_add_char(mlit,"asco",song->compilation); /* compilation */
if(song->composer && (wantsMeta(meta, metaSongComposer))) if(song->composer && (wantsMeta(meta, metaSongComposer)))
g = g && daap_add_string(mlit,"ascp",song->composer); /* composer */ g = g && daap_add_string(mlit,"ascp",song->composer); /* composer */

View File

@ -104,6 +104,8 @@ typedef struct tag_mp3packed {
int bpm; // DB Version 3 int bpm; // DB Version 3
char compilation;
unsigned int id; /* inode */ unsigned int id; /* inode */
int path_len; int path_len;
@ -580,6 +582,7 @@ datum *db_packrecord(MP3FILE *pmp3) {
ppacked->time_modified=pmp3->time_modified; ppacked->time_modified=pmp3->time_modified;
ppacked->time_played=pmp3->time_played; ppacked->time_played=pmp3->time_played;
ppacked->bpm=pmp3->bpm; ppacked->bpm=pmp3->bpm;
ppacked->compilation=pmp3->compilation;
ppacked->id=pmp3->id; ppacked->id=pmp3->id;
ppacked->path_len=STRLEN(pmp3->path); ppacked->path_len=STRLEN(pmp3->path);
@ -682,6 +685,7 @@ int db_unpackrecord(datum *pdatum, MP3FILE *pmp3) {
pmp3->time_modified=ppacked->time_modified; pmp3->time_modified=ppacked->time_modified;
pmp3->time_played=ppacked->time_played; pmp3->time_played=ppacked->time_played;
pmp3->bpm=ppacked->bpm; pmp3->bpm=ppacked->bpm;
pmp3->compilation=ppacked->compilation;
pmp3->id=ppacked->id; pmp3->id=ppacked->id;
offset=0; offset=0;

View File

@ -22,6 +22,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -35,6 +36,7 @@
#include "playlist.h" #include "playlist.h"
#include "restart.h" #include "restart.h"
#define BLKSIZE PIPE_BUF
int *da_get_current_tag_info(int file_fd); int *da_get_current_tag_info(int file_fd);
@ -192,3 +194,384 @@ int da_attach_image(int img_fd, int out_fd, int mp3_fd, int offset)
} }
/**
* Rewrites the stco atom. It goes through the chunk offset atom list
* and adjusts them to take into account the 'covr' atom.
* extra_size is the amount to adjust by.
*/
off_t da_aac_rewrite_stco_atom(off_t extra_size, int out_fd, FILE *aac_fp,
off_t last_pos)
{
int aac_fd;
struct stat sb;
unsigned char buffer[4];
off_t file_size;
int atom_offset;
int atom_length;
off_t cur_pos;
off_t old_pos;
int i;
unsigned int num_entries;
unsigned int offset_entry;
aac_fd = fileno(aac_fp);
fstat(aac_fd, &sb);
file_size = sb.st_size;
/* Drill down to the 'stco' atom which contains offsets to chunks in
the 'mdat' section. These offsets need to be readjusted. */
atom_offset = aac_drilltoatom(aac_fp, "moov:trak:mdia:minf:stbl:stco",
&atom_length);
if (atom_offset != -1) {
/* Skip flags */
fseek(aac_fp, 4, SEEK_CUR);
old_pos = last_pos;
cur_pos = ftell(aac_fp);
/* Copy from last point to this point. */
fseek(aac_fp, old_pos, SEEK_SET);
fcopyblock(aac_fp, out_fd, cur_pos - old_pos);
/* Read number of entries */
fread(buffer, 1, 4, aac_fp);
r_write(out_fd, buffer, 4);
num_entries = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
DPRINTF(ERR_DEBUG, "Readjusting %d 'stco' table offsets.\n", num_entries);
/* PENDING: Error check on num_entries? */
for (i = 0; i < num_entries; i++) {
fread(buffer, 1, 4, aac_fp);
offset_entry = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
/* Adjust chunk offset. */
offset_entry += extra_size;
buffer[3] = offset_entry & 0xFF;
buffer[2] = (offset_entry >> 8) & 0xFF;
buffer[1] = (offset_entry >> 16) & 0xFF;
buffer[0] = (offset_entry >> 24) & 0xFF;
r_write(out_fd, buffer, 4);
offset_entry = 0;
}
return ftell(aac_fp);
} else {
DPRINTF(ERR_LOG, "No 'stco' atom found.\n");
}
return last_pos;
}
/**
* Insert a 'covr' atom.
* extra_size is the size of the atom used to adjust all parent atoms.
*/
off_t da_aac_insert_covr_atom(off_t extra_size, int out_fd, FILE *aac_fp,
off_t last_pos, off_t file_size, int img_fd)
{
int aac_fd;
struct stat sb;
off_t old_pos;
unsigned char buffer[4];
int atom_offset;
int atom_length;
off_t cur_pos;
char *cp;
unsigned char img_type_flag;
/* Figure out image file type since this needs to be encoded in the atom. */
cp = strrchr(config.artfilename, '.');
if (cp) {
if (!strcasecmp(cp, ".jpeg") || !strcasecmp(cp, ".jpg")) {
img_type_flag = 0x0d;
}
else if (!strcasecmp(cp, ".png")) {
img_type_flag = 0x0e;
}
} else {
DPRINTF(ERR_LOG, "Image type '%s' not supported.\n", cp);
return 0;
}
aac_fd = fileno(aac_fp);
fstat(aac_fd, &sb);
file_size = sb.st_size;
rewind(aac_fp);
atom_offset = scan_aac_findatom(aac_fp, file_size, "moov", &atom_length);
if (atom_offset != -1) {
atom_offset = scan_aac_findatom(aac_fp, atom_length - 8, "udta", &atom_length);
if (atom_offset != -1) {
old_pos = last_pos;
cur_pos = ftell(aac_fp) - 8;
DPRINTF(ERR_INFO,"Found udta atom at %ld.\n", cur_pos);
fseek(aac_fp, old_pos, SEEK_SET);
fcopyblock(aac_fp, out_fd, cur_pos - old_pos);
/* Write out new length */
atom_length += extra_size;
buffer[3] = atom_length & 0xFF;
buffer[2] = ( atom_length >> 8 ) & 0xFF;
buffer[1] = ( atom_length >> 16 ) & 0xFF;
buffer[0] = ( atom_length >> 24 ) & 0xFF;
r_write(out_fd, buffer, 4);
cur_pos += 4;
fseek(aac_fp, 8, SEEK_CUR);
atom_offset = scan_aac_findatom(aac_fp, atom_length - 8, "meta", &atom_length);
if (atom_offset != -1) {
old_pos = cur_pos;
cur_pos = ftell(aac_fp) - 8;
DPRINTF(ERR_INFO,"Found meta atom at %ld.\n", cur_pos);
fseek(aac_fp, old_pos, SEEK_SET);
fcopyblock(aac_fp, out_fd, cur_pos - old_pos);
/* Write out new length */
atom_length += extra_size;
buffer[3] = atom_length & 0xFF;
buffer[2] = ( atom_length >> 8 ) & 0xFF;
buffer[1] = ( atom_length >> 16 ) & 0xFF;
buffer[0] = ( atom_length >> 24 ) & 0xFF;
r_write(out_fd, buffer, 4);
cur_pos += 4;
fseek(aac_fp, 12, SEEK_CUR); /* "meta" atom hack. */
atom_offset = scan_aac_findatom(aac_fp, atom_length - 8, "ilst", &atom_length);
if (atom_offset != -1) {
old_pos = cur_pos;
cur_pos = ftell(aac_fp) - 8;
DPRINTF(ERR_INFO,"Found ilst atom at %ld.\n", cur_pos);
fseek(aac_fp, old_pos, SEEK_SET);
fcopyblock(aac_fp, out_fd, cur_pos - old_pos);
old_pos = cur_pos + 4;
cur_pos += atom_length;
/* Write out new length */
atom_length += extra_size;
buffer[3] = atom_length & 0xFF;
buffer[2] = ( atom_length >> 8 ) & 0xFF;
buffer[1] = ( atom_length >> 16 ) & 0xFF;
buffer[0] = ( atom_length >> 24 ) & 0xFF;
r_write(out_fd, buffer, 4);
/* Copy all 'ilst' children (all the MP4 'tags'). We will append
at the end. */
fseek(aac_fp, old_pos, SEEK_SET);
fcopyblock(aac_fp, out_fd, cur_pos - old_pos);
cur_pos = ftell(aac_fp);
/* Write out 'covr' atom */
atom_length = extra_size;
buffer[3] = atom_length & 0xFF;
buffer[2] = ( atom_length >> 8 ) & 0xFF;
buffer[1] = ( atom_length >> 16 ) & 0xFF;
buffer[0] = ( atom_length >> 24 ) & 0xFF;
r_write(out_fd, buffer, 4);
r_write(out_fd, "covr", 4);
/* Write out 'data' atom */
atom_length = extra_size - 8;
buffer[3] = atom_length & 0xFF;
buffer[2] = ( atom_length >> 8 ) & 0xFF;
buffer[1] = ( atom_length >> 16 ) & 0xFF;
buffer[0] = ( atom_length >> 24 ) & 0xFF;
r_write(out_fd, buffer, 4);
r_write(out_fd, "data", 4);
/* Write out 'data' flags */
buffer[3] = img_type_flag;
buffer[2] = 0;
buffer[1] = 0;
buffer[0] = 0;
r_write(out_fd, buffer, 4);
/* Reserved? Zero in any case. */
buffer[3] = 0;
buffer[2] = 0;
buffer[1] = 0;
buffer[0] = 0;
r_write(out_fd, buffer, 4);
/* Now ready for the image stream. Copy it over. */
lseek(img_fd,0,SEEK_SET);
copyfile(img_fd,out_fd);
last_pos = cur_pos;
} else {
DPRINTF(ERR_LOG, "No 'ilst' atom found.\n");
}
} else {
DPRINTF(ERR_LOG, "No 'meta' atom found.\n");
}
} else {
DPRINTF(ERR_LOG, "No 'udta' atom found.\n");
}
} else {
DPRINTF(ERR_LOG, "No 'moov' atom found.\n");
}
/* Seek to position right after 'udta' atom. Let main() stream out the
rest. */
lseek(aac_fd, last_pos, SEEK_SET);
return last_pos;
}
/*
* attach_image
*
* Given an image, output and input aac file descriptor, attach the artwork
* found at the image file descriptor to the aac and stream the new atom to
* the client via the output file descriptor.
* Currently, if the meta info atoms are non-existent, this will fail.
*/
off_t da_aac_attach_image(int img_fd, int out_fd, int aac_fd, int offset)
{
off_t img_size;
int atom_length;
unsigned int extra_size;
off_t file_size;
unsigned char buffer[4];
struct stat sb;
FILE *aac_fp;
off_t stco_atom_pos;
off_t ilst_atom_pos;
off_t last_pos;
fstat(img_fd, &sb);
img_size = sb.st_size;
DPRINTF(ERR_INFO,"Image size (in bytes): %ld.\n", img_size);
/* PENDING: We can be stricter here by checking the shortest header between
PNG and JPG and using that length. */
if (img_size < 1) {
r_close(img_fd);
return 0;
}
/* Include extra bytes for 'covr' atom length (4) and type (4) and its
'data' item length (4) and type (4) plus 4 bytes for the 'data' atom's
flags and 4 reserved(?) bytes. */
extra_size = img_size + 24;
fstat(aac_fd, &sb);
file_size = sb.st_size;
aac_fp = fdopen(dup(aac_fd), "r");
stco_atom_pos = aac_drilltoatom(aac_fp, "moov:trak:mdia:minf:stbl:stco",
&atom_length);
ilst_atom_pos = aac_drilltoatom(aac_fp, "moov:udta:meta:ilst",
&atom_length);
last_pos = aac_drilltoatom(aac_fp, "mdat", &atom_length);
if (last_pos != -1) {
if (offset >= last_pos) {
/* Offset is in the actual music data so don't bother processing
meta data. */
return 0;
}
} else {
DPRINTF(ERR_LOG, "No 'mdat' atom.\n");
return 0;
}
rewind(aac_fp);
/* Re-adjust length of 'moov' atom. */
last_pos = scan_aac_findatom(aac_fp, file_size, "moov", &atom_length);
if (last_pos != -1) {
/* Copy everything from up to this atom */
rewind(aac_fp);
fcopyblock(aac_fp, out_fd, last_pos);
/* Write out new length. */
atom_length += extra_size;
buffer[3] = atom_length & 0xFF;
buffer[2] = ( atom_length >> 8 ) & 0xFF;
buffer[1] = ( atom_length >> 16 ) & 0xFF;
buffer[0] = ( atom_length >> 24 ) & 0xFF;
r_write(out_fd, buffer, 4);
last_pos += 4;
} else {
DPRINTF(ERR_LOG, "Could not find 'moov' atom.\n");
return 0;
}
if (stco_atom_pos < ilst_atom_pos) {
last_pos = da_aac_rewrite_stco_atom(extra_size, out_fd, aac_fp, last_pos);
last_pos = da_aac_insert_covr_atom(extra_size, out_fd, aac_fp, last_pos,
file_size, img_fd);
} else {
last_pos = da_aac_insert_covr_atom(extra_size, out_fd, aac_fp, last_pos,
file_size, img_fd);
last_pos = da_aac_rewrite_stco_atom(extra_size, out_fd, aac_fp, last_pos);
}
/* Seek to position right after last atom. Let main() stream out the rest. */
lseek(aac_fd, last_pos, SEEK_SET);
r_close(img_fd);
fclose(aac_fp);
return last_pos;
}
int copyblock(int fromfd, int tofd, size_t size) {
char buf[BLKSIZE];
int bytesread;
int totalbytes = 0;
int blocksize = BLKSIZE;
int bytesleft;
while (totalbytes < size) {
bytesleft = size - totalbytes;
if (bytesleft < BLKSIZE) {
blocksize = bytesleft;
} else {
blocksize = BLKSIZE;
}
if ((bytesread = r_read(fromfd, buf, blocksize)) < 0)
return -1;
if (bytesread == 0)
return totalbytes;
if (r_write(tofd, buf, bytesread) < 0)
return -1;
totalbytes += bytesread;
}
return totalbytes;
}
int fcopyblock(FILE *fromfp, int tofd, size_t size) {
char buf[BLKSIZE];
int bytesread;
int totalbytes = 0;
int blocksize = BLKSIZE;
int bytesleft;
while (totalbytes < size) {
bytesleft = size - totalbytes;
if (bytesleft < BLKSIZE) {
blocksize = bytesleft;
} else {
blocksize = BLKSIZE;
}
if ((bytesread = fread(buf, 1, blocksize, fromfp)) < blocksize) {
if (ferror(fromfp))
return -1;
}
if (r_write(tofd, buf, bytesread) < 0)
return -1;
if (feof(fromfp))
return 0;
totalbytes += bytesread;
}
return totalbytes;
}

View File

@ -24,5 +24,8 @@
int da_get_image_fd(char *filename); int da_get_image_fd(char *filename);
int da_attach_image(int img_fd, int out_fd, int mp3_fd, int offset); int da_attach_image(int img_fd, int out_fd, int mp3_fd, int offset);
off_t da_aac_attach_image(int img_fd, int out_fd, int aac_fd, int offset);
int copyblock(int fromfd, int tofd, size_t size);
int fcopyblock(FILE *fromfp, int tofd, size_t size);
#endif /* _DYNAMICART_H_ */ #endif /* _DYNAMICART_H_ */

View File

@ -116,6 +116,8 @@ void daap_handler(WS_CONNINFO *pwsc) {
int session_id=0; int session_id=0;
int img_fd; int img_fd;
struct stat sb;
long img_size;
off_t offset=0; off_t offset=0;
off_t real_len; off_t real_len;
@ -300,6 +302,24 @@ void daap_handler(WS_CONNINFO *pwsc) {
} else { } else {
real_len=lseek(file_fd,0,SEEK_END); real_len=lseek(file_fd,0,SEEK_END);
lseek(file_fd,0,SEEK_SET); lseek(file_fd,0,SEEK_SET);
/* Re-adjust content length for cover art */
if((config.artfilename) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
fstat(img_fd, &sb);
img_size = sb.st_size;
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
/*PENDING*/
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
real_len += img_size + 24;
if (offset > img_size + 24) {
offset -= img_size + 24;
}
}
}
file_len = real_len - offset; file_len = real_len - offset;
DPRINTF(ERR_DEBUG,"Thread %d: Length of file (remaining) is %ld\n", DPRINTF(ERR_DEBUG,"Thread %d: Length of file (remaining) is %ld\n",
@ -334,11 +354,16 @@ void daap_handler(WS_CONNINFO *pwsc) {
config.stats.songs_served++; /* FIXME: remove stat races */ config.stats.songs_served++; /* FIXME: remove stat races */
if((config.artfilename) && if((config.artfilename) &&
(!offset) && (strncasecmp(pmp3->type,".mp3",4) ==0) && (!offset) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) { ((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
DPRINTF(ERR_INFO,"Dynamically attaching artwork to %s (fd %d)\n", DPRINTF(ERR_INFO,"Dynamically attaching artwork to %s (fd %d)\n",
pmp3->fname, img_fd); pmp3->fname, img_fd);
da_attach_image(img_fd, pwsc->fd, file_fd, offset); da_attach_image(img_fd, pwsc->fd, file_fd, offset);
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
DPRINTF(ERR_INFO,"Dynamically attaching artwork to %s (fd %d)\n", pmp3->fname, img_fd);
da_aac_attach_image(img_fd, pwsc->fd, file_fd, offset);
}
} else if(offset) { } else if(offset) {
DPRINTF(ERR_INFO,"Seeking to offset %ld\n",(long)offset); DPRINTF(ERR_INFO,"Seeking to offset %ld\n",(long)offset);
lseek(file_fd,offset,SEEK_SET); lseek(file_fd,offset,SEEK_SET);

View File

@ -43,7 +43,6 @@
#include <netinet/in.h> /* htons and friends */ #include <netinet/in.h> /* htons and friends */
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h> /* why here? For osx 10.2, of course! */ #include <dirent.h> /* why here? For osx 10.2, of course! */
#include "daapd.h" #include "daapd.h"
@ -548,7 +547,6 @@ int scan_get_aactags(char *file, MP3FILE *pmp3) {
FILE *fin; FILE *fin;
long atom_offset; long atom_offset;
int atom_length; int atom_length;
long file_size;
long current_offset=0; long current_offset=0;
int current_size; int current_size;
@ -563,100 +561,90 @@ int scan_get_aactags(char *file, MP3FILE *pmp3) {
return -1; return -1;
} }
fseek(fin,0,SEEK_END);
file_size=ftell(fin);
fseek(fin,0,SEEK_SET); fseek(fin,0,SEEK_SET);
atom_offset=scan_aac_findatom(fin,file_size,"moov",&atom_length); atom_offset = aac_drilltoatom(fin, "moov:udta:meta:ilst", &atom_length);
if(atom_offset != -1) { if(atom_offset != -1) {
atom_offset=scan_aac_findatom(fin,atom_length - 8,"udta",&atom_length); /* found the tag section - need to walk through now */
if(atom_offset != -1) {
atom_offset=scan_aac_findatom(fin,atom_length - 8, "meta", &atom_length); while(current_offset < atom_length) {
if(atom_offset != -1) { if(fread((void*)&current_size,1,sizeof(int),fin) != sizeof(int))
fseek(fin,4,SEEK_CUR); /* ???? */ break;
atom_offset=scan_aac_findatom(fin, atom_length - 8, "ilst", &atom_length);
if(atom_offset != -1) {
/* found the tag section - need to walk through now */
while(current_offset < atom_length) {
if(fread((void*)&current_size,1,sizeof(int),fin) != sizeof(int))
break;
current_size=ntohl(current_size); current_size=ntohl(current_size);
if(current_size <= 7) /* something not right */ if(current_size <= 7) /* something not right */
break; break;
if(fread(current_atom,1,4,fin) != 4) if(fread(current_atom,1,4,fin) != 4)
break; break;
len=current_size-7; /* for ill-formed too-short tags */ len=current_size-7; /* for ill-formed too-short tags */
if(len < 22) if(len < 22)
len=22; len=22;
current_data=(char*)malloc(len); /* extra byte */ current_data=(char*)malloc(len); /* extra byte */
memset(current_data,0x00,len); memset(current_data,0x00,len);
if(fread(current_data,1,current_size-8,fin) != current_size-8) if(fread(current_data,1,current_size-8,fin) != current_size-8)
break; break;
if(!memcmp(current_atom,"\xA9" "nam",4)) { /* Song name */ if(!memcmp(current_atom,"\xA9" "nam",4)) { /* Song name */
pmp3->title=strdup((char*)&current_data[16]); pmp3->title=strdup((char*)&current_data[16]);
} else if(!memcmp(current_atom,"\xA9" "ART",4)) { } else if(!memcmp(current_atom,"\xA9" "ART",4)) {
pmp3->artist=strdup((char*)&current_data[16]); pmp3->artist=strdup((char*)&current_data[16]);
} else if(!memcmp(current_atom,"\xA9" "alb",4)) { } else if(!memcmp(current_atom,"\xA9" "alb",4)) {
pmp3->album=strdup((char*)&current_data[16]); pmp3->album=strdup((char*)&current_data[16]);
} else if(!memcmp(current_atom,"\xA9" "cmt",4)) { } else if(!memcmp(current_atom,"\xA9" "cmt",4)) {
pmp3->comment=strdup((char*)&current_data[16]); pmp3->comment=strdup((char*)&current_data[16]);
} else if(!memcmp(current_atom,"\xA9" "wrt",4)) { } else if(!memcmp(current_atom,"\xA9" "wrt",4)) {
pmp3->composer=strdup((char*)&current_data[16]); pmp3->composer=strdup((char*)&current_data[16]);
} else if(!memcmp(current_atom,"\xA9" "grp",4)) { } else if(!memcmp(current_atom,"\xA9" "grp",4)) {
pmp3->grouping=strdup((char*)&current_data[16]); pmp3->grouping=strdup((char*)&current_data[16]);
} else if(!memcmp(current_atom,"\xA9" "gen",4)) { } else if(!memcmp(current_atom,"\xA9" "gen",4)) {
/* can this be a winamp genre??? */ /* can this be a winamp genre??? */
pmp3->genre=strdup((char*)&current_data[16]); pmp3->genre=strdup((char*)&current_data[16]);
} else if(!memcmp(current_atom,"tmpo",4)) { } else if(!memcmp(current_atom,"tmpo",4)) {
us_data=*((unsigned short *)&current_data[16]); us_data=*((unsigned short *)&current_data[16]);
us_data=ntohs(us_data); us_data=ntohs(us_data);
pmp3->bpm=us_data; pmp3->bpm=us_data;
} else if(!memcmp(current_atom,"trkn",4)) { } else if(!memcmp(current_atom,"trkn",4)) {
us_data=*((unsigned short *)&current_data[18]); us_data=*((unsigned short *)&current_data[18]);
us_data=ntohs(us_data); us_data=ntohs(us_data);
pmp3->track=us_data; pmp3->track=us_data;
us_data=*((unsigned short *)&current_data[20]); us_data=*((unsigned short *)&current_data[20]);
us_data=ntohs(us_data); us_data=ntohs(us_data);
pmp3->total_tracks=us_data; pmp3->total_tracks=us_data;
} else if(!memcmp(current_atom,"disk",4)) { } else if(!memcmp(current_atom,"disk",4)) {
us_data=*((unsigned short *)&current_data[18]); us_data=*((unsigned short *)&current_data[18]);
us_data=ntohs(us_data); us_data=ntohs(us_data);
pmp3->disc=us_data; pmp3->disc=us_data;
us_data=*((unsigned short *)&current_data[20]); us_data=*((unsigned short *)&current_data[20]);
us_data=ntohs(us_data); us_data=ntohs(us_data);
pmp3->total_discs=us_data; pmp3->total_discs=us_data;
} else if(!memcmp(current_atom,"\xA9" "day",4)) { } else if(!memcmp(current_atom,"\xA9" "day",4)) {
pmp3->year=atoi((char*)&current_data[16]); pmp3->year=atoi((char*)&current_data[16]);
} else if(!memcmp(current_atom,"gnre",4)) { } else if(!memcmp(current_atom,"gnre",4)) {
genre=(int)(*((char*)&current_data[17])); genre=(int)(*((char*)&current_data[17]));
genre--; genre--;
if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN)) if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
genre=WINAMP_GENRE_UNKNOWN; genre=WINAMP_GENRE_UNKNOWN;
pmp3->genre=strdup(scan_winamp_genre[genre]); pmp3->genre=strdup(scan_winamp_genre[genre]);
} } else if (!memcmp(current_atom, "cpil", 4)) {
pmp3->compilation = current_data[16];
}
free(current_data); free(current_data);
current_offset+=current_size; current_offset+=current_size;
} }
}
}
}
} }
fclose(fin); fclose(fin);
@ -824,7 +812,10 @@ int scan_get_mp3tags(char *file, MP3FILE *pmp3) {
} else if(!strcmp(pid3frame->id,"TBPM")) { } else if(!strcmp(pid3frame->id,"TBPM")) {
pmp3->bpm = atoi(utf8_text); pmp3->bpm = atoi(utf8_text);
DPRINTF(ERR_DEBUG, "BPM: %d\n", pmp3->bpm); DPRINTF(ERR_DEBUG, "BPM: %d\n", pmp3->bpm);
} } else if(!strcmp(pid3frame->id,"TCMP")) {
pmp3->compilation = (char)atoi(utf8_text);
DPRINTF(ERR_DEBUG, "Compilation: %d\n", pmp3->compilation);
}
} }
} }
@ -898,6 +889,60 @@ int scan_get_fileinfo(char *file, MP3FILE *pmp3) {
return 0; return 0;
} }
/*
* aac_drilltoatom
*
* Returns the offset of the atom specified by the given path or -1 if
* not found. atom_path is a colon separated list of atoms specifying
* a path from parent node to the target node. All paths must be specified
* from the root.
*/
off_t aac_drilltoatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length)
{
long atom_offset;
off_t file_size;
char *cur_p, *end_p;
char atom_name[5];
fseek(aac_fp, 0, SEEK_END);
file_size = ftell(aac_fp);
rewind(aac_fp);
end_p = atom_path;
while (*end_p != '\0')
{
end_p++;
}
atom_name[4] = '\0';
cur_p = atom_path;
while (cur_p != NULL)
{
if ((end_p - cur_p) < 4)
{
return -1;
}
strncpy(atom_name, cur_p, 4);
atom_offset = scan_aac_findatom(aac_fp, file_size, atom_name, atom_length);
if (atom_offset == -1)
{
return -1;
}
DPRINTF(ERR_DEBUG, "Found %s atom at offset %ld.\n", atom_name, ftell(aac_fp) - 8);
/* Hack to deal with 'meta' atom which has 4 bytes of crud after it. */
if (!strcmp(atom_name, "meta"))
{
fseek(aac_fp, 4, SEEK_CUR);
}
cur_p = strchr(cur_p, ':');
if (cur_p != NULL)
{
cur_p++;
}
}
return ftell(aac_fp) - 8;
}
/* /*
* scan_get_aacfileinfo * scan_get_aacfileinfo
@ -925,18 +970,15 @@ int scan_get_aacfileinfo(char *file, MP3FILE *pmp3) {
pmp3->file_size=file_size; pmp3->file_size=file_size;
/* now, hunt for the mvhd atom */ /* now, hunt for the mvhd atom */
atom_offset=scan_aac_findatom(infile,file_size,"moov",&atom_length); atom_offset = aac_drilltoatom(infile, "moov:mvhd", &atom_length);
if(atom_offset != -1) { if(atom_offset != -1) {
atom_offset=scan_aac_findatom(infile,atom_length-8,"mvhd",&atom_length); fseek(infile,16,SEEK_CUR);
if(atom_offset != -1) { fread((void*)&temp_int,1,sizeof(int),infile);
fseek(infile,16,SEEK_CUR); temp_int=ntohl(temp_int);
fread((void*)&temp_int,1,sizeof(int),infile); /* DWB: use ms time instead of sec */
temp_int=ntohl(temp_int); pmp3->song_length=temp_int * 1000 / 600;
/* DWB: use ms time instead of sec */
pmp3->song_length=temp_int * 1000 / 600;
DPRINTF(ERR_DEBUG,"Song length: %d seconds\n", pmp3->song_length / 1000); DPRINTF(ERR_DEBUG,"Song length: %d seconds\n", pmp3->song_length / 1000);
}
} }
fclose(infile); fclose(infile);
return 0; return 0;

View File

@ -22,6 +22,8 @@
#ifndef _MP3_SCANNER_H_ #ifndef _MP3_SCANNER_H_
#define _MP3_SCANNER_H_ #define _MP3_SCANNER_H_
#include <sys/types.h>
typedef struct tag_mp3file { typedef struct tag_mp3file {
char *path; char *path;
char *fname; char *fname;
@ -61,10 +63,14 @@ typedef struct tag_mp3file {
/* generated fields */ /* generated fields */
char* description; /* long file type */ char* description; /* long file type */
int item_kind; /* song or movie */ int item_kind; /* song or movie */
char compilation;
} 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);
extern off_t aac_drilltoatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length);
#endif /* _MP3_SCANNER_H_ */ #endif /* _MP3_SCANNER_H_ */