📄 mp3s.c
字号:
* represented as a "synchsafe", or base-128, integer. * This means 28 bits encode 32 bits as follows: * 0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx means * 0000xxxx xxxxxxxx xxxxxxxx xxxxxxxx. -PS */ size = (long)tag.size[0] << (24-3)|(long)tag.size[1] << (16-2)|(long)tag.size[2] << (8-1)|(long)tag.size[3]; /* skip the id3v2 header */ if (tag.flags & 0x10) { /* this is new in id3v2 version 4.0: tag.flags & 0x10 indicates the presence of a "footer", which is 10 bytes in length, and which is *not* included in the stored tag size */ id3size = size+20; } else { id3size = size+10; } } /* next, check for the existence of a 128-byte tag at the end of the file. Let endtagsize be its length. */ endtagsize = 0; lseek(f, -128, SEEK_END); memset(rdt, 0, sizeof(rdt)); read(f, &rdt, 3); if (!strncmp(rdt, "TAG", 3)) /* found end-tag */ endtagsize = 128; /* first mp3 header should begin just after ID3 tag */ lseek(f, id3size, SEEK_SET); if (!getrhdr(f, &m)) { close(f); return(NULL); } fstat(f, &st); r = (mhdr_t *)malloc(sizeof(mhdr_t)); memset(r, 0, sizeof(mhdr_t)); /* sample frequency and size. Note: r->sz is the file size, whereas r->sz1 is the length of the mp3 stream (=file size minus any tags) */ r->freq = freqtab[m.id][m.freq]; r->sz = st.st_size; r->sz1 = r->sz - id3size - endtagsize; /* to set the song length and average bitrate, we must do different things depending on whether the file is VBR (variable bitrate) or not. */ /* check if first frame is an Xing VBR info frame */ if (checkxing(f, m, id3size, r->sz1, &seconds, &avgbrate)) { r->len = seconds; r->bitrate = avgbrate; } else if (checkvbr(f, id3size, st.st_size, m)) { /* Count the number of frames to determine the length in seconds. * number of seconds = numframes * (seconds per frame) * seconds per frame = (samples/frame)*(seconds/sample) = spf * (1/freq) * (seconds/frame is independent of bitrate) * spf = 1152 (MPEG-1 Layer III) or 576 (MPEG-2 Layer III) (576*2=1152) * (samples/frame is independent of bitrate and frequency) * We have to read all the frame headers to determine the number of frames * because each frame can have a different bitrate and therefore a different * length. * * avg bitrate = (sum of bitrates over all frames)/numframes */ lseek(f, id3size, SEEK_SET); numframes = 0; while (getrhdr(f, &h)) { /* at this point we know m.brate < 15, m.freq < 3, and m.layer = 1 * all frames should have same frequency, layer, and id but we don't * check for this */ numframes++; bpf = framesize(h); lseek(f, bpf-4, SEEK_CUR); /* seek to next frame header */ } /* end while getrhdr */ /* at this point we have reached EOF or an invalid header */ if (numframes == 0) { /* no valid headers found */ close(f); free(r); return(NULL); } spf = (m.id) ? 1152 : 576; r->len = numframes * spf / r->freq; /* avg bitrate in kbps = (number of kilobits)/(number of seconds) */ if (r->len == 0) { /* patch #442669, jpavel */ /* We have a problem here. */ close(f); free(r); return(NULL); } r->bitrate = (r->sz1/r->len)/125; } else { /* assume bitrate is constant */ r->bitrate = bratetab[m.id][m.brate]; r->len = (r->sz1/r->bitrate)/125; } /* finally, seek to end of ID3 header and calculate md5 hash */ lseek(f, id3size, SEEK_SET); res = md5hash(r->check, f); if (res) { free(r); close(f); return(NULL); } close(f); return(r);}/* support for ogg vorbis files: get standard information (bitrate, frequency, length in seconds, size in bytes, and checksum) from the names ogg vorbis file. Return NULL if not recognized as a valid ogg vorbis file. */mhdr_t *ogginfo(char *fn){ /* note: since ogg uses variable bitrate, to keep things simple, we use the "nominal", rather than the "actual" bitrate of the ogg stream. This information can be read from the first header packet. Otherwise we'd have to scan the entire file. We also only give a rough estimate of the song length, based on the file size. Sorry. Scanning an ogg vorbis file for that information would require ogg and vorbis parsers, which is too much overhead for us here. */ /* we make some simple assumptions: the file contains an ogg stream revision 0, whose first page contains precisely one package, which is a vorbis "1" header. All other kinds of ogg streams will fail here. Information on ogg streams comes from http://www.xiph.org/ogg/vorbis/doc/framing.html, information on vorbis "1" header comes from vorbis/lib/info.c in the vorbis distribution. */ unsigned char buf[0x58]; int n; int f; mhdr_t *r; struct stat st; int res; /* get file stats and read first 58 bytes */ f = open(fn, O_RDONLY); if (f == -1) { return NULL;goto fail; } fstat(f, &st); n = read(f, buf, 58); if (n<58) { goto fail; } /* check that this is a first page of a logical ogg stream. We don't bother checking the CRC checksum */ if (strncmp(&buf[0], "OggS", 4) != 0) { /* capture pattern */ goto fail; } if (buf[4] != 0) { /* stream structure version */ goto fail; } if ((buf[5] & 3) != 2) { /* ogg header type flag */ goto fail; } if (buf[18] || buf[19] || buf[20] || buf[21]) {/* page sequence no. */ goto fail; } if (buf[26] != 1 || buf[27] != 30) { /* segment table */ goto fail; } /* check that this is a vorbis "1" header */ if (buf[28] != 1) { /* vorbis packet type */ goto fail; } if (strncmp(&buf[29], "vorbis", 6) != 0) { /* vorbis packet identifier */ goto fail; } if (buf[35] || buf[36] || buf[37] || buf[38]) {/* vorbis version */ goto fail; } if ((buf[57] & 1) != 1) { /* EOP check */ goto fail; } /* seems we found an okay vorbis packet, now read the data. We ignore the "upper" and "lower" bitrates and go straight for the nominal bitrate. */ r = (mhdr_t *)malloc(sizeof(mhdr_t)); if (!r) { goto fail; /* out of memory; fail silently */ } /* channels = buf[39]; */ r->freq = buf[40] | (buf[41]<<8) | (buf[42]<<16) | (buf[43]<<24); r->bitrate = buf[48] | (buf[49]<<8) | (buf[50]<<16) | (buf[51]<<24); r->sz = st.st_size; r->sz1 = st.st_size; /* we have to estimate the length, based on bitrate and file size */ if(!r->bitrate) { /* bitrate was for some reason zero */ free(r); goto fail; } r->len = r->sz * 8 / r->bitrate; /* convert from bps to kbps */ r->bitrate /= 1000; /* calculate checksum */ lseek(f, 0, SEEK_SET); res = md5hash(r->check, f); if (res) { free(r); goto fail; } close(f); return r; fail: close(f); return NULL;}/* calculate md5 hash of the given stream, starting from the current file position. md5bocks is the number of blocks to hash. Write to check. Return -1 on error, else 0. New in 1.5.1: if the user variable "hash" is not set, do not hash, but return a constant string instead. */int md5hash(char check[33], int fd) { unsigned char md5[16]; int i; if (nvar_default("hash", 0) == 0) { /* do not hash - return constant string */ strcpy(check, BOGUS_HASH); return 0; } /* We hash the first 300032 bytes, not 300000 bytes as nap used to do. By the way, 300032 = 1024 * 293. -PS */ if (md5_stream_blocks(fd, 293, md5)) { return(-1); } for (i=0;i<16;i++) sprintf(check+2*i, "%02x", md5[i]); /* check[32] = '\0'; */ return(0);}/* the next function really belongs in md5.c, but since that file is taken verbatim from GNU, we prefer not to change it. *//* Compute MD5 message digest for 1024 * KB bytes read from file FD. The resulting message digest number will be written into the 16 bytes beginning at RESBLOCK. Return 1 on error. */int md5_stream_blocks (int fd, int kb, void *resblock){ struct md5_ctx ctx; char buffer[1024 + 72]; size_t sum; /* Initialize the computation context. */ md5_init_ctx (&ctx); /* Iterate over full file contents. */ while (kb>0) { /* We read the file in blocks of 1024 bytes. One call of the computation function processes the whole buffer so that with the next round of the loop another block can be read. */ size_t n; sum = 0; /* Read block. Take care for partial reads. */ do { n = read (fd, buffer + sum, 1024 - sum); sum += n; } while (sum < 1024 && n != 0 && n != -1); if (n == -1) return 1; /* If end of file is reached, end the loop. */ if (n == 0) { /* Add the last bytes if necessary. */ if (sum > 0) md5_process_bytes (buffer, sum, &ctx); break; } /* Process buffer with 1024 bytes. Note that 1024 % 64 == 0 */ md5_process_block (buffer, 1024, &ctx); kb--; } /* Construct result in desired memory. */ md5_finish_ctx (&ctx, resblock); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -