Google Meet Media API를 사용하면 앱이 Google Meet 회의에 참여하여 실시간 미디어 스트림을 사용할 수 있습니다.
클라이언트는 WebRTC를 사용하여 Meet 서버와 통신합니다. 제공된 참조 클라이언트 (C++, TypeScript)는 권장사항을 보여주며 이를 기반으로 직접 빌드하는 것이 좋습니다.
하지만 Meet Media API의 기술 요구사항을 준수하는 완전 맞춤 WebRTC 클라이언트를 빌드할 수도 있습니다.
이 페이지에서는 Meet Media API 세션을 성공적으로 진행하는 데 필요한 주요 WebRTC 개념을 간략하게 설명합니다.
Offer-answer 시그널링
WebRTC는 피어가 서로 신호를 보내 통신하는 P2P (피어 투 피어) 프레임워크입니다. 세션을 시작하기 위해 시작 피어가 원격 피어에 SDP 제안을 전송합니다. 이 혜택에는 다음과 같은 중요한 세부정보가 포함됩니다.
오디오 및 동영상의 미디어 설명
미디어 설명은 P2P 세션 중에 전달되는 내용을 나타냅니다. 설명에는 오디오, 동영상, 데이터의 세 가지 유형이 있습니다.
n 오디오 스트림을 나타내기 위해 제안자는 제안에 n 오디오 미디어 설명을 포함합니다. 동영상도 마찬가지입니다. 하지만 데이터 미디어 설명은 최대 하나만 있을 수 있습니다.
통행 방향
각 오디오 또는 동영상 설명은 RFC
3711에 의해 관리되는 개별 보안 실시간 전송 프로토콜 (SRTP) 스트림을 설명합니다. 이는 양방향이므로 두 피어가 동일한 연결을 통해 미디어를 보내고 받을 수 있습니다.
따라서 제안과 대답 모두의 각 미디어 설명에는 스트림 사용 방식을 설명하는 다음 세 가지 속성 중 하나가 포함됩니다.
sendonly: 오퍼링 피어의 미디어만 전송합니다. 원격 피어가 이 스트림에서 미디어를 전송하지 않습니다.
recvonly: 원격 피어의 미디어만 수신합니다. 제공 피어가 이 스트림에서 미디어를 전송하지 않습니다.
sendrecv: 두 피어 모두 이 스트림에서 전송하고 수신할 수 있습니다.
코덱
각 미디어 설명은 피어가 지원하는 코덱도 지정합니다. Meet Media API의 경우 클라이언트가 기술 요구사항에 지정된 코덱을 (최소한) 지원하지 않으면 클라이언트 제안이 거부됩니다.
DTLS 핸드셰이크
SRTP 스트림은 피어 간의 초기 데이터그램 전송 계층 보안 ('DTLS', RFC
9147) 핸드셰이크로 보호됩니다.
DTLS는 일반적으로 클라이언트-서버 프로토콜입니다. 신호 프로세스 중에 한 피어는 서버로 작동하는 데 동의하고 다른 피어는 피어로 작동하는 데 동의합니다.
각 SRTP 스트림에는 전용 DTLS 연결이 있을 수 있으므로 각 미디어 설명은 DTLS 핸드셰이크에서 피어의 역할을 나타내는 세 가지 속성 중 하나를 지정합니다.
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)
연결 흐름 예
오디오 미디어 설명이 포함된 혜택은 다음과 같습니다.
그림 1. 오디오 미디어 설명이 포함된 혜택의 예
원격 피어는 동일한 수의 미디어 설명 줄이 포함된 SDP 대답으로 응답합니다. 각 줄은 원격 피어가 SRTP 스트림을 통해 제공 클라이언트에 다시 전송하는 미디어를 나타냅니다. 원격 피어는 미디어 설명 항목을 recvonly로 설정하여 제안자로부터 특정 스트림을 거부할 수도 있습니다.
Meet Media API의 경우 클라이언트는 항상 SDP 오퍼를 전송하여 연결을 시작합니다. Meet은 시작자가 아닙니다.
이 동작은 참조 클라이언트(C++, TypeScript)에 의해 내부적으로 관리되지만 맞춤 클라이언트 개발자는 WebRTC의 PeerConnectionInterface를 사용하여 제안을 생성할 수 있습니다.
오디오를 수신하려면 혜택에 수신 전용 오디오 미디어 설명이 정확히 3개 포함되어야 합니다. 피어 연결 객체에서 트랜시버를 설정하면 됩니다.
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()));}}
자바스크립트
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'});
동영상을 수신하려면 제품에 수신 전용 동영상 미디어 설명이 1~3개 포함되어야 합니다. 피어 연결 객체에서 트랜시버를 설정하면 됩니다.
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()));}}
자바스크립트
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'});
혜택에는 항상 데이터 채널이 포함되어야 합니다. 최소한 session-control 및 media-stats 채널은 항상 열려 있어야 합니다. 모든 데이터 채널은 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()));}
자바스크립트
// ...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);};
SDP 제안 및 답변 예시
다음은 유효한 SDP 제안과 일치하는 SDP 답변의 전체 예입니다. 이 제안은 오디오와 단일 동영상 스트림으로 Meet Media API 세션을 협상합니다.
오디오 미디어 설명 3개, 동영상 미디어 설명 1개, 필수 애플리케이션 미디어 설명이 있습니다.
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-07-16(UTC)"],[],[]]