mirror of
				https://github.com/owntone/owntone-server.git
				synced 2025-10-29 15:55:02 -04:00 
			
		
		
		
	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) | ||||
|  * 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: 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))) | ||||
| 	    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))) | ||||
| 	    g = g && daap_add_string(mlit,"ascp",song->composer); /* composer */ | ||||
|  | ||||
| @ -104,6 +104,8 @@ typedef struct tag_mp3packed { | ||||
| 
 | ||||
|     int bpm; // DB Version 3
 | ||||
| 
 | ||||
|     char compilation; | ||||
| 
 | ||||
|     unsigned int id; /* inode */ | ||||
| 
 | ||||
|     int path_len; | ||||
| @ -580,6 +582,7 @@ datum *db_packrecord(MP3FILE *pmp3) { | ||||
|     ppacked->time_modified=pmp3->time_modified; | ||||
|     ppacked->time_played=pmp3->time_played; | ||||
|     ppacked->bpm=pmp3->bpm; | ||||
|     ppacked->compilation=pmp3->compilation; | ||||
|     ppacked->id=pmp3->id; | ||||
| 
 | ||||
|     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_played=ppacked->time_played; | ||||
|     pmp3->bpm=ppacked->bpm; | ||||
|     pmp3->compilation=ppacked->compilation; | ||||
|     pmp3->id=ppacked->id; | ||||
| 
 | ||||
|     offset=0; | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| 
 | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| @ -35,6 +36,7 @@ | ||||
| #include "playlist.h" | ||||
| #include "restart.h" | ||||
| 
 | ||||
| #define BLKSIZE PIPE_BUF | ||||
| 
 | ||||
| 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_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_ */ | ||||
|  | ||||
							
								
								
									
										27
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/main.c
									
									
									
									
									
								
							| @ -116,6 +116,8 @@ void daap_handler(WS_CONNINFO *pwsc) { | ||||
|     int session_id=0; | ||||
| 
 | ||||
|     int img_fd; | ||||
|     struct stat sb; | ||||
|     long img_size; | ||||
| 
 | ||||
|     off_t offset=0; | ||||
|     off_t real_len; | ||||
| @ -300,6 +302,24 @@ void daap_handler(WS_CONNINFO *pwsc) { | ||||
| 	    } else { | ||||
| 		real_len=lseek(file_fd,0,SEEK_END); | ||||
| 		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; | ||||
| 
 | ||||
| 		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 */ | ||||
| 
 | ||||
| 		if((config.artfilename) && | ||||
| 		   (!offset) && (strncasecmp(pmp3->type,".mp3",4) ==0) && | ||||
| 		   (!offset) && | ||||
| 		   ((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", | ||||
| 			    pmp3->fname, img_fd); | ||||
| 		    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) { | ||||
| 		    DPRINTF(ERR_INFO,"Seeking to offset %ld\n",(long)offset); | ||||
| 		    lseek(file_fd,offset,SEEK_SET); | ||||
|  | ||||
| @ -43,7 +43,6 @@ | ||||
| 
 | ||||
| #include <netinet/in.h>  /* htons and friends */ | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| #include <dirent.h>      /* why here?  For osx 10.2, of course! */ | ||||
| 
 | ||||
| #include "daapd.h" | ||||
| @ -548,7 +547,6 @@ int scan_get_aactags(char *file, MP3FILE *pmp3) { | ||||
|     FILE *fin; | ||||
|     long atom_offset; | ||||
|     int atom_length; | ||||
|     long file_size; | ||||
| 
 | ||||
|     long current_offset=0; | ||||
|     int current_size; | ||||
| @ -563,100 +561,90 @@ int scan_get_aactags(char *file, MP3FILE *pmp3) { | ||||
| 	return -1; | ||||
|     } | ||||
| 
 | ||||
|     fseek(fin,0,SEEK_END); | ||||
|     file_size=ftell(fin); | ||||
|     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) { | ||||
| 	atom_offset=scan_aac_findatom(fin,atom_length - 8,"udta",&atom_length); | ||||
| 	if(atom_offset != -1) { | ||||
| 	    atom_offset=scan_aac_findatom(fin,atom_length - 8, "meta", &atom_length); | ||||
| 	    if(atom_offset != -1) {  | ||||
| 		fseek(fin,4,SEEK_CUR);   /* ???? */ | ||||
| 		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; | ||||
|       /* 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 */ | ||||
| 			    break; | ||||
|         if(current_size <= 7) /* something not right */ | ||||
|           break; | ||||
| 
 | ||||
| 			if(fread(current_atom,1,4,fin) != 4)  | ||||
| 			    break; | ||||
|         if(fread(current_atom,1,4,fin) != 4)  | ||||
|           break; | ||||
| 			 | ||||
| 			len=current_size-7;  /* for ill-formed too-short tags */ | ||||
| 			if(len < 22) | ||||
| 			    len=22; | ||||
|         len=current_size-7;  /* for ill-formed too-short tags */ | ||||
|         if(len < 22) | ||||
|           len=22; | ||||
| 
 | ||||
| 			current_data=(char*)malloc(len);  /* extra byte */ | ||||
| 			memset(current_data,0x00,len); | ||||
|         current_data=(char*)malloc(len);  /* extra byte */ | ||||
|         memset(current_data,0x00,len); | ||||
| 
 | ||||
| 			if(fread(current_data,1,current_size-8,fin) != current_size-8)  | ||||
| 			    break; | ||||
|         if(fread(current_data,1,current_size-8,fin) != current_size-8)  | ||||
|           break; | ||||
| 
 | ||||
| 			if(!memcmp(current_atom,"\xA9" "nam",4)) { /* Song name */ | ||||
| 			    pmp3->title=strdup((char*)¤t_data[16]); | ||||
| 			} else if(!memcmp(current_atom,"\xA9" "ART",4)) { | ||||
| 			    pmp3->artist=strdup((char*)¤t_data[16]); | ||||
| 			} else if(!memcmp(current_atom,"\xA9" "alb",4)) { | ||||
| 			    pmp3->album=strdup((char*)¤t_data[16]); | ||||
| 			} else if(!memcmp(current_atom,"\xA9" "cmt",4)) { | ||||
| 			    pmp3->comment=strdup((char*)¤t_data[16]); | ||||
| 			} else if(!memcmp(current_atom,"\xA9" "wrt",4)) { | ||||
| 			    pmp3->composer=strdup((char*)¤t_data[16]); | ||||
| 			} else if(!memcmp(current_atom,"\xA9" "grp",4)) { | ||||
| 			    pmp3->grouping=strdup((char*)¤t_data[16]); | ||||
| 			} else if(!memcmp(current_atom,"\xA9" "gen",4)) { | ||||
| 			    /* can this be a winamp genre??? */ | ||||
| 			    pmp3->genre=strdup((char*)¤t_data[16]); | ||||
| 			} else if(!memcmp(current_atom,"tmpo",4)) { | ||||
| 			    us_data=*((unsigned short *)¤t_data[16]); | ||||
| 			    us_data=ntohs(us_data); | ||||
| 			    pmp3->bpm=us_data; | ||||
| 			} else if(!memcmp(current_atom,"trkn",4)) { | ||||
| 			    us_data=*((unsigned short *)¤t_data[18]); | ||||
| 			    us_data=ntohs(us_data); | ||||
|         if(!memcmp(current_atom,"\xA9" "nam",4)) { /* Song name */ | ||||
|           pmp3->title=strdup((char*)¤t_data[16]); | ||||
|         } else if(!memcmp(current_atom,"\xA9" "ART",4)) { | ||||
|           pmp3->artist=strdup((char*)¤t_data[16]); | ||||
|         } else if(!memcmp(current_atom,"\xA9" "alb",4)) { | ||||
|           pmp3->album=strdup((char*)¤t_data[16]); | ||||
|         } else if(!memcmp(current_atom,"\xA9" "cmt",4)) { | ||||
|           pmp3->comment=strdup((char*)¤t_data[16]); | ||||
|         } else if(!memcmp(current_atom,"\xA9" "wrt",4)) { | ||||
|           pmp3->composer=strdup((char*)¤t_data[16]); | ||||
|         } else if(!memcmp(current_atom,"\xA9" "grp",4)) { | ||||
|           pmp3->grouping=strdup((char*)¤t_data[16]); | ||||
|         } else if(!memcmp(current_atom,"\xA9" "gen",4)) { | ||||
|           /* can this be a winamp genre??? */ | ||||
|           pmp3->genre=strdup((char*)¤t_data[16]); | ||||
|         } else if(!memcmp(current_atom,"tmpo",4)) { | ||||
|           us_data=*((unsigned short *)¤t_data[16]); | ||||
|           us_data=ntohs(us_data); | ||||
|           pmp3->bpm=us_data; | ||||
|         } else if(!memcmp(current_atom,"trkn",4)) { | ||||
|           us_data=*((unsigned short *)¤t_data[18]); | ||||
|           us_data=ntohs(us_data); | ||||
| 
 | ||||
| 			    pmp3->track=us_data; | ||||
|           pmp3->track=us_data; | ||||
| 
 | ||||
| 			    us_data=*((unsigned short *)¤t_data[20]); | ||||
| 			    us_data=ntohs(us_data); | ||||
|           us_data=*((unsigned short *)¤t_data[20]); | ||||
|           us_data=ntohs(us_data); | ||||
| 
 | ||||
| 			    pmp3->total_tracks=us_data; | ||||
| 			} else if(!memcmp(current_atom,"disk",4)) { | ||||
| 			    us_data=*((unsigned short *)¤t_data[18]); | ||||
| 			    us_data=ntohs(us_data); | ||||
|           pmp3->total_tracks=us_data; | ||||
|         } else if(!memcmp(current_atom,"disk",4)) { | ||||
|           us_data=*((unsigned short *)¤t_data[18]); | ||||
|           us_data=ntohs(us_data); | ||||
| 
 | ||||
| 			    pmp3->disc=us_data; | ||||
|           pmp3->disc=us_data; | ||||
| 
 | ||||
| 			    us_data=*((unsigned short *)¤t_data[20]); | ||||
| 			    us_data=ntohs(us_data); | ||||
|           us_data=*((unsigned short *)¤t_data[20]); | ||||
|           us_data=ntohs(us_data); | ||||
| 
 | ||||
| 			    pmp3->total_discs=us_data; | ||||
| 			} else if(!memcmp(current_atom,"\xA9" "day",4)) { | ||||
| 			    pmp3->year=atoi((char*)¤t_data[16]); | ||||
| 			} else if(!memcmp(current_atom,"gnre",4)) { | ||||
| 			    genre=(int)(*((char*)¤t_data[17])); | ||||
| 			    genre--; | ||||
|           pmp3->total_discs=us_data; | ||||
|         } else if(!memcmp(current_atom,"\xA9" "day",4)) { | ||||
|           pmp3->year=atoi((char*)¤t_data[16]); | ||||
|         } else if(!memcmp(current_atom,"gnre",4)) { | ||||
|           genre=(int)(*((char*)¤t_data[17])); | ||||
|           genre--; | ||||
| 			     | ||||
| 			    if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN)) | ||||
| 				genre=WINAMP_GENRE_UNKNOWN; | ||||
|           if((genre < 0) || (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); | ||||
| 			current_offset+=current_size; | ||||
| 		    } | ||||
| 		} | ||||
| 	    } | ||||
| 	} | ||||
|         free(current_data); | ||||
|         current_offset+=current_size; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     fclose(fin); | ||||
| @ -824,7 +812,10 @@ int scan_get_mp3tags(char *file, MP3FILE *pmp3) { | ||||
| 		} else if(!strcmp(pid3frame->id,"TBPM")) { | ||||
| 		    pmp3->bpm = atoi(utf8_text); | ||||
| 		    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; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * 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 | ||||
| @ -925,18 +970,15 @@ int scan_get_aacfileinfo(char *file, MP3FILE *pmp3) { | ||||
|     pmp3->file_size=file_size; | ||||
| 
 | ||||
|     /* 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) { | ||||
| 	atom_offset=scan_aac_findatom(infile,atom_length-8,"mvhd",&atom_length); | ||||
| 	if(atom_offset != -1) { | ||||
| 	    fseek(infile,16,SEEK_CUR); | ||||
| 	    fread((void*)&temp_int,1,sizeof(int),infile); | ||||
| 	    temp_int=ntohl(temp_int); | ||||
| 	    /* DWB: use ms time instead of sec */ | ||||
| 	    pmp3->song_length=temp_int * 1000 / 600; | ||||
|       fseek(infile,16,SEEK_CUR); | ||||
|       fread((void*)&temp_int,1,sizeof(int),infile); | ||||
|       temp_int=ntohl(temp_int); | ||||
|       /* 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); | ||||
|     return 0; | ||||
|  | ||||
| @ -22,6 +22,8 @@ | ||||
| #ifndef _MP3_SCANNER_H_ | ||||
| #define _MP3_SCANNER_H_ | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| 
 | ||||
| typedef struct tag_mp3file { | ||||
|     char *path; | ||||
|     char *fname; | ||||
| @ -61,10 +63,14 @@ typedef struct tag_mp3file { | ||||
|     /* generated fields */ | ||||
|     char* description;		/* long file type */ | ||||
|     int item_kind;		/* song or movie */ | ||||
| 
 | ||||
|   char compilation; | ||||
| } MP3FILE; | ||||
| 
 | ||||
| extern int scan_init(char *path); | ||||
| 
 | ||||
| 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_ */ | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user