Os clientes usam o WebRTC para se comunicar com os servidores do Meet. Os clientes de referência fornecidos (C++,
TypeScript) demonstram as práticas recomendadas, e
incentivamos você a criar diretamente com base neles.
No entanto, também é possível criar clientes WebRTC totalmente personalizados que aderem aos
requisitos técnicos
da API Meet Media.
Esta página descreve os principais conceitos do WebRTC necessários para uma sessão bem-sucedida da API Meet Media.
Sinalização de oferta-resposta
O WebRTC é um framework ponto a ponto (P2P), em que os pares se comunicam sinalizando uns aos outros. Para iniciar uma sessão, o par de inicialização envia uma oferta de SDP para um par remoto. Essa oferta inclui os seguintes detalhes importantes:
Descrições de mídia para áudio e vídeo
As descrições de mídia indicam o que é comunicado durante as sessões P2P. Há três tipos de descrições: áudio, vídeo e dados.
Para indicar n streams de áudio, o ofertante inclui n descrições de mídia de áudio na oferta. O mesmo vale para vídeo. No entanto, haverá apenas uma descrição de mídia de dados no máximo.
Direção
Cada descrição de áudio ou vídeo descreve streams individuais do Secure Real-time Transport
Protocol (SRTP), regidos por RFC
3711. Eles são bidirecionais, permitindo que dois pares enviem e recebam mídia na mesma conexão.
Por isso, cada descrição de mídia (na oferta e na resposta) contém um dos três atributos que descrevem como o stream deve ser usado:
sendonly: envia apenas mídia do par de oferta. O par remoto não enviará mídia nesse stream.
recvonly: recebe apenas mídia do par remoto. O par de oferta não enviará mídia nesse stream.
sendrecv: os dois pares podem enviar e receber nesse stream.
Codecs
Cada descrição de mídia também especifica os codecs que um par oferece suporte. No caso da
API Meet Media, as ofertas de clientes são rejeitadas, a menos que ofereçam suporte
(pelo menos) aos codecs especificados nos requisitos
técnicos.
Handshake de DTLS
Os streams SRTP são protegidos por um handshake inicial
Datagram Transport Layer Security ("DTLS", RFC
9147) entre os pares.
O DTLS é tradicionalmente um protocolo cliente-servidor. Durante o processo de sinalização, um par concorda em atuar como servidor, enquanto o outro atua como um par.
Como cada stream SRTP pode ter sua própria conexão DTLS dedicada, cada descrição de mídia especifica um dos três atributos para indicar o papel do par no handshake de DTLS:
a=setup:actpass: o par de oferta é adiado para a escolha do par remoto.
a=setup:active: esse par atua como o cliente.
a=setup:passive: esse par atua como o servidor.
Descrições de mídia do aplicativo
Os canais de dados (RFC 8831) são
uma abstração do Stream Control Transmission Protocol ("SCTP", RFC
9260).
Para abrir canais de dados durante a fase de sinalização inicial, a oferta precisa conter uma descrição de mídia do aplicativo. Ao contrário das descrições de áudio e vídeo, as descrições de aplicativos não especificam direção ou codecs.
Candidatos do ICE
Os candidatos do Interactive Connectivity Establishment ("ICE", RFC
8445) de um par são uma lista de rotas que um par remoto pode usar para estabelecer uma conexão.
O produto cartesiano das listas dos dois pares, conhecido como pares de candidatos,
representa as rotas possíveis entre dois pares. Esses pares são testados para determinar a rota ideal.
importcom.google.api.core.ApiFuture;importcom.google.apps.meet.v2beta.ConnectActiveConferenceRequest;importcom.google.apps.meet.v2beta.ConnectActiveConferenceResponse;importcom.google.apps.meet.v2beta.SpaceName;importcom.google.apps.meet.v2beta.SpacesServiceClient;publicclassAsyncConnectActiveConference{publicstaticvoidmain(String[]args)throwsException{asyncConnectActiveConference();}publicstaticvoidasyncConnectActiveConference()throwsException{// 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_librarytry(SpacesServiceClientspacesServiceClient=SpacesServiceClient.create()){ConnectActiveConferenceRequestrequest=ConnectActiveConferenceRequest.newBuilder().setName(SpaceName.of("[SPACE]").toString()).setOffer("offer105650780").build();ApiFuture<ConnectActiveConferenceResponse>future=spacesServiceClient.connectActiveConferenceCallable().futureCall(request);// Do something.ConnectActiveConferenceResponseresponse=future.get();}}}
usingGoogle.Apps.Meet.V2Beta;usingSystem.Threading.Tasks;publicsealedpartialclassGeneratedSpacesServiceClientSnippets{/// <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>publicasyncTaskConnectActiveConferenceAsync(){// Create clientSpacesServiceClientspacesServiceClient=awaitSpacesServiceClient.CreateAsync();// Initialize request argument(s)stringname="spaces/[SPACE]";// Make the requestConnectActiveConferenceResponseresponse=awaitspacesServiceClient.ConnectActiveConferenceAsync(name);}}
/** * 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 libraryconst{SpacesServiceClient}=require('@google-apps/meet').v2beta;// Instantiates a clientconstmeetClient=newSpacesServiceClient();asyncfunctioncallConnectActiveConference(){// Construct requestconstrequest={name,offer,};// Run requestconstresponse=awaitmeetClient.connectActiveConference(request);console.log(response);}callConnectActiveConference();
# 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.htmlfromgoogle.appsimportmeet_v2betaasyncdefsample_connect_active_conference():# Create a clientclient=meet_v2beta.SpacesServiceAsyncClient()# Initialize request argument(s)request=meet_v2beta.ConnectActiveConferenceRequest(name="name_value",offer="offer_value",)# Make the requestresponse=awaitclient.connect_active_conference(request=request)# Handle the responseprint(response)
Exemplo de fluxo de conexão
Confira uma oferta com uma descrição de mídia de áudio:
Figura 1. Exemplo de oferta com uma descrição de mídia de áudio.
O par remoto responde com uma resposta de SDP que contém o mesmo número de linhas de descrição de mídia. Cada linha indica qual mídia, se houver, o par remoto envia de volta ao cliente de oferta nos streams SRTP. O par remoto também pode rejeitar streams específicos do ofertante definindo a entrada de descrição de mídia como recvonly.
Para a API Meet Media, os clientes sempre enviam a oferta de SDP para iniciar uma conexão. O Meet nunca é o iniciador.
Esse comportamento é gerenciado internamente pelos clientes de referência
(C++, TypeScript),
mas os desenvolvedores de clientes personalizados podem usar o WebRTC's PeerConnectionInterface para
gerar uma oferta.
Para se conectar ao Meet, a oferta precisa obedecer a requisitos específicos
requisitos:
O cliente sempre precisa atuar como o cliente no handshake de DTLS. Portanto, cada descrição de mídia na oferta precisa especificar a=setup:actpass ou a=setup:active.
Cada linha de descrição de mídia precisa oferecer suporte a todos os necessários
codecs para esse tipo de mídia:
Áudio:Opus
Vídeo:VP8, VP9, AV1
Para receber áudio, a oferta precisa incluir exatamente três descrições de mídia de áudio somente de recebimento. Para fazer isso, defina transceptores no objeto de conexão de pares.
C++
// ...rtc::scoped_refptr<webrtc::PeerConnectionInterface>peer_connection;for(inti=0;i < 3;++i){webrtc::RtpTransceiverInitaudio_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()){returnabsl::InternalError(absl::StrCat("Failed to add audio transceiver: ",audio_result.error().message()));}}
JavaScript
pc=newRTCPeerConnection();// Configure client to receive audio from Meet servers.pc.addTransceiver('audio',{'direction':'recvonly'});pc.addTransceiver('audio',{'direction':'recvonly'});pc.addTransceiver('audio',{'direction':'recvonly'});
Para receber vídeo, a oferta precisa incluir de uma a três descrições de mídia de vídeo somente de recebimento. Para fazer isso, defina transceptores no objeto de conexão de pares.
C++
// ...rtc::scoped_refptr<webrtc::PeerConnectionInterface>peer_connection;for(uint32_ti=0;i < configurations.receiving_video_stream_count;++i){webrtc::RtpTransceiverInitvideo_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()){returnabsl::InternalError(absl::StrCat("Failed to add video transceiver: ",video_result.error().message()));}}
JavaScript
pc=newRTCPeerConnection();// Configure client to receive video from Meet servers.pc.addTransceiver('video',{'direction':'recvonly'});pc.addTransceiver('video',{'direction':'recvonly'});pc.addTransceiver('video',{'direction':'recvonly'});
A oferta sempre precisa incluir canais de dados. No mínimo, os canais session-control e media-stats precisam estar sempre abertos. Todos os canais de dados precisam ser ordered.
C++
// ...// All data channels must be ordered.constexprwebrtc::DataChannelInitkDataChannelConfig={.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()){returnabsl::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()){returnabsl::InternalError(absl::StrCat("Failed to create data channel ",data_channel_label,": ",stats_create_result.error().message()));}
JavaScript
// ...pc=newRTCPeerConnection();// All data channels must be ordered.constdataChannelConfig={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);};
Exemplo de oferta e resposta de SDP
Confira um exemplo completo de uma oferta de SDP válida e uma resposta de SDP correspondente. Essa oferta negocia uma sessão da API Meet Media com áudio e um único stream de vídeo.
Observe que há três descrições de mídia de áudio, uma descrição de mídia de vídeo e a descrição de mídia do aplicativo necessária.
[[["Fácil de entender","easyToUnderstand","thumb-up"],["Meu problema foi resolvido","solvedMyProblem","thumb-up"],["Outro","otherUp","thumb-up"]],[["Não contém as informações de que eu preciso","missingTheInformationINeed","thumb-down"],["Muito complicado / etapas demais","tooComplicatedTooManySteps","thumb-down"],["Desatualizado","outOfDate","thumb-down"],["Problema na tradução","translationIssue","thumb-down"],["Problema com as amostras / o código","samplesCodeIssue","thumb-down"],["Outro","otherDown","thumb-down"]],["Última atualização 2026-04-01 UTC."],[],[]]