// HasID3V1Tag returns true if an ID3v1 tag is present at the end of r. func HasID3v1Tag(r readerutil.SizeReaderAt) (bool, error) { if r.Size() < ID3v1TagLength { return false, nil } buf := make([]byte, len(id3v1Magic), len(id3v1Magic)) if _, err := r.ReadAt(buf, r.Size()-ID3v1TagLength); err != nil { return false, fmt.Errorf("Failed to read ID3v1 data: %v", err) } if bytes.Equal(buf, id3v1Magic) { return true, nil } return false, nil }
// GetMPEGAudioDuration reads the first frame in r and returns the audio length with millisecond precision. // Format details are at http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header. func GetMPEGAudioDuration(r readerutil.SizeReaderAt) (time.Duration, error) { var header uint32 if err := binary.Read(io.NewSectionReader(r, 0, r.Size()), binary.BigEndian, &header); err != nil { return 0, fmt.Errorf("Failed to read MPEG frame header: %v", err) } getBits := func(startBit, numBits uint) uint32 { return (header << startBit) >> (32 - numBits) } if getBits(0, 11) != 0x7ff { return 0, errors.New("Missing sync bits in MPEG frame header") } var version mpegVersion var ok bool if version, ok = mpegVersionsById[getBits(11, 2)]; !ok { return 0, errors.New("Invalid MPEG version index") } var layer mpegLayer if layer, ok = mpegLayersByIndex[getBits(13, 2)]; !ok { return 0, errors.New("Invalid MPEG layer index") } bitrate := mpegBitrates[version][layer][getBits(16, 4)] if bitrate == 0 { return 0, errors.New("Invalid MPEG bitrate") } samplingRate := mpegSamplingRates[version][getBits(20, 2)] if samplingRate == 0 { return 0, errors.New("Invalid MPEG sample rate") } samplesPerFrame := mpegSamplesPerFrame[version][layer] var xingHeaderStart int64 = 4 // Skip "side information". if getBits(24, 2) == 0x3 { // Channel mode; 0x3 is mono. xingHeaderStart += 17 } else { xingHeaderStart += 32 } // Skip 16-bit CRC if present. if getBits(15, 1) == 0x0 { // 0x0 means "has protection". xingHeaderStart += 2 } b := make([]byte, 12, 12) if _, err := r.ReadAt(b, xingHeaderStart); err != nil { return 0, fmt.Errorf("Unable to read Xing header at %d: %v", xingHeaderStart, err) } var ms int64 if bytes.Equal(b[0:4], xingHeaderName) || bytes.Equal(b[0:4], infoHeaderName) { r := bytes.NewReader(b[4:]) var xingFlags uint32 binary.Read(r, binary.BigEndian, &xingFlags) if xingFlags&0x1 == 0x0 { return 0, fmt.Errorf("Xing header at %d lacks number of frames", xingHeaderStart) } var numFrames uint32 binary.Read(r, binary.BigEndian, &numFrames) ms = int64(samplesPerFrame) * int64(numFrames) * 1000 / int64(samplingRate) } else { // Okay, no Xing VBR header. Assume that the file has a constant bitrate. // (The other alternative is to read the whole file and examine each frame.) ms = r.Size() / int64(bitrate) * 8 } return time.Duration(ms) * time.Millisecond, nil }