📄 id3.c
字号:
char tmp[4]; unsigned char version; char *buffer = entry->id3v2buf; int bytesread = 0; int buffersize = sizeof(entry->id3v2buf); unsigned char global_flags; int flags; int skip; bool global_unsynch = false; bool unsynch = false; int data_length_ind; int i; int rc; global_ff_found = false; /* Bail out if the tag is shorter than 10 bytes */ if(entry->id3v2len < 10) return; /* Read the ID3 tag version from the header */ lseek(fd, 0, SEEK_SET); if(10 != read(fd, header, 10)) return; /* Get the total ID3 tag size */ size = entry->id3v2len - 10; version = header[3]; switch ( version ) { case 2: version = ID3_VER_2_2; minframesize = 8; break; case 3: version = ID3_VER_2_3; minframesize = 12; break; case 4: version = ID3_VER_2_4; minframesize = 12; break; default: /* unsupported id3 version */ return; } entry->id3version = version; entry->tracknum = entry->year = 0; entry->genre = 0xff; entry->title = entry->artist = entry->album = NULL; global_flags = header[5]; /* Skip the extended header if it is present */ if(version >= ID3_VER_2_4) { if(global_flags & 0x40) { if(4 != read(fd, header, 4)) return; framelen = UNSYNC(header[0], header[1], header[2], header[3]); lseek(fd, framelen - 4, SEEK_CUR); } } /* Is unsynchronization applied? */ if(global_flags & 0x80) { global_unsynch = true; } /* * We must have at least minframesize bytes left for the * remaining frames to be interesting */ while(size >= minframesize ) { flags = 0; /* Read frame header and check length */ if(version >= ID3_VER_2_3) { if(global_unsynch && version <= ID3_VER_2_3) rc = read_unsynched(fd, header, 10); else rc = read(fd, header, 10); if(rc != 10) return; /* Adjust for the 10 bytes we read */ size -= 10; flags = BYTES2INT(0, 0, header[8], header[9]); if (version >= ID3_VER_2_4) { framelen = UNSYNC(header[4], header[5], header[6], header[7]); } else { /* version .3 files don't use synchsafe ints for * size */ framelen = BYTES2INT(header[4], header[5], header[6], header[7]); } } else { if(6 != read(fd, header, 6)) return; /* Adjust for the 6 bytes we read */ size -= 6; framelen = BYTES2INT(0, header[3], header[4], header[5]); } /* Keep track of the total size */ totframelen = framelen; if(framelen == 0) return; unsynch = false; data_length_ind = 0; if(flags) { skip = 0; if(flags & 0x0040) { /* Grouping identity */ lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ framelen--; } if(flags & 0x000c) /* Compression or encryption */ { /* Skip it using the total size in case it was truncated */ size -= totframelen; lseek(fd, totframelen, SEEK_CUR); continue; } if(flags & 0x0002) /* Unsynchronization */ unsynch = true; if(flags & 0x0001) { /* Data length indicator */ if(4 != read(fd, tmp, 4)) return; data_length_ind = UNSYNC(tmp[0], tmp[1], tmp[2], tmp[3]); framelen -= 4; } } /* If the frame is larger than the remaining buffer space we try to read as much as would fit in the buffer */ if(framelen >= buffersize - bufferpos) framelen = buffersize - bufferpos - 1; DEBUGF("id3v2 frame: %.4s\n", header); /* Check for certain frame headers 'size' is the amount of frame bytes remaining. We decrement it by the amount of bytes we read. If we fail to read as many bytes as we expect, we assume that we can't read from this file, and bail out. For each frame. we will iterate over the list of supported tags, and read the tag into entry's buffer. All tags will be kept as strings, for cases where a number won't do, e.g., YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969" TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name" Text is more flexible, and as the main use of id3 data is to display it, converting it to an int just means reconverting to display it, at a runtime cost. For tags that the current code does convert to ints, a post processing function will be called via a pointer to function. */ for (i=0; i<TAGLIST_SIZE; i++) { struct tag_resolver* tr = &taglist[i]; char** ptag = (char**) (((char*)entry) + tr->offset); char* tag; if( !*ptag && !memcmp( header, tr->tag, tr->tag_length ) ) { /* found a tag matching one in tagList, and not yet filled */ if(global_unsynch && version <= ID3_VER_2_3) bytesread = read_unsynched(fd, buffer + bufferpos, framelen); else bytesread = read(fd, buffer + bufferpos, framelen); if( bytesread != framelen ) return; size -= bytesread; *ptag = buffer + bufferpos; if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) bytesread = unsynchronize_frame(*ptag, bytesread); unicode_munge( ptag, &bytesread ); tag = *ptag; tag[bytesread + 1] = 0; bufferpos += bytesread + 2; if( tr->ppFunc ) bufferpos = tr->ppFunc(entry, tag, bufferpos); break; } } if( i == TAGLIST_SIZE ) { /* no tag in tagList was found, or it was a repeat. skip it using the total size */ if(global_unsynch && version <= ID3_VER_2_3) { skip_unsynched(fd, totframelen); } else { if(data_length_ind) totframelen = data_length_ind; size -= totframelen; if( lseek(fd, totframelen, SEEK_CUR) == -1 ) return; } } }}/* * Calculates the size of the ID3v2 tag. * * Arguments: file - the file to search for a tag. * * Returns: the size of the tag or 0 if none was found */static int getid3v2len(int fd) { char buf[6]; int offset; /* Make sure file has a ID3 tag */ if((-1 == lseek(fd, 0, SEEK_SET)) || (read(fd, buf, 6) != 6) || (strncmp(buf, "ID3", strlen("ID3")) != 0)) offset = 0; /* Now check what the ID3v2 size field says */ else if(read(fd, buf, 4) != 4) offset = 0; else offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10; DEBUGF("ID3V2 Length: 0x%x\n", offset); return offset;}/* * Calculates the length (in milliseconds) of an MP3 file. * * Modified to only use integers. * * Arguments: file - the file to calculate the length upon * entry - the entry to update with the length * * Returns: the song length in milliseconds, * 0 means that it couldn't be calculated */static int getsonglength(int fd, struct mp3entry *entry){ unsigned int filetime = 0; struct mp3info info; int bytecount; /* Start searching after ID3v2 header */ if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) return 0; bytecount = get_mp3file_info(fd, &info); DEBUGF("Space between ID3V2 tag and first audio frame: 0x%x bytes\n", bytecount); if(bytecount < 0) return -1; bytecount += entry->id3v2len; entry->bitrate = info.bitrate; entry->frequency = info.frequency; entry->version = info.version; entry->layer = info.layer; /* If the file time hasn't been established, this may be a fixed rate MP3, so just use the default formula */ filetime = info.file_time; if(filetime == 0) { /* * Now song length is * ((filesize)/(bytes per frame))*(time per frame) */ filetime = entry->filesize/info.frame_size*info.frame_time; } entry->tpf = info.frame_time; entry->bpf = info.frame_size; entry->vbr = info.is_vbr; entry->has_toc = info.has_toc; memcpy(entry->toc, info.toc, sizeof(info.toc)); entry->vbr_header_pos = info.vbr_header_pos; /* Update the seek point for the first playable frame */ entry->first_frame_offset = bytecount; DEBUGF("First frame is at %x\n", entry->first_frame_offset); return filetime;}/* * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc) * about an MP3 file and updates it's entry accordingly. * * Arguments: entry - the entry to check and update with the new information * * Returns: void */bool mp3info(struct mp3entry *entry, char *filename) { int fd; fd = open(filename, O_RDONLY); if(-1 == fd) return true; memset(entry, 0, sizeof(struct mp3entry)); strncpy(entry->path, filename, sizeof(entry->path)); entry->title = NULL; entry->filesize = filesize(fd); entry->id3v2len = getid3v2len(fd); entry->tracknum = 0; entry->genre = 0xff; if (entry->id3v2len) setid3v2title(fd, entry); entry->length = getsonglength(fd, entry); /* Subtract the meta information from the file size to get the true size of the MP3 stream */ entry->filesize -= entry->first_frame_offset; /* only seek to end of file if no id3v2 tags were found */ if (!entry->id3v2len) { if(!entry->title) setid3v1title(fd, entry); } close(fd); if(!entry->length || (entry->filesize < 8 )) /* no song length or less than 8 bytes is hereby considered to be an invalid mp3 and won't be played by us! */ return true; return false;}#ifdef DEBUG_STANDALONEchar *secs2str(int ms){ static char buffer[32]; int secs = ms/1000; ms %= 1000; snprintf(buffer, sizeof(buffer), "%d:%02d.%d", secs/60, secs%60, ms/100); return buffer;}int main(int argc, char **argv){ int i; for(i=1; i<argc; i++) { struct mp3entry mp3; mp3.album = "Bogus"; if(mp3info(&mp3, argv[i])) { printf("Failed to get %s\n", argv[i]); return 0; } printf("****** File: %s\n" " Title: %s\n" " Artist: %s\n" " Album: %s\n" " Genre: %s (%d) \n" " Composer: %s\n" " Year: %s (%d)\n" " Track: %s (%d)\n" " Length: %s / %d s\n" " Bitrate: %d\n" " Frequency: %d\n", argv[i], mp3.title?mp3.title:"<blank>", mp3.artist?mp3.artist:"<blank>", mp3.album?mp3.album:"<blank>", mp3.genre_string?mp3.genre_string:"<blank>", mp3.genre, mp3.composer?mp3.composer:"<blank>", mp3.year_string?mp3.year_string:"<blank>", mp3.year, mp3.track_string?mp3.track_string:"<blank>", mp3.tracknum, secs2str(mp3.length), mp3.length/1000, mp3.bitrate, mp3.frequency); } return 0;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -