From 6cda3cc488ff2e3e93b104763c007297b023bcd1 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 2 Jul 2018 23:09:20 -0700 Subject: [PATCH] Implement SDP generation for each track This is still a WIP, this deserves a well thought out rewrite in the future --- internal/network/port-send.go | 2 +- internal/sdp/util.go | 54 +++++++++++++++++++++++++++++------ rtcpeerconnection.go | 43 ++++++++++++++++++++-------- 3 files changed, 77 insertions(+), 22 deletions(-) diff --git a/internal/network/port-send.go b/internal/network/port-send.go index 6e70814e..fe4f581a 100644 --- a/internal/network/port-send.go +++ b/internal/network/port-send.go @@ -17,7 +17,7 @@ func (p *Port) Send(packet *rtp.Packet) { p.srtpContextsLock.Lock() srtpContext, ok := p.srtpContexts[contextMapKey] if !ok { - srtpContext, err = srtp.CreateContext([]byte(authed.pair.ClientWriteKey[0:16]), []byte(authed.pair.ClientWriteKey[16:]), authed.pair.Profile, 2581832418) + srtpContext, err = srtp.CreateContext([]byte(authed.pair.ClientWriteKey[0:16]), []byte(authed.pair.ClientWriteKey[16:]), authed.pair.Profile, packet.SSRC) if err != nil { fmt.Println("Failed to build SRTP context") continue diff --git a/internal/sdp/util.go b/internal/sdp/util.go index 9afac330..bac54e70 100644 --- a/internal/sdp/util.go +++ b/internal/sdp/util.go @@ -1,15 +1,31 @@ package sdp import ( + "fmt" "math/rand" "strconv" "strings" ) +// SessionBuilderTrack represents a single track in a SessionBuilder +type SessionBuilderTrack struct { + SSRC uint32 + IsAudio bool +} + +// SessionBuilder provides an easy way to build an SDP for an RTCPeerConnection +type SessionBuilder struct { + IceUsername, IcePassword, Fingerprint string + + Candidates []string + + Tracks []*SessionBuilderTrack +} + // BaseSessionDescription generates a default SDP response that is ice-lite, initiates the DTLS session and supports VP8, VP9 and Opus -func BaseSessionDescription(iceUsername, icePassword, fingerprint string, candidates []string) *SessionDescription { +func BaseSessionDescription(b *SessionBuilder) *SessionDescription { addMediaCandidates := func(m *MediaDescription) *MediaDescription { - m.Attributes = append(m.Attributes, candidates...) + m.Attributes = append(m.Attributes, b.Candidates...) m.Attributes = append(m.Attributes, "end-of-candidates") return m } @@ -21,10 +37,10 @@ func BaseSessionDescription(iceUsername, icePassword, fingerprint string, candid "setup:active", "mid:audio", "sendrecv", - "ice-ufrag:" + iceUsername, - "ice-pwd:" + icePassword, + "ice-ufrag:" + b.IceUsername, + "ice-pwd:" + b.IcePassword, "ice-lite", - "fingerprint:sha-256 " + fingerprint, + "fingerprint:sha-256 " + b.Fingerprint, "rtcp-mux", "rtcp-rsize", "rtpmap:111 opus/48000/2", @@ -39,10 +55,10 @@ func BaseSessionDescription(iceUsername, icePassword, fingerprint string, candid "setup:active", "mid:video", "sendrecv", - "ice-ufrag:" + iceUsername, - "ice-pwd:" + icePassword, + "ice-ufrag:" + b.IceUsername, + "ice-pwd:" + b.IcePassword, "ice-lite", - "fingerprint:sha-256 " + fingerprint, + "fingerprint:sha-256 " + b.Fingerprint, "rtcp-mux", "rtcp-rsize", "rtpmap:96 VP8/90000", @@ -50,6 +66,26 @@ func BaseSessionDescription(iceUsername, icePassword, fingerprint string, candid }, } + mediaStreamsAttribute := "msid-semantic: WMS" + for i, track := range b.Tracks { + var attributes *[]string + if track.IsAudio { + attributes = &audioMediaDescription.Attributes + } else { + attributes = &videoMediaDescription.Attributes + } + appendAttr := func(attr string) { + *attributes = append(*attributes, attr) + } + + appendAttr("ssrc:" + fmt.Sprint(track.SSRC) + " cname:pion" + strconv.Itoa(i)) + appendAttr("ssrc:" + fmt.Sprint(track.SSRC) + " msid:pion" + strconv.Itoa(i) + " pion" + strconv.Itoa(i)) + appendAttr("ssrc:" + fmt.Sprint(track.SSRC) + " mslabel:pion" + strconv.Itoa(i)) + appendAttr("ssrc:" + fmt.Sprint(track.SSRC) + " label:pion" + strconv.Itoa(i)) + + mediaStreamsAttribute += " pion" + strconv.Itoa(i) + } + sessionID := strconv.FormatUint(uint64(rand.Uint32())<<32+uint64(rand.Uint32()), 10) return &SessionDescription{ ProtocolVersion: 0, @@ -58,7 +94,7 @@ func BaseSessionDescription(iceUsername, icePassword, fingerprint string, candid Timing: []string{"0 0"}, Attributes: []string{ "group:BUNDLE audio video", - "msid-semantic: WMS", + mediaStreamsAttribute, }, MediaDescriptions: []*MediaDescription{ addMediaCandidates(audioMediaDescription), diff --git a/rtcpeerconnection.go b/rtcpeerconnection.go index fbbd26cc..5b7c0e8e 100644 --- a/rtcpeerconnection.go +++ b/rtcpeerconnection.go @@ -4,6 +4,7 @@ import ( "fmt" "math/rand" "sync" + "time" "github.com/pions/webrtc/internal/dtls" "github.com/pions/webrtc/internal/network" @@ -11,11 +12,15 @@ import ( "github.com/pions/webrtc/internal/util" "github.com/pions/webrtc/pkg/ice" "github.com/pions/webrtc/pkg/rtp" - "github.com/pions/webrtc/pkg/rtp/codecs" + "github.com/pkg/errors" ) +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} + // TrackType determines the type of media we are sending receiving type TrackType int @@ -55,6 +60,8 @@ type RTCPeerConnection struct { ports []*network.Port remoteDescription *sdp.SessionDescription + + localTracks []*sdp.SessionBuilderTrack } // Public @@ -96,7 +103,13 @@ func (r *RTCPeerConnection) CreateOffer() error { r.ports = append(r.ports, port) } - r.LocalDescription = sdp.BaseSessionDescription(r.iceUsername, r.icePassword, r.tlscfg.Fingerprint(), candidates) + r.LocalDescription = sdp.BaseSessionDescription(&sdp.SessionBuilder{ + IceUsername: r.iceUsername, + IcePassword: r.icePassword, + Fingerprint: r.tlscfg.Fingerprint(), + Candidates: candidates, + Tracks: r.localTracks, + }) return nil } @@ -105,20 +118,26 @@ func (r *RTCPeerConnection) CreateOffer() error { // This function returns a channel to push buffers on, and an error if the channel can't be added // Closing the channel ends this stream func (r *RTCPeerConnection) AddTrack(mediaType TrackType) (buffers chan<- []byte, err error) { + if mediaType != VP8 { + panic("TODO Discarding packet, need media parsing") + } + trackInput := make(chan []byte, 15) go func() { - packetizer := rtp.NewPacketizer(1500, 96, 123, &codecs.VP8Payloader{}, rtp.NewRandomSequencer()) + ssrc := rand.Uint32() + sdpTrack := &sdp.SessionBuilderTrack{SSRC: ssrc} + if mediaType == Opus { + sdpTrack.IsAudio = true + } + + r.localTracks = append(r.localTracks, sdpTrack) + packetizer := rtp.NewPacketizer(1500, 96, ssrc, &codecs.VP8Payloader{}, rtp.NewRandomSequencer()) for { - if mediaType == VP8 { - packets := packetizer.Packetize(<-trackInput) - for _, p := range packets { - for _, port := range r.ports { - port.Send(p) - } + packets := packetizer.Packetize(<-trackInput) + for _, p := range packets { + for _, port := range r.ports { + port.Send(p) } - } else { - <-trackInput - fmt.Println("TODO Discarding packet, need media parsing") } } }()