File: mp3.go 1 package mediainfo 2 3 import ( 4 "errors" 5 "io" 6 "math" 7 ) 8 9 // http://www.mp3-tech.org/programmer/frame_header.html 10 11 /* 12 aaaaaaaaaaa bb cc d eeee ff g h ii jj k l mm 13 11111111111 00 00 0 0000 00 0 0 00 00 0 0 00 frame-sync mask 14 00000000000 11 00 0 0000 00 0 0 00 00 0 0 00 audio version mask 15 00000000000 00 11 0 0000 00 0 0 00 00 0 0 00 audio layer mask 16 00000000000 00 00 1 0000 00 0 0 00 00 0 0 00 CRC check mask 17 00000000000 00 00 0 1111 00 0 0 00 00 0 0 00 bit-rate index mask 18 00000000000 00 00 0 0000 11 0 0 00 00 0 0 00 sample-rate index mask 19 00000000000 00 00 0 0000 00 1 0 00 00 0 0 00 frame-padding check mask 20 21 aaaaaaaa aaabbccd eeeeffgh iijjklmm 22 */ 23 24 // these errors are for the rarely-used reserved options in frame headers 25 var ( 26 ErrMP3ReservedLayer = errors.New("MP3 data use reserved format layer") 27 ErrMP3ReservedVersion = errors.New("MP3 data use reserved format versions") 28 ) 29 30 var mp3BitRates = []int{ 31 0, 0, 0, 0, 0, 0, // free bit-rates 32 32000, 32000, 32000, 32000, 8000, 8000, 33 64000, 48000, 40000, 48000, 16000, 16000, 34 96000, 56000, 48000, 56000, 24000, 24000, 35 128000, 64000, 56000, 64000, 32000, 32000, 36 160000, 80000, 64000, 80000, 40000, 40000, 37 192000, 96000, 80000, 96000, 48000, 48000, 38 224000, 112000, 96000, 112000, 56000, 56000, 39 256000, 128000, 112000, 128000, 64000, 64000, 40 288000, 160000, 128000, 144000, 80000, 80000, 41 320000, 192000, 160000, 160000, 96000, 96000, 42 352000, 224000, 192000, 176000, 112000, 112000, 43 384000, 256000, 224000, 192000, 128000, 128000, 44 416000, 320000, 256000, 224000, 144000, 144000, 45 448000, 384000, 320000, 256000, 160000, 160000, 46 0, 0, 0, 0, 0, 0, // reserved (invalid) space for bit-rates 47 } 48 49 var mp3SampleRates = []int{ 50 44100, 22050, 11025, 51 48000, 24000, 12000, 52 32000, 16000, 8000, 53 0, 0, 0, 54 } 55 56 // https://stackoverflow.com/questions/6220660/calculating-the-length-of-mp3-frames-in-milliseconds 57 var mp3SamplesPerFrame = []int{ 58 384, 1152, 1152, // MPEG 1 59 384, 1152, 576, // MPEG 2 60 384, 1152, 576, // MPEG 2.5 61 } 62 63 // mp3Duration tries to find the duration in seconds of the MP3 stream given 64 func mp3Duration(r io.ReadSeeker) (seconds float64, err error) { 65 // buffers larger than 32kb don't seem to speed things up further 66 var b [32 * 1024]byte 67 68 // how many leading bytes to skip/ignore from current chunk 69 skip := 0 70 71 for i := 0; true; i++ { 72 n, err := r.Read(b[:]) 73 if n <= 0 { 74 return seconds, nil 75 } 76 if err != nil && err != io.EOF { 77 return seconds, err 78 } 79 80 // only check for ID3v2 metadata on the first chunk read 81 if i == 0 && n >= 10 { 82 skip = calcSizeID3v2(b[:n]) 83 } 84 85 // done, if there aren't enough data for a frame intro 86 if n < 3 { 87 return seconds, nil 88 } 89 90 // check whether whole chunk needs skipping, as unlikely as that is 91 if n < skip { 92 skip -= n 93 continue 94 } 95 96 dt, skipnext, err := mp3SliceDuration(b[skip:n]) 97 if err != nil { 98 return seconds, err 99 } 100 101 skip = skipnext 102 seconds += dt 103 } 104 105 return seconds, nil 106 } 107 108 // mp3SliceDuration handles the slice logic for func mp3Duration 109 func mp3SliceDuration(b []byte) (sec float64, skip int, err error) { 110 // when there aren't enough data for a frame, the duration is 0 seconds 111 if len(b) < 3 { 112 return 0, 0, nil 113 } 114 115 // upper-limit for index is 2 less than length, since there's a 2-byte 116 // look-ahead in loop 117 for i := 0; i < len(b)-2; i++ { 118 // frames start with their first 11 bits all on 119 syn := b[i] 120 if syn != 255 { 121 // not all the first 8 bits are on: not a frame-sync 122 continue 123 } 124 h1 := b[i+1] 125 if h1 < 224 { 126 // not all the 3 extra bits are on: not a frame-sync 127 continue 128 } 129 h2 := b[i+2] 130 131 // check the audio layer number using the 3rd-last and 2nd-last bits 132 layer := 0 133 switch h1 & 0b00000110 { 134 case 0: 135 // return t, ErrMP3ReservedLayer 136 continue 137 case 2: 138 layer = 3 139 case 4: 140 layer = 2 141 case 6: 142 layer = 1 143 } 144 145 // check the MPEG version using the 5th-last and 4th-last bits: 146 // version 3 means MPEG 2.5 147 version := 0 148 switch h1 & 0b00011000 { 149 case 0: // MPEG 2.5 150 version = 3 151 case 8: // reserved (invalid) // 0b00001000 152 // return t, ErrMP3ReservedVersion 153 // ignore frames with a reserved-value version, instead of 154 // giving an error 155 continue 156 case 16: // MPEG 2 // 0b00010000 157 version = 2 158 case 24: // MPEG 1 // 0b00011000 159 version = 1 160 } 161 162 // check for frame padding using the 2nd-last bit 163 padding := 0 164 if h2&0b00000010 != 0 { 165 padding = 1 166 } 167 168 bitRateRow := int(h2 >> 4) 169 if bitRateRow == 0 || bitRateRow == 15 { 170 continue 171 } 172 173 sampleRateRow := int(h2 & 0b00001100 >> 2) 174 if sampleRateRow == 3 { 175 continue 176 } 177 178 bitRate := 0 179 switch version { 180 case 1: 181 bitRate = mp3BitRates[6*bitRateRow+layer-1] 182 case 2, 3: 183 bitRate = mp3BitRates[6*bitRateRow+2*layer-1] 184 } 185 sampleRate := mp3SampleRates[3*sampleRateRow+version-1] 186 187 // update time duration value 188 spf := mp3SamplesPerFrame[3*(version-1)+layer-1] 189 sec += float64(spf) / float64(sampleRate) 190 191 // calculate how many bytes to jump forward 192 // 193 // http://www.mp3-converter.com/mp3codec/frames.htm 194 // the formula suggested there seems wrong 195 // frame_size = 144 * bit_rate / (sample_rate + padding) 196 // and should instead be 197 // frame_size = floor(144 * bit_rate / sample_rate) + padding 198 n := int(math.Floor(float64(144*bitRate)/float64(sampleRate))) + padding 199 200 // handle skipping inside current slice 201 if i+n < len(b)-3 { 202 // jump ahead by n - 1 instead of n, since the loop already adds 1 203 i += n - 1 204 continue 205 } 206 207 // handle skipping beyond current slice 208 skip = n - (len(b) - i) 209 if skip < 0 { 210 skip = 0 211 } 212 return sec, skip, nil 213 } 214 215 return sec, 0, nil 216 }