github.com/livekit/protocol@v1.16.1-0.20240517185851-47e4c6bba773/sdp/sdp.go (about) 1 // Copyright 2023 LiveKit, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sdp 16 17 import ( 18 "strings" 19 20 "github.com/pion/sdp/v3" 21 "github.com/pion/webrtc/v3" 22 ) 23 24 func GetMidValue(media *sdp.MediaDescription) string { 25 for _, attr := range media.Attributes { 26 if attr.Key == sdp.AttrKeyMID { 27 return attr.Value 28 } 29 } 30 return "" 31 } 32 33 func ExtractFingerprint(desc *sdp.SessionDescription) (string, string, error) { 34 fingerprints := make([]string, 0) 35 36 if fingerprint, haveFingerprint := desc.Attribute("fingerprint"); haveFingerprint { 37 fingerprints = append(fingerprints, fingerprint) 38 } 39 40 for _, m := range desc.MediaDescriptions { 41 if fingerprint, haveFingerprint := m.Attribute("fingerprint"); haveFingerprint { 42 fingerprints = append(fingerprints, fingerprint) 43 } 44 } 45 46 if len(fingerprints) < 1 { 47 return "", "", webrtc.ErrSessionDescriptionNoFingerprint 48 } 49 50 for _, m := range fingerprints { 51 if m != fingerprints[0] { 52 return "", "", webrtc.ErrSessionDescriptionConflictingFingerprints 53 } 54 } 55 56 parts := strings.Split(fingerprints[0], " ") 57 if len(parts) != 2 { 58 return "", "", webrtc.ErrSessionDescriptionInvalidFingerprint 59 } 60 return parts[1], parts[0], nil 61 } 62 63 func ExtractDTLSRole(desc *sdp.SessionDescription) webrtc.DTLSRole { 64 for _, md := range desc.MediaDescriptions { 65 setup, ok := md.Attribute(sdp.AttrKeyConnectionSetup) 66 if !ok { 67 continue 68 } 69 70 if setup == sdp.ConnectionRoleActive.String() { 71 return webrtc.DTLSRoleClient 72 } 73 74 if setup == sdp.ConnectionRolePassive.String() { 75 return webrtc.DTLSRoleServer 76 } 77 } 78 79 // 80 // If 'setup' attribute is not available, use client role 81 // as that is the default behaviour of answerers 82 // 83 // There seems to be some differences in how role is decided. 84 // libwebrtc (Chrome) code - (https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/pc/jsep_transport.cc;l=592;drc=369fb686729e7eb20d2bd09717cec14269a399d7) 85 // does not mention anything about ICE role when determining 86 // DTLS Role. 87 // 88 // But, ORTC has this - https://github.com/w3c/ortc/issues/167#issuecomment-69409953 89 // and pion/webrtc follows that (https://github.com/pion/webrtc/blob/e071a4eded1efd5d9b401bcfc4efacb3a2a5a53c/dtlstransport.go#L269) 90 // 91 // So if remote is ice-lite, pion will use DTLSRoleServer when answering 92 // while browsers pick DTLSRoleClient. 93 // 94 return webrtc.DTLSRoleClient 95 } 96 97 func ExtractICECredential(desc *sdp.SessionDescription) (string, string, error) { 98 remotePwds := []string{} 99 remoteUfrags := []string{} 100 101 if ufrag, haveUfrag := desc.Attribute("ice-ufrag"); haveUfrag { 102 remoteUfrags = append(remoteUfrags, ufrag) 103 } 104 if pwd, havePwd := desc.Attribute("ice-pwd"); havePwd { 105 remotePwds = append(remotePwds, pwd) 106 } 107 108 for _, m := range desc.MediaDescriptions { 109 if ufrag, haveUfrag := m.Attribute("ice-ufrag"); haveUfrag { 110 remoteUfrags = append(remoteUfrags, ufrag) 111 } 112 if pwd, havePwd := m.Attribute("ice-pwd"); havePwd { 113 remotePwds = append(remotePwds, pwd) 114 } 115 } 116 117 if len(remoteUfrags) == 0 { 118 return "", "", webrtc.ErrSessionDescriptionMissingIceUfrag 119 } else if len(remotePwds) == 0 { 120 return "", "", webrtc.ErrSessionDescriptionMissingIcePwd 121 } 122 123 for _, m := range remoteUfrags { 124 if m != remoteUfrags[0] { 125 return "", "", webrtc.ErrSessionDescriptionConflictingIceUfrag 126 } 127 } 128 129 for _, m := range remotePwds { 130 if m != remotePwds[0] { 131 return "", "", webrtc.ErrSessionDescriptionConflictingIcePwd 132 } 133 } 134 135 return remoteUfrags[0], remotePwds[0], nil 136 } 137 138 func ExtractStreamID(media *sdp.MediaDescription) (string, bool) { 139 var streamID string 140 msid, ok := media.Attribute(sdp.AttrKeyMsid) 141 if !ok { 142 return "", false 143 } 144 ids := strings.Split(msid, " ") 145 if len(ids) < 2 { 146 streamID = msid 147 } else { 148 streamID = ids[1] 149 } 150 return streamID, true 151 }