DEV Community

Masui Masanori
Masui Masanori

Posted on

[Pion/WebRTC][TypeScript] Use specific video codec

Intro

At least in my environment, "VP8" is used as the default video codec for WebRTC.
But according to the SDP what I read last time, other codecs should be available as well.

So in this time, I will try using specific video codec.

Because the video codec used by WebRTC is determined by the SDP of both the Offer and Answer, I will try server-side(Offer) and client-side(Answer)/

Sample

[Client-side] Remove other video codecs from Offer SDP

To force using specific video codec, I can remove other codecs from Offer SDP.

videoCodecRemover.ts

import { hasAnyTexts } from "../hasAnyTexts"; /** Remove video codecs from SDP except specified ones */ export function removeVideoCodec(orgsdp: string, leaveMimeType: string): string { if(!hasAnyTexts(leaveMimeType)) { return orgsdp; } const codecs = RTCRtpSender.getCapabilities("video")?.codecs; if(codecs == null) { return orgsdp; } let modsdp = orgsdp; for(const c of codecs) { if(c.mimeType === leaveMimeType) { continue; } modsdp = internalRemoveVideoCodec(modsdp, c.mimeType); } return modsdp; } /** Remove target MIME Type from SDP */ function internalRemoveVideoCodec(sdp: string, removeMimeType: string): string { const videoCodec = removeMimeType.replace("video/", ""); const codecre = new RegExp('(a=rtpmap:(\\d*) ' + videoCodec + '/90000\\r\\n)'); const rtpmaps = sdp.match(codecre); if (rtpmaps == null || rtpmaps.length <= 2) { return sdp; } const rtpmap = rtpmaps[2]; let modsdp = sdp.replace(codecre, ""); modsdp = modsdp.replace(new RegExp('(a=rtcp-fb:' + rtpmap + '.*\r\n)', 'g'), ""); modsdp = modsdp.replace(new RegExp('(a=fmtp:' + rtpmap + '.*\r\n)', 'g'), ""); const aptpre = new RegExp('(a=fmtp:(\\d*) apt=' + rtpmap + '\\r\\n)'); const aptmaps = modsdp.match(aptpre); let fmtpmap = ""; if (aptmaps != null && aptmaps.length >= 3) { fmtpmap = aptmaps[2] ?? ""; modsdp = modsdp.replace(aptpre, ""); // remove video codecs by the "fmtpmap" value. modsdp = modsdp.replace(new RegExp('(a=rtpmap:' + fmtpmap + '.*\r\n)', 'g'), ""); modsdp = modsdp.replace(new RegExp('(a=rtcp-fb:' + fmtpmap + '.*\r\n)', 'g'), ""); } const videore = /(m=video.*\r\n)/; const videolines = modsdp.match(videore); if (videolines != null) { //If many m=video are found in SDP, this program doesn't work. const videoline = videolines[0].substring(0, videolines[0].length - 2); const videoelem = videoline.split(" "); let modvideoline = videoelem[0] ?? ""; for (let i = 1; i < videoelem.length; i++) { if (videoelem[i] == rtpmap || videoelem[i] == fmtpmap) { continue; } modvideoline += " " + videoelem[i]; } modvideoline += "\r\n"; modsdp = modsdp.replace(videore, modvideoline); } return internalRemoveVideoCodec(modsdp, removeMimeType); } 
Enter fullscreen mode Exit fullscreen mode

main.page.ts

... function handleReceivedMessage(value: string) { const message = JSON.parse(value); if(!checkIsClientMessage(message)) { console.error(`Invalid message type ${value}`); return; } // eslint-disable-next-line @typescript-eslint/no-explicit-any let offerData: any; let preferredMimeType: string|null = null; switch(message.event) { ... case "offer": offerData = JSON.parse(message.data); if(view.getForceVideoCodec()) { // 1. SDP, 2. MIME Type offerData.sdp = removeVideoCodec(offerData.sdp, view.getPreferredVideoCodec()); } else { ... } if(webrtc.handleOffer(offerData, preferredMimeType) !== true) { close(); } break; ... } } ... 
Enter fullscreen mode Exit fullscreen mode

Before

... m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 121 127 120 125 107 108 109 123 118 116 c=IN IP4 0.0.0.0 a=setup:actpass a=mid:0 a=ice-ufrag:qTksQATCTrhvEAcY a=ice-pwd:dGLtcbyrUNKsdYxHDEjVuYgwUWdMqqkN a=rtcp-mux a=rtcp-rsize a=rtpmap:96 VP8/90000 a=rtcp-fb:96 goog-remb a=rtcp-fb:96 ccm fir a=rtcp-fb:96 nack a=rtcp-fb:96 nack pli a=rtcp-fb:96 nack a=rtcp-fb:96 nack pli a=rtcp-fb:96 transport-cc a=rtpmap:97 rtx/90000 a=fmtp:97 apt=96 a=rtcp-fb:97 nack a=rtcp-fb:97 nack pli a=rtcp-fb:97 transport-cc a=rtpmap:98 VP9/90000 a=fmtp:98 profile-id=0 a=rtcp-fb:98 goog-remb a=rtcp-fb:98 ccm fir a=rtcp-fb:98 nack a=rtcp-fb:98 nack pli a=rtcp-fb:98 nack a=rtcp-fb:98 nack pli a=rtcp-fb:98 transport-cc a=rtpmap:99 rtx/90000 a=fmtp:99 apt=98 a=rtcp-fb:99 nack a=rtcp-fb:99 nack pli a=rtcp-fb:99 transport-cc a=rtpmap:100 VP9/90000 a=fmtp:100 profile-id=1 a=rtcp-fb:100 goog-remb a=rtcp-fb:100 ccm fir a=rtcp-fb:100 nack a=rtcp-fb:100 nack pli a=rtcp-fb:100 nack a=rtcp-fb:100 nack pli a=rtcp-fb:100 transport-cc a=rtpmap:101 rtx/90000 a=fmtp:101 apt=100 a=rtcp-fb:101 nack a=rtcp-fb:101 nack pli a=rtcp-fb:101 transport-cc a=rtpmap:102 H264/90000 a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f a=rtcp-fb:102 goog-remb a=rtcp-fb:102 ccm fir a=rtcp-fb:102 nack a=rtcp-fb:102 nack pli a=rtcp-fb:102 nack a=rtcp-fb:102 nack pli a=rtcp-fb:102 transport-cc a=rtpmap:121 rtx/90000 a=fmtp:121 apt=102 a=rtcp-fb:121 nack a=rtcp-fb:121 nack pli a=rtcp-fb:121 transport-cc a=rtpmap:127 H264/90000 a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f a=rtcp-fb:127 goog-remb a=rtcp-fb:127 ccm fir a=rtcp-fb:127 nack a=rtcp-fb:127 nack pli a=rtcp-fb:127 nack a=rtcp-fb:127 nack pli a=rtcp-fb:127 transport-cc a=rtpmap:120 rtx/90000 a=fmtp:120 apt=127 a=rtcp-fb:120 nack a=rtcp-fb:120 nack pli a=rtcp-fb:120 transport-cc a=rtpmap:125 H264/90000 a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=rtcp-fb:125 goog-remb a=rtcp-fb:125 ccm fir a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=rtcp-fb:125 transport-cc a=rtpmap:107 rtx/90000 a=fmtp:107 apt=125 a=rtcp-fb:107 nack a=rtcp-fb:107 nack pli a=rtcp-fb:107 transport-cc a=rtpmap:108 H264/90000 a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f a=rtcp-fb:108 goog-remb a=rtcp-fb:108 ccm fir a=rtcp-fb:108 nack a=rtcp-fb:108 nack pli a=rtcp-fb:108 nack a=rtcp-fb:108 nack pli a=rtcp-fb:108 transport-cc a=rtpmap:109 rtx/90000 a=fmtp:109 apt=108 a=rtcp-fb:109 nack a=rtcp-fb:109 nack pli a=rtcp-fb:109 transport-cc a=rtpmap:123 H264/90000 a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032 a=rtcp-fb:123 goog-remb a=rtcp-fb:123 ccm fir a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=rtcp-fb:123 transport-cc a=rtpmap:118 rtx/90000 a=fmtp:118 apt=123 a=rtcp-fb:118 nack a=rtcp-fb:118 nack pli a=rtcp-fb:118 transport-cc a=rtpmap:116 ulpfec/90000 a=rtcp-fb:116 nack a=rtcp-fb:116 nack pli a=rtcp-fb:116 transport-cc a=extmap:1 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=recvonly ... 
Enter fullscreen mode Exit fullscreen mode

After

... m=video 9 UDP/TLS/RTP/SAVPF 102 127 125 108 123 c=IN IP4 0.0.0.0 a=setup:actpass a=mid:0 a=ice-ufrag:qTksQATCTrhvEAcY a=ice-pwd:dGLtcbyrUNKsdYxHDEjVuYgwUWdMqqkN a=rtcp-mux a=rtcp-rsize a=rtpmap:102 H264/90000 a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f a=rtcp-fb:102 goog-remb a=rtcp-fb:102 ccm fir a=rtcp-fb:102 nack a=rtcp-fb:102 nack pli a=rtcp-fb:102 nack a=rtcp-fb:102 nack pli a=rtcp-fb:102 transport-cc a=rtpmap:127 H264/90000 a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f a=rtcp-fb:127 goog-remb a=rtcp-fb:127 ccm fir a=rtcp-fb:127 nack a=rtcp-fb:127 nack pli a=rtcp-fb:127 nack a=rtcp-fb:127 nack pli a=rtcp-fb:127 transport-cc a=rtpmap:125 H264/90000 a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=rtcp-fb:125 goog-remb a=rtcp-fb:125 ccm fir a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=rtcp-fb:125 transport-cc a=rtpmap:108 H264/90000 a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f a=rtcp-fb:108 goog-remb a=rtcp-fb:108 ccm fir a=rtcp-fb:108 nack a=rtcp-fb:108 nack pli a=rtcp-fb:108 nack a=rtcp-fb:108 nack pli a=rtcp-fb:108 transport-cc a=rtpmap:123 H264/90000 a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032 a=rtcp-fb:123 goog-remb a=rtcp-fb:123 ccm fir a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=rtcp-fb:123 transport-cc a=extmap:1 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=recvonly ... 
Enter fullscreen mode Exit fullscreen mode

This method works, but has problems.
If another client is already connected with a different video codec, the connection will fail.

[Client-side] setCodecPreferences

Because ghe priority of the video codecs used matches the order of description in SDP, I sort them by "setCodePreferences"

preferVideoCodec.ts

import { hasAnyTexts } from "ts/hasAnyTexts"; /** Prefer Specified Video Codec */ export function preferVideoCodec(codecs: RTCRtpCodecCapability[], mimeType: string): RTCRtpCodecCapability[] { // skip sorting video codecs if the mimeType is empty. if(!hasAnyTexts(mimeType)) { return codecs; } const otherCodecs: RTCRtpCodecCapability[] = []; const sortedCodecs: RTCRtpCodecCapability[] = []; codecs.forEach((codec) => { if (codec.mimeType === mimeType) { sortedCodecs.push(codec); } else { otherCodecs.push(codec); } }); return sortedCodecs.concat(otherCodecs); } 
Enter fullscreen mode Exit fullscreen mode

webrtc.controller.ts

... public handleOffer(data: RTCSessionDescription | null | undefined, preferredMimeType: string|null): boolean { ... this.peerConnection.setRemoteDescription(data); // prefer specific video codec before calling createAnswer if(hasAnyTexts(preferredMimeType)) { this.peerConnection.getTransceivers() .forEach((transceiver) => { const kind = transceiver.sender.track?.kind; if(kind == null) { return; } if (kind === "video") { let codecs = RTCRtpSender.getCapabilities(kind)?.codecs; if(codecs == null) { return; } codecs = preferVideoCodec(codecs, preferredMimeType); transceiver.setCodecPreferences([...codecs, ...codecs]); } }); } this.peerConnection.createAnswer() .then(answer => { if (this.peerConnection != null) { this.peerConnection.setLocalDescription(answer); } if (this.answerSentEvent != null) { this.answerSentEvent(answer); } }); return true; } ... 
Enter fullscreen mode Exit fullscreen mode

After(Answer SDP, I select H264)

... m=video 60274 UDP/TLS/RTP/SAVPF 102 121 127 120 125 107 108 109 123 118 96 97 98 99 116 c=IN IP4 60.46.180.81 a=rtcp:9 IN IP4 0.0.0.0 a=candidate:4106528496 1 udp 2122260223 192.168.86.22 60274 typ host generation 0 network-id 1 network-cost 10 a=candidate:801605297 1 udp 1686052607 60.46.180.81 60274 typ srflx raddr 192.168.86.22 rport 60274 generation 0 network-id 1 network-cost 10 a=candidate:2321187837 1 tcp 1518280447 192.168.86.22 9 typ host tcptype active generation 0 network-id 1 network-cost 10 a=ice-ufrag:1jxi a=ice-pwd:6y/munHjivGnsDyd+lTRNimE a=ice-options:trickle a=fingerprint:sha-256 BB:4B:E6:D0:EE:59:B3:1D:1C:F1:50:59:C6:4E:C7:92:57:94:AF:46:D9:42:1E:6D:5C:EE:CB:69:3D:72:AE:02 a=setup:active a=mid:0 a=extmap:1 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=sendonly a=msid:67076c8b-0491-43fc-b940-66433c9e00bd c98ca17c-7198-495a-90e3-d122c57be656 a=rtcp-mux a=rtcp-rsize a=rtpmap:102 H264/90000 a=rtcp-fb:102 goog-remb a=rtcp-fb:102 ccm fir a=rtcp-fb:102 nack a=rtcp-fb:102 nack pli a=rtcp-fb:102 transport-cc a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f a=rtpmap:121 rtx/90000 a=rtcp-fb:121 nack a=rtcp-fb:121 nack pli a=rtcp-fb:121 transport-cc a=fmtp:121 apt=102 a=rtpmap:127 H264/90000 a=rtcp-fb:127 goog-remb a=rtcp-fb:127 ccm fir a=rtcp-fb:127 nack a=rtcp-fb:127 nack pli a=rtcp-fb:127 transport-cc a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f a=rtpmap:120 rtx/90000 a=rtcp-fb:120 nack a=rtcp-fb:120 nack pli a=rtcp-fb:120 transport-cc a=fmtp:120 apt=127 a=rtpmap:125 H264/90000 a=rtcp-fb:125 goog-remb a=rtcp-fb:125 ccm fir a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=rtcp-fb:125 transport-cc a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=rtpmap:107 rtx/90000 a=rtcp-fb:107 nack a=rtcp-fb:107 nack pli a=rtcp-fb:107 transport-cc a=fmtp:107 apt=125 a=rtpmap:108 H264/90000 a=rtcp-fb:108 goog-remb a=rtcp-fb:108 ccm fir a=rtcp-fb:108 nack a=rtcp-fb:108 nack pli a=rtcp-fb:108 transport-cc a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f a=rtpmap:109 rtx/90000 a=rtcp-fb:109 nack a=rtcp-fb:109 nack pli a=rtcp-fb:109 transport-cc a=fmtp:109 apt=108 a=rtpmap:123 H264/90000 a=rtcp-fb:123 goog-remb a=rtcp-fb:123 ccm fir a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=rtcp-fb:123 transport-cc a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032 a=rtpmap:118 rtx/90000 a=rtcp-fb:118 nack a=rtcp-fb:118 nack pli a=rtcp-fb:118 transport-cc a=fmtp:118 apt=123 a=rtpmap:96 VP8/90000 a=rtcp-fb:96 goog-remb a=rtcp-fb:96 ccm fir a=rtcp-fb:96 nack a=rtcp-fb:96 nack pli a=rtcp-fb:96 transport-cc a=rtpmap:97 rtx/90000 a=rtcp-fb:97 nack a=rtcp-fb:97 nack pli a=rtcp-fb:97 transport-cc a=fmtp:97 apt=96 a=rtpmap:98 VP9/90000 a=rtcp-fb:98 goog-remb a=rtcp-fb:98 ccm fir a=rtcp-fb:98 nack a=rtcp-fb:98 nack pli a=rtcp-fb:98 transport-cc a=fmtp:98 profile-id=0 a=rtpmap:99 rtx/90000 a=rtcp-fb:99 nack a=rtcp-fb:99 nack pli a=rtcp-fb:99 transport-cc a=fmtp:99 apt=98 a=rtpmap:116 ulpfec/90000 a=rtcp-fb:116 nack a=rtcp-fb:116 nack pli a=rtcp-fb:116 transport-cc a=ssrc-group:FID 1751253213 3231317468 a=ssrc:1751253213 cname:TI63w5WXK1xmxz8A a=ssrc:1751253213 msid:67076c8b-0491-43fc-b940-66433c9e00bd c98ca17c-7198-495a-90e3-d122c57be656 a=ssrc:3231317468 cname:TI63w5WXK1xmxz8A a=ssrc:3231317468 msid:67076c8b-0491-43fc-b940-66433c9e00bd c98ca17c-7198-495a-90e3-d122c57be656 ... 
Enter fullscreen mode Exit fullscreen mode

[Server-side] setCodecPreferences

Pion/WebRTC also has "setCodecPreferences".

So I can sort video codecs for Offer SDP as same as the client-side.

peerConnectionState.go

... func NewPeerConnection() (*webrtc.PeerConnection, error) { peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ { URLs: []string{ "stun:stun.l.google.com:19302", }, }, }, }) if err != nil { return nil, err } for _, typ := range []webrtc.RTPCodecType{webrtc.RTPCodecTypeVideo, webrtc.RTPCodecTypeAudio} { if _, err := peerConnection.AddTransceiverFromKind(typ, webrtc.RTPTransceiverInit{ Direction: webrtc.RTPTransceiverDirectionRecvonly, }); err != nil { return nil, err } } for _, trn := range peerConnection.GetTransceivers() { if trn.Kind() == webrtc.RTPCodecTypeVideo { // prefer "H264" for example preferVideoCodec(trn, "video/H264") } } return peerConnection, nil } ... func preferVideoCodec(trn *webrtc.RTPTransceiver, mimeType string) { originalCodecs := trn.Receiver().GetParameters().Codecs sortedCodecs := make([]webrtc.RTPCodecParameters, len(originalCodecs)) index := 0 for _, codec := range originalCodecs { if codec.MimeType == mimeType { sortedCodecs[index] = codec index += 1 } } for _, codec := range originalCodecs { if codec.MimeType != mimeType { sortedCodecs[index] = codec index += 1 } } trn.SetCodecPreferences(sortedCodecs) } 
Enter fullscreen mode Exit fullscreen mode

After(Offer SDP)

... m=video 9 UDP/TLS/RTP/SAVPF 102 127 125 108 123 96 97 98 99 100 101 121 120 107 109 118 116 c=IN IP4 0.0.0.0 a=setup:actpass a=mid:0 a=ice-ufrag:psvRzwNhTPFgjkEt a=ice-pwd:VdywVCOKklKxZknlYRpLusBpjaTxOneN a=rtcp-mux a=rtcp-rsize a=rtpmap:102 H264/90000 a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f a=rtcp-fb:102 goog-remb a=rtcp-fb:102 ccm fir a=rtcp-fb:102 nack a=rtcp-fb:102 nack pli a=rtcp-fb:102 nack a=rtcp-fb:102 nack pli a=rtcp-fb:102 transport-cc a=rtpmap:127 H264/90000 a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f a=rtcp-fb:127 goog-remb a=rtcp-fb:127 ccm fir a=rtcp-fb:127 nack a=rtcp-fb:127 nack pli a=rtcp-fb:127 nack a=rtcp-fb:127 nack pli a=rtcp-fb:127 transport-cc a=rtpmap:125 H264/90000 a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=rtcp-fb:125 goog-remb a=rtcp-fb:125 ccm fir a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=rtcp-fb:125 transport-cc a=rtpmap:108 H264/90000 a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f a=rtcp-fb:108 goog-remb a=rtcp-fb:108 ccm fir a=rtcp-fb:108 nack a=rtcp-fb:108 nack pli a=rtcp-fb:108 nack a=rtcp-fb:108 nack pli a=rtcp-fb:108 transport-cc a=rtpmap:123 H264/90000 a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032 a=rtcp-fb:123 goog-remb a=rtcp-fb:123 ccm fir a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=rtcp-fb:123 transport-cc a=rtpmap:96 VP8/90000 a=rtcp-fb:96 goog-remb a=rtcp-fb:96 ccm fir a=rtcp-fb:96 nack a=rtcp-fb:96 nack pli a=rtcp-fb:96 nack a=rtcp-fb:96 nack pli a=rtcp-fb:96 transport-cc a=rtpmap:97 rtx/90000 a=fmtp:97 apt=96 a=rtcp-fb:97 nack a=rtcp-fb:97 nack pli a=rtcp-fb:97 transport-cc a=rtpmap:98 VP9/90000 a=fmtp:98 profile-id=0 a=rtcp-fb:98 goog-remb a=rtcp-fb:98 ccm fir a=rtcp-fb:98 nack a=rtcp-fb:98 nack pli a=rtcp-fb:98 nack a=rtcp-fb:98 nack pli a=rtcp-fb:98 transport-cc a=rtpmap:99 rtx/90000 a=fmtp:99 apt=98 a=rtcp-fb:99 nack a=rtcp-fb:99 nack pli a=rtcp-fb:99 transport-cc a=rtpmap:100 VP9/90000 a=fmtp:100 profile-id=1 a=rtcp-fb:100 goog-remb a=rtcp-fb:100 ccm fir a=rtcp-fb:100 nack a=rtcp-fb:100 nack pli a=rtcp-fb:100 nack a=rtcp-fb:100 nack pli a=rtcp-fb:100 transport-cc a=rtpmap:101 rtx/90000 a=fmtp:101 apt=100 a=rtcp-fb:101 nack a=rtcp-fb:101 nack pli a=rtcp-fb:101 transport-cc a=rtpmap:121 rtx/90000 a=fmtp:121 apt=102 a=rtcp-fb:121 nack a=rtcp-fb:121 nack pli a=rtcp-fb:121 transport-cc a=rtpmap:120 rtx/90000 a=fmtp:120 apt=127 a=rtcp-fb:120 nack a=rtcp-fb:120 nack pli a=rtcp-fb:120 transport-cc a=rtpmap:107 rtx/90000 a=fmtp:107 apt=125 a=rtcp-fb:107 nack a=rtcp-fb:107 nack pli a=rtcp-fb:107 transport-cc a=rtpmap:109 rtx/90000 a=fmtp:109 apt=108 a=rtcp-fb:109 nack a=rtcp-fb:109 nack pli a=rtcp-fb:109 transport-cc a=rtpmap:118 rtx/90000 a=fmtp:118 apt=123 a=rtcp-fb:118 nack a=rtcp-fb:118 nack pli a=rtcp-fb:118 transport-cc a=rtpmap:116 ulpfec/90000 a=rtcp-fb:116 nack a=rtcp-fb:116 nack pli a=rtcp-fb:116 transport-cc a=extmap:1 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=recvonly ... 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)