diff --git a/go.mod b/go.mod index b9c45d6..63c44ac 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,11 @@ go 1.13 require ( github.com/blackjack/webcam v0.0.0-20191123110216-08fa32efcb67 + github.com/faiface/beep v1.0.2 github.com/jfreymuth/pulse v0.0.0-20200107133239-fe42f62ea140 github.com/pion/webrtc/v2 v2.1.19-0.20200106051345-726a16faa60d github.com/satori/go.uuid v1.2.0 gopkg.in/hraban/opus.v2 v2.0.0-20191117073431-57179dff69a6 ) + +replace github.com/faiface/beep => ../../faiface/beep diff --git a/go.sum b/go.sum index 0782a02..698c14c 100644 --- a/go.sum +++ b/go.sum @@ -7,14 +7,28 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell v1.1.1/go.mod h1:K1udHkiR3cOtlpKG5tZPD5XxrF7v2y7lDq7Whcj+xkQ= github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw= +github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherwasm v0.1.1/go.mod h1:kx4n9a+MzHH0BJJhvlsQ65hqLFXDO/m256AsaDPQ+/4= +github.com/gopherjs/gopherwasm v1.0.0 h1:32nge/RlujS1Im4HNCJPp0NbBOAeBXFuT1KonUuLl+Y= +github.com/gopherjs/gopherwasm v1.0.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI= +github.com/hajimehoshi/go-mp3 v0.1.1/go.mod h1:4i+c5pDNKDrxl1iu9iG90/+fhP37lio6gNhjCx9WBJw= +github.com/hajimehoshi/oto v0.1.1/go.mod h1:hUiLWeBQnbDu4pZsAhOnGqMI1ZGibS6e2qhQdfpwz04= +github.com/hajimehoshi/oto v0.3.1 h1:cpf/uIv4Q0oc5uf9loQn7PIehv+mZerh+0KKma6gzMk= +github.com/hajimehoshi/oto v0.3.1/go.mod h1:e9eTLBB9iZto045HLbzfHJIc+jP3xaKrjZTghvb6fdM= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jfreymuth/oggvorbis v1.0.0/go.mod h1:abe6F9QRjuU9l+2jek3gj46lu40N4qlYxh2grqkLEDM= github.com/jfreymuth/pulse v0.0.0-20200107133239-fe42f62ea140 h1:z8Rh8GKvXMJ3sQfXYWCF5NMLnVshzqdPtjvcfyPMrgQ= github.com/jfreymuth/pulse v0.0.0-20200107133239-fe42f62ea140/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no= +github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -22,8 +36,11 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9 h1:tbuodUh2vuhOVZAdW3NEUvosFHUMJwUNl7jk/VSEiwc= github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= +github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4= github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA= github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mewkiz/flac v1.0.5/go.mod h1:EHZNU32dMF6alpurYyKHDLYpW1lYpBZ5WrXi/VuNIGs= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -78,6 +95,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20180710024300-14dda7b62fcd h1:nLIcFw7GiqKXUS7HiChg6OAYWgASB2H97dZKd1GhDSs= +golang.org/x/exp v0.0.0-20180710024300-14dda7b62fcd/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/mobile v0.0.0-20180806140643-507816974b79 h1:t2JRgCWkY7Qaa1J2jal+wqC9OjbyHCHwIA9rVlRUSMo= +golang.org/x/mobile v0.0.0-20180806140643-507816974b79/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -86,6 +109,7 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -93,6 +117,7 @@ golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab h1:FvshnhkKW+LO3HWHodML8kuVX golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/codec/codec.go b/pkg/codec/codec.go index 9614e69..f174e08 100644 --- a/pkg/codec/codec.go +++ b/pkg/codec/codec.go @@ -1,6 +1,10 @@ package codec -import "image" +import ( + "github.com/pion/mediadevices/pkg/io/audio" + "image" + "io" +) type VideoEncoder interface { Encode(img image.Image) ([]byte, error) @@ -21,13 +25,10 @@ type VideoSetting struct { type VideoEncoderBuilder func(s VideoSetting) (VideoEncoder, error) type VideoDecoderBuilder func(s VideoSetting) (VideoDecoder, error) -type AudioEncoder interface { - Encode([]int16) ([]byte, error) - Close() error -} - type AudioSetting struct { - SampleRate int + InSampleRate, OutSampleRate int + // Latency in ms + Latency float64 } -type AudioEncoderBuilder func(s AudioSetting) (AudioEncoder, error) +type AudioEncoderBuilder func(r audio.Reader, s AudioSetting) (io.ReadCloser, error) diff --git a/pkg/codec/opus/opus.go b/pkg/codec/opus/opus.go index 35c9230..675a2c0 100644 --- a/pkg/codec/opus/opus.go +++ b/pkg/codec/opus/opus.go @@ -2,52 +2,111 @@ package opus import ( "fmt" + "io" + "math" + "reflect" + "unsafe" + "github.com/faiface/beep" "github.com/pion/mediadevices/pkg/codec" + "github.com/pion/mediadevices/pkg/io/audio" "github.com/pion/webrtc/v2" "gopkg.in/hraban/opus.v2" ) type encoder struct { engine *opus.Encoder - buff []byte + inBuff [][2]float32 + reader audio.Reader } -var _ codec.AudioEncoder = &encoder{} +var latencies = []float64{5, 10, 20, 40, 60} + +var _ io.ReadCloser = &encoder{} var _ codec.AudioEncoderBuilder = codec.AudioEncoderBuilder(NewEncoder) func init() { codec.Register(webrtc.Opus, codec.AudioEncoderBuilder(NewEncoder)) } -func NewEncoder(s codec.AudioSetting) (codec.AudioEncoder, error) { - channels := 1 // mono - engine, err := opus.NewEncoder(48000, channels, opus.AppVoIP) +func NewEncoder(r audio.Reader, s codec.AudioSetting) (io.ReadCloser, error) { + if s.InSampleRate == 0 { + return nil, fmt.Errorf("opus: InSampleRate is required") + } + + if s.OutSampleRate == 0 { + s.OutSampleRate = 48000 + } + + if s.Latency == 0 { + s.Latency = 20 + } + + // Select the nearest supported latency + var targetLatency float64 + nearestDist := math.Inf(+1) + for _, latency := range latencies { + dist := math.Abs(latency - s.Latency) + if dist >= nearestDist { + break + } + + nearestDist = dist + targetLatency = latency + } + + // Since audio.Reader only supports stereo mode, channels is always 2 + channels := 2 + + engine, err := opus.NewEncoder(s.OutSampleRate, channels, opus.AppVoIP) if err != nil { return nil, err } - buffSize := 1024 - buff := make([]byte, buffSize) - return &encoder{engine, buff}, nil + inBuffSize := targetLatency * float64(s.OutSampleRate) / 1000 + inBuff := make([][2]float32, int(inBuffSize)) + streamer := audio.ToBeep(r) + newSampleRate := beep.SampleRate(s.OutSampleRate) + oldSampleRate := beep.SampleRate(s.InSampleRate) + streamer = beep.Resample(3, oldSampleRate, newSampleRate, streamer) + + reader := audio.FromBeep(streamer) + e := encoder{engine, inBuff, reader} + return &e, nil } -func (e *encoder) Encode(b []int16) ([]byte, error) { - frameSize := len(b) // must be interleaved if stereo - frameSizeMs := float32(frameSize) * 1000 / 48000 - switch frameSizeMs { - case 2.5, 5, 10, 20, 40, 60: - // Good. - default: - return nil, fmt.Errorf("Illegal frame size: %d bytes (%f ms)", frameSize, frameSizeMs) +func flatten(samples [][2]float32) []float32 { + if len(samples) == 0 { + return nil } - n, err := e.engine.Encode(b, e.buff) + data := uintptr(unsafe.Pointer(&samples[0])) + l := len(samples) * 2 + return *(*[]float32)(unsafe.Pointer(&reflect.SliceHeader{Data: data, Len: l, Cap: l})) +} + +func (e *encoder) Read(p []byte) (n int, err error) { + var curN int + + // While the buffer is not full, keep reading so that we meet the latency requirement + for curN < len(e.inBuff) { + n, err := e.reader.Read(e.inBuff[curN:]) + if err != nil { + return 0, err + } + + curN += n + } if err != nil { - return nil, err + return 0, err } - return e.buff[:n], nil + n, err = e.engine.EncodeFloat32(flatten(e.inBuff), p) + if err != nil { + return n, err + } + + return n, nil } func (e *encoder) Close() error { diff --git a/pkg/codec/registrar.go b/pkg/codec/registrar.go index aee525c..e226833 100644 --- a/pkg/codec/registrar.go +++ b/pkg/codec/registrar.go @@ -2,6 +2,8 @@ package codec import ( "fmt" + "github.com/pion/mediadevices/pkg/io/audio" + "io" ) var ( @@ -39,11 +41,11 @@ func BuildVideoDecoder(name string, s VideoSetting) (VideoDecoder, error) { return b(s) } -func BuildAudioEncoder(name string, s AudioSetting) (AudioEncoder, error) { +func BuildAudioEncoder(name string, r audio.Reader, s AudioSetting) (io.ReadCloser, error) { b, ok := audioEncoders[name] if !ok { return nil, fmt.Errorf("codec: can't find %s audio encoder", name) } - return b(s) + return b(r, s) } diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go index b4df7ae..c64ed4f 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go @@ -1,6 +1,7 @@ package driver import ( + "github.com/pion/mediadevices/pkg/io/audio" "github.com/pion/mediadevices/pkg/frame" ) @@ -14,7 +15,6 @@ const ( ) type DataCb func(b []byte) -type AudioDataCb func(b []int16) type OpenCloser interface { Open() error @@ -42,13 +42,14 @@ type VideoSetting struct { } type AudioCapable interface { - Start(setting AudioSetting, cb AudioDataCb) error + Start(setting AudioSetting) (audio.Reader, error) Stop() error Settings() []AudioSetting } type AudioSetting struct { SampleRate int + Mono bool } type Adapter interface { diff --git a/pkg/driver/microphone_linux.go b/pkg/driver/microphone_linux.go index 05f2291..6638a87 100644 --- a/pkg/driver/microphone_linux.go +++ b/pkg/driver/microphone_linux.go @@ -1,12 +1,16 @@ package driver import ( + "io" + "github.com/jfreymuth/pulse" + "github.com/pion/mediadevices/pkg/io/audio" ) type microphone struct { - c *pulse.Client - s *pulse.RecordStream + c *pulse.Client + s *pulse.RecordStream + samplesChan chan<- []float32 } var _ AudioAdapter = µphone{} @@ -34,31 +38,60 @@ func (m *microphone) Close() error { return nil } -func (m *microphone) Start(setting AudioSetting, cb AudioDataCb) error { - buff := make([]int16, 960) - n := 0 - handler := func(b []int16) { - for n+len(b) >= 960 { - nCopied := copy(buff[n:], b) - cb(buff) - n = 0 - b = b[nCopied:] +func (m *microphone) Start(setting AudioSetting) (audio.Reader, error) { + var options []pulse.RecordOption + if setting.Mono { + options = append(options, pulse.RecordMono) + } else { + options = append(options, pulse.RecordStereo) + } + options = append(options, pulse.RecordSampleRate(48000), pulse.RecordBufferFragmentSize(512)) + + samplesChan := make(chan []float32, 1) + var buff []float32 + var bi int + var more bool + + reader := audio.ReaderFunc(func(samples [][2]float32) (n int, err error) { + for i := range samples { + // if we don't have anything left in buff, we'll wait until we receive + // more samples + if bi == len(buff) { + buff, more = <-samplesChan + if !more { + return i, io.EOF + } + bi = 0 + } + + samples[i][0] = buff[bi] + if !setting.Mono { + samples[i][1] = buff[bi+1] + bi++ + } + bi++ } - nCopied := copy(buff[n:], b) - n += nCopied + + return len(samples), nil + }) + + handler := func(b []float32) { + samplesChan <- b } - stream, err := m.c.NewRecord(handler, pulse.RecordSampleRate(48000), pulse.RecordLatency(0.005)) + stream, err := m.c.NewRecord(handler, options...) if err != nil { - return err + return nil, err } stream.Start() m.s = stream - return nil + m.samplesChan = samplesChan + return reader, nil } func (m *microphone) Stop() error { + close(m.samplesChan) m.s.Stop() return nil } @@ -71,10 +104,8 @@ func (m *microphone) Info() Info { } func (m *microphone) Settings() []AudioSetting { - src, err := m.c.DefaultSource() - if err != nil { - return nil - } - - return []AudioSetting{AudioSetting{src.SampleRate()}} + return []AudioSetting{AudioSetting{ + SampleRate: 48000, + Mono: false, + }} } diff --git a/pkg/io/audio/beep.go b/pkg/io/audio/beep.go new file mode 100644 index 0000000..2d610d1 --- /dev/null +++ b/pkg/io/audio/beep.go @@ -0,0 +1,87 @@ +package audio + +import ( + "io" + + "github.com/faiface/beep" +) + +type beepStreamer struct { + err error + r Reader + buff [][2]float32 +} + +func ToBeep(r Reader) beep.Streamer { + if r == nil { + panic("FromReader requires a non-nil Reader") + } + + return &beepStreamer{r: r} +} + +func (b *beepStreamer) Stream(samples [][2]float64) (n int, ok bool) { + // Since there was an error, the stream has to be drained + if b.err != nil { + return 0, false + } + + if len(b.buff) < len(samples) { + b.buff = append(b.buff, make([][2]float32, len(samples)-len(b.buff))...) + } + + n, err := b.r.Read(b.buff[:len(samples)]) + if err != nil { + b.err = err + if err != io.EOF { + return 0, false + } + } + + for i := 0; i < n; i++ { + samples[i][0] = float64(b.buff[i][0]) + samples[i][1] = float64(b.buff[i][1]) + } + + return n, true +} + +func (b *beepStreamer) Err() error { + return b.err +} + +type beepReader struct { + s beep.Streamer + buff [][2]float64 +} + +func FromBeep(s beep.Streamer) Reader { + if s == nil { + panic("FromStreamer requires a non-nil beep.Streamer") + } + + return &beepReader{s: s} +} + +func (r *beepReader) Read(samples [][2]float32) (n int, err error) { + if len(r.buff) < len(samples) { + r.buff = append(r.buff, make([][2]float64, len(samples)-len(r.buff))...) + } + + n, ok := r.s.Stream(r.buff[:len(samples)]) + if !ok { + err := r.s.Err() + if err == nil { + err = io.EOF + } + + return n, err + } + + for i := 0; i < n; i++ { + samples[i][0] = float32(r.buff[i][0]) + samples[i][1] = float32(r.buff[i][1]) + } + + return n, nil +} diff --git a/track.go b/track.go index c143c32..80ebfa9 100644 --- a/track.go +++ b/track.go @@ -2,6 +2,7 @@ package mediadevices import ( "fmt" + "io" "math/rand" "github.com/pion/mediadevices/pkg/codec" @@ -137,7 +138,7 @@ type audioTrack struct { *track d driver.AudioDriver setting driver.AudioSetting - encoder codec.AudioEncoder + encoder io.ReadCloser } var _ Tracker = &audioTrack{} @@ -148,44 +149,43 @@ func newAudioTrack(pc *webrtc.PeerConnection, d driver.AudioDriver, setting driv return nil, err } - encoder, err := codec.BuildAudioEncoder(codecName, codec.AudioSetting{ - SampleRate: setting.SampleRate, - }) + reader, err := d.Start(setting) if err != nil { return nil, err } - vt := audioTrack{ + codecSetting := codec.AudioSetting{ + InSampleRate: setting.SampleRate, + } + + encoder, err := codec.BuildAudioEncoder(codecName, reader, codecSetting) + if err != nil { + return nil, err + } + + at := audioTrack{ track: t, d: d, setting: setting, encoder: encoder, } - - err = d.Start(setting, vt.dataCb) - if err != nil { - encoder.Close() - return nil, err - } - - return &vt, nil + go at.start() + return &at, nil } -func (vt *audioTrack) dataCb(b []int16) { - encoded, err := vt.encoder.Encode(b) - if err != nil { - // TODO: probably do some logging here - return - } - - err = vt.s.sample(encoded) - if err != nil { - // TODO: probably do some logging here - return +func (t *audioTrack) start() { + buff := make([]byte, 1024) + for { + n, err := t.encoder.Read(buff) + if err != nil { + // TODO: better error handling + panic(err) + } + t.s.sample(buff[:n]) } } -func (vt *audioTrack) Stop() { - vt.d.Stop() - vt.encoder.Close() +func (t *audioTrack) Stop() { + t.d.Stop() + t.encoder.Close() }