معاينة المطور:
يتوفّر كجزء من برنامج الاستخدام المبكر في Google Workspace للمطوّرين ، الذي يتيح استخدام المنتج قبل إطلاقه لميزات معيّنة.
لاستخدام Meet Media API للوصول إلى الوسائط في الوقت الفعلي من اجتماع، يجب أن يكون مشروع Google Cloud ومستخدم OAuth الأساسي وجميع المشاركين في الاجتماع مسجّلين في "برنامج معاينة المطوّرين".
تتيح Google Meet Media API لتطبيقك الانضمام إلى مؤتمر على Google Meet واستهلاك بث الوسائط في الوقت الفعلي.
يستخدم العملاء WebRTC للتواصل مع خوادم Meet. توضّح برامج العميل المرجعية المتوفّرة (C++ وTypeScript ) الممارسات المقترَحة، وننصحك بالاستفادة منها مباشرةً.
ومع ذلك، يمكنك أيضًا إنشاء برامج عميل WebRTC مخصّصة بالكامل تتوافق مع المتطلبات الفنية لواجهة برمجة تطبيقات Meet Media.
توضّح هذه الصفحة المفاهيم الأساسية في WebRTC المطلوبة لإجراء جلسة ناجحة باستخدام Meet Media API.
إشارات العرض والرد
WebRTC هو إطار عمل من نظير إلى نظير (P2P)، حيث يتواصل النظراء من خلال إرسال إشارات إلى بعضهم البعض. لبدء جلسة، يرسل الجهاز المضيف عرض SDP إلى جهاز بعيد. يتضمّن هذا العرض التفاصيل المهمة التالية:
تشير أوصاف الوسائط إلى ما يتم التواصل بشأنه خلال جلسات الند للند. تتوفّر ثلاثة أنواع من الأوصاف: الصوت والفيديو والبيانات.
للإشارة إلى n بث صوتي، يدرج مقدّم العرض أوصاف وسائط n صوتية في العرض. وينطبق الأمر نفسه على الفيديوهات. ومع ذلك، لن يكون هناك سوى وصف واحد لوسائط البيانات كحد أقصى.
اتجاهات الطريق
يصف كل وصف صوتي أو مرئي تدفقات بروتوكول النقل الآمن في الوقت الفعلي (SRTP) الفردية التي تخضع لسياسة RFC
3711 . وهي ثنائية الاتجاه، ما يتيح لجهازَين نظيرَين إرسال واستقبال الوسائط عبر الاتصال نفسه.
لهذا السبب، يحتوي كل وصف للوسائط (في كل من العرض والرد) على إحدى السمات الثلاث التي توضّح كيفية استخدام البث:
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 candidates
مرشّحو إنشاء الاتصالات التفاعلية (ICE، RFC
8445 ) لأحد الأجهزة النظيرة هم قائمة بالمسارات التي يمكن لجهاز نظير بعيد استخدامها لإنشاء اتصال.
يمثّل حاصل الضرب الديكارتي لقائمتَي الأجهزة المتصلة، والمعروف باسم أزواج الأجهزة المرشّحة ، المسارات المحتملة بين جهازَين متصلَين. ويتم اختبار هذه الأزواج لتحديد المسار الأمثل.
إرسال إشارة من خلال واجهة برمجة تطبيقات Meet REST
استخدِم Meet REST API لتنفيذ عملية إرسال الإشارات offer-answer . يقدّم تطبيقك عرضًا من بروتوكول وصف الجلسة (SDP) إلى طريقة connectActiveConference() ويتلقّى ردًا من بروتوكول وصف الجلسة (SDP) في المقابل.
تعرض عيّنات الرموز البرمجية التالية كيفية استدعاء الطريقة:
مثال على مسار الربط
إليك عرضًا يتضمّن وصفًا لوسائط صوتية:
الشكل 1. مثال على عرض يتضمّن وصفًا لوسائط صوتية
يستجيب الجهاز البعيد بنوع SDP
answer يحتوي على العدد نفسه من أسطر وصف الوسائط. يشير كل سطر إلى الوسائط التي يرسلها الجهاز البعيد إلى العميل الذي يقدّم العرض عبر بث SRTP، إن وُجدت. قد يرفض الجهاز البعيد أيضًا بثوقًا معيّنة من مقدّم العرض من خلال ضبط إدخال وصف الوسائط على recvonly.
بالنسبة إلى Meet Media API، يرسل العملاء دائمًا عرض SDP لبدء اتصال. لا يبدأ Meet الاجتماع أبدًا.
تتم إدارة هذا السلوك داخليًا من خلال البرامج المرجعية
(C++ وTypeScript )،
ولكن يمكن لمطوّري البرامج المخصّصة استخدام PeerConnectionInterface في WebRTC
لإنشاء عرض.
للاتصال باجتماع Meet، يجب أن يستوفي العرض الترويجي
المتطلبات التالية:
يجب أن يعمل العميل دائمًا كعميل في عملية تبادل بيانات DTLS، لذا يجب أن يحدّد كل وصف وسائط في العرض إما a=setup:actpass أو a=setup:active.
يجب أن يتوافق كل سطر من سطور وصف الوسائط مع جميع برامج الترميز المطلوبة لنوع الوسائط هذا:
الصوت: Opus
الفيديو: VP8 وVP9 وAV1
لتلقّي الصوت، يجب أن يتضمّن العرض 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' });
لاستلام الفيديو، يجب أن يتضمّن العرض من وصف واحد إلى ثلاثة أوصاف لوسائط الفيديو التي يمكن استلامها فقط. يمكنك إجراء ذلك من خلال ضبط أجهزة الإرسال والاستقبال على عنصر اتصال الند للند.
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' });
يجب أن يتضمّن العرض دائمًا قنوات بيانات. يجب أن تكون القناتان session-control وmedia-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 تتضمّن الصوت وفيديو واحدًا.
لاحظ أنّ هناك ثلاثة أوصاف لملفات وسائط صوتية، ووصف واحد لملف وسائط فيديو، ووصف ملف وسائط التطبيق المطلوب.
ملاحظة: يتم فصل الأقسام إلى صفوف لتسهيل عرضها.
عرض SDP للعميل
ردّ بروتوكول وصف الجلسة (SDP) من Meet Media API
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