mirror of
https://github.com/pion/mediadevices.git
synced 2025-10-05 08:36:55 +08:00
Update audio APIs to use audio.Reader instead
This commit is contained in:
3
go.mod
3
go.mod
@@ -4,8 +4,11 @@ go 1.13
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/blackjack/webcam v0.0.0-20191123110216-08fa32efcb67
|
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/jfreymuth/pulse v0.0.0-20200107133239-fe42f62ea140
|
||||||
github.com/pion/webrtc/v2 v2.1.19-0.20200106051345-726a16faa60d
|
github.com/pion/webrtc/v2 v2.1.19-0.20200106051345-726a16faa60d
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
gopkg.in/hraban/opus.v2 v2.0.0-20191117073431-57179dff69a6
|
gopkg.in/hraban/opus.v2 v2.0.0-20191117073431-57179dff69a6
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/faiface/beep => ../../faiface/beep
|
||||||
|
25
go.sum
25
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/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 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
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 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
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 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
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 h1:z8Rh8GKvXMJ3sQfXYWCF5NMLnVshzqdPtjvcfyPMrgQ=
|
||||||
github.com/jfreymuth/pulse v0.0.0-20200107133239-fe42f62ea140/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no=
|
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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
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/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 h1:tbuodUh2vuhOVZAdW3NEUvosFHUMJwUNl7jk/VSEiwc=
|
||||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
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 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
|
||||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
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.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 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
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-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 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
|
||||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
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-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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
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 h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-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-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-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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/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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
package codec
|
package codec
|
||||||
|
|
||||||
import "image"
|
import (
|
||||||
|
"github.com/pion/mediadevices/pkg/io/audio"
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
type VideoEncoder interface {
|
type VideoEncoder interface {
|
||||||
Encode(img image.Image) ([]byte, error)
|
Encode(img image.Image) ([]byte, error)
|
||||||
@@ -21,13 +25,10 @@ type VideoSetting struct {
|
|||||||
type VideoEncoderBuilder func(s VideoSetting) (VideoEncoder, error)
|
type VideoEncoderBuilder func(s VideoSetting) (VideoEncoder, error)
|
||||||
type VideoDecoderBuilder func(s VideoSetting) (VideoDecoder, error)
|
type VideoDecoderBuilder func(s VideoSetting) (VideoDecoder, error)
|
||||||
|
|
||||||
type AudioEncoder interface {
|
|
||||||
Encode([]int16) ([]byte, error)
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type AudioSetting struct {
|
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)
|
||||||
|
@@ -2,52 +2,111 @@ package opus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/faiface/beep"
|
||||||
"github.com/pion/mediadevices/pkg/codec"
|
"github.com/pion/mediadevices/pkg/codec"
|
||||||
|
"github.com/pion/mediadevices/pkg/io/audio"
|
||||||
"github.com/pion/webrtc/v2"
|
"github.com/pion/webrtc/v2"
|
||||||
"gopkg.in/hraban/opus.v2"
|
"gopkg.in/hraban/opus.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type encoder struct {
|
type encoder struct {
|
||||||
engine *opus.Encoder
|
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)
|
var _ codec.AudioEncoderBuilder = codec.AudioEncoderBuilder(NewEncoder)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
codec.Register(webrtc.Opus, codec.AudioEncoderBuilder(NewEncoder))
|
codec.Register(webrtc.Opus, codec.AudioEncoderBuilder(NewEncoder))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEncoder(s codec.AudioSetting) (codec.AudioEncoder, error) {
|
func NewEncoder(r audio.Reader, s codec.AudioSetting) (io.ReadCloser, error) {
|
||||||
channels := 1 // mono
|
if s.InSampleRate == 0 {
|
||||||
engine, err := opus.NewEncoder(48000, channels, opus.AppVoIP)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
buffSize := 1024
|
inBuffSize := targetLatency * float64(s.OutSampleRate) / 1000
|
||||||
buff := make([]byte, buffSize)
|
inBuff := make([][2]float32, int(inBuffSize))
|
||||||
return &encoder{engine, buff}, nil
|
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) {
|
func flatten(samples [][2]float32) []float32 {
|
||||||
frameSize := len(b) // must be interleaved if stereo
|
if len(samples) == 0 {
|
||||||
frameSizeMs := float32(frameSize) * 1000 / 48000
|
return nil
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
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 {
|
func (e *encoder) Close() error {
|
||||||
|
@@ -2,6 +2,8 @@ package codec
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pion/mediadevices/pkg/io/audio"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -39,11 +41,11 @@ func BuildVideoDecoder(name string, s VideoSetting) (VideoDecoder, error) {
|
|||||||
return b(s)
|
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]
|
b, ok := audioEncoders[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("codec: can't find %s audio encoder", name)
|
return nil, fmt.Errorf("codec: can't find %s audio encoder", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return b(s)
|
return b(r, s)
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package driver
|
package driver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/pion/mediadevices/pkg/io/audio"
|
||||||
"github.com/pion/mediadevices/pkg/frame"
|
"github.com/pion/mediadevices/pkg/frame"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,7 +15,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DataCb func(b []byte)
|
type DataCb func(b []byte)
|
||||||
type AudioDataCb func(b []int16)
|
|
||||||
|
|
||||||
type OpenCloser interface {
|
type OpenCloser interface {
|
||||||
Open() error
|
Open() error
|
||||||
@@ -42,13 +42,14 @@ type VideoSetting struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AudioCapable interface {
|
type AudioCapable interface {
|
||||||
Start(setting AudioSetting, cb AudioDataCb) error
|
Start(setting AudioSetting) (audio.Reader, error)
|
||||||
Stop() error
|
Stop() error
|
||||||
Settings() []AudioSetting
|
Settings() []AudioSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
type AudioSetting struct {
|
type AudioSetting struct {
|
||||||
SampleRate int
|
SampleRate int
|
||||||
|
Mono bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Adapter interface {
|
type Adapter interface {
|
||||||
|
@@ -1,12 +1,16 @@
|
|||||||
package driver
|
package driver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/jfreymuth/pulse"
|
"github.com/jfreymuth/pulse"
|
||||||
|
"github.com/pion/mediadevices/pkg/io/audio"
|
||||||
)
|
)
|
||||||
|
|
||||||
type microphone struct {
|
type microphone struct {
|
||||||
c *pulse.Client
|
c *pulse.Client
|
||||||
s *pulse.RecordStream
|
s *pulse.RecordStream
|
||||||
|
samplesChan chan<- []float32
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ AudioAdapter = µphone{}
|
var _ AudioAdapter = µphone{}
|
||||||
@@ -34,31 +38,60 @@ func (m *microphone) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *microphone) Start(setting AudioSetting, cb AudioDataCb) error {
|
func (m *microphone) Start(setting AudioSetting) (audio.Reader, error) {
|
||||||
buff := make([]int16, 960)
|
var options []pulse.RecordOption
|
||||||
n := 0
|
if setting.Mono {
|
||||||
handler := func(b []int16) {
|
options = append(options, pulse.RecordMono)
|
||||||
for n+len(b) >= 960 {
|
} else {
|
||||||
nCopied := copy(buff[n:], b)
|
options = append(options, pulse.RecordStereo)
|
||||||
cb(buff)
|
}
|
||||||
n = 0
|
options = append(options, pulse.RecordSampleRate(48000), pulse.RecordBufferFragmentSize(512))
|
||||||
b = b[nCopied:]
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.Start()
|
stream.Start()
|
||||||
m.s = stream
|
m.s = stream
|
||||||
return nil
|
m.samplesChan = samplesChan
|
||||||
|
return reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *microphone) Stop() error {
|
func (m *microphone) Stop() error {
|
||||||
|
close(m.samplesChan)
|
||||||
m.s.Stop()
|
m.s.Stop()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -71,10 +104,8 @@ func (m *microphone) Info() Info {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *microphone) Settings() []AudioSetting {
|
func (m *microphone) Settings() []AudioSetting {
|
||||||
src, err := m.c.DefaultSource()
|
return []AudioSetting{AudioSetting{
|
||||||
if err != nil {
|
SampleRate: 48000,
|
||||||
return nil
|
Mono: false,
|
||||||
}
|
}}
|
||||||
|
|
||||||
return []AudioSetting{AudioSetting{src.SampleRate()}}
|
|
||||||
}
|
}
|
||||||
|
87
pkg/io/audio/beep.go
Normal file
87
pkg/io/audio/beep.go
Normal file
@@ -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
|
||||||
|
}
|
54
track.go
54
track.go
@@ -2,6 +2,7 @@ package mediadevices
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/pion/mediadevices/pkg/codec"
|
"github.com/pion/mediadevices/pkg/codec"
|
||||||
@@ -137,7 +138,7 @@ type audioTrack struct {
|
|||||||
*track
|
*track
|
||||||
d driver.AudioDriver
|
d driver.AudioDriver
|
||||||
setting driver.AudioSetting
|
setting driver.AudioSetting
|
||||||
encoder codec.AudioEncoder
|
encoder io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Tracker = &audioTrack{}
|
var _ Tracker = &audioTrack{}
|
||||||
@@ -148,44 +149,43 @@ func newAudioTrack(pc *webrtc.PeerConnection, d driver.AudioDriver, setting driv
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder, err := codec.BuildAudioEncoder(codecName, codec.AudioSetting{
|
reader, err := d.Start(setting)
|
||||||
SampleRate: setting.SampleRate,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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,
|
track: t,
|
||||||
d: d,
|
d: d,
|
||||||
setting: setting,
|
setting: setting,
|
||||||
encoder: encoder,
|
encoder: encoder,
|
||||||
}
|
}
|
||||||
|
go at.start()
|
||||||
err = d.Start(setting, vt.dataCb)
|
return &at, nil
|
||||||
if err != nil {
|
|
||||||
encoder.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &vt, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vt *audioTrack) dataCb(b []int16) {
|
func (t *audioTrack) start() {
|
||||||
encoded, err := vt.encoder.Encode(b)
|
buff := make([]byte, 1024)
|
||||||
if err != nil {
|
for {
|
||||||
// TODO: probably do some logging here
|
n, err := t.encoder.Read(buff)
|
||||||
return
|
if err != nil {
|
||||||
}
|
// TODO: better error handling
|
||||||
|
panic(err)
|
||||||
err = vt.s.sample(encoded)
|
}
|
||||||
if err != nil {
|
t.s.sample(buff[:n])
|
||||||
// TODO: probably do some logging here
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vt *audioTrack) Stop() {
|
func (t *audioTrack) Stop() {
|
||||||
vt.d.Stop()
|
t.d.Stop()
|
||||||
vt.encoder.Close()
|
t.encoder.Close()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user