mirror of
https://github.com/pion/webrtc.git
synced 2025-10-05 23:26:58 +08:00
Migrate SDP generation to Unified Plan
This commit has breaking changes. This API change means we can no longer support an arbitrary number of receivers. For every track you want to receive you MUST call PeerConnection.AddTransceiver We do now support sending an multiple audio/video feeds. You can see this behavior via gstreamer-receive and gstreamer-send currently. Resolves #54
This commit is contained in:
@@ -14,7 +14,7 @@ go get github.com/pions/webrtc/examples/gstreamer-receive
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Open gstreamer-receive example page
|
### Open gstreamer-receive example page
|
||||||
[jsfiddle.net](https://jsfiddle.net/pdm7bqfr/) you should see your Webcam, two text-areas and a 'Start Session' button
|
[jsfiddle.net](https://jsfiddle.net/8t2g5Lar/) you should see your Webcam, two text-areas and a 'Start Session' button
|
||||||
|
|
||||||
### Run gstreamer-receive with your browsers SessionDescription as stdin
|
### Run gstreamer-receive with your browsers SessionDescription as stdin
|
||||||
In the jsfiddle the top textarea is your browser, copy that and:
|
In the jsfiddle the top textarea is your browser, copy that and:
|
||||||
|
@@ -4,12 +4,14 @@ Browser base64 Session Description<br />
|
|||||||
|
|
||||||
Golang base64 Session Description<br />
|
Golang base64 Session Description<br />
|
||||||
<textarea id="remoteSessionDescription"></textarea> <br/>
|
<textarea id="remoteSessionDescription"></textarea> <br/>
|
||||||
<button onclick="window.startSession()"> Start Session </button><br />
|
<button onclick="window.startSession()"> Start Session </button>
|
||||||
|
<button onclick="window.addDisplayCapture()" id="displayCapture"> Display Capture </button><br />
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
Video<br />
|
Video<br />
|
||||||
<video id="video1" width="160" height="120" autoplay muted></video> <br />
|
<div id="localVideos"></div> <br />
|
||||||
|
|
||||||
|
|
||||||
Logs<br />
|
Logs<br />
|
||||||
<div id="logs"></div>
|
<div id="logs"></div>
|
||||||
|
@@ -7,13 +7,24 @@ let pc = new RTCPeerConnection({
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
var log = msg => {
|
let log = msg => {
|
||||||
document.getElementById('logs').innerHTML += msg + '<br>'
|
document.getElementById('logs').innerHTML += msg + '<br>'
|
||||||
}
|
}
|
||||||
|
let displayVideo = video => {
|
||||||
|
var el = document.createElement('video')
|
||||||
|
el.srcObject = video
|
||||||
|
el.autoplay = true
|
||||||
|
el.muted = true
|
||||||
|
el.width = 160
|
||||||
|
el.height = 120
|
||||||
|
|
||||||
|
document.getElementById('localVideos').appendChild(el)
|
||||||
|
return video
|
||||||
|
}
|
||||||
|
|
||||||
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
|
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
|
||||||
.then(stream => {
|
.then(stream => {
|
||||||
pc.addStream(document.getElementById('video1').srcObject = stream)
|
pc.addStream(displayVideo(stream))
|
||||||
pc.createOffer().then(d => pc.setLocalDescription(d)).catch(log)
|
pc.createOffer().then(d => pc.setLocalDescription(d)).catch(log)
|
||||||
}).catch(log)
|
}).catch(log)
|
||||||
|
|
||||||
@@ -36,3 +47,11 @@ window.startSession = () => {
|
|||||||
alert(e)
|
alert(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.addDisplayCapture = () => {
|
||||||
|
navigator.mediaDevices.getDisplayMedia().then(stream => {
|
||||||
|
document.getElementById('displayCapture').disabled = true
|
||||||
|
pc.addStream(displayVideo(stream))
|
||||||
|
pc.createOffer().then(d => pc.setLocalDescription(d)).catch(log)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -32,6 +32,15 @@ func gstreamerReceiveMain() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow us to receive 1 audio track, and 2 video tracks
|
||||||
|
if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeAudio); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeVideo); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeVideo); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set a handler for when a new remote track starts, this handler creates a gstreamer pipeline
|
// Set a handler for when a new remote track starts, this handler creates a gstreamer pipeline
|
||||||
// for the given codec
|
// for the given codec
|
||||||
peerConnection.OnTrack(func(track *webrtc.Track, receiver *webrtc.RTPReceiver) {
|
peerConnection.OnTrack(func(track *webrtc.Track, receiver *webrtc.RTPReceiver) {
|
||||||
|
@@ -14,7 +14,7 @@ go get github.com/pions/webrtc/examples/gstreamer-send
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Open gstreamer-send example page
|
### Open gstreamer-send example page
|
||||||
[jsfiddle.net](https://jsfiddle.net/Laf7ujeo/164/) you should see two text-areas and a 'Start Session' button
|
[jsfiddle.net](https://jsfiddle.net/z7ms3u5r/) you should see two text-areas and a 'Start Session' button
|
||||||
|
|
||||||
### Run gstreamer-send with your browsers SessionDescription as stdin
|
### Run gstreamer-send with your browsers SessionDescription as stdin
|
||||||
In the jsfiddle the top textarea is your browser, copy that and:
|
In the jsfiddle the top textarea is your browser, copy that and:
|
||||||
|
@@ -27,7 +27,11 @@ pc.onicecandidate = event => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pc.createOffer({ offerToReceiveVideo: true, offerToReceiveAudio: true }).then(d => pc.setLocalDescription(d)).catch(log)
|
// Offer to receive 1 audio, and 2 video tracks
|
||||||
|
pc.addTransceiver('audio', {'direction': 'recvonly'})
|
||||||
|
pc.addTransceiver('video', {'direction': 'recvonly'})
|
||||||
|
pc.addTransceiver('video', {'direction': 'recvonly'})
|
||||||
|
pc.createOffer().then(d => pc.setLocalDescription(d)).catch(log)
|
||||||
|
|
||||||
window.startSession = () => {
|
window.startSession = () => {
|
||||||
let sd = document.getElementById('remoteSessionDescription').value
|
let sd = document.getElementById('remoteSessionDescription').value
|
||||||
|
@@ -40,21 +40,31 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Create a audio track
|
// Create a audio track
|
||||||
opusTrack, err := peerConnection.NewTrack(webrtc.DefaultPayloadTypeOpus, rand.Uint32(), "audio", "pion1")
|
audioTrack, err := peerConnection.NewTrack(webrtc.DefaultPayloadTypeOpus, rand.Uint32(), "audio", "pion1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
_, err = peerConnection.AddTrack(opusTrack)
|
_, err = peerConnection.AddTrack(audioTrack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a video track
|
// Create a video track
|
||||||
vp8Track, err := peerConnection.NewTrack(webrtc.DefaultPayloadTypeVP8, rand.Uint32(), "video", "pion2")
|
firstVideoTrack, err := peerConnection.NewTrack(webrtc.DefaultPayloadTypeVP8, rand.Uint32(), "video", "pion2")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
_, err = peerConnection.AddTrack(vp8Track)
|
_, err = peerConnection.AddTrack(firstVideoTrack)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a second video track
|
||||||
|
secondVideoTrack, err := peerConnection.NewTrack(webrtc.DefaultPayloadTypeVP8, rand.Uint32(), "video", "pion3")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
_, err = peerConnection.AddTrack(secondVideoTrack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -85,8 +95,8 @@ func main() {
|
|||||||
fmt.Println(signal.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
|
|
||||||
// Start pushing buffers on these tracks
|
// Start pushing buffers on these tracks
|
||||||
gst.CreatePipeline(webrtc.Opus, []*webrtc.Track{opusTrack}, *audioSrc).Start()
|
gst.CreatePipeline(webrtc.Opus, []*webrtc.Track{audioTrack}, *audioSrc).Start()
|
||||||
gst.CreatePipeline(webrtc.VP8, []*webrtc.Track{vp8Track}, *videoSrc).Start()
|
gst.CreatePipeline(webrtc.VP8, []*webrtc.Track{firstVideoTrack, secondVideoTrack}, *videoSrc).Start()
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
|
@@ -68,6 +68,13 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow us to receive 1 audio track, and 1 video track
|
||||||
|
if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeAudio); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeVideo); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||||
fmt.Printf("Connection State has changed %s \n", connectionState.String())
|
fmt.Printf("Connection State has changed %s \n", connectionState.String())
|
||||||
})
|
})
|
||||||
|
@@ -61,6 +61,13 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow us to receive 1 audio track, and 1 video track
|
||||||
|
if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeAudio); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeVideo); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
opusFile, err := opuswriter.New("output.opus", 48000, 2)
|
opusFile, err := opuswriter.New("output.opus", 48000, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@@ -10,7 +10,7 @@ go get github.com/pions/webrtc/examples/sfu-minimal
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Open sfu-minimal example page
|
### Open sfu-minimal example page
|
||||||
[jsfiddle.net](https://jsfiddle.net/4g03uqrx/) You should see two buttons 'Publish a Broadcast' and 'Join a Broadcast'
|
[jsfiddle.net](https://jsfiddle.net/zhpya3n9/) You should see two buttons 'Publish a Broadcast' and 'Join a Broadcast'
|
||||||
|
|
||||||
### Run SFU Minimal
|
### Run SFU Minimal
|
||||||
#### Linux/macOS
|
#### Linux/macOS
|
||||||
|
@@ -27,7 +27,8 @@ window.createSession = isPublisher => {
|
|||||||
.catch(log)
|
.catch(log)
|
||||||
}).catch(log)
|
}).catch(log)
|
||||||
} else {
|
} else {
|
||||||
pc.createOffer({ offerToReceiveVideo: true })
|
pc.addTransceiver('video', {'direction': 'recvonly'})
|
||||||
|
pc.createOffer()
|
||||||
.then(d => pc.setLocalDescription(d))
|
.then(d => pc.setLocalDescription(d))
|
||||||
.catch(log)
|
.catch(log)
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pions/rtcp"
|
"github.com/pions/rtcp"
|
||||||
@@ -46,6 +47,11 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow us to receive 1 video track
|
||||||
|
if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeVideo); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
localTrackChan := make(chan *webrtc.Track)
|
localTrackChan := make(chan *webrtc.Track)
|
||||||
// Set a handler for when a new remote track starts, this just distributes all our packets
|
// Set a handler for when a new remote track starts, this just distributes all our packets
|
||||||
// to connected peers
|
// to connected peers
|
||||||
@@ -75,7 +81,8 @@ func main() {
|
|||||||
panic(readErr)
|
panic(readErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = localTrack.Write(rtpBuf[:i]); err != nil {
|
// ErrClosedPipe means we don't have any subscribers, this is ok if no peers have connected yet
|
||||||
|
if _, err = localTrack.Write(rtpBuf[:i]); err != nil && err != io.ErrClosedPipe {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -69,6 +70,12 @@ func room(w http.ResponseWriter, r *http.Request) {
|
|||||||
pubReceiver, err = api.NewPeerConnection(peerConnectionConfig)
|
pubReceiver, err = api.NewPeerConnection(peerConnectionConfig)
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
|
_, err = pubReceiver.AddTransceiver(webrtc.RTPCodecTypeAudio)
|
||||||
|
checkError(err)
|
||||||
|
|
||||||
|
_, err = pubReceiver.AddTransceiver(webrtc.RTPCodecTypeVideo)
|
||||||
|
checkError(err)
|
||||||
|
|
||||||
pubReceiver.OnTrack(func(remoteTrack *webrtc.Track, receiver *webrtc.RTPReceiver) {
|
pubReceiver.OnTrack(func(remoteTrack *webrtc.Track, receiver *webrtc.RTPReceiver) {
|
||||||
if remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeVP8 || remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeVP9 || remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeH264 {
|
if remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeVP8 || remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeVP9 || remoteTrack.PayloadType() == webrtc.DefaultPayloadTypeH264 {
|
||||||
|
|
||||||
@@ -94,7 +101,10 @@ func room(w http.ResponseWriter, r *http.Request) {
|
|||||||
videoTrackLock.RLock()
|
videoTrackLock.RLock()
|
||||||
_, err = videoTrack.Write(rtpBuf[:i])
|
_, err = videoTrack.Write(rtpBuf[:i])
|
||||||
videoTrackLock.RUnlock()
|
videoTrackLock.RUnlock()
|
||||||
checkError(err)
|
|
||||||
|
if err != io.ErrClosedPipe {
|
||||||
|
checkError(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -113,7 +123,9 @@ func room(w http.ResponseWriter, r *http.Request) {
|
|||||||
audioTrackLock.RLock()
|
audioTrackLock.RLock()
|
||||||
_, err = audioTrack.Write(rtpBuf[:i])
|
_, err = audioTrack.Write(rtpBuf[:i])
|
||||||
audioTrackLock.RUnlock()
|
audioTrackLock.RUnlock()
|
||||||
checkError(err)
|
if err != io.ErrClosedPipe {
|
||||||
|
checkError(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -103,7 +103,10 @@ window.createSession = isPublisher => {
|
|||||||
document.getElementById('msginput').style = 'display: none'
|
document.getElementById('msginput').style = 'display: none'
|
||||||
dataChannel = pc.createDataChannel('data')
|
dataChannel = pc.createDataChannel('data')
|
||||||
dataChannel.onmessage = e => log(`receive data from '${dataChannel.label}' payload '${e.data}'`)
|
dataChannel.onmessage = e => log(`receive data from '${dataChannel.label}' payload '${e.data}'`)
|
||||||
pc.createOffer({ offerToReceiveVideo: true , offerToReceiveAudio: true})
|
pc.addTransceiver('audio', {'direction': 'recvonly'})
|
||||||
|
pc.addTransceiver('video', {'direction': 'recvonly'})
|
||||||
|
|
||||||
|
pc.createOffer()
|
||||||
.then(d => pc.setLocalDescription(d))
|
.then(d => pc.setLocalDescription(d))
|
||||||
.catch(log)
|
.catch(log)
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ package webrtc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pions/rtp"
|
"github.com/pions/rtp"
|
||||||
"github.com/pions/rtp/codecs"
|
"github.com/pions/rtp/codecs"
|
||||||
@@ -164,6 +165,18 @@ func (t RTPCodecType) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewRTPCodecType creates a RTPCodecType from a string
|
||||||
|
func NewRTPCodecType(r string) RTPCodecType {
|
||||||
|
switch {
|
||||||
|
case strings.EqualFold(r, "audio"):
|
||||||
|
return RTPCodecTypeAudio
|
||||||
|
case strings.EqualFold(r, "video"):
|
||||||
|
return RTPCodecTypeVideo
|
||||||
|
default:
|
||||||
|
return RTPCodecType(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RTPCodec represents a codec supported by the PeerConnection
|
// RTPCodec represents a codec supported by the PeerConnection
|
||||||
type RTPCodec struct {
|
type RTPCodec struct {
|
||||||
RTPCodecCapability
|
RTPCodecCapability
|
||||||
|
@@ -7,6 +7,8 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
mathRand "math/rand"
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -135,7 +137,7 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
|
|||||||
pc.iceTransport = iceTransport
|
pc.iceTransport = iceTransport
|
||||||
|
|
||||||
// Create the DTLS transport
|
// Create the DTLS transport
|
||||||
dtlsTransport, err := pc.createDTLSTransport()
|
dtlsTransport, err := pc.api.NewDTLSTransport(pc.iceTransport, pc.configuration.Certificates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -431,10 +433,6 @@ func (pc *PeerConnection) GetConfiguration() Configuration {
|
|||||||
return pc.configuration
|
return pc.configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// --- FIXME - BELOW CODE NEEDS REVIEW/CLEANUP
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// CreateOffer starts the PeerConnection and generates the localDescription
|
// CreateOffer starts the PeerConnection and generates the localDescription
|
||||||
func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription, error) {
|
func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription, error) {
|
||||||
useIdentity := pc.idpLoginURL != nil
|
useIdentity := pc.idpLoginURL != nil
|
||||||
@@ -461,16 +459,18 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription
|
|||||||
}
|
}
|
||||||
|
|
||||||
bundleValue := "BUNDLE"
|
bundleValue := "BUNDLE"
|
||||||
|
bundleCount := 0
|
||||||
if pc.addRTPMediaSection(d, RTPCodecTypeAudio, "audio", iceParams, RTPTransceiverDirectionSendrecv, candidates, sdp.ConnectionRoleActpass) {
|
appendBundle := func() {
|
||||||
bundleValue += " audio"
|
bundleValue += " " + strconv.Itoa(bundleCount)
|
||||||
}
|
bundleCount++
|
||||||
if pc.addRTPMediaSection(d, RTPCodecTypeVideo, "video", iceParams, RTPTransceiverDirectionSendrecv, candidates, sdp.ConnectionRoleActpass) {
|
|
||||||
bundleValue += " video"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pc.addDataMediaSection(d, "data", iceParams, candidates, sdp.ConnectionRoleActpass)
|
for _, t := range pc.GetTransceivers() {
|
||||||
d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue+" data")
|
pc.addTransceiverSDP(d, t, bundleCount, iceParams, candidates, sdp.ConnectionRoleActpass)
|
||||||
|
appendBundle()
|
||||||
|
}
|
||||||
|
pc.addDataMediaSection(d, bundleCount, iceParams, candidates, sdp.ConnectionRoleActive)
|
||||||
|
appendBundle()
|
||||||
|
|
||||||
for _, m := range d.MediaDescriptions {
|
for _, m := range d.MediaDescriptions {
|
||||||
m.WithPropertyAttribute("setup:actpass")
|
m.WithPropertyAttribute("setup:actpass")
|
||||||
@@ -535,11 +535,6 @@ func (pc *PeerConnection) createICETransport() *ICETransport {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PeerConnection) createDTLSTransport() (*DTLSTransport, error) {
|
|
||||||
dtlsTransport, err := pc.api.NewDTLSTransport(pc.iceTransport, pc.configuration.Certificates)
|
|
||||||
return dtlsTransport, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateAnswer starts the PeerConnection and generates the localDescription
|
// CreateAnswer starts the PeerConnection and generates the localDescription
|
||||||
func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (SessionDescription, error) {
|
func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (SessionDescription, error) {
|
||||||
useIdentity := pc.idpLoginURL != nil
|
useIdentity := pc.idpLoginURL != nil
|
||||||
@@ -567,43 +562,64 @@ func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (SessionDescripti
|
|||||||
d := sdp.NewJSEPSessionDescription(useIdentity)
|
d := sdp.NewJSEPSessionDescription(useIdentity)
|
||||||
pc.addFingerprint(d)
|
pc.addFingerprint(d)
|
||||||
|
|
||||||
bundleValue := "BUNDLE"
|
getDirection := func(media *sdp.MediaDescription) RTPTransceiverDirection {
|
||||||
for _, remoteMedia := range pc.RemoteDescription().parsed.MediaDescriptions {
|
for _, a := range media.Attributes {
|
||||||
// TODO @trivigy better SDP parser
|
if direction := NewRTPTransceiverDirection(a.Key); direction != RTPTransceiverDirection(Unknown) {
|
||||||
var peerDirection RTPTransceiverDirection
|
return direction
|
||||||
midValue := ""
|
}
|
||||||
for _, a := range remoteMedia.Attributes {
|
}
|
||||||
|
return RTPTransceiverDirection(Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
localTransceivers := append([]*RTPTransceiver{}, pc.GetTransceivers()...)
|
||||||
|
satisfyPeerMedia := func(kind RTPCodecType, direction RTPTransceiverDirection) *RTPTransceiver {
|
||||||
|
for i := range localTransceivers {
|
||||||
|
t := localTransceivers[i]
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(*a.String(), "mid"):
|
case t.kind != kind:
|
||||||
midValue = (*a.String())[len("mid:"):]
|
continue
|
||||||
case strings.HasPrefix(*a.String(), "sendrecv"):
|
case direction == RTPTransceiverDirectionSendrecv && t.Direction != RTPTransceiverDirectionSendrecv:
|
||||||
peerDirection = RTPTransceiverDirectionSendrecv
|
continue
|
||||||
case strings.HasPrefix(*a.String(), "sendonly"):
|
case direction != RTPTransceiverDirectionSendrecv && direction == t.Direction:
|
||||||
peerDirection = RTPTransceiverDirectionSendonly
|
continue
|
||||||
case strings.HasPrefix(*a.String(), "recvonly"):
|
case direction == RTPTransceiverDirectionInactive:
|
||||||
peerDirection = RTPTransceiverDirectionRecvonly
|
continue
|
||||||
}
|
}
|
||||||
|
localTransceivers = append(localTransceivers[:i], localTransceivers[i+1:]...)
|
||||||
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
appendBundle := func() {
|
return &RTPTransceiver{
|
||||||
bundleValue += " " + midValue
|
kind: kind,
|
||||||
}
|
Direction: RTPTransceiverDirectionInactive,
|
||||||
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(*remoteMedia.MediaName.String(), "audio"):
|
|
||||||
if pc.addRTPMediaSection(d, RTPCodecTypeAudio, midValue, iceParams, peerDirection, candidates, sdp.ConnectionRoleActive) {
|
|
||||||
appendBundle()
|
|
||||||
}
|
|
||||||
case strings.HasPrefix(*remoteMedia.MediaName.String(), "video"):
|
|
||||||
if pc.addRTPMediaSection(d, RTPCodecTypeVideo, midValue, iceParams, peerDirection, candidates, sdp.ConnectionRoleActive) {
|
|
||||||
appendBundle()
|
|
||||||
}
|
|
||||||
case strings.HasPrefix(*remoteMedia.MediaName.String(), "application"):
|
|
||||||
pc.addDataMediaSection(d, midValue, iceParams, candidates, sdp.ConnectionRoleActive)
|
|
||||||
appendBundle()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bundleValue := "BUNDLE"
|
||||||
|
bundleCount := 0
|
||||||
|
appendBundle := func() {
|
||||||
|
bundleValue += " " + strconv.Itoa(bundleCount)
|
||||||
|
bundleCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, media := range pc.RemoteDescription().parsed.MediaDescriptions {
|
||||||
|
if media.MediaName.Media == "application" {
|
||||||
|
pc.addDataMediaSection(d, bundleCount, iceParams, candidates, sdp.ConnectionRoleActive)
|
||||||
|
appendBundle()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := NewRTPCodecType(media.MediaName.Media)
|
||||||
|
direction := getDirection(media)
|
||||||
|
if kind == 0 || direction == RTPTransceiverDirection(Unknown) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t := satisfyPeerMedia(kind, direction)
|
||||||
|
pc.addTransceiverSDP(d, t, bundleCount, iceParams, candidates, sdp.ConnectionRoleActive)
|
||||||
|
appendBundle()
|
||||||
|
}
|
||||||
d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue)
|
d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue)
|
||||||
|
|
||||||
sdp, err := d.Marshal()
|
sdp, err := d.Marshal()
|
||||||
@@ -939,13 +955,9 @@ func (pc *PeerConnection) openSRTP() {
|
|||||||
|
|
||||||
for _, media := range pc.RemoteDescription().parsed.MediaDescriptions {
|
for _, media := range pc.RemoteDescription().parsed.MediaDescriptions {
|
||||||
for _, attr := range media.Attributes {
|
for _, attr := range media.Attributes {
|
||||||
var codecType RTPCodecType
|
|
||||||
switch media.MediaName.Media {
|
codecType := NewRTPCodecType(media.MediaName.Media)
|
||||||
case "audio":
|
if codecType == 0 {
|
||||||
codecType = RTPCodecTypeAudio
|
|
||||||
case "video":
|
|
||||||
codecType = RTPCodecTypeVideo
|
|
||||||
default:
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -957,63 +969,68 @@ func (pc *PeerConnection) openSRTP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
incomingSSRCes[uint32(ssrc)] = codecType
|
incomingSSRCes[uint32(ssrc)] = codecType
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range incomingSSRCes {
|
localTransceivers := append([]*RTPTransceiver{}, pc.GetTransceivers()...)
|
||||||
go func(ssrc uint32, codecType RTPCodecType) {
|
for ssrc := range incomingSSRCes {
|
||||||
receiver, err := pc.api.NewRTPReceiver(codecType, pc.dtlsTransport)
|
for i := range localTransceivers {
|
||||||
if err != nil {
|
t := localTransceivers[i]
|
||||||
pc.log.Warnf("Could not create RTPReceiver %s", err)
|
switch {
|
||||||
return
|
case incomingSSRCes[ssrc] != t.kind:
|
||||||
|
continue
|
||||||
|
case t.Direction != RTPTransceiverDirectionRecvonly && t.Direction != RTPTransceiverDirectionSendrecv:
|
||||||
|
continue
|
||||||
|
case t.Receiver == nil:
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = receiver.Receive(RTPReceiveParameters{
|
localTransceivers = append(localTransceivers[:i], localTransceivers[i+1:]...)
|
||||||
Encodings: RTPDecodingParameters{
|
go func(ssrc uint32, receiver *RTPReceiver) {
|
||||||
RTPCodingParameters{SSRC: ssrc},
|
err := receiver.Receive(RTPReceiveParameters{
|
||||||
}}); err != nil {
|
Encodings: RTPDecodingParameters{
|
||||||
pc.log.Warnf("RTPReceiver Receive failed %s", err)
|
RTPCodingParameters{SSRC: ssrc},
|
||||||
return
|
}})
|
||||||
}
|
if err != nil {
|
||||||
|
pc.log.Warnf("RTPReceiver Receive failed %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
pc.newRTPTransceiver(
|
if err = receiver.Track().determinePayloadType(); err != nil {
|
||||||
receiver,
|
pc.log.Warnf("Could not determine PayloadType for SSRC %d", receiver.Track().SSRC())
|
||||||
nil,
|
return
|
||||||
RTPTransceiverDirectionRecvonly,
|
}
|
||||||
)
|
|
||||||
|
|
||||||
if err = receiver.Track().determinePayloadType(); err != nil {
|
pc.mu.RLock()
|
||||||
pc.log.Warnf("Could not determine PayloadType for SSRC %d", receiver.Track().SSRC())
|
defer pc.mu.RUnlock()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pc.mu.RLock()
|
sdpCodec, err := pc.currentLocalDescription.parsed.GetCodecForPayloadType(receiver.Track().PayloadType())
|
||||||
defer pc.mu.RUnlock()
|
if err != nil {
|
||||||
|
pc.log.Warnf("no codec could be found in RemoteDescription for payloadType %d", receiver.Track().PayloadType())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
sdpCodec, err := pc.currentLocalDescription.parsed.GetCodecForPayloadType(receiver.Track().PayloadType())
|
codec, err := pc.api.mediaEngine.getCodecSDP(sdpCodec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pc.log.Warnf("no codec could be found in RemoteDescription for payloadType %d", receiver.Track().PayloadType())
|
pc.log.Warnf("codec %s in not registered", sdpCodec)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
codec, err := pc.api.mediaEngine.getCodecSDP(sdpCodec)
|
receiver.Track().mu.Lock()
|
||||||
if err != nil {
|
receiver.Track().kind = codec.Type
|
||||||
pc.log.Warnf("codec %s in not registered", sdpCodec)
|
receiver.Track().codec = codec
|
||||||
return
|
receiver.Track().mu.Unlock()
|
||||||
}
|
|
||||||
|
|
||||||
receiver.Track().mu.Lock()
|
if pc.onTrackHandler != nil {
|
||||||
receiver.Track().kind = codec.Type
|
pc.onTrack(receiver.Track(), receiver)
|
||||||
receiver.Track().codec = codec
|
} else {
|
||||||
receiver.Track().mu.Unlock()
|
pc.log.Warnf("OnTrack unset, unable to handle incoming media streams")
|
||||||
|
}
|
||||||
if pc.onTrackHandler != nil {
|
}(ssrc, t.Receiver)
|
||||||
pc.onTrack(receiver.Track(), receiver)
|
break
|
||||||
} else {
|
}
|
||||||
pc.log.Warnf("OnTrack unset, unable to handle incoming media streams")
|
|
||||||
}
|
|
||||||
}(i, incomingSSRCes[i])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1190,6 +1207,7 @@ func (pc *PeerConnection) AddTrack(track *Track) (*RTPSender, error) {
|
|||||||
nil,
|
nil,
|
||||||
sender,
|
sender,
|
||||||
RTPTransceiverDirectionSendonly,
|
RTPTransceiverDirectionSendonly,
|
||||||
|
track.Kind(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1202,9 +1220,60 @@ func (pc *PeerConnection) AddTrack(track *Track) (*RTPSender, error) {
|
|||||||
// panic("not implemented yet") // FIXME NOT-IMPLEMENTED nolint
|
// panic("not implemented yet") // FIXME NOT-IMPLEMENTED nolint
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func (pc *PeerConnection) AddTransceiver() RTPTransceiver {
|
// AddTransceiver Create a new RTCRtpTransceiver and add it to the set of transceivers.
|
||||||
// panic("not implemented yet") // FIXME NOT-IMPLEMENTED nolint
|
func (pc *PeerConnection) AddTransceiver(trackOrKind RTPCodecType, init ...RtpTransceiverInit) (*RTPTransceiver, error) {
|
||||||
// }
|
direction := RTPTransceiverDirectionSendrecv
|
||||||
|
if len(init) > 1 {
|
||||||
|
return nil, fmt.Errorf("AddTransceiver only accepts one RtpTransceiverInit")
|
||||||
|
} else if len(init) == 1 {
|
||||||
|
direction = init[0].Direction
|
||||||
|
}
|
||||||
|
|
||||||
|
switch direction {
|
||||||
|
case RTPTransceiverDirectionSendrecv:
|
||||||
|
receiver, err := pc.api.NewRTPReceiver(trackOrKind, pc.dtlsTransport)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadType := DefaultPayloadTypeOpus
|
||||||
|
if trackOrKind == RTPCodecTypeVideo {
|
||||||
|
payloadType = DefaultPayloadTypeVP8
|
||||||
|
}
|
||||||
|
|
||||||
|
track, err := pc.NewTrack(uint8(payloadType), mathRand.Uint32(), util.RandSeq(trackDefaultIDLength), util.RandSeq(trackDefaultLabelLength))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pc.newRTPTransceiver(
|
||||||
|
receiver,
|
||||||
|
sender,
|
||||||
|
RTPTransceiverDirectionSendrecv,
|
||||||
|
trackOrKind,
|
||||||
|
), nil
|
||||||
|
|
||||||
|
case RTPTransceiverDirectionRecvonly:
|
||||||
|
receiver, err := pc.api.NewRTPReceiver(trackOrKind, pc.dtlsTransport)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pc.newRTPTransceiver(
|
||||||
|
receiver,
|
||||||
|
nil,
|
||||||
|
RTPTransceiverDirectionRecvonly,
|
||||||
|
trackOrKind,
|
||||||
|
), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("AddTransceiver currently only suports recvonly and sendrecv")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CreateDataChannel creates a new DataChannel object with the given label
|
// CreateDataChannel creates a new DataChannel object with the given label
|
||||||
// and optional DataChannelInit used to configure properties of the
|
// and optional DataChannelInit used to configure properties of the
|
||||||
@@ -1406,20 +1475,6 @@ func (pc *PeerConnection) iceStateChange(newState ICEConnectionState) {
|
|||||||
pc.onICEConnectionStateChange(newState)
|
pc.onICEConnectionStateChange(newState)
|
||||||
}
|
}
|
||||||
|
|
||||||
func localDirection(weSend bool, peerDirection RTPTransceiverDirection) RTPTransceiverDirection {
|
|
||||||
theySend := (peerDirection == RTPTransceiverDirectionSendrecv || peerDirection == RTPTransceiverDirectionSendonly)
|
|
||||||
switch {
|
|
||||||
case weSend && theySend:
|
|
||||||
return RTPTransceiverDirectionSendrecv
|
|
||||||
case weSend && !theySend:
|
|
||||||
return RTPTransceiverDirectionSendonly
|
|
||||||
case !weSend && theySend:
|
|
||||||
return RTPTransceiverDirectionRecvonly
|
|
||||||
}
|
|
||||||
|
|
||||||
return RTPTransceiverDirectionInactive
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pc *PeerConnection) addFingerprint(d *sdp.SessionDescription) {
|
func (pc *PeerConnection) addFingerprint(d *sdp.SessionDescription) {
|
||||||
// TODO: Handle multiple certificates
|
// TODO: Handle multiple certificates
|
||||||
for _, fingerprint := range pc.configuration.Certificates[0].GetFingerprints() {
|
for _, fingerprint := range pc.configuration.Certificates[0].GetFingerprints() {
|
||||||
@@ -1427,26 +1482,15 @@ func (pc *PeerConnection) addFingerprint(d *sdp.SessionDescription) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PeerConnection) addRTPMediaSection(d *sdp.SessionDescription, codecType RTPCodecType, midValue string, iceParams ICEParameters, peerDirection RTPTransceiverDirection, candidates []ICECandidate, dtlsRole sdp.ConnectionRole) bool {
|
func (pc *PeerConnection) addTransceiverSDP(d *sdp.SessionDescription, t *RTPTransceiver, midOffset int, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole) {
|
||||||
if codecs := pc.api.mediaEngine.getCodecsByKind(codecType); len(codecs) == 0 {
|
media := sdp.NewJSEPMediaDescription(t.kind.String(), []string{}).
|
||||||
d.WithMedia(&sdp.MediaDescription{
|
|
||||||
MediaName: sdp.MediaName{
|
|
||||||
Media: codecType.String(),
|
|
||||||
Port: sdp.RangedPort{Value: 0},
|
|
||||||
Protos: []string{"UDP", "TLS", "RTP", "SAVPF"},
|
|
||||||
Formats: []string{"0"},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
media := sdp.NewJSEPMediaDescription(codecType.String(), []string{}).
|
|
||||||
WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()). // TODO: Support other connection types
|
WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()). // TODO: Support other connection types
|
||||||
WithValueAttribute(sdp.AttrKeyMID, midValue).
|
WithValueAttribute(sdp.AttrKeyMID, strconv.Itoa(midOffset)).
|
||||||
WithICECredentials(iceParams.UsernameFragment, iceParams.Password).
|
WithICECredentials(iceParams.UsernameFragment, iceParams.Password).
|
||||||
WithPropertyAttribute(sdp.AttrKeyRTCPMux). // TODO: support RTCP fallback
|
WithPropertyAttribute(sdp.AttrKeyRTCPMux). // TODO: support RTCP fallback
|
||||||
WithPropertyAttribute(sdp.AttrKeyRTCPRsize) // TODO: Support Reduced-Size RTCP?
|
WithPropertyAttribute(sdp.AttrKeyRTCPRsize) // TODO: Support Reduced-Size RTCP?
|
||||||
|
|
||||||
for _, codec := range pc.api.mediaEngine.getCodecsByKind(codecType) {
|
for _, codec := range pc.api.mediaEngine.getCodecsByKind(t.kind) {
|
||||||
media.WithCodec(codec.PayloadType, codec.Name, codec.ClockRate, codec.Channels, codec.SDPFmtpLine)
|
media.WithCodec(codec.PayloadType, codec.Name, codec.ClockRate, codec.Channels, codec.SDPFmtpLine)
|
||||||
|
|
||||||
for _, feedback := range codec.RTPCodecCapability.RTCPFeedback {
|
for _, feedback := range codec.RTPCodecCapability.RTCPFeedback {
|
||||||
@@ -1454,19 +1498,13 @@ func (pc *PeerConnection) addRTPMediaSection(d *sdp.SessionDescription, codecTyp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
weSend := false
|
if t.Sender != nil && t.Sender.track != nil {
|
||||||
for _, transceiver := range pc.rtpTransceivers {
|
track := t.Sender.track
|
||||||
if transceiver.Sender == nil ||
|
media = media.WithPropertyAttribute("msid:" + track.Label() + " " + track.ID())
|
||||||
transceiver.Sender.track == nil ||
|
media = media.WithMediaSource(track.SSRC(), track.Label() /* cname */, track.Label() /* streamLabel */, track.ID())
|
||||||
transceiver.Sender.track.Kind() != codecType {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
weSend = true
|
|
||||||
track := transceiver.Sender.track
|
|
||||||
media = media.WithMediaSource(track.SSRC(), track.Label() /* cname */, track.Label() /* streamLabel */, track.Label())
|
|
||||||
}
|
}
|
||||||
media = media.WithPropertyAttribute(localDirection(weSend, peerDirection).String())
|
|
||||||
|
|
||||||
|
media = media.WithPropertyAttribute(t.Direction.String())
|
||||||
for _, c := range candidates {
|
for _, c := range candidates {
|
||||||
sdpCandidate := c.toSDP()
|
sdpCandidate := c.toSDP()
|
||||||
sdpCandidate.ExtensionAttributes = append(sdpCandidate.ExtensionAttributes, sdp.ICECandidateAttribute{Key: "generation", Value: "0"})
|
sdpCandidate.ExtensionAttributes = append(sdpCandidate.ExtensionAttributes, sdp.ICECandidateAttribute{Key: "generation", Value: "0"})
|
||||||
@@ -1475,12 +1513,14 @@ func (pc *PeerConnection) addRTPMediaSection(d *sdp.SessionDescription, codecTyp
|
|||||||
sdpCandidate.Component = 2
|
sdpCandidate.Component = 2
|
||||||
media.WithICECandidate(sdpCandidate)
|
media.WithICECandidate(sdpCandidate)
|
||||||
}
|
}
|
||||||
media.WithPropertyAttribute("end-of-candidates")
|
if len(candidates) != 0 {
|
||||||
|
media.WithPropertyAttribute("end-of-candidates")
|
||||||
|
}
|
||||||
|
|
||||||
d.WithMedia(media)
|
d.WithMedia(media)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PeerConnection) addDataMediaSection(d *sdp.SessionDescription, midValue string, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole) {
|
func (pc *PeerConnection) addDataMediaSection(d *sdp.SessionDescription, midOffset int, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole) {
|
||||||
media := (&sdp.MediaDescription{
|
media := (&sdp.MediaDescription{
|
||||||
MediaName: sdp.MediaName{
|
MediaName: sdp.MediaName{
|
||||||
Media: "application",
|
Media: "application",
|
||||||
@@ -1497,7 +1537,7 @@ func (pc *PeerConnection) addDataMediaSection(d *sdp.SessionDescription, midValu
|
|||||||
},
|
},
|
||||||
}).
|
}).
|
||||||
WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()). // TODO: Support other connection types
|
WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()). // TODO: Support other connection types
|
||||||
WithValueAttribute(sdp.AttrKeyMID, midValue).
|
WithValueAttribute(sdp.AttrKeyMID, strconv.Itoa(midOffset)).
|
||||||
WithPropertyAttribute(RTPTransceiverDirectionSendrecv.String()).
|
WithPropertyAttribute(RTPTransceiverDirectionSendrecv.String()).
|
||||||
WithPropertyAttribute("sctpmap:5000 webrtc-datachannel 1024").
|
WithPropertyAttribute("sctpmap:5000 webrtc-datachannel 1024").
|
||||||
WithICECredentials(iceParams.UsernameFragment, iceParams.Password)
|
WithICECredentials(iceParams.UsernameFragment, iceParams.Password)
|
||||||
@@ -1531,12 +1571,14 @@ func (pc *PeerConnection) newRTPTransceiver(
|
|||||||
receiver *RTPReceiver,
|
receiver *RTPReceiver,
|
||||||
sender *RTPSender,
|
sender *RTPSender,
|
||||||
direction RTPTransceiverDirection,
|
direction RTPTransceiverDirection,
|
||||||
|
kind RTPCodecType,
|
||||||
) *RTPTransceiver {
|
) *RTPTransceiver {
|
||||||
|
|
||||||
t := &RTPTransceiver{
|
t := &RTPTransceiver{
|
||||||
Receiver: receiver,
|
Receiver: receiver,
|
||||||
Sender: sender,
|
Sender: sender,
|
||||||
Direction: direction,
|
Direction: direction,
|
||||||
|
kind: kind,
|
||||||
}
|
}
|
||||||
pc.mu.Lock()
|
pc.mu.Lock()
|
||||||
defer pc.mu.Unlock()
|
defer pc.mu.Unlock()
|
||||||
|
@@ -37,6 +37,11 @@ func TestPeerConnection_Media_Sample(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = pcAnswer.AddTransceiver(RTPCodecTypeVideo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
awaitRTPRecv := make(chan bool)
|
awaitRTPRecv := make(chan bool)
|
||||||
awaitRTPRecvClosed := make(chan bool)
|
awaitRTPRecvClosed := make(chan bool)
|
||||||
awaitRTPSend := make(chan bool)
|
awaitRTPSend := make(chan bool)
|
||||||
@@ -191,6 +196,16 @@ func TestPeerConnection_Media_Shutdown(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = pcOffer.AddTransceiver(RTPCodecTypeVideo, RtpTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = pcAnswer.AddTransceiver(RTPCodecTypeVideo, RtpTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
opusTrack, err := pcOffer.NewTrack(DefaultPayloadTypeOpus, rand.Uint32(), "audio", "pion1")
|
opusTrack, err := pcOffer.NewTrack(DefaultPayloadTypeOpus, rand.Uint32(), "audio", "pion1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -366,6 +381,11 @@ func TestPeerConnection_Media_Closed(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = pcAnswer.AddTransceiver(RTPCodecTypeVideo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
vp8Writer, err := pcOffer.NewTrack(DefaultPayloadTypeVP8, rand.Uint32(), "video", "pion2")
|
vp8Writer, err := pcOffer.NewTrack(DefaultPayloadTypeVP8, rand.Uint32(), "video", "pion2")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
package webrtc
|
package webrtc
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// RTPTransceiver represents a combination of an RTPSender and an RTPReceiver that share a common mid.
|
// RTPTransceiver represents a combination of an RTPSender and an RTPReceiver that share a common mid.
|
||||||
type RTPTransceiver struct {
|
type RTPTransceiver struct {
|
||||||
@@ -14,6 +16,7 @@ type RTPTransceiver struct {
|
|||||||
// firedDirection RTPTransceiverDirection
|
// firedDirection RTPTransceiverDirection
|
||||||
// receptive bool
|
// receptive bool
|
||||||
stopped bool
|
stopped bool
|
||||||
|
kind RTPCodecType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RTPTransceiver) setSendingTrack(track *Track) error {
|
func (t *RTPTransceiver) setSendingTrack(track *Track) error {
|
||||||
|
8
rtptransceiverinit.go
Normal file
8
rtptransceiverinit.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package webrtc
|
||||||
|
|
||||||
|
// RtpTransceiverInit dictionary is used when calling the WebRTC function addTransceiver() to provide configuration options for the new transceiver.
|
||||||
|
type RtpTransceiverInit struct {
|
||||||
|
Direction RTPTransceiverDirection
|
||||||
|
SendEncodings []RTPEncodingParameters
|
||||||
|
// Streams []*Track
|
||||||
|
}
|
6
track.go
6
track.go
@@ -11,7 +11,11 @@ import (
|
|||||||
"github.com/pions/webrtc/pkg/media"
|
"github.com/pions/webrtc/pkg/media"
|
||||||
)
|
)
|
||||||
|
|
||||||
const rtpOutboundMTU = 1400
|
const (
|
||||||
|
rtpOutboundMTU = 1400
|
||||||
|
trackDefaultIDLength = 16
|
||||||
|
trackDefaultLabelLength = 16
|
||||||
|
)
|
||||||
|
|
||||||
// Track represents a single media track
|
// Track represents a single media track
|
||||||
type Track struct {
|
type Track struct {
|
||||||
|
Reference in New Issue
Block a user