了解 Media API 概念

借助 Google Meet Media API,您的应用可以加入 Google Meet 会议并使用实时媒体串流。

客户端使用 WebRTC 与 Meet 服务器通信。提供的参考客户端(C++TypeScript)展示了推荐做法,我们建议您直接基于这些做法构建应用。

不过,您也可以构建完全自定义的 WebRTC 客户端,使其符合 Meet Media API 的技术要求

本页面概述了成功建立 Meet Media API 会话所需的关键 WebRTC 概念。

出价-回答信号

WebRTC 是一个点对点 (P2P) 框架,其中对等设备通过相互发送信号进行通信。如需开始会话,发起方对等体会向远程对等体发送 SDP 报价。此优惠包含以下重要详细信息:

音频和视频的媒体说明

媒体说明用于指明点对点会话期间传输的内容。描述分为三种:音频、视频和数据。

如需指明 n 音频串流,提供商应在提供内容中添加 n 音频媒体描述。视频也是如此。不过,数据媒体描述最多只有一个。

方向

每个音频或视频说明都描述了由 RFC 3711 管理的各个安全实时传输协议 (SRTP) 串流。这些连接是双向的,允许两个对等方通过同一连接发送和接收媒体。

因此,每个媒体说明(在提供方和回答中)都包含以下三个属性之一,用于说明应如何使用数据流:

  • sendonly:仅发送来自提供方对等的媒体。远程对等方不会在此串流中发送媒体。

  • recvonly:仅接收来自远程对等方的媒体。提供方对等不会在此串流中发送媒体。

  • sendrecv:两个对等方都可以在此数据流上发送和接收数据。

编解码器

每个媒体描述还会指定对等支持的编解码器。对于 Meet Media API,除非客户端提供的媒体至少支持技术要求中指定的编解码器,否则系统会拒绝其提供的媒体。

DTLS 握手

SRTP 流通过对等方之间的初始数据报传输层安全协议 (“DTLS”RFC 9147) 握手进行保护。DTLS 传统上是一种客户端到服务器协议;在信号传送过程中,一个对等方同意充当服务器,另一个对等方充当对等方。

由于每个 SRTP 流都可能有自己的专用 DTLS 连接,因此每个媒体描述都指定以下三个属性之一,以指示对等方在 DTLS 握手中的角色:

  • a=setup:actpass提供对等方会遵循远程对等方的选择。

  • a=setup:active:此对等方充当客户端。

  • a=setup:passive:此对等方充当服务器。

应用媒体说明

数据通道 (RFC 8831) 是对流控制传输协议(“SCTP”RFC 9260)的抽象。

如需在初始信令阶段打开数据通道,优惠必须包含应用媒体描述。与音频和视频说明不同,应用说明不会指定方向或编解码器。

ICE 候选人

对等方的Interactive Connectivity Establishment(“ICE”RFC 8445)候选对象是远程对等方可能用于建立连接的路由列表。

两个对等点列表的笛卡尔积(称为候选对)表示两个对等点之间的潜在路线。系统会对这些对进行测试,以确定最佳路线。

通过 Meet REST API 发送信号

使用 Meet REST API 执行此提供-接听信号。您的应用向 connectActiveConference() 方法提供 SDP 提议,并接收 SDP 响应

连接流程示例

以下是包含音频媒体说明的商品:

包含音频媒体说明的商品示例。
图 1. 包含音频媒体说明的商品示例。

远程对等方会响应一个包含相同数量媒体描述行的 SDP 响应。每行都表示远程对等方通过 SRTP 流向提供方客户端发送的媒体(如果有)。远程对等方还可以通过将该媒体描述条目设置为 recvonly 来拒绝提供方提供的特定串流。

对于 Meet Media API,客户端始终发送 SDP 报价以发起连接。Meet 绝不会是发起者。

此行为由参考客户端(C++TypeScript)在内部管理,但自定义客户端的开发者可以使用 WebRTC 的 PeerConnectionInterface 生成 offer。

