Dynamic art for AAC files from Paul Kim
This commit is contained in:
parent
feda9d5a02
commit
7cc249142e
6
CREDITS
6
CREDITS
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
27
src/main.c
27
src/main.c
|
@ -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);
|
||||||
|
|
|
@ -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*)¤t_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*)¤t_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*)¤t_data[16]);
|
pmp3->title=strdup((char*)¤t_data[16]);
|
||||||
} else if(!memcmp(current_atom,"\xA9" "ART",4)) {
|
} else if(!memcmp(current_atom,"\xA9" "ART",4)) {
|
||||||
pmp3->artist=strdup((char*)¤t_data[16]);
|
pmp3->artist=strdup((char*)¤t_data[16]);
|
||||||
} else if(!memcmp(current_atom,"\xA9" "alb",4)) {
|
} else if(!memcmp(current_atom,"\xA9" "alb",4)) {
|
||||||
pmp3->album=strdup((char*)¤t_data[16]);
|
pmp3->album=strdup((char*)¤t_data[16]);
|
||||||
} else if(!memcmp(current_atom,"\xA9" "cmt",4)) {
|
} else if(!memcmp(current_atom,"\xA9" "cmt",4)) {
|
||||||
pmp3->comment=strdup((char*)¤t_data[16]);
|
pmp3->comment=strdup((char*)¤t_data[16]);
|
||||||
} else if(!memcmp(current_atom,"\xA9" "wrt",4)) {
|
} else if(!memcmp(current_atom,"\xA9" "wrt",4)) {
|
||||||
pmp3->composer=strdup((char*)¤t_data[16]);
|
pmp3->composer=strdup((char*)¤t_data[16]);
|
||||||
} else if(!memcmp(current_atom,"\xA9" "grp",4)) {
|
} else if(!memcmp(current_atom,"\xA9" "grp",4)) {
|
||||||
pmp3->grouping=strdup((char*)¤t_data[16]);
|
pmp3->grouping=strdup((char*)¤t_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*)¤t_data[16]);
|
pmp3->genre=strdup((char*)¤t_data[16]);
|
||||||
} else if(!memcmp(current_atom,"tmpo",4)) {
|
} else if(!memcmp(current_atom,"tmpo",4)) {
|
||||||
us_data=*((unsigned short *)¤t_data[16]);
|
us_data=*((unsigned short *)¤t_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 *)¤t_data[18]);
|
us_data=*((unsigned short *)¤t_data[18]);
|
||||||
us_data=ntohs(us_data);
|
us_data=ntohs(us_data);
|
||||||
|
|
||||||
pmp3->track=us_data;
|
pmp3->track=us_data;
|
||||||
|
|
||||||
us_data=*((unsigned short *)¤t_data[20]);
|
us_data=*((unsigned short *)¤t_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 *)¤t_data[18]);
|
us_data=*((unsigned short *)¤t_data[18]);
|
||||||
us_data=ntohs(us_data);
|
us_data=ntohs(us_data);
|
||||||
|
|
||||||
pmp3->disc=us_data;
|
pmp3->disc=us_data;
|
||||||
|
|
||||||
us_data=*((unsigned short *)¤t_data[20]);
|
us_data=*((unsigned short *)¤t_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*)¤t_data[16]);
|
pmp3->year=atoi((char*)¤t_data[16]);
|
||||||
} else if(!memcmp(current_atom,"gnre",4)) {
|
} else if(!memcmp(current_atom,"gnre",4)) {
|
||||||
genre=(int)(*((char*)¤t_data[17]));
|
genre=(int)(*((char*)¤t_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;
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
Loading…
Reference in New Issue