-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathformat.go
145 lines (121 loc) · 4.04 KB
/
format.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package audio
import (
"errors"
"io"
"sync"
"sync/atomic"
"github.com/qiniu/x/bufiox"
)
// ErrFormat indicates that decoding encountered an unknown format.
var ErrFormat = errors.New("audio: unknown format")
// -------------------------------------------------------------------------------------
// Decoded represents a decoded audio.
type Decoded interface {
io.ReadSeeker
// SampleRate returns the sample rate like 44100.
SampleRate() int
// Channels returns the number of channels. One channel is mono playback.
// Two channels are stereo playback. No other values are supported.
Channels() int
// BytesPerSample returns the number of bytes per sample per channel.
// The usual value is 2. Only values 1 and 2 are supported.
BytesPerSample() int
// Length returns the total size in bytes. It returns -1 when the total size is not
// available. e.g. when the given source is not io.Seeker.
Length() int64
}
// Config holds an audio's configurations.
type Config struct {
// TODO:
}
// DecodeFunc prototype.
type DecodeFunc = func(io.ReadSeeker) (Decoded, error)
// DecodeConfigFunc prototype.
type DecodeConfigFunc = func(io.ReadSeeker) (Config, error)
// -------------------------------------------------------------------------------------
// A format holds an audio format's name, magic header and how to decode it.
type format struct {
name, magic string
decode DecodeFunc
decodeConfig DecodeConfigFunc
}
// Formats is the list of registered formats.
var (
formatsMu sync.Mutex
atomicFormats atomic.Value
)
// RegisterFormat registers an audio format for use by Decode.
// Name is the name of the format, like "mp3" or "wav".
// Magic is the magic prefix that identifies the format's encoding. The magic
// string can contain "?" wildcards that each match any one byte.
// Decode is the function that decodes the encoded audio.
// decodeCfg is the function that decodes just its configuration.
func RegisterFormat(name, magic string, decode DecodeFunc, decodeCfg DecodeConfigFunc) {
formatsMu.Lock()
formats, _ := atomicFormats.Load().([]format)
atomicFormats.Store(append(formats, format{name, magic, decode, decodeCfg}))
formatsMu.Unlock()
}
// -------------------------------------------------------------------------------------
// A reader is an io.Reader that can also peek ahead.
type reader interface {
io.ReadSeeker
Peek(int) ([]byte, error)
}
// asReader converts an io.ReadSeeker to a reader.
func asReader(r io.ReadSeeker) reader {
if rr, ok := r.(reader); ok {
return rr
}
return bufiox.NewReader(r)
}
// Match reports whether magic matches b. Magic may contain "?" wildcards.
func match(magic string, b []byte) bool {
if len(magic) != len(b) {
return false
}
for i, c := range b {
if magic[i] != c && magic[i] != '?' {
return false
}
}
return true
}
// Sniff determines the format of r's data.
func sniff(r reader) format {
formats, _ := atomicFormats.Load().([]format)
for _, f := range formats {
b, err := r.Peek(len(f.magic))
if err == nil && match(f.magic, b) {
return f
}
}
return format{}
}
// Decode decodes an audio that has been encoded in a registered format.
// The string returned is the format name used during format registration.
// Format registration is typically done by an init function in the codec-
// specific package.
func Decode(r io.ReadSeeker) (Decoded, string, error) {
rr := asReader(r)
f := sniff(rr)
if f.decode == nil {
return nil, "", ErrFormat
}
m, err := f.decode(rr)
return m, f.name, err
}
// DecodeConfig decodes the basic configurations of an audio that has
// been encoded in a registered format. The string returned is the format name
// used during format registration. Format registration is typically done by
// an init function in the codec-specific package.
func DecodeConfig(r io.ReadSeeker) (Config, string, error) {
rr := asReader(r)
f := sniff(rr)
if f.decodeConfig == nil {
return Config{}, "", ErrFormat
}
c, err := f.decodeConfig(rr)
return c, f.name, err
}
// -------------------------------------------------------------------------------------