Mantenha tudo organizado com as coleções
Salve e categorize o conteúdo com base nas suas preferências.
A API Google Meet Media permite que seu app participe de uma conferência do Google Meet e consuma fluxos de mídia em tempo real.
Os clientes usam o WebRTC para se comunicar com os servidores do Meet. Os clientes de referência fornecidos (C++, TypeScript) demonstram práticas recomendadas, e você pode criar diretamente com base neles.
No entanto, também é possível criar clientes WebRTC totalmente personalizados que atendam aos requisitos
técnicos da API Meet Media.
Nesta página, descrevemos 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 participantes se comunicam sinalizando
uns aos outros. Para iniciar uma sessão, o peer iniciador envia uma oferta de SDP a um peer remoto. Esta 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. Existem três tipos de descrições: áudio, vídeo e dados.
Para indicar fluxos de áudio n, o ofertante inclui descrições de mídia de áudio n na oferta. O mesmo vale para vídeos. No entanto, haverá apenas uma descrição de mídia data no máximo.
Direção
Cada descrição de áudio ou vídeo descreve fluxos individuais do Secure Real-time Transport Protocol (SRTP), regidos por RFC
3711. Eles são bidirecionais, permitindo que dois usuários enviem e recebam mídia pela mesma conexão.
Por isso, cada descrição de mídia (na oferta e na resposta) contém um de três atributos que descrevem como o stream deve ser usado:
sendonly: envia apenas mídia do usuário que fez a oferta. O peer remoto não vai enviar mídia neste stream.
recvonly: recebe apenas mídia do peer remoto. O usuário que fez a oferta não vai enviar mídia nessa transmissão.
sendrecv: os dois participantes podem enviar e receber nessa transmissão.
Codecs
Cada descrição de mídia também especifica os codecs compatíveis com um peer. No caso da API Meet Media, as ofertas do cliente são rejeitadas, a menos que sejam compatíveis com (pelo menos) os codecs especificados nos requisitos técnicos.
Handshake de DTLS
Os fluxos SRTP são protegidos por um handshake inicial de Datagram Transport Layer Security ("DTLS", RFC
9147) entre os participantes.
O DTLS é tradicionalmente um protocolo de cliente para servidor. Durante o processo de sinalização, um peer concorda em atuar como servidor, enquanto o outro atua como peer.
Como cada fluxo SRTP pode ter uma conexão DTLS dedicada, cada
descrição de mídia especifica um de três atributos para indicar a função do peer
no handshake DTLS:
a=setup:actpass: o par de oferta adia a escolha do
par remoto.
a=setup:active: esse elemento funciona como o cliente.
a=setup:passive: esse peer 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 inicial de sinalização, 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 ICE
Os candidatos Interactive Connectivity Establishment (ICE, RFC
8445) de um peer são uma lista de
rotas que um peer remoto pode usar para estabelecer uma conexão.
O produto cartesiano das listas dos dois peers, conhecido como pares candidatos,
representa as rotas possíveis entre dois peers. 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 peer remoto responde com uma resposta
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 peer
remoto envia de volta ao cliente de oferta nos streams SRTP. O peer
remoto também pode rejeitar streams específicos do ofertante definindo essa 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 PeerConnectionInterface do WebRTC para
gerar uma oferta.
Para se conectar ao Meet, a oferta precisa obedecer a requisitos específicos:
O cliente sempre precisa agir como o cliente no handshake DTLS. Portanto, toda
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 ser compatível com todos os codecs obrigatórios 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 isso, defina transceptores no objeto
de conexão de mesmo nível.
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 isso, defina transceptores no objeto
de conexão de mesmo nível.
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 precisa sempre 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 e uma resposta de SDP válidas. Essa oferta negocia uma sessão da API Meet Media com áudio e um único fluxo de vídeo.
Observe que há três descrições de mídia de áudio, uma de vídeo e a descrição de mídia do aplicativo obrigatória.