Skip to content

Commit a68a45c

Browse files
authored
Merge branch 'master' into debugLogger
2 parents d94d8e7 + f3ebfac commit a68a45c

File tree

3 files changed

+393
-17
lines changed

3 files changed

+393
-17
lines changed

rtcpeerconnection.go

+186-17
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ type RTCPeerConnection struct {
9898
// OnNegotiationNeeded func() // FIXME NOT-USED
9999
// OnIceCandidate func() // FIXME NOT-USED
100100
// OnIceCandidateError func() // FIXME NOT-USED
101-
// OnSignalingStateChange func() // FIXME NOT-USED
102101

103102
// OnIceGatheringStateChange func() // FIXME NOT-USED
104103
// OnConnectionStateChange func() // FIXME NOT-USED
105104

105+
onSignalingStateChangeHandler func(RTCSignalingState)
106106
onICEConnectionStateChangeHandler func(ice.ConnectionState)
107107
onTrackHandler func(*RTCTrack)
108108
onDataChannelHandler func(*RTCDataChannel)
@@ -225,6 +225,33 @@ func (pc *RTCPeerConnection) initConfiguration(configuration RTCConfiguration) e
225225
return nil
226226
}
227227

228+
// OnSignalingStateChange sets an event handler which is invoked when the
229+
// peer connection's signaling state changes
230+
func (pc *RTCPeerConnection) OnSignalingStateChange(f func(RTCSignalingState)) {
231+
pc.Lock()
232+
defer pc.Unlock()
233+
pc.onSignalingStateChangeHandler = f
234+
}
235+
236+
func (pc *RTCPeerConnection) onSignalingStateChange(newState RTCSignalingState) (done chan struct{}) {
237+
pc.RLock()
238+
hdlr := pc.onSignalingStateChangeHandler
239+
pc.RUnlock()
240+
241+
done = make(chan struct{})
242+
if hdlr == nil {
243+
close(done)
244+
return
245+
}
246+
247+
go func() {
248+
hdlr(newState)
249+
close(done)
250+
}()
251+
252+
return
253+
}
254+
228255
// OnDataChannel sets an event handler which is invoked when a data
229256
// channel message arrives from a remote peer.
230257
func (pc *RTCPeerConnection) OnDataChannel(f func(*RTCDataChannel)) {
@@ -424,13 +451,19 @@ func (pc *RTCPeerConnection) CreateOffer(options *RTCOfferOptions) (RTCSessionDe
424451
m.WithPropertyAttribute("setup:actpass")
425452
}
426453

427-
pc.CurrentLocalDescription = &RTCSessionDescription{
454+
desc := RTCSessionDescription{
428455
Type: RTCSdpTypeOffer,
429456
Sdp: d.Marshal(),
430457
parsed: d,
431458
}
459+
pc.lastOffer = desc.Sdp
432460

433-
return *pc.CurrentLocalDescription, nil
461+
// FIXME: This doesn't follow the JS API spec, but removing it
462+
// would mean our examples and existing code have to change
463+
if err := pc.SetLocalDescription(desc); err != nil {
464+
return RTCSessionDescription{}, err
465+
}
466+
return desc, nil
434467
}
435468

436469
// CreateAnswer starts the RTCPeerConnection and generates the localDescription
@@ -448,7 +481,7 @@ func (pc *RTCPeerConnection) CreateAnswer(options *RTCAnswerOptions) (RTCSession
448481
d := sdp.NewJSEPSessionDescription(pc.networkManager.DTLSFingerprint(), useIdentity)
449482

450483
bundleValue := "BUNDLE"
451-
for _, remoteMedia := range pc.CurrentRemoteDescription.parsed.MediaDescriptions {
484+
for _, remoteMedia := range pc.RemoteDescription().parsed.MediaDescriptions {
452485
// TODO @trivigy better SDP parser
453486
var peerDirection RTCRtpTransceiverDirection
454487
midValue := ""
@@ -484,18 +517,148 @@ func (pc *RTCPeerConnection) CreateAnswer(options *RTCAnswerOptions) (RTCSession
484517

485518
d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue)
486519

487-
pc.CurrentLocalDescription = &RTCSessionDescription{
520+
desc := RTCSessionDescription{
488521
Type: RTCSdpTypeAnswer,
489522
Sdp: d.Marshal(),
490523
parsed: d,
491524
}
492-
return *pc.CurrentLocalDescription, nil
525+
pc.lastAnswer = desc.Sdp
526+
527+
// FIXME: This doesn't follow the JS API spec, but removing it
528+
// would mean our examples and existing code have to change
529+
if err := pc.SetLocalDescription(desc); err != nil {
530+
return RTCSessionDescription{}, err
531+
}
532+
return desc, nil
493533
}
494534

495-
// // SetLocalDescription sets the SessionDescription of the local peer
496-
// func (pc *RTCPeerConnection) SetLocalDescription() {
497-
// panic("not implemented yet") // FIXME NOT-IMPLEMENTED nolint
498-
// }
535+
// 4.4.1.6 Set the RTCSessionDescription
536+
func (pc *RTCPeerConnection) setDescription(sd *RTCSessionDescription, op rtcStateChangeOp) error {
537+
if pc.isClosed {
538+
return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
539+
}
540+
541+
cur := pc.SignalingState
542+
setLocal := rtcStateChangeOpSetLocal
543+
setRemote := rtcStateChangeOpSetRemote
544+
newSdpDoesNotMatchOffer := &rtcerr.InvalidModificationError{Err: errors.New("New sdp does not match previous offer")}
545+
newSdpDoesNotMatchAnswer := &rtcerr.InvalidModificationError{Err: errors.New("New sdp does not match previous answer")}
546+
547+
var nextState RTCSignalingState
548+
var err error
549+
switch op {
550+
case setLocal:
551+
switch sd.Type {
552+
// stable->SetLocal(offer)->have-local-offer
553+
case RTCSdpTypeOffer:
554+
if sd.Sdp != pc.lastOffer {
555+
return newSdpDoesNotMatchOffer
556+
}
557+
nextState, err = checkNextSignalingState(cur, RTCSignalingStateHaveLocalOffer, setLocal, sd.Type)
558+
if err == nil {
559+
pc.PendingLocalDescription = sd
560+
}
561+
// have-remote-offer->SetLocal(answer)->stable
562+
// have-local-pranswer->SetLocal(answer)->stable
563+
case RTCSdpTypeAnswer:
564+
if sd.Sdp != pc.lastAnswer {
565+
return newSdpDoesNotMatchAnswer
566+
}
567+
nextState, err = checkNextSignalingState(cur, RTCSignalingStateStable, setLocal, sd.Type)
568+
if err == nil {
569+
pc.CurrentLocalDescription = sd
570+
pc.CurrentRemoteDescription = pc.PendingRemoteDescription
571+
pc.PendingRemoteDescription = nil
572+
pc.PendingLocalDescription = nil
573+
}
574+
case RTCSdpTypeRollback:
575+
nextState, err = checkNextSignalingState(cur, RTCSignalingStateStable, setLocal, sd.Type)
576+
if err == nil {
577+
pc.PendingLocalDescription = nil
578+
}
579+
// have-remote-offer->SetLocal(pranswer)->have-local-pranswer
580+
case RTCSdpTypePranswer:
581+
if sd.Sdp != pc.lastAnswer {
582+
return newSdpDoesNotMatchAnswer
583+
}
584+
nextState, err = checkNextSignalingState(cur, RTCSignalingStateHaveLocalPranswer, setLocal, sd.Type)
585+
if err == nil {
586+
pc.PendingLocalDescription = sd
587+
}
588+
default:
589+
return &rtcerr.OperationError{Err: fmt.Errorf("Invalid state change op: %s(%s)", op, sd.Type)}
590+
}
591+
case setRemote:
592+
switch sd.Type {
593+
// stable->SetRemote(offer)->have-remote-offer
594+
case RTCSdpTypeOffer:
595+
nextState, err = checkNextSignalingState(cur, RTCSignalingStateHaveRemoteOffer, setRemote, sd.Type)
596+
if err == nil {
597+
pc.PendingRemoteDescription = sd
598+
}
599+
// have-local-offer->SetRemote(answer)->stable
600+
// have-remote-pranswer->SetRemote(answer)->stable
601+
case RTCSdpTypeAnswer:
602+
nextState, err = checkNextSignalingState(cur, RTCSignalingStateStable, setRemote, sd.Type)
603+
if err == nil {
604+
pc.CurrentRemoteDescription = sd
605+
pc.CurrentLocalDescription = pc.PendingLocalDescription
606+
pc.PendingRemoteDescription = nil
607+
pc.PendingLocalDescription = nil
608+
}
609+
case RTCSdpTypeRollback:
610+
nextState, err = checkNextSignalingState(cur, RTCSignalingStateStable, setRemote, sd.Type)
611+
if err == nil {
612+
pc.PendingRemoteDescription = nil
613+
}
614+
// have-local-offer->SetRemote(pranswer)->have-remote-pranswer
615+
case RTCSdpTypePranswer:
616+
nextState, err = checkNextSignalingState(cur, RTCSignalingStateHaveRemotePranswer, setRemote, sd.Type)
617+
if err == nil {
618+
pc.PendingRemoteDescription = sd
619+
}
620+
default:
621+
return &rtcerr.OperationError{Err: fmt.Errorf("Invalid state change op: %s(%s)", op, sd.Type)}
622+
}
623+
default:
624+
return &rtcerr.OperationError{Err: fmt.Errorf("Unhandled state change op: %q", op)}
625+
}
626+
627+
if err == nil {
628+
pc.SignalingState = nextState
629+
pc.onSignalingStateChange(nextState)
630+
}
631+
return err
632+
}
633+
634+
// SetLocalDescription sets the SessionDescription of the local peer
635+
func (pc *RTCPeerConnection) SetLocalDescription(desc RTCSessionDescription) error {
636+
if pc.isClosed {
637+
return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
638+
}
639+
640+
// JSEP 5.4
641+
if desc.Sdp == "" {
642+
switch desc.Type {
643+
case RTCSdpTypeAnswer, RTCSdpTypePranswer:
644+
desc.Sdp = pc.lastAnswer
645+
case RTCSdpTypeOffer:
646+
desc.Sdp = pc.lastOffer
647+
default:
648+
return &rtcerr.InvalidModificationError{
649+
Err: fmt.Errorf("Invalid SDP type supplied to SetLocalDescription(): %s", desc.Type),
650+
}
651+
}
652+
}
653+
654+
// TODO: Initiate ICE candidate gathering?
655+
656+
desc.parsed = &sdp.SessionDescription{}
657+
if err := desc.parsed.Unmarshal(desc.Sdp); err != nil {
658+
return err
659+
}
660+
return pc.setDescription(&desc, rtcStateChangeOpSetLocal)
661+
}
499662

500663
// LocalDescription returns PendingLocalDescription if it is not null and
501664
// otherwise it returns CurrentLocalDescription. This property is used to
@@ -510,9 +673,21 @@ func (pc *RTCPeerConnection) LocalDescription() *RTCSessionDescription {
510673

511674
// SetRemoteDescription sets the SessionDescription of the remote peer
512675
func (pc *RTCPeerConnection) SetRemoteDescription(desc RTCSessionDescription) error {
676+
// FIXME: Remove this when renegotiation is supported
513677
if pc.CurrentRemoteDescription != nil {
514678
return errors.Errorf("remoteDescription is already defined, SetRemoteDescription can only be called once")
515679
}
680+
if pc.isClosed {
681+
return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
682+
}
683+
684+
desc.parsed = &sdp.SessionDescription{}
685+
if err := desc.parsed.Unmarshal(desc.Sdp); err != nil {
686+
return err
687+
}
688+
if err := pc.setDescription(&desc, rtcStateChangeOpSetRemote); err != nil {
689+
return err
690+
}
516691

517692
weOffer := true
518693
remoteUfrag := ""
@@ -521,13 +696,7 @@ func (pc *RTCPeerConnection) SetRemoteDescription(desc RTCSessionDescription) er
521696
weOffer = false
522697
}
523698

524-
pc.CurrentRemoteDescription = &desc
525-
pc.CurrentRemoteDescription.parsed = &sdp.SessionDescription{}
526-
if err := pc.CurrentRemoteDescription.parsed.Unmarshal(pc.CurrentRemoteDescription.Sdp); err != nil {
527-
return err
528-
}
529-
530-
for _, m := range pc.CurrentRemoteDescription.parsed.MediaDescriptions {
699+
for _, m := range pc.RemoteDescription().parsed.MediaDescriptions {
531700
for _, a := range m.Attributes {
532701
if strings.HasPrefix(*a.String(), "candidate") {
533702
if c := sdp.ICECandidateUnmarshal(*a.String()); c != nil {

rtcsignalingstate.go

+99
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
package webrtc
22

3+
import (
4+
"fmt"
5+
6+
"github.com/pions/webrtc/pkg/rtcerr"
7+
"github.com/pkg/errors"
8+
)
9+
10+
type rtcStateChangeOp int
11+
12+
const (
13+
rtcStateChangeOpSetLocal rtcStateChangeOp = iota + 1
14+
rtcStateChangeOpSetRemote
15+
)
16+
17+
func (op rtcStateChangeOp) String() string {
18+
switch op {
19+
case rtcStateChangeOpSetLocal:
20+
return "SetLocal"
21+
case rtcStateChangeOpSetRemote:
22+
return "SetRemote"
23+
default:
24+
return "Unknown State Change Operation"
25+
}
26+
}
27+
328
// RTCSignalingState indicates the signaling state of the offer/answer process.
429
type RTCSignalingState int
530

@@ -78,3 +103,77 @@ func (t RTCSignalingState) String() string {
78103
return ErrUnknownType.Error()
79104
}
80105
}
106+
107+
func checkNextSignalingState(cur, next RTCSignalingState, op rtcStateChangeOp, sdpType RTCSdpType) (RTCSignalingState, error) {
108+
// Special case for rollbacks
109+
if sdpType == RTCSdpTypeRollback && cur == RTCSignalingStateStable {
110+
return cur, &rtcerr.InvalidModificationError{
111+
Err: errors.New("Can't rollback from stable state"),
112+
}
113+
}
114+
115+
// 4.3.1 valid state transitions
116+
switch cur {
117+
case RTCSignalingStateStable:
118+
switch op {
119+
case rtcStateChangeOpSetLocal:
120+
// stable->SetLocal(offer)->have-local-offer
121+
if sdpType == RTCSdpTypeOffer && next == RTCSignalingStateHaveLocalOffer {
122+
return next, nil
123+
}
124+
case rtcStateChangeOpSetRemote:
125+
// stable->SetRemote(offer)->have-remote-offer
126+
if sdpType == RTCSdpTypeOffer && next == RTCSignalingStateHaveRemoteOffer {
127+
return next, nil
128+
}
129+
}
130+
case RTCSignalingStateHaveLocalOffer:
131+
if op == rtcStateChangeOpSetRemote {
132+
switch sdpType {
133+
// have-local-offer->SetRemote(answer)->stable
134+
case RTCSdpTypeAnswer:
135+
if next == RTCSignalingStateStable {
136+
return next, nil
137+
}
138+
// have-local-offer->SetRemote(pranswer)->have-remote-pranswer
139+
case RTCSdpTypePranswer:
140+
if next == RTCSignalingStateHaveRemotePranswer {
141+
return next, nil
142+
}
143+
}
144+
}
145+
case RTCSignalingStateHaveRemotePranswer:
146+
if op == rtcStateChangeOpSetRemote && sdpType == RTCSdpTypeAnswer {
147+
// have-remote-pranswer->SetRemote(answer)->stable
148+
if next == RTCSignalingStateStable {
149+
return next, nil
150+
}
151+
}
152+
case RTCSignalingStateHaveRemoteOffer:
153+
if op == rtcStateChangeOpSetLocal {
154+
switch sdpType {
155+
// have-remote-offer->SetLocal(answer)->stable
156+
case RTCSdpTypeAnswer:
157+
if next == RTCSignalingStateStable {
158+
return next, nil
159+
}
160+
// have-remote-offer->SetLocal(pranswer)->have-local-pranswer
161+
case RTCSdpTypePranswer:
162+
if next == RTCSignalingStateHaveLocalPranswer {
163+
return next, nil
164+
}
165+
}
166+
}
167+
case RTCSignalingStateHaveLocalPranswer:
168+
if op == rtcStateChangeOpSetLocal && sdpType == RTCSdpTypeAnswer {
169+
// have-local-pranswer->SetLocal(answer)->stable
170+
if next == RTCSignalingStateStable {
171+
return next, nil
172+
}
173+
}
174+
}
175+
176+
return cur, &rtcerr.InvalidModificationError{
177+
Err: fmt.Errorf("Invalid proposed signaling state transition %s->%s(%s)->%s", cur, op, sdpType, next),
178+
}
179+
}

0 commit comments

Comments
 (0)