|
@@ -0,0 +1,220 @@
|
|
|
+package common
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "encoding/binary"
|
|
|
+ "io"
|
|
|
+ "math"
|
|
|
+ "mime/multipart"
|
|
|
+)
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ versions = []string{"2.5", "x", "2", "1"}
|
|
|
+ layers = []string{"x", "3", "2", "1"}
|
|
|
+ bitRates = map[string][]int{
|
|
|
+ "V1Lx": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
|
+ "V1L1": []int{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448},
|
|
|
+ "V1L2": []int{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384},
|
|
|
+ "V1L3": []int{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320},
|
|
|
+ "V2Lx": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
|
+ "V2L1": []int{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
|
|
|
+ "V2L2": []int{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
|
|
|
+ "V2L3": []int{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
|
|
|
+ "VxLx": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
|
+ "VxL1": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
|
+ "VxL2": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
|
+ "VxL3": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
|
+ }
|
|
|
+ sampleRates = map[string][]int{
|
|
|
+ "x": []int{0, 0, 0},
|
|
|
+ "1": []int{44100, 48000, 32000},
|
|
|
+ "2": []int{22050, 24000, 16000},
|
|
|
+ "2.5": []int{11025, 12000, 8000},
|
|
|
+ }
|
|
|
+ samples = map[string]map[string]int{
|
|
|
+ "x": {
|
|
|
+ "x": 0,
|
|
|
+ "1": 0,
|
|
|
+ "2": 0,
|
|
|
+ "3": 0,
|
|
|
+ },
|
|
|
+ "1": { //MPEGv1, Layers 1,2,3
|
|
|
+ "x": 0,
|
|
|
+ "1": 384,
|
|
|
+ "2": 1152,
|
|
|
+ "3": 1152,
|
|
|
+ },
|
|
|
+ "2": { //MPEGv2/2.5, Layers 1,2,3
|
|
|
+ "x": 0,
|
|
|
+ "1": 384,
|
|
|
+ "2": 1152,
|
|
|
+ "3": 576,
|
|
|
+ },
|
|
|
+ }
|
|
|
+)
|
|
|
+
|
|
|
+type frame struct {
|
|
|
+ bitRate int
|
|
|
+ sampleRate int
|
|
|
+ frameSize int
|
|
|
+ sample int
|
|
|
+}
|
|
|
+
|
|
|
+func skipID3(buffer []byte) int {
|
|
|
+ var id3v2Flags, z0, z1, z2, z3 byte
|
|
|
+ var tagSize, footerSize int
|
|
|
+
|
|
|
+ //http://id3.org/d3v2.3.0
|
|
|
+ if buffer[0] == 0x49 && buffer[1] == 0x44 && buffer[2] == 0x33 { //'ID3'
|
|
|
+ id3v2Flags = buffer[5]
|
|
|
+ if (id3v2Flags & 0x10) != 0 {
|
|
|
+ footerSize = 10
|
|
|
+ } else {
|
|
|
+ footerSize = 0
|
|
|
+ }
|
|
|
+
|
|
|
+ //ID3 size encoding is crazy (7 bits in each of 4 bytes)
|
|
|
+ z0 = buffer[6]
|
|
|
+ z1 = buffer[7]
|
|
|
+ z2 = buffer[8]
|
|
|
+ z3 = buffer[9]
|
|
|
+ if ((z0 & 0x80) == 0) && ((z1 & 0x80) == 0) && ((z2 & 0x80) == 0) && ((z3 & 0x80) == 0) {
|
|
|
+ tagSize = (((int)(z0 & 0x7f)) * 2097152) +
|
|
|
+ (((int)(z1 & 0x7f)) * 16384) +
|
|
|
+ (((int)(z2 & 0x7f)) * 128) +
|
|
|
+ ((int)(z3 & 0x7f))
|
|
|
+ return 10 + tagSize + footerSize
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0
|
|
|
+}
|
|
|
+
|
|
|
+func frameSize(samples int, layer string, bitRate, sampleRate, paddingBit int) int {
|
|
|
+ if sampleRate == 0 {
|
|
|
+ return 0
|
|
|
+ } else if layer == "1" {
|
|
|
+ return ((samples * bitRate * 125 / sampleRate) + paddingBit*4)
|
|
|
+ } else { //layer 2, 3
|
|
|
+ return (((samples * bitRate * 125) / sampleRate) + paddingBit)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func parseFrameHeader(header []byte) *frame {
|
|
|
+ b1 := header[1]
|
|
|
+ b2 := header[2]
|
|
|
+
|
|
|
+ versionBits := (b1 & 0x18) >> 3
|
|
|
+ version := versions[versionBits]
|
|
|
+
|
|
|
+ var simpleVersion string
|
|
|
+ if version == "2.5" {
|
|
|
+ simpleVersion = "2"
|
|
|
+ } else {
|
|
|
+ simpleVersion = version
|
|
|
+ }
|
|
|
+
|
|
|
+ layerBits := (b1 & 0x06) >> 1
|
|
|
+ layer := layers[layerBits]
|
|
|
+
|
|
|
+ bitRateKey := "V" + simpleVersion + "L" + layer
|
|
|
+ bitRateIndex := (b2 & 0xf0) >> 4
|
|
|
+
|
|
|
+ var bitRate int
|
|
|
+ frameBitRates, exists := bitRates[bitRateKey]
|
|
|
+ if exists && int(bitRateIndex) < len(frameBitRates) {
|
|
|
+ bitRate = frameBitRates[bitRateIndex]
|
|
|
+ } else {
|
|
|
+ bitRate = 0
|
|
|
+ }
|
|
|
+
|
|
|
+ sampleRateIdx := (b2 & 0x0c) >> 2
|
|
|
+ var sampleRate int
|
|
|
+ frameSampleRates, exists := sampleRates[version]
|
|
|
+ if exists && int(sampleRateIdx) < len(frameSampleRates) {
|
|
|
+ sampleRate = frameSampleRates[sampleRateIdx]
|
|
|
+ } else {
|
|
|
+ sampleRate = 0
|
|
|
+ }
|
|
|
+ sample := samples[simpleVersion][layer]
|
|
|
+
|
|
|
+ paddingBit := (b2 & 0x02) >> 1
|
|
|
+ return &frame{
|
|
|
+ bitRate: bitRate,
|
|
|
+ sampleRate: sampleRate,
|
|
|
+ frameSize: frameSize(sample, layer, bitRate, sampleRate, int(paddingBit)),
|
|
|
+ sample: sample,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func round(duration float64) float64 {
|
|
|
+ return math.Round(duration*1000) / 1000 //round to nearest ms
|
|
|
+}
|
|
|
+
|
|
|
+// BoxHeader 信息头
|
|
|
+type BoxHeader2 struct {
|
|
|
+ Size uint32
|
|
|
+ FourccType [4]byte
|
|
|
+ Size64 uint64
|
|
|
+}
|
|
|
+
|
|
|
+func getHeaderBoxInfo2(data []byte) (boxHeader BoxHeader2) {
|
|
|
+ buf := bytes.NewBuffer(data)
|
|
|
+ binary.Read(buf, binary.BigEndian, &boxHeader)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Calculate returns the duration of an mp3 file
|
|
|
+func GetMP3PlayDuration(file multipart.File) (duration float64, err error) {
|
|
|
+ var size int64
|
|
|
+ var info = make([]byte, 0x10)
|
|
|
+ var boxHeader BoxHeader2
|
|
|
+ file.ReadAt(info, 0)
|
|
|
+ boxHeader = getHeaderBoxInfo2(info)
|
|
|
+ size = int64(boxHeader.Size64)
|
|
|
+ buffer := make([]byte, 100)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ var bytesRead int
|
|
|
+ bytesRead, err = file.Read(buffer)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if bytesRead < 100 {
|
|
|
+ err = errors.New("Corrupt file")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ offset := int64(skipID3(buffer))
|
|
|
+ buffer = make([]byte, 10)
|
|
|
+ for offset < size {
|
|
|
+ bytesRead, e := file.ReadAt(buffer, offset)
|
|
|
+ if e != nil && e != io.EOF {
|
|
|
+ err = e
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if bytesRead < 10 {
|
|
|
+ duration = round(duration)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if buffer[0] == 0xff && (buffer[1]&0xe0) == 0xe0 {
|
|
|
+ info := parseFrameHeader(buffer)
|
|
|
+ if info.frameSize > 0 && info.sample > 0 {
|
|
|
+ offset += int64(info.frameSize)
|
|
|
+ duration += (float64(info.sample) / float64(info.sampleRate))
|
|
|
+ } else {
|
|
|
+ offset++ //Corrupt file?
|
|
|
+ }
|
|
|
+ } else if buffer[0] == 0x54 && buffer[1] == 0x41 && buffer[2] == 0x47 { //'TAG'
|
|
|
+ offset += 128 //Skip over id3v1 tag size
|
|
|
+ } else {
|
|
|
+ offset++ //Corrupt file?
|
|
|
+ }
|
|
|
+ }
|
|
|
+ duration = round(duration)
|
|
|
+ return
|
|
|
+}
|