要与 Meet 关联,商品必须符合特定要求

  1. 客户端始终必须在 DTLS 握手中充当客户端,因此,报价中的每个媒体描述都必须指定 a=setup:actpassa=setup:active

  2. 每行媒体描述都必须支持相应媒体类型的所有必需编解码器

    • 音频Opus
    • 视频VP8VP9AV1
  3. 如需接收音频,商品必须包含恰好 3 条仅接收音频的媒体说明。为此,您可以在对等连接对象上设置收发器。

    C++

    // ...
    rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection;
    
    for (int i = 0; i < 3; ++i) {
      webrtc::RtpTransceiverInit audio_init;
      audio_init.direction = webrtc::RtpTransceiverDirection::kRecvOnly;
      audio_init.stream_ids = {absl::StrCat("audio_stream_", i)};
    
      webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
        audio_result = peer_connection->AddTransceiver(
          cricket::MediaType::MEDIA_TYPE_AUDIO, audio_init);
    
      if (!audio_result.ok()) {
        return absl::InternalError(absl::StrCat("Failed to add audio transceiver: ",
                                                audio_result.error().message()));
      }
    }
    

    JavaScript

    pc = new RTCPeerConnection();
    
    // Configure client to receive audio from Meet servers.
    pc.addTransceiver('audio', {'direction':'recvonly'});
    pc.addTransceiver('audio', {'direction':'recvonly'});
    pc.addTransceiver('audio', {'direction':'recvonly'});
    
  4. 如需接收视频,商品必须包含 1-3 个仅接收视频的媒体说明。为此,您可以在对等连接对象上设置收发器。

    C++

    // ...
    rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection;
    
    for (uint32_t i = 0; i < configurations.receiving_video_stream_count; ++i) {
      webrtc::RtpTransceiverInit video_init;
      video_init.direction = webrtc::RtpTransceiverDirection::kRecvOnly;
      video_init.stream_ids = {absl::StrCat("video_stream_", i)};
    
      webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
          video_result = peer_connection->AddTransceiver(
            cricket::MediaType::MEDIA_TYPE_VIDEO, video_init);
    
      if (!video_result.ok()) {
        return absl::InternalError(absl::StrCat("Failed to add video transceiver: ",
                                                video_result.error().message()));
      }
    }
    

    JavaScript

    pc = new RTCPeerConnection();
    
    // Configure client to receive video from Meet servers.
    pc.addTransceiver('video', {'direction':'recvonly'});
    pc.addTransceiver('video', {'direction':'recvonly'});
    pc.addTransceiver('video', {'direction':'recvonly'});
    
  5. 优惠始终必须包含数据渠道。至少,session-controlmedia-stats 通道应始终处于打开状态。所有数据通道都必须为 ordered

    C++

    // ...
    // All data channels must be ordered.
    constexpr webrtc::DataChannelInit kDataChannelConfig = {.ordered = true};
    
    rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection;
    
    // Signal session-control data channel.
    webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::DataChannelInterface>>
      session_create_result = peer_connection->CreateDataChannelOrError(
        "session-control", &kDataChannelConfig);
    
    if (!session_create_result.ok()) {
      return absl::InternalError(absl::StrCat("Failed to create data channel ",
                                              data_channel_label, ": ",
                                              session_create_result.error().message()));
    }
    
    // Signal media-stats data channel.
    webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::DataChannelInterface>>
      stats_create_result = peer_connection->CreateDataChannelOrError(
        "media-stats", &kDataChannelConfig);
    
    if (!stats_create_result.ok()) {
      return absl::InternalError(absl::StrCat("Failed to create data channel ",
                                              data_channel_label, ": ",
                                              stats_create_result.error().message()));
    }
    

    JavaScript

    // ...
    pc = new RTCPeerConnection();
    
    // All data channels must be ordered.
    const dataChannelConfig = {
      ordered: true,
    };
    
    // Signal session-control data channel.
    sessionControlChannel = pc.createDataChannel('session-control', dataChannelConfig);
    sessionControlChannel.onopen = () => console.log("data channel is now open");
    sessionControlChannel.onclose = () => console.log("data channel is now closed");
    sessionControlChannel.onmessage = async (e) => {
      console.log("data channel message", e.data);
    };
    
    // Signal media-stats data channel.
    mediaStatsChannel = pc.createDataChannel('media-stats', dataChannelConfig);
    mediaStatsChannel.onopen = () => console.log("data channel is now open");
    mediaStatsChannel.onclose = () => console.log("data channel is now closed");
    mediaStatsChannel.onmessage = async (e) => {
      console.log("data channel message", e.data);
    };
    

