github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/media/audio.go (about)

     1  /*
     2  Copyright 2014 The Camlistore Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package media provides means for querying information about audio and video data.
    18  package media
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/binary"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"time"
    27  
    28  	"camlistore.org/pkg/types"
    29  )
    30  
    31  // ID3v1TagLength is the length of an MP3 ID3v1 tag in bytes.
    32  const ID3v1TagLength = 128
    33  
    34  // id3v1Magic is the byte sequence appearing at the beginning of an ID3v1 tag.
    35  var id3v1Magic = []byte("TAG")
    36  
    37  // HasID3V1Tag returns true if an ID3v1 tag is present at the end of r.
    38  func HasID3v1Tag(r types.SizeReaderAt) (bool, error) {
    39  	if r.Size() < ID3v1TagLength {
    40  		return false, nil
    41  	}
    42  
    43  	buf := make([]byte, len(id3v1Magic), len(id3v1Magic))
    44  	if _, err := r.ReadAt(buf, r.Size()-ID3v1TagLength); err != nil {
    45  		return false, fmt.Errorf("Failed to read ID3v1 data: ", err)
    46  	}
    47  	if bytes.Equal(buf, id3v1Magic) {
    48  		return true, nil
    49  	}
    50  	return false, nil
    51  }
    52  
    53  type mpegVersion int
    54  
    55  const (
    56  	mpegVersion1 mpegVersion = iota
    57  	mpegVersion2
    58  	mpegVersion2_5
    59  )
    60  
    61  // mpegVersionsById maps from a 2-bit version ID from an MPEG header to the corresponding MPEG audio version.
    62  var mpegVersionsById = map[uint32]mpegVersion{
    63  	0x0: mpegVersion2_5,
    64  	0x2: mpegVersion2,
    65  	0x3: mpegVersion1,
    66  }
    67  
    68  type mpegLayer int
    69  
    70  const (
    71  	mpegLayer1 mpegLayer = iota
    72  	mpegLayer2
    73  	mpegLayer3
    74  )
    75  
    76  // mpegLayersByIndex maps from a 2-bit layer index from an MPEG header to the corresponding MPEG layer.
    77  var mpegLayersByIndex = map[uint32]mpegLayer{
    78  	0x1: mpegLayer3,
    79  	0x2: mpegLayer2,
    80  	0x3: mpegLayer1,
    81  }
    82  
    83  // mpegBitrates is indexed by a 4-bit bitrate index from an MPEG header. Values are in kilobits.
    84  var mpegBitrates = map[mpegVersion]map[mpegLayer][16]int{
    85  	mpegVersion1: {
    86  		mpegLayer1: {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
    87  		mpegLayer2: {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
    88  		mpegLayer3: {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0},
    89  	},
    90  	mpegVersion2: {
    91  		mpegLayer1: {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
    92  		mpegLayer2: {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
    93  		mpegLayer3: {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
    94  	},
    95  	mpegVersion2_5: {
    96  		mpegLayer1: {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
    97  		mpegLayer2: {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
    98  		mpegLayer3: {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
    99  	},
   100  }
   101  
   102  // mpegSamplingRates is indexed by a 2-bit sampling rate index from an MPEG header. Values are in hertz.
   103  var mpegSamplingRates = map[mpegVersion][4]int{
   104  	mpegVersion1:   {44100, 48000, 32000, 0},
   105  	mpegVersion2:   {22050, 24000, 16000, 0},
   106  	mpegVersion2_5: {11025, 12000, 8000, 0},
   107  }
   108  
   109  var mpegSamplesPerFrame = map[mpegVersion]map[mpegLayer]int{
   110  	mpegVersion1: {
   111  		mpegLayer1: 384,
   112  		mpegLayer2: 1152,
   113  		mpegLayer3: 1152,
   114  	},
   115  	mpegVersion2: {
   116  		mpegLayer1: 384,
   117  		mpegLayer2: 1152,
   118  		mpegLayer3: 576,
   119  	},
   120  	mpegVersion2_5: {
   121  		mpegLayer1: 384,
   122  		mpegLayer2: 1152,
   123  		mpegLayer3: 576,
   124  	},
   125  }
   126  
   127  var xingHeaderName = []byte("Xing")
   128  var infoHeaderName = []byte("Info")
   129  
   130  // GetMPEGAudioDuration reads the first frame in r and returns the audio length with millisecond precision.
   131  // Format details are at http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header.
   132  func GetMPEGAudioDuration(r types.SizeReaderAt) (time.Duration, error) {
   133  	var header uint32
   134  	if err := binary.Read(io.NewSectionReader(r, 0, r.Size()), binary.BigEndian, &header); err != nil {
   135  		return 0, fmt.Errorf("Failed to read MPEG frame header: %v", err)
   136  	}
   137  	getBits := func(startBit, numBits uint) uint32 {
   138  		return (header << startBit) >> (32 - numBits)
   139  	}
   140  
   141  	if getBits(0, 11) != 0x7ff {
   142  		return 0, errors.New("Missing sync bits in MPEG frame header")
   143  	}
   144  	var version mpegVersion
   145  	var ok bool
   146  	if version, ok = mpegVersionsById[getBits(11, 2)]; !ok {
   147  		return 0, errors.New("Invalid MPEG version index")
   148  	}
   149  	var layer mpegLayer
   150  	if layer, ok = mpegLayersByIndex[getBits(13, 2)]; !ok {
   151  		return 0, errors.New("Invalid MPEG layer index")
   152  	}
   153  	bitrate := mpegBitrates[version][layer][getBits(16, 4)]
   154  	if bitrate == 0 {
   155  		return 0, errors.New("Invalid MPEG bitrate")
   156  	}
   157  	samplingRate := mpegSamplingRates[version][getBits(20, 2)]
   158  	if samplingRate == 0 {
   159  		return 0, errors.New("Invalid MPEG sample rate")
   160  	}
   161  	samplesPerFrame := mpegSamplesPerFrame[version][layer]
   162  
   163  	var xingHeaderStart int64 = 4
   164  	// Skip "side information".
   165  	if getBits(24, 2) == 0x3 { // Channel mode; 0x3 is mono.
   166  		xingHeaderStart += 17
   167  	} else {
   168  		xingHeaderStart += 32
   169  	}
   170  	// Skip 16-bit CRC if present.
   171  	if getBits(15, 1) == 0x0 { // 0x0 means "has protection".
   172  		xingHeaderStart += 2
   173  	}
   174  
   175  	b := make([]byte, 12, 12)
   176  	if _, err := r.ReadAt(b, xingHeaderStart); err != nil {
   177  		return 0, fmt.Errorf("Unable to read Xing header at %d: %v", xingHeaderStart, err)
   178  	}
   179  	var ms int64
   180  	if bytes.Equal(b[0:4], xingHeaderName) || bytes.Equal(b[0:4], infoHeaderName) {
   181  		r := bytes.NewReader(b[4:])
   182  		var xingFlags uint32
   183  		binary.Read(r, binary.BigEndian, &xingFlags)
   184  		if xingFlags&0x1 == 0x0 {
   185  			return 0, fmt.Errorf("Xing header at %d lacks number of frames", xingHeaderStart)
   186  		}
   187  		var numFrames uint32
   188  		binary.Read(r, binary.BigEndian, &numFrames)
   189  		ms = int64(samplesPerFrame) * int64(numFrames) * 1000 / int64(samplingRate)
   190  	} else {
   191  		// Okay, no Xing VBR header. Assume that the file has a constant bitrate.
   192  		// (The other alternative is to read the whole file and examine each frame.)
   193  		ms = r.Size() / int64(bitrate) * 8
   194  	}
   195  	return time.Duration(ms) * time.Millisecond, nil
   196  }