Concetti dell'API Meet Media

L'API Google Meet Media consente alla tua app di partecipare a una conferenza Google Meet e utilizzare flussi multimediali in tempo reale.

I client utilizzano WebRTC per comunicare con i server Meet. I client di riferimento forniti (C++, TypeScript) mostrano le pratiche consigliate e ti invitiamo a utilizzarli come base per la tua implementazione.

Tuttavia, puoi anche creare client WebRTC completamente personalizzati che rispettino i requisiti tecnici dell'API Meet Media.

Questa pagina descrive i concetti chiave di WebRTC necessari per una sessione dell'API Meet Media riuscita.

Segnalazione di offerta/risposta

WebRTC è un framework peer-to-peer (P2P) in cui i peer comunicano segnalandosi a vicenda. Per iniziare una sessione, il peer che la avvia invia un'offerta SDP a un peer remoto. Questa offerta include i seguenti dettagli importanti:

Descrizioni dei contenuti multimediali per audio e video

Le descrizioni dei contenuti multimediali indicano cosa viene comunicato durante le sessioni P2P. Esistono tre tipi di descrizioni: audio, video e dati.

Per indicare i flussi audio n, l'offerente include le descrizioni dei contenuti multimediali audio n nell'offerta. Lo stesso vale per i video. Tuttavia, ci sarà al massimo una descrizione dei contenuti multimediali dati.

Senso di marcia

Ogni descrizione audio o video descrive singoli stream Secure Real-time Transport Protocol (SRTP), regolati da RFC 3711. Sono bidirezionali, consentendo a due peer di inviare e ricevere contenuti multimediali tramite la stessa connessione.

