github.com/pion/webrtc/v4@v4.0.1/interceptor.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 //go:build !js 5 // +build !js 6 7 package webrtc 8 9 import ( 10 "sync/atomic" 11 12 "github.com/pion/interceptor" 13 "github.com/pion/interceptor/pkg/nack" 14 "github.com/pion/interceptor/pkg/report" 15 "github.com/pion/interceptor/pkg/rfc8888" 16 "github.com/pion/interceptor/pkg/twcc" 17 "github.com/pion/rtp" 18 "github.com/pion/sdp/v3" 19 ) 20 21 // RegisterDefaultInterceptors will register some useful interceptors. 22 // If you want to customize which interceptors are loaded, you should copy the 23 // code from this method and remove unwanted interceptors. 24 func RegisterDefaultInterceptors(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error { 25 if err := ConfigureNack(mediaEngine, interceptorRegistry); err != nil { 26 return err 27 } 28 29 if err := ConfigureRTCPReports(interceptorRegistry); err != nil { 30 return err 31 } 32 33 if err := ConfigureSimulcastExtensionHeaders(mediaEngine); err != nil { 34 return err 35 } 36 37 return ConfigureTWCCSender(mediaEngine, interceptorRegistry) 38 } 39 40 // ConfigureRTCPReports will setup everything necessary for generating Sender and Receiver Reports 41 func ConfigureRTCPReports(interceptorRegistry *interceptor.Registry) error { 42 reciver, err := report.NewReceiverInterceptor() 43 if err != nil { 44 return err 45 } 46 47 sender, err := report.NewSenderInterceptor() 48 if err != nil { 49 return err 50 } 51 52 interceptorRegistry.Add(reciver) 53 interceptorRegistry.Add(sender) 54 return nil 55 } 56 57 // ConfigureNack will setup everything necessary for handling generating/responding to nack messages. 58 func ConfigureNack(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error { 59 generator, err := nack.NewGeneratorInterceptor() 60 if err != nil { 61 return err 62 } 63 64 responder, err := nack.NewResponderInterceptor() 65 if err != nil { 66 return err 67 } 68 69 mediaEngine.RegisterFeedback(RTCPFeedback{Type: "nack"}, RTPCodecTypeVideo) 70 mediaEngine.RegisterFeedback(RTCPFeedback{Type: "nack", Parameter: "pli"}, RTPCodecTypeVideo) 71 interceptorRegistry.Add(responder) 72 interceptorRegistry.Add(generator) 73 return nil 74 } 75 76 // ConfigureTWCCHeaderExtensionSender will setup everything necessary for adding 77 // a TWCC header extension to outgoing RTP packets. This will allow the remote peer to generate TWCC reports. 78 func ConfigureTWCCHeaderExtensionSender(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error { 79 if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeVideo); err != nil { 80 return err 81 } 82 83 if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeAudio); err != nil { 84 return err 85 } 86 87 i, err := twcc.NewHeaderExtensionInterceptor() 88 if err != nil { 89 return err 90 } 91 92 interceptorRegistry.Add(i) 93 return nil 94 } 95 96 // ConfigureTWCCSender will setup everything necessary for generating TWCC reports. 97 func ConfigureTWCCSender(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error { 98 mediaEngine.RegisterFeedback(RTCPFeedback{Type: TypeRTCPFBTransportCC}, RTPCodecTypeVideo) 99 if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeVideo); err != nil { 100 return err 101 } 102 103 mediaEngine.RegisterFeedback(RTCPFeedback{Type: TypeRTCPFBTransportCC}, RTPCodecTypeAudio) 104 if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeAudio); err != nil { 105 return err 106 } 107 108 generator, err := twcc.NewSenderInterceptor() 109 if err != nil { 110 return err 111 } 112 113 interceptorRegistry.Add(generator) 114 return nil 115 } 116 117 // ConfigureCongestionControlFeedback registers congestion control feedback as 118 // defined in RFC 8888 (https://datatracker.ietf.org/doc/rfc8888/) 119 func ConfigureCongestionControlFeedback(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error { 120 mediaEngine.RegisterFeedback(RTCPFeedback{Type: TypeRTCPFBACK, Parameter: "ccfb"}, RTPCodecTypeVideo) 121 mediaEngine.RegisterFeedback(RTCPFeedback{Type: TypeRTCPFBACK, Parameter: "ccfb"}, RTPCodecTypeAudio) 122 generator, err := rfc8888.NewSenderInterceptor() 123 if err != nil { 124 return err 125 } 126 interceptorRegistry.Add(generator) 127 return nil 128 } 129 130 // ConfigureSimulcastExtensionHeaders enables the RTP Extension Headers needed for Simulcast 131 func ConfigureSimulcastExtensionHeaders(mediaEngine *MediaEngine) error { 132 if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}, RTPCodecTypeVideo); err != nil { 133 return err 134 } 135 136 if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI}, RTPCodecTypeVideo); err != nil { 137 return err 138 } 139 140 return mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdesRepairRTPStreamIDURI}, RTPCodecTypeVideo) 141 } 142 143 type interceptorToTrackLocalWriter struct{ interceptor atomic.Value } // interceptor.RTPWriter } 144 145 func (i *interceptorToTrackLocalWriter) WriteRTP(header *rtp.Header, payload []byte) (int, error) { 146 if writer, ok := i.interceptor.Load().(interceptor.RTPWriter); ok && writer != nil { 147 return writer.Write(header, payload, interceptor.Attributes{}) 148 } 149 150 return 0, nil 151 } 152 153 func (i *interceptorToTrackLocalWriter) Write(b []byte) (int, error) { 154 packet := &rtp.Packet{} 155 if err := packet.Unmarshal(b); err != nil { 156 return 0, err 157 } 158 159 return i.WriteRTP(&packet.Header, packet.Payload) 160 } 161 162 // nolint: unparam 163 func createStreamInfo(id string, ssrc, ssrcRTX, ssrcFEC SSRC, payloadType, payloadTypeRTX, payloadTypeFEC PayloadType, codec RTPCodecCapability, webrtcHeaderExtensions []RTPHeaderExtensionParameter) *interceptor.StreamInfo { 164 headerExtensions := make([]interceptor.RTPHeaderExtension, 0, len(webrtcHeaderExtensions)) 165 for _, h := range webrtcHeaderExtensions { 166 headerExtensions = append(headerExtensions, interceptor.RTPHeaderExtension{ID: h.ID, URI: h.URI}) 167 } 168 169 feedbacks := make([]interceptor.RTCPFeedback, 0, len(codec.RTCPFeedback)) 170 for _, f := range codec.RTCPFeedback { 171 feedbacks = append(feedbacks, interceptor.RTCPFeedback{Type: f.Type, Parameter: f.Parameter}) 172 } 173 174 return &interceptor.StreamInfo{ 175 ID: id, 176 Attributes: interceptor.Attributes{}, 177 SSRC: uint32(ssrc), 178 SSRCRetransmission: uint32(ssrcRTX), 179 SSRCForwardErrorCorrection: uint32(ssrcFEC), 180 PayloadType: uint8(payloadType), 181 PayloadTypeRetransmission: uint8(payloadTypeRTX), 182 PayloadTypeForwardErrorCorrection: uint8(payloadTypeFEC), 183 RTPHeaderExtensions: headerExtensions, 184 MimeType: codec.MimeType, 185 ClockRate: codec.ClockRate, 186 Channels: codec.Channels, 187 SDPFmtpLine: codec.SDPFmtpLine, 188 RTCPFeedback: feedbacks, 189 } 190 }