Add sfu example with websocket

sfu example with websocket, support video and audio. easy to use

Resolves #507
This commit is contained in:
adwpc
2019-03-09 20:56:20 +08:00
committed by Sean DuBois
parent 43e0a64ba2
commit fb72f20f66
11 changed files with 611 additions and 0 deletions

View File

@@ -92,6 +92,7 @@ Check out the **[contributing wiki](https://github.com/pions/webrtc/wiki/Contrib
* [frank](https://github.com/feixiao) - *Building examples on OSX*
* [mxmCherry](https://github.com/mxmCherry)
* [Alex Browne](https://github.com/albrow) - *JavaScript/WASM bindings*
* [adwpc](https://github.com/adwpc) - *SFU example with websocket*
### License
MIT License - see [LICENSE](LICENSE) for full text

27
examples/sfu-ws/README.md Normal file
View File

@@ -0,0 +1,27 @@
# sfu-ws
sfu-ws is a pion-WebRTC application that demonstrates how to broadcast a video to many peers, while only requiring the broadcaster to upload once.
This could serve as the building block to building conferencing software, and other applications where publishers are bandwidth constrained.
## Instructions
### Download sfu-ws
```
go get github.com/pions/webrtc/examples/sfu-ws
```
### Run SFU
#### Linux/macOS
go build
./sfu-ws
### Start a publisher
* Click `Publish`
### Start a Subscriber
* Click `Subscribe`
You can start one publisher and many subscriber
Congrats, you have used pion-WebRTC! Now start building something cool

8
examples/sfu-ws/go.mod Normal file
View File

@@ -0,0 +1,8 @@
module github.com/pions/webrtc/examples/sfu-ws
require (
github.com/gorilla/websocket v1.4.0
github.com/pions/rtcp v1.1.0
github.com/pions/transport v0.4.0 // indirect
github.com/pions/webrtc v1.2.1-0.20190311062446-43e0a64ba287
)

50
examples/sfu-ws/go.sum Normal file
View File

@@ -0,0 +1,50 @@
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/cloudflare/sidh v0.0.0-20181111220428-fc8e6378752b/go.mod h1:o/DcCuWFr9jFzwO+c3y1hhwqKHHKfJ7HvLhWUwRnqfo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pions/datachannel v1.2.0/go.mod h1:MKPEKJRwX/a9/tyQvcVTUI9szyf8ZuUyZxSA9AVMSro=
github.com/pions/dtls v1.2.1/go.mod h1:OgJcO0SqrDdQzqkCTdAp4xCQlbCmwZtGyhbthbq9zIA=
github.com/pions/qtls-vendor-extracted v0.0.0-20190210024908-018998217c65/go.mod h1:tSUehzG/8OAT3JvWvnovveLfRMM8NvgfN1LzwSrBX5s=
github.com/pions/quic v0.0.1/go.mod h1:q62rRbOZG6Keu45rWWljWZHXmB3H7fKdeJ1KtNcDrNQ=
github.com/pions/quic-go v0.7.1-0.20190211221741-ec20a8498576/go.mod h1:YvOsXPS6wXEfRGJobrsWSOBmlN6dkEIg+cUpnSDLkhc=
github.com/pions/rtcp v1.1.0 h1:WJarRpNaGE7aScgTXARaNIObMhIbftcbiPuhhqvDtl4=
github.com/pions/rtcp v1.1.0/go.mod h1:Q5twXlqiz775Yn37X0cl4lAsfSk8EiHgeNkte59jBY4=
github.com/pions/rtp v1.0.1/go.mod h1:GDIt4UYlSz7za4vfaLqihGJJ+yLvgPshnqrF/lm3vcM=
github.com/pions/sctp v1.3.0/go.mod h1:GZTG/xApE7wdUFEQq2Rmzgxl/+YaB/L1k8xUl1D5bmo=
github.com/pions/sctp v1.4.1/go.mod h1:dAna+Ct/aIIFiGW45yhGzuQjULWD7ni1vjoKHa9DsyU=
github.com/pions/sdp/v2 v2.1.0/go.mod h1:KGRBcHfpkgJXjrzKJz2wj/Jf1KWnsHdoIiqtayQ5QmE=
github.com/pions/srtp v1.1.0/go.mod h1:GvEoPJmI8sLqyjdPPdTZaCKNQ9e/+9OCCpck01s9uNQ=
github.com/pions/stun v0.2.0/go.mod h1:rMdCIsqqnTLC4MOHJE3LNiFQRfIjUDzI1kzx//7oPOM=
github.com/pions/transport v0.0.0-20190110151433-e7cbf7d5f464/go.mod h1:HLhzI7I0k8TyiQ99hfRZNRf84lG76eaFnZHnVy/wFnM=
github.com/pions/transport v0.1.0/go.mod h1:HLhzI7I0k8TyiQ99hfRZNRf84lG76eaFnZHnVy/wFnM=
github.com/pions/transport v0.2.0/go.mod h1:HLhzI7I0k8TyiQ99hfRZNRf84lG76eaFnZHnVy/wFnM=
github.com/pions/transport v0.4.0 h1:1N6fluzmj5W/16eFLDsCB18s/xEkjVTek0K4IJz75FU=
github.com/pions/transport v0.4.0/go.mod h1:9gvUd8ZeyU4ZX7dhNuUq97mPoekopkd7eCJEyhKwVO0=
github.com/pions/webrtc v1.2.1-0.20190311062446-43e0a64ba287 h1:skL8VgjrCGaoMCL+o2cumZ1uyLdRtK12TuLLM5PT30o=
github.com/pions/webrtc v1.2.1-0.20190311062446-43e0a64ba287/go.mod h1:H4lYxyochp11YxW5AGmNam36neRoNDfPcqhSFUItaes=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

47
examples/sfu-ws/main.go Normal file
View File

@@ -0,0 +1,47 @@
package main
import (
"flag"
"fmt"
"net/http"
"github.com/pions/webrtc"
)
func checkError(err error) {
if err != nil {
panic(err)
}
}
func init() {
// Generate pem file for https
genPem()
// Create a MediaEngine object to configure the supported codec
m = webrtc.MediaEngine{}
// Setup the codecs you want to use.
m.RegisterCodec(webrtc.NewRTPVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000))
m.RegisterCodec(webrtc.NewRTPOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000))
// Create the API object with the MediaEngine
api = webrtc.NewAPI(webrtc.WithMediaEngine(m))
}
func main() {
port := flag.String("p", "8443", "https port")
flag.Parse()
// Websocket handle func
http.HandleFunc("/ws", room)
// Html handle func
http.HandleFunc("/", web)
// Support https, so we can test by lan
fmt.Println("Web listening :" + *port)
panic(http.ListenAndServeTLS(":"+*port, "cert.pem", "key.pem", nil))
}

180
examples/sfu-ws/room.go Normal file
View File

@@ -0,0 +1,180 @@
package main
import (
"net/http"
"sync"
"sync/atomic"
"time"
"github.com/gorilla/websocket"
"github.com/pions/rtcp"
"github.com/pions/webrtc"
)
// Peer config
var peerConnectionConfig = webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
var (
// Media engine
m webrtc.MediaEngine
// API object
api *webrtc.API
// Publisher Peer
pubCount int32
pubReceiver *webrtc.PeerConnection
// Local track
videoTrack *webrtc.Track
audioTrack *webrtc.Track
videoTrackLock *sync.RWMutex = new(sync.RWMutex)
audioTrackLock *sync.RWMutex = new(sync.RWMutex)
// Websocket upgrader
upgrader = websocket.Upgrader{}
)
const (
rtcpPLIInterval = time.Second * 3
)
func room(w http.ResponseWriter, r *http.Request) {
// Websocket client
c, err := upgrader.Upgrade(w, r, nil)
checkError(err)
defer c.Close()
// Read sdp from websocket
mt, msg, err := c.ReadMessage()
checkError(err)
if atomic.LoadInt32(&pubCount) == 0 {
atomic.AddInt32(&pubCount, 1)
// Create a new RTCPeerConnection
pubReceiver, err = api.NewPeerConnection(peerConnectionConfig)
checkError(err)
pubReceiver.OnTrack(func(remoteTrack *webrtc.Track, receiver *webrtc.RTPReceiver) {
if remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeVP8 || remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeVP9 || remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeH264 {
// Create a local video track, all our SFU clients will be fed via this track
var err error
videoTrackLock.Lock()
videoTrack, err = pubReceiver.NewTrack(remoteTrack.PayloadType(), remoteTrack.SSRC(), "video", "pion")
videoTrackLock.Unlock()
checkError(err)
// Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval
go func() {
ticker := time.NewTicker(rtcpPLIInterval)
for range ticker.C {
checkError(pubReceiver.SendRTCP(&rtcp.PictureLossIndication{MediaSSRC: videoTrack.SSRC()}))
}
}()
rtpBuf := make([]byte, 1400)
for {
i, err := remoteTrack.Read(rtpBuf)
checkError(err)
videoTrackLock.RLock()
_, err = videoTrack.Write(rtpBuf[:i])
videoTrackLock.RUnlock()
checkError(err)
}
} else {
// Create a local audio track, all our SFU clients will be fed via this track
var err error
audioTrackLock.Lock()
audioTrack, err = pubReceiver.NewTrack(remoteTrack.PayloadType(), remoteTrack.SSRC(), "audio", "pion")
audioTrackLock.Unlock()
checkError(err)
rtpBuf := make([]byte, 1400)
for {
i, err := remoteTrack.Read(rtpBuf)
checkError(err)
audioTrackLock.RLock()
_, err = audioTrack.Write(rtpBuf[:i])
audioTrackLock.RUnlock()
checkError(err)
}
}
})
// Set the remote SessionDescription
checkError(pubReceiver.SetRemoteDescription(
webrtc.SessionDescription{
SDP: string(msg),
Type: webrtc.SDPTypeOffer,
}))
// Create answer
answer, err := pubReceiver.CreateAnswer(nil)
checkError(err)
// Sets the LocalDescription, and starts our UDP listeners
checkError(pubReceiver.SetLocalDescription(answer))
// Send server sdp to publisher
checkError(c.WriteMessage(mt, []byte(answer.SDP)))
} else {
// Create a new PeerConnection
subSender, err := api.NewPeerConnection(peerConnectionConfig)
checkError(err)
// Waiting for publisher track finish
for {
videoTrackLock.RLock()
if videoTrack == nil {
videoTrackLock.RUnlock()
//if videoTrack == nil, waiting..
time.Sleep(100 * time.Millisecond)
} else {
videoTrackLock.RUnlock()
break
}
}
// Add local video track
videoTrackLock.RLock()
_, err = subSender.AddTrack(videoTrack)
videoTrackLock.RUnlock()
checkError(err)
// Add local audio track
audioTrackLock.RLock()
_, err = subSender.AddTrack(audioTrack)
audioTrackLock.RUnlock()
checkError(err)
// Set the remote SessionDescription
checkError(subSender.SetRemoteDescription(
webrtc.SessionDescription{
SDP: string(msg),
Type: webrtc.SDPTypeOffer,
}))
// Create answer
answer, err := subSender.CreateAnswer(nil)
checkError(err)
// Sets the LocalDescription, and starts our UDP listeners
checkError(subSender.SetLocalDescription(answer))
// Send server sdp to subscriber
checkError(c.WriteMessage(mt, []byte(answer.SDP)))
}
}

125
examples/sfu-ws/sfu.html Normal file
View File

@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>sfu</title>
<style type="text/css">
.comments {
width:100%;/*auto width*/
overflow:auto;
word-break:break-all;
}
</style>
</head>
<body>
<video id="video1" width="320" height="240" autoplay muted controls></video> <br />
<button class="sessbtn" onclick="window.createSession(true)">Publish</button>
<button class="sessbtn" onclick="window.createSession(false)">Subscribe</button>
<div id="signalingContainer" style="display: none">
Client SDP<textarea class="comments" id="localSDP" readonly="true" rows=10 cols=30 onpropertychange= "this.style.posHeight=this.scrollHeight "></textarea>
Server SDP<textarea class="comments" id="remoteSDP" readonly="true" rows=10 cols=30 onpropertychange= "this.style.posHeight=this.scrollHeight "></textarea>
<!-- <button onclick="window.startSession()"> Start Session </button> -->
</div>
<div id="logs"></div>
<script>
var log = msg => {
document.getElementById('logs').innerHTML += msg + '<br>'
}
var sock = null;
var wsuri = "wss://" + location.host + "/ws";
window.onload = function() {
sock = new WebSocket(wsuri);
sock.onopen = function() {
<!-- console.log("websocket connected to " + wsuri); -->
}
sock.onclose = function(e) {
<!-- sock = new WebSocket('wss://' + location.host + '/ws'); -->
<!-- console.log("websocket connection closed (" + e.code + ")"); -->
}
sock.onmessage = function(e) {
<!-- console.log("websocket message received: " + e.data); -->
document.getElementById('remoteSDP').value = e.data
<!-- console.log("startSession"); -->
window.startSession()
}
sock.onerror = function(e) {
<!-- console.log("websocket error: " + e.data); -->
}
};
window.createSession = isPublisher => {
let pc = new RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
})
pc.oniceconnectionstatechange = e => log(pc.iceConnectionState)
pc.onicecandidate = event => {
if (event.candidate === null) {
document.getElementById('localSDP').value = pc.localDescription.sdp;
<!-- document.getElementById('localSDP').value = pc.localDescription.sdp; -->
sock.send(pc.localDescription.sdp);
<!-- console.log("send sdp to server:==============\n" + pc.localDescription.sdp); -->
}
}
if (isPublisher) {
navigator.mediaDevices.getUserMedia({ video: true, audio: true})
.then(stream => pc.addStream(document.getElementById('video1').srcObject = stream))
.catch(log)
pc.onnegotiationneeded = e => {
<!-- console.log("Publisher createOffer") -->
pc.createOffer()
.then(d => pc.setLocalDescription(d))
.catch(log)
}
} else {
<!-- console.log("Subcriber createOffer") -->
pc.createOffer({ offerToReceiveVideo: true , offerToReceiveAudio: true})
.then(d => pc.setLocalDescription(d))
.catch(log)
<!-- console.log("Subcriber ontrack") -->
pc.ontrack = function (event) {
var el = document.getElementById('video1')
el.srcObject = event.streams[0]
el.autoplay = true
el.controls = true
}
}
window.startSession = () => {
let sd = document.getElementById('remoteSDP').value
if (sd === '') {
return alert('Session Description must not be empty')
}
try {
pc.setRemoteDescription(new RTCSessionDescription({type:'answer', sdp:sd}))
} catch (e) {
alert(e)
}
}
let btns = document.getElementsByClassName('sessbtn')
for (let i = 0; i < btns.length; i++) {
btns[i].style = 'display: none'
}
document.getElementById('signalingContainer').style = 'display: block'
}
</script>
</body>
</html>

60
examples/sfu-ws/web.go Normal file
View File

@@ -0,0 +1,60 @@
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"html/template"
"math/big"
"net/http"
"os"
"time"
)
func genPem() {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
checkError(err)
SNLimit := new(big.Int).Lsh(big.NewInt(1), 128)
SN, err := rand.Int(rand.Reader, SNLimit)
checkError(err)
template := x509.Certificate{
SerialNumber: SN,
Subject: pkix.Name{
Organization: []string{"test"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
}
template.DNSNames = append(template.DNSNames, "localhost")
template.EmailAddresses = append(template.EmailAddresses, "test@test.com")
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
checkError(err)
certFile, err := os.Create("cert.pem")
checkError(err)
pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
certFile.Close()
keyFile, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
checkError(err)
// pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv.(*rsa.PrivateKey))})
pem.Encode(keyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
keyFile.Close()
}
func web(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
t, _ := template.ParseFiles("sfu.html")
checkError(t.Execute(w, nil))
}
}

1
go.mod
View File

@@ -1,6 +1,7 @@
module github.com/pions/webrtc
require (
github.com/gorilla/websocket v1.4.0
github.com/pions/datachannel v1.2.0
github.com/pions/dtls v1.2.1
github.com/pions/quic v0.0.1

2
go.sum
View File

@@ -10,6 +10,8 @@ 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/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=

110
track_test.go Normal file
View File

@@ -0,0 +1,110 @@
package webrtc
import (
"math/rand"
"testing"
)
func TestNewVideoTrack(t *testing.T) {
m := MediaEngine{}
m.RegisterCodec(NewRTPVP8Codec(DefaultPayloadTypeVP8, 90000))
api := NewAPI(WithMediaEngine(m))
peerConfig := Configuration{
ICEServers: []ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
peer, _ := api.NewPeerConnection(peerConfig)
_, err := peer.NewTrack(DefaultPayloadTypeVP8, rand.Uint32(), "video", "pion")
if err != nil {
t.Error("Failed to new video track")
}
}
func TestNewAudioTrack(t *testing.T) {
m := MediaEngine{}
m.RegisterCodec(NewRTPOpusCodec(DefaultPayloadTypeOpus, 48000))
api := NewAPI(WithMediaEngine(m))
peerConfig := Configuration{
ICEServers: []ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
peer, _ := api.NewPeerConnection(peerConfig)
_, err := peer.NewTrack(DefaultPayloadTypeOpus, rand.Uint32(), "audio", "pion")
if err != nil {
t.Error("Failed to new audio track")
}
}
func TestNewTracks(t *testing.T) {
m := MediaEngine{}
m.RegisterCodec(NewRTPOpusCodec(DefaultPayloadTypeOpus, 48000))
m.RegisterCodec(NewRTPVP8Codec(DefaultPayloadTypeVP8, 90000))
api := NewAPI(WithMediaEngine(m))
peerConfig := Configuration{
ICEServers: []ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
peer, _ := api.NewPeerConnection(peerConfig)
_, err := peer.NewTrack(DefaultPayloadTypeOpus, rand.Uint32(), "audio", "pion")
if err != nil {
t.Error("Failed to new audio track")
}
_, err = peer.NewTrack(DefaultPayloadTypeVP8, rand.Uint32(), "video", "pion")
if err != nil {
t.Error("Failed to new video track")
}
}
func TestNewTracksWrite(t *testing.T) {
m := MediaEngine{}
m.RegisterCodec(NewRTPOpusCodec(DefaultPayloadTypeOpus, 48000))
m.RegisterCodec(NewRTPVP8Codec(DefaultPayloadTypeVP8, 90000))
api := NewAPI(WithMediaEngine(m))
peerConfig := Configuration{
ICEServers: []ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
peer, _ := api.NewPeerConnection(peerConfig)
videoTrack, err := peer.NewTrack(DefaultPayloadTypeOpus, rand.Uint32(), "audio", "pion")
if err != nil {
t.Error("Failed to new video track")
}
audioTrack, err := peer.NewTrack(DefaultPayloadTypeVP8, rand.Uint32(), "video", "pion")
if err != nil {
t.Error("Failed to new audio track")
}
rtpBuf := make([]byte, 1400)
_, err = videoTrack.Write(rtpBuf)
if err != nil {
t.Error("Failed to write to video track")
}
_, err = audioTrack.Write(rtpBuf)
if err != nil {
t.Error("Failed to write to audio track")
}
}