Per questo motivo, ogni descrizione dei contenuti multimediali (sia nell'offerta che nella risposta) contiene uno dei tre attributi che descrivono come deve essere utilizzato lo stream:

  • sendonly: invia solo i contenuti multimediali del peer che offre. Il peer remoto non invierà contenuti multimediali su questo stream.

  • recvonly: riceve solo i contenuti multimediali dal peer remoto. Il peer che ha effettuato l'offerta non invierà contenuti multimediali su questo stream.

  • sendrecv: entrambi i peer possono inviare e ricevere su questo stream.

Codec

Ogni descrizione multimediale specifica anche i codec supportati da un peer. Nel caso dell'API Meet Media, le offerte dei client vengono rifiutate a meno che non supportino (almeno) i codec specificati nei requisiti tecnici.

Handshake DTLS

I flussi SRTP sono protetti da un handshake iniziale Datagram Transport Layer Security ("DTLS", RFC 9147) tra i peer. DTLS è tradizionalmente un protocollo client-server; durante la procedura di segnalazione, un peer accetta di fungere da server, mentre l'altro da peer.

Poiché ogni flusso SRTP potrebbe avere la propria connessione DTLS dedicata, ogni descrizione multimediale specifica uno dei tre attributi per indicare il ruolo del peer nell'handshake DTLS:

  • a=setup:actpass: il peer che offre si adegua alla scelta del peer remoto.

  • a=setup:active: questo peer funge da client.

  • a=setup:passive: questo peer funge da server.

Descrizioni dei contenuti multimediali dell'applicazione

I canali di dati (RFC 8831) sono un'astrazione del Stream Control Transmission Protocol ("SCTP", RFC 9260).

Per aprire i canali di dati durante la fase di segnalazione iniziale, l'offerta deve contenere una descrizione dei media dell'applicazione. A differenza delle descrizioni audio e video, le descrizioni delle app non specificano la direzione o i codec.

Candidati ICE

I candidati Interactive Connectivity Establishment ("ICE", RFC 8445) di un peer sono un elenco di percorsi che un peer remoto può utilizzare per stabilire una connessione.

Il prodotto cartesiano degli elenchi dei due peer, noto come coppie candidate, rappresenta i potenziali percorsi tra due peer. Queste coppie vengono testate per determinare il percorso ottimale.

Segnalazione tramite l'API REST di Meet

Utilizza l'API REST di Meet per eseguire questa segnalazione offer-answer. La tua app fornisce un'offerta SDP al metodo connectActiveConference() e riceve in cambio una risposta SDP.

I seguenti esempi di codice mostrano come chiamare il metodo:

Java

java-meet/samples/snippets/generated/com/google/apps/meet/v2beta/spacesservice/connectactiveconference/AsyncConnectActiveConference.java
import com.google.api.core.ApiFuture;
import com.google.apps.meet.v2beta.ConnectActiveConferenceRequest;
import com.google.apps.meet.v2beta.ConnectActiveConferenceResponse;
import com.google.apps.meet.v2beta.SpaceName;
import com.google.apps.meet.v2beta.SpacesServiceClient;

public class AsyncConnectActiveConference {

  public static void main(String[] args) throws Exception {
    asyncConnectActiveConference();
  }

  public static void asyncConnectActiveConference() throws Exception {
    // This snippet has been automatically generated and should be regarded as a code template only.
    // It will require modifications to work:
    // - It may require correct/in-range values for request initialization.
    // - It may require specifying regional endpoints when creating the service client as shown in
    // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
    try (SpacesServiceClient spacesServiceClient = SpacesServiceClient.create()) {
      ConnectActiveConferenceRequest request =
          ConnectActiveConferenceRequest.newBuilder()
              .setName(SpaceName.of("[SPACE]").toString())
              .setOffer("offer105650780")
              .build();
      ApiFuture<ConnectActiveConferenceResponse> future =
          spacesServiceClient.connectActiveConferenceCallable().futureCall(request);
      // Do something.
      ConnectActiveConferenceResponse response = future.get();
    }
  }
}

C#

apis/Google.Apps.Meet.V2Beta/Google.Apps.Meet.V2Beta.GeneratedSnippets/SpacesServiceClient.ConnectActiveConferenceAsyncSnippet.g.cs
using Google.Apps.Meet.V2Beta;
using System.Threading.Tasks;

public sealed partial class GeneratedSpacesServiceClientSnippets
{
    /// <summary>Snippet for ConnectActiveConferenceAsync</summary>
    /// <remarks>
    /// This snippet has been automatically generated and should be regarded as a code template only.
    /// It will require modifications to work:
    /// - It may require correct/in-range values for request initialization.
    /// - It may require specifying regional endpoints when creating the service client as shown in
    ///   https://cloud.google.com/dotnet/docs/reference/help/client-configuration#endpoint.
    /// </remarks>
    public async Task ConnectActiveConferenceAsync()
    {
        // Create client
        SpacesServiceClient spacesServiceClient = await SpacesServiceClient.CreateAsync();
        // Initialize request argument(s)
        string name = "spaces/[SPACE]";
        // Make the request
        ConnectActiveConferenceResponse response = await spacesServiceClient.ConnectActiveConferenceAsync(name);
    }
}

Node.js

packages/google-apps-meet/samples/generated/v2beta/spaces_service.connect_active_conference.js
/**
 * This snippet has been automatically generated and should be regarded as a code template only.
 * It will require modifications to work.
 * It may require correct/in-range values for request initialization.
 * TODO(developer): Uncomment these variables before running the sample.
 */
/**
 *  Required. Resource name of the space.
 *  Format: spaces/{spaceId}
 */
// const name = 'abc123'
/**
 *  Required. WebRTC SDP (Session Description Protocol) offer from the client.
 *  The format is defined by RFC
 *  8866 (https://www.rfc-editor.org/rfc/rfc8866) with mandatory keys defined
 *  by RFC 8829 (https://www.rfc-editor.org/rfc/rfc8829). This is the standard
 *  SDP format generated by a peer connection's createOffer() and
 *  createAnswer() methods.
 */
// const offer = 'abc123'

// Imports the Meet library
const {SpacesServiceClient} = require('@google-apps/meet').v2beta;

// Instantiates a client
const meetClient = new SpacesServiceClient();

async function callConnectActiveConference() {
  // Construct request
  const request = {
    name,
    offer,
  };

  // Run request
  const response = await meetClient.connectActiveConference(request);
  console.log(response);
}

callConnectActiveConference();

Python

packages/google-apps-meet/samples/generated_samples/meet_v2beta_generated_spaces_service_connect_active_conference_async.py
# This snippet has been automatically generated and should be regarded as a
# code template only.
# It will require modifications to work:
# - It may require correct/in-range values for request initialization.
# - It may require specifying regional endpoints when creating the service
#   client as shown in:
#   https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.apps import meet_v2beta


async def sample_connect_active_conference():
    # Create a client
    client = meet_v2beta.SpacesServiceAsyncClient()

    # Initialize request argument(s)
    request = meet_v2beta.ConnectActiveConferenceRequest(
        name="name_value",
        offer="offer_value",
    )

    # Make the request
    response = await client.connect_active_conference(request=request)

    # Handle the response
    print(response)

Flusso di connessione di esempio

Ecco un'offerta con una descrizione audio del media:

Esempio di offerta con una descrizione audio.
Figura 1. Esempio di offerta con una descrizione audio.

Il peer remoto risponde con una risposta SDP contenente lo stesso numero di righe di descrizione dei media. Ogni riga indica i contenuti multimediali, se presenti, che il peer remoto invia al client che offre la sessione tramite i flussi SRTP. Il peer remoto potrebbe anche rifiutare flussi specifici dell'offerente impostando la voce della descrizione multimediale su recvonly.

Per l'API Meet Media, i client inviano sempre l'offerta SDP per avviare una connessione. Meet non è mai l'iniziatore.

Questo comportamento è gestito internamente dai client di riferimento (C++, TypeScript), ma gli sviluppatori di client personalizzati possono utilizzare PeerConnectionInterface di WebRTC per generare un'offerta.

Per connettersi a Meet Meet, l'offerta deve rispettare requisiti specifici:

  1. Il client deve sempre fungere da client nell'handshake DTLS, quindi ogni descrizione dei contenuti multimediali nell'offerta deve specificare a=setup:actpass o a=setup:active.

  2. Ogni riga di descrizione dei contenuti multimediali deve supportare tutti i codec obbligatori per quel tipo di contenuti multimediali:

    • Audio: Opus
    • Video: VP8, VP9, AV1
  3. Per ricevere l'audio, l'offerta deve includere esattamente tre descrizioni di contenuti multimediali audio di sola ricezione. Puoi farlo impostando i transceiver sull'oggetto connessione peer.

    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. Per ricevere video, l'offerta deve includere 1-3 descrizioni di media video di sola ricezione. Puoi farlo impostando i transceiver sull'oggetto connessione peer.

    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. L'offerta deve sempre includere i canali di dati. Come minimo, i canali session-control e media-stats devono essere sempre aperti. Tutti i canali di dati devono essere 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);
    };
    

Esempio di offerta e risposta SDP

Ecco un esempio completo di un'offerta SDP valida e della risposta SDP corrispondente. Questa offerta negozia una sessione dell'API Meet Media con audio e un singolo stream video.

Osserva che sono presenti tre descrizioni di contenuti multimediali audio, una descrizione di contenuti multimediali video e la descrizione di contenuti multimediali dell'applicazione richiesta.

Offerta SDP del cliente Risposta SDP dell'API Meet Media
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