SDP 报价和回复示例

以下是有效 SDP 报价和匹配 SDP 回答的完整示例。此方案会协商包含音频和单个视频串流的 Meet Media API 会话。

请注意,其中包含 3 个音频媒体说明、1 个视频媒体说明以及必需的应用媒体说明。

显示示例

客户端 SDP 优惠 Meet 媒体 API SDP 回答
v=0
o=- 1479484780199836840 3 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1 2 3 4
a=extmap-allow-mixed
a=msid-semantic: WMS
v=0
o=- 0 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1 2 3 4
a=msid-semantic: WMS virtual-6666 virtual-video-7777/7777
a=ice-lite
m=audio 59905 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126
c=IN IP4 136.55.18.35
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:3490152339 1 udp 2113937151 46ae665f-23fd-49df-a002-6d12bc897a54.local 59905 typ host generation 0 network-cost 999
a=candidate:1937170525 1 udp 2113939711 aa575ae6-68fc-4155-83c1-007a1f5e8e55.local 58304 typ host generation 0 network-cost 999
a=candidate:2999458021 1 udp 1677732095 2605:a601:55ab:b000:615a:2317:bf6b:7a30 58304 typ srflx raddr :: rport 0 generation 0 network-cost 999
a=candidate:2517543359 1 udp 1677729535 136.55.18.35 59905 typ srflx raddr 0.0.0.0 rport 0 generation 0 network-cost 999
a=ice-ufrag:0HPF
a=ice-pwd:GcBv48eO/q64iPxb7MHKS87y
a=ice-options:trickle
a=fingerprint:sha-256 71:0A:DD:DF:D1:63:8E:D5:CB:E6:2B:6D:41:1D:D4:EE:79:B2:95:97:8A:F0:64:FF:10:37:8D:41:ED:DB:EC:C4
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
m=audio 19306 UDP/TLS/RTP/SAVPF 111
c=IN IP4 142.250.82.213
a=rtcp:9 IN IP4 0.0.0.0
a=candidate: 1 udp 2113932031 142.250.82.213 19306 typ host generation 0
a=candidate: 1 tcp 2113932030 142.250.82.253 19306 typ host tcptype passive generation 0
a=candidate: 1 ssltcp 2113932029 142.250.82.253 19313 typ host generation 0
a=candidate: 1 udp 2113939711 2001:4860:4864:6:4000::19 19306 typ host generation 0
a=candidate: 1 tcp 2113939710 2001:4860:4864:6:8000::5 19306 typ host tcptype passive generation 0
a=candidate: 1 ssltcp 2113939709 2001:4860:4864:6:8000::5 19313 typ host generation 0
a=ice-ufrag:K8mRD3UolM6pjwoKAhgCCIoBnCgCIAEQ
a=ice-pwd:+7DfqMEDEFB6dLAKfGjT41l7ygg=
a=fingerprint:sha-256 32:C0:9D:17:AD:99:E2:B8:2D:FD:5D:87:D4:36:44:4A:5B:3E:EE:EA:F2:BE:BE:72:3B:66:4C:F2:57:3C:0D:FF
a=setup:passive
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=sendonly
a=msid:virtual-6666 virtual-6666
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=ssrc:6666 cname:6666
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:0HPF
a=ice-pwd:GcBv48eO/q64iPxb7MHKS87y
a=ice-options:trickle
a=fingerprint:sha-256 71:0A:DD:DF:D1:63:8E:D5:CB:E6:2B:6D:41:1D:D4:EE:79:B2:95:97:8A:F0:64:FF:10:37:8D:41:ED:DB:EC:C4
a=setup:actpass
a=mid:1
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
m=audio 9 UDP/TLS/RTP/SAVPF 111
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:K8mRD3UolM6pjwoKAhgCCIoBnCgCIAEQ
a=ice-pwd:+7DfqMEDEFB6dLAKfGjT41l7ygg=
a=fingerprint:sha-256 32:C0:9D:17:AD:99:E2:B8:2D:FD:5D:87:D4:36:44:4A:5B:3E:EE:EA:F2:BE:BE:72:3B:66:4C:F2:57:3C:0D:FF
a=setup:passive
a=mid:1
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=sendonly
a=msid:virtual-6667 virtual-6667
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=ssrc:6667 cname:6667
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:0HPF
a=ice-pwd:GcBv48eO/q64iPxb7MHKS87y
a=ice-options:trickle
a=fingerprint:sha-256 71:0A:DD:DF:D1:63:8E:D5:CB:E6:2B:6D:41:1D:D4:EE:79:B2:95:97:8A:F0:64:FF:10:37:8D:41:ED:DB:EC:C4
a=setup:actpass
a=mid:2
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
m=audio 9 UDP/TLS/RTP/SAVPF 111
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:K8mRD3UolM6pjwoKAhgCCIoBnCgCIAEQ
a=ice-pwd:+7DfqMEDEFB6dLAKfGjT41l7ygg=
a=fingerprint:sha-256 32:C0:9D:17:AD:99:E2:B8:2D:FD:5D:87:D4:36:44:4A:5B:3E:EE:EA:F2:BE:BE:72:3B:66:4C:F2:57:3C:0D:FF
a=setup:passive
a=mid:2
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=sendonly
a=msid:virtual-6668 virtual-6668
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=ssrc:6668 cname:6668
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=ice-ufrag:0HPF
a=ice-pwd:GcBv48eO/q64iPxb7MHKS87y
a=ice-options:trickle
a=fingerprint:sha-256 71:0A:DD:DF:D1:63:8E:D5:CB:E6:2B:6D:41:1D:D4:EE:79:B2:95:97:8A:F0:64:FF:10:37:8D:41:ED:DB:EC:C4
a=setup:actpass
a=mid:3
a=sctp-port:5000
a=max-message-size:262144
m=application 9 DTLS/SCTP 5000
c=IN IP4 0.0.0.0
a=ice-ufrag:K8mRD3UolM6pjwoKAhgCCIoBnCgCIAEQ
a=ice-pwd:+7DfqMEDEFB6dLAKfGjT41l7ygg=
a=fingerprint:sha-256 32:C0:9D:17:AD:99:E2:B8:2D:FD:5D:87:D4:36:44:4A:5B:3E:EE:EA:F2:BE:BE:72:3B:66:4C:F2:57:3C:0D:FF
a=setup:passive
a=mid:3
a=sctpmap:5000 webrtc-datachannel 1024
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 35 36 37 38 102 103 104 105 106 107 108 109 127 125 39 40 41 42 43 44 45 46 47 48 112 113 114 115 116 117 118 49
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:0HPF
a=ice-pwd:GcBv48eO/q64iPxb7MHKS87y
a=ice-options:trickle
a=fingerprint:sha-256 71:0A:DD:DF:D1:63:8E:D5:CB:E6:2B:6D:41:1D:D4:EE:79:B2:95:97:8A:F0:64:FF:10:37:8D:41:ED:DB:EC:C4
a=setup:actpass
a=mid:4
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 VP9/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 profile-id=2
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:35 VP9/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=fmtp:35 profile-id=1
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:37 VP9/90000
a=rtcp-fb:37 goog-remb
a=rtcp-fb:37 transport-cc
a=rtcp-fb:37 ccm fir
a=rtcp-fb:37 nack
a=rtcp-fb:37 nack pli
a=fmtp:37 profile-id=3
a=rtpmap:38 rtx/90000
a=fmtp:38 apt=37
a=rtpmap:102 H264/90000
a=rtcp-fb:102 goog-remb
a=rtcp-fb:102 transport-cc
a=rtcp-fb:102 ccm fir
a=rtcp-fb:102 nack
a=rtcp-fb:102 nack pli
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:103 rtx/90000
a=fmtp:103 apt=102
a=rtpmap:104 H264/90000
a=rtcp-fb:104 goog-remb
a=rtcp-fb:104 transport-cc
a=rtcp-fb:104 ccm fir
a=rtcp-fb:104 nack
a=rtcp-fb:104 nack pli
a=fmtp:104 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
a=rtpmap:105 rtx/90000
a=fmtp:105 apt=104
a=rtpmap:106 H264/90000
a=rtcp-fb:106 goog-remb
a=rtcp-fb:106 transport-cc
a=rtcp-fb:106 ccm fir
a=rtcp-fb:106 nack
a=rtcp-fb:106 nack pli
a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=106
a=rtpmap:108 H264/90000
a=rtcp-fb:108 goog-remb
a=rtcp-fb:108 transport-cc
a=rtcp-fb:108 ccm fir
a=rtcp-fb:108 nack
a=rtcp-fb:108 nack pli
a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
a=rtpmap:109 rtx/90000
a=fmtp:109 apt=108
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f
a=rtpmap:125 rtx/90000
a=fmtp:125 apt=127
a=rtpmap:39 H264/90000
a=rtcp-fb:39 goog-remb
a=rtcp-fb:39 transport-cc
a=rtcp-fb:39 ccm fir
a=rtcp-fb:39 nack
a=rtcp-fb:39 nack pli
a=fmtp:39 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f
a=rtpmap:40 rtx/90000
a=fmtp:40 apt=39
a=rtpmap:41 H264/90000
a=rtcp-fb:41 goog-remb
a=rtcp-fb:41 transport-cc
a=rtcp-fb:41 ccm fir
a=rtcp-fb:41 nack
a=rtcp-fb:41 nack pli
a=fmtp:41 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=f4001f
a=rtpmap:42 rtx/90000
a=fmtp:42 apt=41
a=rtpmap:43 H264/90000
a=rtcp-fb:43 goog-remb
a=rtcp-fb:43 transport-cc
a=rtcp-fb:43 ccm fir
a=rtcp-fb:43 nack
a=rtcp-fb:43 nack pli
a=fmtp:43 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=f4001f
a=rtpmap:44 rtx/90000
a=fmtp:44 apt=43
a=rtpmap:45 AV1/90000
a=rtcp-fb:45 goog-remb
a=rtcp-fb:45 transport-cc
a=rtcp-fb:45 ccm fir
a=rtcp-fb:45 nack
a=rtcp-fb:45 nack pli
a=fmtp:45 level-idx=5;profile=0;tier=0
a=rtpmap:46 rtx/90000
a=fmtp:46 apt=45
a=rtpmap:47 AV1/90000
a=rtcp-fb:47 goog-remb
a=rtcp-fb:47 transport-cc
a=rtcp-fb:47 ccm fir
a=rtcp-fb:47 nack
a=rtcp-fb:47 nack pli
a=fmtp:47 level-idx=5;profile=1;tier=0
a=rtpmap:48 rtx/90000
a=fmtp:48 apt=47
a=rtpmap:112 H264/90000
a=rtcp-fb:112 goog-remb
a=rtcp-fb:112 transport-cc
a=rtcp-fb:112 ccm fir
a=rtcp-fb:112 nack
a=rtcp-fb:112 nack pli
a=fmtp:112 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f
a=rtpmap:113 rtx/90000
a=fmtp:113 apt=112
a=rtpmap:114 H264/90000
a=rtcp-fb:114 goog-remb
a=rtcp-fb:114 transport-cc
a=rtcp-fb:114 ccm fir
a=rtcp-fb:114 nack
a=rtcp-fb:114 nack pli
a=fmtp:114 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=64001f
a=rtpmap:115 rtx/90000
a=fmtp:115 apt=114
a=rtpmap:116 red/90000
a=rtpmap:117 rtx/90000
a=fmtp:117 apt=116
a=rtpmap:118 ulpfec/90000
a=rtpmap:49 flexfec-03/90000
a=rtcp-fb:49 goog-remb
a=rtcp-fb:49 transport-cc
a=fmtp:49 repair-window=10000000
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:K8mRD3UolM6pjwoKAhgCCIoBnCgCIAEQ
a=ice-pwd:+7DfqMEDEFB6dLAKfGjT41l7ygg=
a=fingerprint:sha-256 32:C0:9D:17:AD:99:E2:B8:2D:FD:5D:87:D4:36:44:4A:5B:3E:EE:EA:F2:BE:BE:72:3B:66:4C:F2:57:3C:0D:FF
a=setup:passive
a=mid:4
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=sendonly
a=msid:virtual-video-7777/7777 virtual-video-7777/7777
a=rtcp-mux
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtcp-fb:96 goog-remb
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=rtcp-fb:98 goog-remb
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=ssrc-group:FID 7777 7778
a=ssrc:7777 cname:7777
a=ssrc:7778 cname:7777