Implemented OggReadFile() func to get raw data from an OGG file. Also a func OggGetDuration() was implemented that returns duration of a song.
This commit is contained in:
parent
aaf14e0c83
commit
17eeebf1f3
@ -6,9 +6,57 @@ for certain tag names ending with an = character, e.g. artist= and title=.
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const bufferLength = 4096
|
||||||
|
const OggMagicSequence = "OggS"
|
||||||
|
const OggVorbisSequence = "vorbis"
|
||||||
|
|
||||||
|
// OggReadFile returns a head of an OGG file, and a buffer contains last frame.
|
||||||
|
func OggReadFile(path string) (buf_start, buf_end []byte, _ error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_start = make([]byte, bufferLength)
|
||||||
|
|
||||||
|
if _, err := f.Read(buf_start); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_end = make([]byte, bufferLength)
|
||||||
|
|
||||||
|
if _, err := f.Seek(-bufferLength, io.SeekEnd); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fst, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for offset := int64(bufferLength); offset <= fst.Size(); offset += bufferLength {
|
||||||
|
if _, err := f.Seek(-offset, io.SeekEnd); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := f.Read(buf_end); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Contains(buf_end, []byte(OggMagicSequence)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// OggGetTag is searching for a certain tag in a given buffer buf and returns
|
// OggGetTag is searching for a certain tag in a given buffer buf and returns
|
||||||
// its value.
|
// its value.
|
||||||
//
|
//
|
||||||
@ -29,3 +77,22 @@ func OggGetTag(buf []byte, tag string) string {
|
|||||||
valLen := int(buf[tagIdx-4]) - tagNameLen
|
valLen := int(buf[tagIdx-4]) - tagNameLen
|
||||||
return string(buf[valStart : valStart+valLen])
|
return string(buf[valStart : valStart+valLen])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OggGetDuration returns song's duration in milliseconds.
|
||||||
|
func OggGetDuration(buf_start, buf_end []byte) time.Duration {
|
||||||
|
rateIdx := bytes.Index(buf_start, []byte(OggVorbisSequence)) +
|
||||||
|
len(OggVorbisSequence) + 5
|
||||||
|
rateBytes := buf_start[rateIdx : rateIdx+4]
|
||||||
|
rate := int32(rateBytes[0]) + int32(rateBytes[1])<<8 +
|
||||||
|
int32(rateBytes[2])<<16 + int32(rateBytes[3])<<24
|
||||||
|
|
||||||
|
granuleIdx := bytes.LastIndex(buf_end, []byte(OggMagicSequence)) +
|
||||||
|
len(OggMagicSequence) + 2
|
||||||
|
granuleBytes := buf_end[granuleIdx : granuleIdx+8]
|
||||||
|
granule := int64(granuleBytes[0]) + int64(granuleBytes[1])<<8 +
|
||||||
|
int64(granuleBytes[2])<<16 + int64(granuleBytes[3])<<24 +
|
||||||
|
int64(granuleBytes[4])<<32 + int64(granuleBytes[5])<<40 +
|
||||||
|
int64(granuleBytes[6])<<48 + int64(granuleBytes[7])<<56
|
||||||
|
|
||||||
|
return time.Duration(granule*1000/int64(rate)) * time.Millisecond
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package oggtag
|
package oggtag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -33,3 +34,31 @@ func BenchmarkOggGetTag(b *testing.B) {
|
|||||||
OggGetTag(buf, "artist")
|
OggGetTag(buf, "artist")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOggReadFile(t *testing.T) {
|
||||||
|
bs, bf, err := OggReadFile(sampleSong)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("bs = ", len(bs), "; bf =", len(bf), "bf(OggS) =", bytes.LastIndex(bf, []byte("OggS")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkOggGetDuration(b *testing.B) {
|
||||||
|
bs, bf, err := OggReadFile(sampleSong)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
OggGetDuration(bs, bf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOggGetDuration(t *testing.T) {
|
||||||
|
bs, bf, err := OggReadFile(sampleSong)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Log(OggGetDuration(bs, bf))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user