קל לארגן דפים בעזרת אוספים
אפשר לשמור ולסווג תוכן על סמך ההעדפות שלך.
Google Meet Media API מאפשר לאפליקציה שלכם להצטרף לועידה ב-Google Meet ולצרוך מדיה בזמן אמת.
לקוחות משתמשים ב-WebRTC כדי לתקשר עם שרתי Meet. לקוחות ההפניה שסופקו (C++, TypeScript) מדגימים שיטות מומלצות, ומומלץ לבנות ישירות על בסיסם.
עם זאת, אפשר גם ליצור לקוחות WebRTC בהתאמה אישית מלאה, שעומדים בדרישות הטכניות של Meet Media API.
בדף הזה מפורטים מושגים מרכזיים ב-WebRTC שנדרשים כדי להשתמש ב-Meet Media API.
איתות של הצעה ותשובה
WebRTC היא מסגרת עמית לעמית (P2P), שבה העמיתים מתקשרים באמצעות איתות אחד לשני. כדי להתחיל סשן, עמית שיוזם את הסשן שולח הצעת SDP לעמית מרוחק. ההצעה הזו
כוללת את הפרטים החשובים הבאים:
תיאורי מדיה לאודיו ולסרטונים
תיאורי המדיה מציינים מה מועבר במהלך סשנים של P2P. יש שלושה סוגים של תיאורים: אודיו, וידאו ונתונים.
כדי לציין n טראקים של אודיו, הצד שמציע את ההצעה כולל n תיאורים של מדיה עם אודיו בהצעה. אותו עיקרון נכון גם לגבי סרטון. עם זאת, יהיה רק תיאור אחד של נתוני המדיה לכל היותר.
כיוון
כל תיאור אודיו או וידאו מתאר סטרימינג של פרוטוקול להעברה בטוחה בזמן אמת (SRTP) שמוגדר על ידי RFC
3711. החיבורים האלה הם דו-כיווניים,
ומאפשרים לשני עמיתים לשלוח ולקבל מדיה באותו חיבור.
לכן, כל תיאור של מדיה (גם בהצעה וגם בתשובה) מכיל אחד משלושת המאפיינים הבאים שמתארים את אופן השימוש בזרם:
sendonly: שליחת מדיה רק מהעמית שמציע את ההצעה. הצד השני המרוחק לא ישלח מדיה בזרם הזה.
recvonly: מקבל רק מדיה מהצד המרוחק. הצד שמציע את השיחה לא ישלח מדיה בזרם הזה.
sendrecv: שני הצדדים יכולים לשלוח ולקבל נתונים בזרם הזה.
רכיבי קודק
בכל תיאור של מדיה מצוינים גם רכיבי ה-codec שנתמכים על ידי עמית. במקרה של Meet Media API, הצעות של לקוחות נדחות אלא אם הן תומכות (לפחות) בקודקים שצוינו בדרישות הטכניות.
לחיצת יד בפרוטוקול DTLS
ההצפנה של הסטרימינג ב-SRTP מתבצעת באמצעות לחיצת יד ראשונית של Datagram Transport Layer Security (DTLS, RFC
9147) בין העמיתים.
DTLS הוא פרוטוקול מסוג לקוח לשרת. במהלך תהליך האיתות, צד אחד מסכים לפעול כשרת והצד השני פועל כעמית.
לכל סטרימינג של SRTP יכול להיות חיבור DTLS ייעודי משלו, ולכן כל תיאור של מדיה מציין אחד משלושת המאפיינים הבאים כדי לציין את תפקיד העמית בתהליך האימות ב-DTLS:
a=setup:actpass: המוצר מאפשר לצד השני לבחור.
a=setup:active: העמית הזה פועל כלקוח.
a=setup:passive: העמית הזה פועל כשרת.
תיאורים של מדיה באפליקציה
ערוצי נתונים (RFC 8831) הם הפשטה של פרוטוקול Stream Control Transmission Protocol (SCTP, RFC
9260).
כדי לפתוח ערוצי נתונים במהלך שלב האיתות הראשוני, ההצעה צריכה לכלול תיאור של מדיה באפליקציה. בשונה מתיאורי אודיו ווידאו, בתיאורי אפליקציות לא מציינים כיוון או קודקים.
מועמדים ל-ICE
מועמדים להקמת קישוריות אינטראקטיבית (ICE, RFC
8445) של עמית הם רשימה של מסלולים שעמית מרוחק יכול להשתמש בהם כדי ליצור חיבור.
המכפלה הקרטזית של רשימות שני העמיתים, שנקראת זוגות מועמדים, מייצגת את המסלולים הפוטנציאליים בין שני עמיתים. הזוגות האלה נבדקים כדי לקבוע את המסלול האופטימלי.
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), אבל מפתחים של לקוחות בהתאמה אישית יכולים להשתמש ב-PeerConnectionInterface של WebRTC כדי ליצור הצעה.
כדי להתחבר ל-Meet, השובר צריך לעמוד בדרישות ספציפיות:
הלקוח תמיד צריך לפעול כלקוח בתהליך הלחיצה של DTLS, ולכן בכל תיאור מדיה בהצעה צריך לציין a=setup:actpass או a=setup:active.
כל שורת תיאור מדיה צריכה לתמוך בכל הקודקים הנדרשים לסוג המדיה הזה:
אודיו:Opus
סרטון:VP8, VP9, AV1
כדי לקבל אודיו, המבצע צריך לכלול בדיוק 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()));}}
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'});
כדי לקבל סרטון, המוצר צריך לכלול 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()));}}
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'});
המוצר תמיד צריך לכלול ערוצי נתונים. לפחות הערוצים 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()));}
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);};
דוגמה להצעת SDP ולתשובה
זוהי דוגמה מלאה להצעת SDP תקינה ולתשובת SDP תואמת. ההצעה הזו מנהלת משא ומתן על סשן של Meet Media API עם אודיו וזרם וידאו יחיד.
שימו לב שיש שלושה תיאורים של מדיה מסוג אודיו, תיאור אחד של מדיה מסוג וידאו ותיאור המדיה הנדרש של האפליקציה.