תקשורת בזמן אמת עם WebRTC

1. מבוא

‫WebRTC הוא פרויקט קוד פתוח שמאפשר תקשורת בזמן אמת של אודיו, וידאו ונתונים באפליקציות אינטרנט ובאפליקציות מקוריות.

ל-WebRTC יש כמה ממשקי API של JavaScript – לחיצה על הקישורים תציג הדגמות.

איפה אפשר להשתמש ב-WebRTC?

ב-Firefox, ב-Opera וב-Chrome במחשב וב-Android. ‫WebRTC זמין גם באפליקציות מותאמות ל-iOS ול-Android.

מהו סימון?

‫WebRTC משתמש ב-RTCPeerConnection כדי להעביר נתוני סטרימינג בין דפדפנים, אבל הוא גם צריך מנגנון לתיאום התקשורת ולשליחת הודעות בקרה. התהליך הזה נקרא איתות. פרוטוקולים ושיטות לאיתות לא מוגדרים ב-WebRTC. ב-codelab הזה תשתמשו ב-Socket.IO להעברת הודעות, אבל יש הרבה חלופות.

מהם STUN ו-TURN?

פרוטוקול WebRTC מיועד לעבודה בין עמיתים, כך שהמשתמשים יכולים להתחבר בנתיב הישיר ביותר שאפשר. עם זאת, WebRTC מתוכנן להתמודד עם רשתות בעולם האמיתי: אפליקציות לקוח צריכות לעבור דרך שערי NAT וחומות אש, ורשתות עמית לעמית צריכות פתרונות חלופיים למקרה שחיבור ישיר נכשל. כחלק מהתהליך הזה, ממשקי ה-API של WebRTC משתמשים בשרתי STUN כדי לקבל את כתובת ה-IP של המחשב, ובשרתי TURN כדי לפעול כשרתי ממסר במקרה שתקשורת ישירה בין עמיתים נכשלת. (הסבר מפורט יותר על WebRTC)

האם WebRTC מאובטח?

הצפנה היא חובה בכל רכיבי WebRTC, ואפשר להשתמש בממשקי ה-API של JavaScript רק ממקורות מאובטחים (HTTPS או localhost). מנגנוני האיתות לא מוגדרים בתקני WebRTC, ולכן אתם צריכים לוודא שאתם משתמשים בפרוטוקולים מאובטחים.

2. סקירה כללית

ליצור אפליקציה כדי לקבל סרטון ולצלם תמונות באמצעות מצלמת האינטרנט ולשתף אותן מקצה לקצה (P2P) באמצעות WebRTC. במהלך התהליך תלמדו איך להשתמש בממשקי WebRTC API העיקריים ולהגדיר שרת הודעות באמצעות Node.js.

מה תלמדו

  • איך מקבלים וידאו ממצלמת האינטרנט
  • הזרמת סרטון באמצעות RTCPeerConnection
  • שידור נתונים באמצעות RTCDataChannel
  • הגדרה של שירות איתות להחלפת הודעות
  • שילוב של חיבור עמית לעמית ואיתות
  • איך מצלמים תמונה ומשתפים אותה דרך ערוץ נתונים

הדרישות

  • ‫Chrome מגרסה 47 ואילך
  • Web Server for Chrome או להשתמש בשרת אינטרנט משלכם.
  • קוד לדוגמה
  • כלי לעריכת טקסט
  • ידע בסיסי ב-HTML, ‏ CSS ו-JavaScript

3. קבלת קוד לדוגמה

הורדת הקוד

אם אתם מכירים את git, אתם יכולים להוריד את הקוד של ה-codelab הזה מ-GitHub על ידי שיבוט שלו:

git clone https://github.com/googlecodelabs/webrtc-web

לחלופין, אפשר ללחוץ על הלחצן הבא כדי להוריד קובץ ZIP של הקוד:

פותחים את קובץ ה-ZIP שהורד. הפעולה הזו תפרוס תיקיית פרויקט (adaptive-web-media) שמכילה תיקייה אחת לכל שלב ב-Codelab הזה, יחד עם כל המשאבים שתצטרכו.

כל עבודת התכנות תתבצע בספרייה שנקראת work.

התיקיות step-nn מכילות גרסה סופית לכל שלב ב-Codelab הזה. הן מופיעות שם לצורך השוואה.

התקנה ואימות של שרת אינטרנט

אתם יכולים להשתמש בשרת האינטרנט שלכם, אבל ה-codelab הזה מיועד לעבוד בצורה טובה עם שרת האינטרנט של Chrome. אם האפליקציה עדיין לא מותקנת, אפשר להתקין אותה מחנות האינטרנט של Chrome.

6ddeb4aee53c0f0e.png

אחרי שמתקינים את האפליקציה Web Server for Chrome, לוחצים על קיצור הדרך לאפליקציות Chrome מסרגל הסימניות, מדף הכרטיסייה החדשה או ממרכז האפליקציות:

1d2b4aa977ab7e24.png

לוחצים על סמל שרת האינטרנט:

27fce4494f641883.png

לאחר מכן תופיע תיבת הדו-שיח הבאה, שבה אפשר להגדיר את שרת האינטרנט המקומי:

Screen Shot 2016-02-18 at 11.48.14 AM.png

לוחצים על הלחצן בחירת תיקייה ובוחרים את התיקייה work שיצרתם. כך תוכלו לראות את העבודה שלכם בתהליך ב-Chrome דרך כתובת ה-URL שמודגשת בתיבת הדו-שיח של שרת האינטרנט בקטע כתובות URL של שרת האינטרנט.

בקטע אפשרויות, מסמנים את התיבה לצד הצגה אוטומטית של index.html, כמו שמוצג בהמשך:

Screen Shot 2016-02-18 at 11.56.30 AM.png

לאחר מכן, עוצרים ומפעילים מחדש את השרת על ידי הזזת המתג עם התווית Web Server: STARTED שמאלה ואז ימינה.

Screen Shot 2016-02-18 at 12.22.18 PM.png

עכשיו נכנסים לאתר העבודה בדפדפן האינטרנט על ידי לחיצה על כתובת ה-URL של שרת האינטרנט שמסומנת בהדגשה. אמור להופיע דף שנראה כך, שמתאים ל-work/index.html:

18a705cb6ccc5181.png

ברור שהאפליקציה הזו עדיין לא עושה משהו מעניין – עד עכשיו היא רק שלד מינימלי שמשמש אותנו כדי לוודא ששרת האינטרנט שלכם פועל בצורה תקינה. בשלבים הבאים תוסיפו פונקציונליות ותכונות פריסה.

4. הפעלת סטרימינג של וידאו ממצלמת האינטרנט

מה תלמדו

בשלב הזה נסביר איך:

  • לקבל סטרימינג של וידאו ממצלמת האינטרנט.
  • לשנות את ההפעלה של השידור.
  • שימוש ב-CSS וב-SVG כדי לערוך סרטונים.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-01.

קצת HTML...

מוסיפים רכיב video ורכיב script לקובץ index.html בספרייה work:

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <video autoplay playsinline></video>

  <script src="js/main.js"></script>

</body>

</html>

‫…and a pinch of JavaScript

מוסיפים את הקוד הבא לקובץ main.js בתיקייה js:

'use strict';

// On this codelab, you will be streaming only video (video: true).
const mediaStreamConstraints = {
  video: true,
};

// Video element where stream will be placed.
const localVideo = document.querySelector('video');

// Local stream that will be reproduced on the video.
let localStream;

// Handles success by adding the MediaStream to the video element.
function gotLocalMediaStream(mediaStream) {
  localStream = mediaStream;
  localVideo.srcObject = mediaStream;
}

// Handles error by logging a message to the console with the error message.
function handleLocalMediaStreamError(error) {
  console.log('navigator.getUserMedia error: ', error);
}

// Initializes media stream.
navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
  .then(gotLocalMediaStream).catch(handleLocalMediaStreamError);

רוצה לנסות?

פותחים את index.html בדפדפן, וצריך לראות משהו כזה (עם התצוגה ממצלמת האינטרנט, כמובן):

9297048e43ed0f3d.png

איך זה עובד

אחרי השיחה ב-getUserMedia(), הדפדפן מבקש מהמשתמש הרשאה לגשת למצלמה שלו (אם זו הפעם הראשונה שמתבקשת גישה למצלמה למקור הנוכחי). אם הפעולה בוצעה ללא שגיאות, מוחזר MediaStream שאפשר להשתמש בו ברכיב מדיה באמצעות המאפיין srcObject:

navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
  .then(gotLocalMediaStream).catch(handleLocalMediaStreamError);


}
function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
}

הארגומנט constraints מאפשר לציין אילו מדיה רוצים לקבל. בדוגמה הזו, רק וידאו, כי האודיו מושבת כברירת מחדל:

const mediaStreamConstraints = {
  video: true,
};

אפשר להשתמש באילוצים כדי לציין דרישות נוספות, כמו רזולוציית הסרטון:

const hdConstraints = {
  video: {
    width: {
      min: 1280
    },
    height: {
      min: 720
    }
  }
}

במפרט MediaTrackConstraints מפורטים כל סוגי האילוצים האפשריים, אבל לא כל האפשרויות נתמכות בכל הדפדפנים. אם הרזולוציה המבוקשת לא נתמכת על ידי המצלמה שנבחרה כרגע, בקשת getUserMedia() תידחה עם OverconstrainedError והמשתמש לא יתבקש לתת הרשאה לגישה למצלמה שלו.

אם הפעולה getUserMedia() מצליחה, זרם הווידאו ממצלמת האינטרנט מוגדר כמקור של רכיב הווידאו:

function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
}

נקודות בונוס

  • האובייקט localStream שמועבר אל getUserMedia() נמצא בהיקף גלובלי, כך שאפשר לבדוק אותו ממסוף הדפדפן: פותחים את המסוף, מקלידים stream ומקישים על Return. (כדי להציג את המסוף ב-Chrome, מקישים על Ctrl-Shift-J, או על Command-Option-J אם אתם משתמשים ב-Mac).
  • מה מחזירה הפונקציה localStream.getVideoTracks()?
  • רוצה לנסות להתקשר אל localStream.getVideoTracks()[0].stop()?
  • בודקים את אובייקט המגבלות: מה קורה כשמשנים אותו ל-{audio: true, video: true}?
  • מה גודל רכיב הסרטון? איך אפשר לקבל את הגודל הטבעי של הסרטון מ-JavaScript, ולא את גודל התצוגה? אפשר להשתמש בכלי הפיתוח ל-Chrome כדי לבדוק.
  • אפשר לנסות להוסיף פילטרים של CSS לרכיב הסרטון. לדוגמה:
video {
  filter: blur(4px) invert(1) opacity(0.5);
}
  • כדאי לנסות להוסיף מסנני SVG. לדוגמה:
video {
   filter: hue-rotate(180deg) saturate(200%);
 }

מה למדתם

בשלב הזה למדתם איך:

  • קבלת סרטון ממצלמת האינטרנט.
  • הגדרת מגבלות על מדיה.
  • לשנות את רכיב הווידאו.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-01.

טיפים

  • אל תשכחו להוסיף את מאפיין autoplay לאלמנט video. בלי זה, תראו רק פריים אחד!
  • יש עוד הרבה אפשרויות של getUserMedia() אילוצים. אפשר לצפות בהדגמה בכתובת webrtc.github.io/samples/src/content/peerconnection/constraints. כפי שתראו, יש באתר הזה הרבה דוגמאות מעניינות ל-WebRTC.

שיטה מומלצת

  • מוודאים שרכיב הווידאו לא חורג מהגבולות של הקונטיינר שלו. הוספנו את האפשרויות width וmax-width כדי להגדיר גודל מועדף וגודל מקסימלי לסרטון. הדפדפן יחשב את הגובה באופן אוטומטי:
video {
  max-width: 100%;
  width: 320px;
}

הבא בתור

יש לכם סרטון, אבל איך משדרים אותו? ההסבר מופיע בשלב הבא.

5. הזרמת סרטון באמצעות RTCPeerConnection

מה תלמדו

בשלב הזה נסביר איך:

  • אפשר להשתמש ב-WebRTC shim,‏ adapter.js, כדי להסתיר את ההבדלים בין הדפדפנים.
  • משתמשים ב-RTCPeerConnection API כדי להפעיל סטרימינג של סרטון.
  • שליטה בלכידה ובסטרימינג של מדיה.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-2.

מה זה RTCPeerConnection?

‫RTCPeerConnection הוא API לביצוע שיחות WebRTC להזרמת וידאו ואודיו ולהחלפת נתונים.

בדוגמה הזו מוגדר חיבור בין שני אובייקטים של RTCPeerConnection (שנקראים עמיתים) באותו דף.

אין לזה שימוש מעשי, אבל זה טוב כדי להבין איך RTCPeerConnection פועל.

הוספת רכיבים לסרטונים ולחצני בקרה

בקובץ index.html מחליפים את רכיב הווידאו היחיד בשני רכיבי וידאו ובשלושה לחצנים:

<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>


<div>
  <button id="startButton">Start</button>
  <button id="callButton">Call</button>
  <button id="hangupButton">Hang Up</button>
</div>

רכיב וידאו אחד יציג את הסטרימינג מ-getUserMedia() והשני יציג את אותו סרטון שמוזרם באמצעות RTCPeerconnection. (ביישום בעולם האמיתי, רכיב וידאו אחד יציג את הזרם המקומי והשני את הזרם המרוחק).

הוספת ה-shim של adapter.js

מוסיפים קישור לגרסה הנוכחית של adapter.js מעל הקישור אל main.js:

<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

הקובץ Index.html אמור להיראות עכשיו כך:

<!DOCTYPE html>
<html>

<head>
  <title>Realtime communication with WebRTC</title>
  <link rel="stylesheet" href="css/main.css" />
</head>

<body>
  <h1>Realtime communication with WebRTC</h1>

  <video id="localVideo" autoplay playsinline></video>
  <video id="remoteVideo" autoplay playsinline></video>

  <div>
    <button id="startButton">Start</button>
    <button id="callButton">Call</button>
    <button id="hangupButton">Hang Up</button>
  </div>

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

התקנת הקוד של RTCPeerConnection

מחליפים את main.js בגרסה שבתיקייה step-02.

ביצוע השיחה

פותחים את index.html, לוחצים על הלחצן Start כדי לקבל סרטון ממצלמת האינטרנט, ולוחצים על Call כדי ליצור את חיבור ה-P2P. שני האלמנטים של הסרטון יציגו את אותו סרטון (מהמצלמה). כדי לראות את הרישום ביומן של WebRTC, צריך לעיין במסוף הדפדפן.

איך זה עובד

בשלב הזה מתבצעות הרבה פעולות...

‫WebRTC משתמש ב-RTCPeerConnection API כדי להגדיר חיבור להזרמת וידאו בין לקוחות WebRTC, שנקראים עמיתים.

בדוגמה הזו, שני אובייקטים של RTCPeerConnection נמצאים באותו דף: pc1 ו-pc2. אין לזה שימוש מעשי, אבל זה טוב כדי להדגים איך ממשקי ה-API עובדים.

הגדרת שיחה בין עמיתים ב-WebRTC כוללת שלוש משימות:

  • יוצרים RTCPeerConnection לכל צד של השיחה, ובכל צד מוסיפים את הזרם המקומי מ-getUserMedia().
  • קבלת מידע על הרשת ושיתוף שלו: נקודות קצה פוטנציאליות לחיבור נקראות מועמדים ל-ICE.
  • קבלת תיאורים מקומיים ומרוחקים ושיתוף שלהם: מטא-נתונים על מדיה מקומית בפורמט SDP.

נניח שעינת וירון רוצים להשתמש ב-RTCPeerConnection כדי להגדיר וידאו צ'אט.

קודם כל, מיכל ויוסי מחליפים מידע על הרשת. המונח 'מציאת מועמדים' מתייחס לתהליך של מציאת ממשקי רשת ויציאות באמצעות מסגרת ICE.

  1. עינת יוצרת אובייקט RTCPeerConnection עם handler של onicecandidate (addEventListener('icecandidate')). הקוד הזה תואם לקוד הבא מתוך main.js:
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);
  1. אליס מתקשרת אל getUserMedia() ומוסיפה את הזרם שמועבר אליו:
navigator.mediaDevices.getUserMedia(mediaStreamConstraints).
  then(gotLocalMediaStream).
  catch(handleLocalMediaStreamError);
function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
  localStream = mediaStream;
  trace('Received local stream.');
  callButton.disabled = false;  // Enable call button.
}
localPeerConnection.addStream(localStream);
trace('Added local stream to localPeerConnection.');
  1. הפונקציה לטיפול באירועים onicecandidate משלב 1 מופעלת כשמועמדים לרשת הופכים לזמינים.
  2. עינת שולחת לבוב נתוני מועמדים שעברו סריאליזציה. באפליקציה אמיתית, התהליך הזה (שנקרא איתות) מתבצע באמצעות שירות הודעות – נסביר איך עושים את זה בשלב מאוחר יותר. כמובן, בשלב הזה שני אובייקטי ה-RTCPeerConnection נמצאים באותו דף ויכולים לתקשר ישירות בלי צורך בהעברת הודעות חיצונית.
  3. כשבוב מקבל הודעת מועמד מאליס, הוא מתקשר אל addIceCandidate() כדי להוסיף את המועמד לתיאור של העמית המרוחק:
function handleConnection(event) {
  const peerConnection = event.target;
  const iceCandidate = event.candidate;

  if (iceCandidate) {
    const newIceCandidate = new RTCIceCandidate(iceCandidate);
    const otherPeer = getOtherPeer(peerConnection);

    otherPeer.addIceCandidate(newIceCandidate)
      .then(() => {
        handleConnectionSuccess(peerConnection);
      }).catch((error) => {
        handleConnectionFailure(peerConnection, error);
      });

    trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
          `${event.candidate.candidate}.`);
  }
}

בנוסף, עמיתים ב-WebRTC צריכים לגלות ולהחליף מידע על מדיה של אודיו ווידאו מקומיים ומרוחקים, כמו רזולוציה ויכולות קודק. העברת האותות להחלפת פרטי הגדרת המדיה מתבצעת באמצעות החלפת בלובים של מטא-נתונים, שנקראים הצעה ותשובה, בפורמט Session Description Protocol, שנקרא SDP:

  1. דפנה מריצה את ה-method‏ createOffer() של RTCPeerConnection. ההבטחה שמוחזרת מספקת RTCSessionDescription: תיאור הסשן המקומי של אליס:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. אם הפעולה תצליח, אליס תגדיר את התיאור המקומי באמצעות setLocalDescription() ואז תשלח את תיאור הסשן הזה לבוב דרך ערוץ האיתות שלהם.
  2. בוב מגדיר את התיאור שאליס שלחה לו כתיאור מרוחק באמצעות setRemoteDescription().
  3. ירון מפעיל את השיטה createAnswer() של RTCPeerConnection, ומעביר לה את התיאור המרוחק שהוא קיבל מעינת, כדי שאפשר יהיה ליצור סשן מקומי שתואם לשלה. ההבטחה createAnswer() מעבירה RTCSessionDescription: ירון מגדיר את זה כתיאור המקומי ושולח אותו לדנה.
  4. כשעינת מקבלת את תיאור הסשן של ירון, היא מגדירה אותו כתיאור המרוחק באמצעות setRemoteDescription().
// Logs offer creation and sets peer connection session descriptions.
function createdOffer(description) {
  trace(`Offer from localPeerConnection:\n${description.sdp}`);

  trace('localPeerConnection setLocalDescription start.');
  localPeerConnection.setLocalDescription(description)
    .then(() => {
      setLocalDescriptionSuccess(localPeerConnection);
    }).catch(setSessionDescriptionError);

  trace('remotePeerConnection setRemoteDescription start.');
  remotePeerConnection.setRemoteDescription(description)
    .then(() => {
      setRemoteDescriptionSuccess(remotePeerConnection);
    }).catch(setSessionDescriptionError);

  trace('remotePeerConnection createAnswer start.');
  remotePeerConnection.createAnswer()
    .then(createdAnswer)
    .catch(setSessionDescriptionError);
}

// Logs answer to offer creation and sets peer connection session descriptions.
function createdAnswer(description) {
  trace(`Answer from remotePeerConnection:\n${description.sdp}.`);

  trace('remotePeerConnection setLocalDescription start.');
  remotePeerConnection.setLocalDescription(description)
    .then(() => {
      setLocalDescriptionSuccess(remotePeerConnection);
    }).catch(setSessionDescriptionError);

  trace('localPeerConnection setRemoteDescription start.');
  localPeerConnection.setRemoteDescription(description)
    .then(() => {
      setRemoteDescriptionSuccess(localPeerConnection);
    }).catch(setSessionDescriptionError);
}
  1. פינג!

נקודות בונוס

  1. אפשר לעיין בכתובת chrome://webrtc-internals. כך אפשר לקבל נתונים סטטיסטיים של WebRTC ונתוני ניפוי באגים. (רשימה מלאה של כתובות URL ב-Chrome זמינה בכתובת chrome://about).
  2. הגדרת סגנון לדף באמצעות CSS:
  • ממקמים את הסרטונים זה לצד זה.
  • להגדיר את אותה רוחב ללחצנים, עם טקסט גדול יותר.
  • מוודאים שהפריסה פועלת בנייד.
  1. במסוף של כלי הפיתוח ל-Chrome, בודקים את localStream, localPeerConnection ו-remotePeerConnection.
  2. במסוף, מסתכלים על localPeerConnectionpc1.localDescription. איך נראה פורמט SDP?

מה למדתם

בשלב הזה למדתם איך:

  • אפשר להשתמש ב-WebRTC shim,‏ adapter.js, כדי להסתיר את ההבדלים בין הדפדפנים.
  • משתמשים ב-RTCPeerConnection API כדי להפעיל סטרימינג של סרטון.
  • שליטה בלכידה ובסטרימינג של מדיה.
  • שיתוף של מדיה ופרטי רשת בין עמיתים כדי לאפשר שיחת WebRTC.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-2.

טיפים

  • יש הרבה מה ללמוד בשלב הזה! באתר webrtc.org אפשר למצוא משאבים נוספים עם הסברים מפורטים על RTCPeerConnection. בדף הזה יש הצעות למסגרות JavaScript – אם אתם רוצים להשתמש ב-WebRTC, אבל לא רוצים להתעסק עם ממשקי ה-API.
  • מידע נוסף על adapter.js shim זמין במאגר adapter.js ב-GitHub.
  • רוצים לראות איך נראית אפליקציית הווידאו צ'אט הכי טובה בעולם? כדאי לעיין ב-AppRTC, האפליקציה הקנונית של פרויקט WebRTC לשיחות WebRTC: אפליקציה, קוד. זמן הגדרת השיחה הוא פחות מ-500 אלפיות השנייה.

שיטה מומלצת

  • כדי שהקוד שלכם יהיה עמיד לאורך זמן, כדאי להשתמש בממשקי ה-API החדשים שמבוססים על Promise, ולהפעיל תאימות לדפדפנים שלא תומכים בהם באמצעות adapter.js.

הבא בתור

בשלב הזה נסביר איך להשתמש ב-WebRTC כדי להזרים וידאו בין עמיתים – אבל ה-codelab הזה עוסק גם בנתונים!

בשלב הבא מוסבר איך להזרים נתונים שרירותיים באמצעות RTCDataChannel.

6. שימוש ב-RTCDataChannel להחלפת נתונים

מה תלמדו

  • איך להחליף נתונים בין נקודות קצה של WebRTC (עמיתים).

גרסה מלאה של השלב הזה נמצאת בתיקייה step-03.

עדכון ה-HTML

בשלב הזה, תשתמשו בערוצי נתונים של WebRTC כדי לשלוח טקסט בין שני רכיבי textarea באותו דף. זה לא מאוד שימושי, אבל זה מדגים איך אפשר להשתמש ב-WebRTC כדי לשתף נתונים וגם וידאו בסטרימינג.

מסירים את רכיבי הסרטון והלחצן מהקובץ index.html ומחליפים אותם בקוד ה-HTML הבא:

<textarea id="dataChannelSend" disabled
    placeholder="Press Start, enter some text, then press Send."></textarea>
<textarea id="dataChannelReceive" disabled></textarea>

<div id="buttons">
  <button id="startButton">Start</button>
  <button id="sendButton">Send</button>
  <button id="closeButton">Stop</button>
</div>

באזור הטקסט הראשון אפשר להזין טקסט, ובאזור הטקסט השני הטקסט יוצג בזמן שהוא מועבר בין העמיתים.

הקובץ index.html אמור להיראות עכשיו כך:

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <textarea id="dataChannelSend" disabled
    placeholder="Press Start, enter some text, then press Send."></textarea>
  <textarea id="dataChannelReceive" disabled></textarea>

  <div id="buttons">
    <button id="startButton">Start</button>
    <button id="sendButton">Send</button>
    <button id="closeButton">Stop</button>
  </div>

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>

</body>

</html>

עדכון של JavaScript

מחליפים את main.js בתוכן של step-03/js/main.js.

כדי לנסות להזרים נתונים בין עמיתים: פותחים את index.html, לוחצים על Start כדי להגדיר את חיבור העמיתים, מזינים טקסט ב-textarea בצד ימין ואז לוחצים על Send כדי להעביר את הטקסט באמצעות ערוצי נתונים של WebRTC.

איך זה עובד

הקוד הזה משתמש ב-RTCPeerConnection וב-RTCDataChannel כדי לאפשר החלפה של הודעות טקסט.

חלק גדול מהקוד בשלב הזה זהה לקוד בדוגמה של RTCPeerConnection.

רוב הקוד החדש נמצא בפונקציות sendData() ו-createConnection():

function createConnection() {
  dataChannelSend.placeholder = '';
  var servers = null;
  pcConstraint = null;
  dataConstraint = null;
  trace('Using SCTP based data channels');
  // For SCTP, reliable and ordered delivery is true by default.
  // Add localConnection to global scope to make it visible
  // from the browser console.
  window.localConnection = localConnection =
      new RTCPeerConnection(servers, pcConstraint);
  trace('Created local peer connection object localConnection');

  sendChannel = localConnection.createDataChannel('sendDataChannel',
      dataConstraint);
  trace('Created send data channel');

  localConnection.onicecandidate = iceCallback1;
  sendChannel.onopen = onSendChannelStateChange;
  sendChannel.onclose = onSendChannelStateChange;

  // Add remoteConnection to global scope to make it visible
  // from the browser console.
  window.remoteConnection = remoteConnection =
      new RTCPeerConnection(servers, pcConstraint);
  trace('Created remote peer connection object remoteConnection');

  remoteConnection.onicecandidate = iceCallback2;
  remoteConnection.ondatachannel = receiveChannelCallback;

  localConnection.createOffer().then(
    gotDescription1,
    onCreateSessionDescriptionError
  );
  startButton.disabled = true;
  closeButton.disabled = false;
}

function sendData() {
  var data = dataChannelSend.value;
  sendChannel.send(data);
  trace('Sent Data: ' + data);
}

התחביר של RTCDataChannel דומה בכוונה ל-WebSocket, עם שיטת send() ואירוע message.

שימו לב לשימוש ב-dataConstraint. אפשר להגדיר ערוצי נתונים כדי לאפשר שיתוף נתונים מסוגים שונים – לדוגמה, לתת עדיפות למסירה מהימנה על פני ביצועים. מידע נוסף על האפשרויות זמין ב-Mozilla Developer Network.

נקודות בונוס

  1. ב-SCTP, הפרוטוקול שבו משתמשים בערוצי הנתונים של WebRTC, העברת נתונים אמינה ומסודרת מופעלת כברירת מחדל. מתי יכול להיות שיהיה צורך ב-RTCDataChannel כדי לספק מסירה מהימנה של נתונים, ומתי יכול להיות שביצועים יהיו חשובים יותר – גם אם זה אומר לאבד חלק מהנתונים?
  2. משתמשים ב-CSS כדי לשפר את פריסת הדף, ומוסיפים מאפיין placeholder לאזור הטקסט של dataChannelReceive.
  3. בודקים את הדף בנייד.

מה למדתם

בשלב הזה למדתם איך:

  • יצירת חיבור בין שני עמיתים ב-WebRTC.
  • החלפת נתוני טקסט בין העמיתים.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-03.

מידע נוסף

הבא בתור

למדנו איך להחליף נתונים בין עמיתים באותו דף, אבל איך עושים את זה בין מכונות שונות? קודם צריך להגדיר ערוץ איתות להחלפת הודעות מטא-נתונים. בשלב הבא נסביר איך עושים את זה.

7. הגדרה של שירות איתות להחלפת הודעות

מה תלמדו

בשלב הזה נסביר איך:

  • משתמשים ב-npm כדי להתקין את יחסי התלות של הפרויקט כפי שמצוין ב-package.json
  • מריצים שרת Node.js ומשתמשים ב-node-static כדי להציג קבצים סטטיים.
  • הגדרת שירות העברת הודעות ב-Node.js באמצעות Socket.IO.
  • אפשר להשתמש בהם כדי ליצור 'חדרים' ולהחליף הודעות.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-04.

מושגים

כדי להגדיר שיחה ב-WebRTC ולתחזק אותה, לקוחות (עמיתים) של WebRTC צריכים להחליף מטא-נתונים:

  • מידע על המועמד (ברשת).
  • הודעות הצעה ותשובה שמספקות מידע על מדיה, כמו רזולוציה וקודקים.

במילים אחרות, נדרשת החלפה של מטא-נתונים לפני שמתבצע סטרימינג של אודיו, וידאו או נתונים בין עמיתים. התהליך הזה נקרא סימון.

בשלבים הקודמים, אובייקטי ה-RTCPeerConnection של השולח והמקבל נמצאים באותו דף, ולכן 'העברת אותות' היא פשוט העברת מטא-נתונים בין אובייקטים.

ביישום בעולם האמיתי, חיבורי ה-RTCPeerConnection של השולח והמקבל פועלים בדפי אינטרנט במכשירים שונים, וצריך דרך לתקשר מטא-נתונים ביניהם.

לשם כך משתמשים בשרת איתות: שרת שיכול להעביר הודעות בין לקוחות WebRTC (עמיתים). ההודעות בפועל הן טקסט פשוט: אובייקטים של JavaScript שהומרו למחרוזת.

דרישה מוקדמת: התקנת Node.js

כדי להריץ את השלבים הבאים ב-codelab הזה (תיקיות step-04 עד step-06), תצטרכו להריץ שרת ב-localhost באמצעות Node.js.

אפשר להוריד ולהתקין את Node.js מהקישור הזה או דרך מנהל החבילות המועדף.

אחרי ההתקנה, תוכלו לייבא את התלות שנדרשת לשלבים הבאים (הפעלת npm install), וגם להפעיל שרת קטן במארח המקומי כדי להריץ את ה-codelab (הפעלת node index.js). הפקודות האלה יצוינו בהמשך, כשיהיה בהן צורך.

על היישום

‫WebRTC משתמש ב-API של JavaScript בצד הלקוח, אבל כדי להשתמש בו בעולם האמיתי צריך גם שרת איתות (העברת הודעות), וגם שרתי STUN ו-TURN. מידע נוסף זמין כאן.

בשלב הזה תיצרו שרת פשוט לאיתות ב-Node.js, באמצעות מודול Socket.IO Node.js וספריית JavaScript להעברת הודעות. ניסיון ב-Node.js וב-Socket.IO יהיה שימושי, אבל לא חיוני. רכיבי ההודעות פשוטים מאוד.

בדוגמה הזו, השרת (אפליקציית Node.js) מוטמע בקובץ index.js, והלקוח שפועל בו (אפליקציית האינטרנט) מוטמע בקובץ index.html.

לאפליקציית Node.js בשלב הזה יש שתי משימות.

קודם כל, הוא פועל כממסר הודעות:

socket.on('message', function (message) {
  log('Got message: ', message);
  socket.broadcast.emit('message', message);
});

שנית, הוא מנהל 'חדרים' לשיחות וידאו ב-WebRTC:

if (numClients === 0) {
  socket.join(room);
  socket.emit('created', room, socket.id);
} else if (numClients === 1) {
  socket.join(room);
  socket.emit('joined', room, socket.id);
  io.sockets.in(room).emit('ready');
} else { // max two clients
  socket.emit('full', room);
}

אפליקציית WebRTC הפשוטה שלנו תאפשר לשני עמיתים לכל היותר לשתף חדר.

HTML ו-JavaScript

מעדכנים את index.html כך שייראה כך:

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <script src="/socket.io/socket.io.js"></script>
  <script src="js/main.js"></script>
  
</body>

</html>

בשלב הזה לא יוצג כלום בדף: כל הרישום מתבצע במסוף הדפדפן. (כדי להציג את המסוף ב-Chrome, מקישים על Ctrl-Shift-J, או על Command-Option-J אם אתם משתמשים ב-Mac).

מחליפים את js/main.js בקוד הבא:

'use strict';

var isInitiator;

window.room = prompt("Enter room name:");

var socket = io.connect();

if (room !== "") {
  console.log('Message from client: Asking to join room ' + room);
  socket.emit('create or join', room);
}

socket.on('created', function(room, clientId) {
  isInitiator = true;
});

socket.on('full', function(room) {
  console.log('Message from client: Room ' + room + ' is full :^(');
});

socket.on('ipaddr', function(ipaddr) {
  console.log('Message from client: Server IP address is ' + ipaddr);
});

socket.on('joined', function(room, clientId) {
  isInitiator = false;
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

הגדרת Socket.IO להרצה ב-Node.js

יכול להיות שראיתם בקובץ ה-HTML שאתם משתמשים בקובץ Socket.IO:

<script src="/socket.io/socket.io.js"></script>

ברמה העליונה של ספריית work, יוצרים קובץ בשם package.json עם התוכן הבא:

{
  "name": "webrtc-codelab",
  "version": "0.0.1",
  "description": "WebRTC codelab",
  "dependencies": {
    "node-static": "^0.7.10",
    "socket.io": "^1.2.0"
  }
}

זהו קובץ מניפסט של אפליקציה שמציין לכלי לניהול חבילות Node‏ (npm) אילו תלויות בפרויקט צריך להתקין.

כדי להתקין תלות (כמו /socket.io/socket.io.js), מריצים את הפקודה הבאה מטרמינל שורת הפקודה, בספריית העבודה:

npm install

יוצג יומן התקנה שמסתיים במשהו כזה:

3ab06b7bcc7664b9.png

כפי שאפשר לראות, npm התקין את יחסי התלות שהוגדרו ב-package.json.

יוצרים קובץ חדש בשם index.js ברמה העליונה של הספרייה work (לא בספרייה js) ומוסיפים את הקוד הבא:

'use strict';

var os = require('os');
var nodeStatic = require('node-static');
var http = require('http');
var socketIO = require('socket.io');

var fileServer = new(nodeStatic.Server)();
var app = http.createServer(function(req, res) {
  fileServer.serve(req, res);
}).listen(8080);

var io = socketIO.listen(app);
io.sockets.on('connection', function(socket) {

  // convenience function to log server messages on the client
  function log() {
    var array = ['Message from server:'];
    array.push.apply(array, arguments);
    socket.emit('log', array);
  }

  socket.on('message', function(message) {
    log('Client said: ', message);
    // for a real app, would be room-only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', function(room) {
    log('Received request to create or join room ' + room);

    var clientsInRoom = io.sockets.adapter.rooms[room];
    var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;

    log('Room ' + room + ' now has ' + numClients + ' client(s)');

    if (numClients === 0) {
      socket.join(room);
      log('Client ID ' + socket.id + ' created room ' + room);
      socket.emit('created', room, socket.id);

    } else if (numClients === 1) {
      log('Client ID ' + socket.id + ' joined room ' + room);
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room, socket.id);
      io.sockets.in(room).emit('ready');
    } else { // max two clients
      socket.emit('full', room);
    }
  });

  socket.on('ipaddr', function() {
    var ifaces = os.networkInterfaces();
    for (var dev in ifaces) {
      ifaces[dev].forEach(function(details) {
        if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
          socket.emit('ipaddr', details.address);
        }
      });
    }
  });

});

בטרמינל של שורת הפקודה, מריצים את הפקודה הבאה בספרייה work:

node index.js

פותחים את localhost:8080 בדפדפן.

בכל פעם שתפתחו את כתובת ה-URL הזו, תתבקשו להזין את שם החדר. כדי להצטרף לאותו חדר, צריך לבחור את אותו שם חדר בכל פעם, למשל 'foo'.

פותחים את הדף 'כרטיסייה חדשה' ופותחים שוב את localhost:8080. בוחרים את אותו שם חדר.

פותחים את localhost:8080 בכרטיסייה או בחלון שלישיים. בוחרים שוב את אותו שם של החדר.

בודקים את המסוף בכל אחת מהכרטיסיות: אמור להופיע הרישום מה-JavaScript שלמעלה.

נקודות בונוס

  1. אילו מנגנונים חלופיים להעברת הודעות יכולים להיות אפשריים? באילו בעיות יכול להיות שתיתקלו בשימוש ב-WebSocket 'טהור'?
  2. אילו בעיות יכולות להיות קשורות להרחבת האפליקציה הזו? האם אפשר לפתח שיטה לבדיקה של אלפים או מיליונים של בקשות לחדרים בו-זמנית?
  3. האפליקציה הזו משתמשת בהודעה של JavaScript כדי לקבל שם לחדר. מנסים להבין איך אפשר לקבל את שם החדר מכתובת ה-URL. לדוגמה, localhost:8080/foo ייתן את שם החדר foo.

מה למדתם

בשלב הזה למדתם איך:

  • משתמשים ב-npm כדי להתקין את יחסי התלות של הפרויקט כפי שמצוין בקובץ package.json
  • מריצים שרת Node.js כדי להגיש קבצים סטטיים.
  • הגדרת שירות הודעות ב-Node.js באמצעות socket.io.
  • אפשר להשתמש בהם כדי ליצור 'חדרים' ולהחליף הודעות.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-04.

מידע נוסף

הבא בתור

כאן מוסבר איך משתמשים באיתות כדי לאפשר לשני משתמשים ליצור חיבור ישיר.

8. שילוב של חיבור עמית לעמית ואיתות

מה תלמדו

בשלב הזה נסביר איך:

  • הפעלת שירות איתות WebRTC באמצעות Socket.IO שפועל ב-Node.js
  • השירות הזה משמש להחלפת מטא-נתונים של WebRTC בין עמיתים.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-05.

החלפת HTML ו-JavaScript

מחליפים את התוכן של index.html בתוכן הבא:

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="/css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <div id="videos">
    <video id="localVideo" autoplay muted></video>
    <video id="remoteVideo" autoplay></video>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>
  
</body>

</html>

מחליפים את js/main.js בתוכן של step-05/js/main.js.

הפעלת שרת Node.js

אם אתם לא מבצעים את ה-codelab הזה מהספרייה work, יכול להיות שתצטרכו להתקין את יחסי התלות של התיקייה step-05 או של תיקיית העבודה הנוכחית. מריצים את הפקודה הבאה מספריית העבודה:

npm install

אחרי ההתקנה, אם שרת Node.js לא פועל, מפעילים אותו באמצעות הפקודה הבאה בספרייה work:

node index.js

מוודאים שמשתמשים בגרסה של index.js מהשלב הקודם שמטמיעה את Socket.IO. מידע נוסף על Node ו-Socket IO זמין בקטע 'הגדרת שירות איתות להחלפת הודעות'.

פותחים את localhost:8080 בדפדפן.

פותחים שוב את localhost:8080 בכרטיסייה או בחלון חדשים. רכיב וידאו אחד יציג את הסטרימינג המקומי מ-getUserMedia() והשני יציג את הווידאו 'המרוחק' שמוזרם באמצעות RTCPeerconnection.

צפייה ברישום ביומן במסוף הדפדפן.

נקודות בונוס

  1. באפליקציה הזו אפשר לנהל רק וידאו צ'אט בארבע עיניים. איך אפשר לשנות את העיצוב כדי לאפשר ליותר מאדם אחד לשתף את אותו חדר צ'אט וידאו?
  2. בדוגמה, שם החדר foo מוטמע בקוד. מה הדרך הכי טובה להפעיל שמות אחרים לחדרים?
  3. איך המשתמשים ישתפו את שם החדר? אפשר לנסות ליצור חלופה לשיתוף שמות החדרים.
  4. איך אפשר לשנות את האפליקציה

מה למדתם

בשלב הזה למדתם איך:

  • הפעלת שירות לאיתות WebRTC באמצעות Socket.IO שפועל ב-Node.js.
  • השירות הזה משמש להחלפת מטא-נתונים של WebRTC בין עמיתים.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-05.

טיפים

  • נתונים לניפוי באגים וסטטיסטיקות של WebRTC זמינים בכתובת chrome://webrtc-internals.
  • אפשר להשתמש באתר test.webrtc.org כדי לבדוק את הסביבה המקומית ולבדוק את המצלמה והמיקרופון.
  • אם נתקלתם בבעיות מוזרות שקשורות לשמירה במטמון, כדאי לנסות את הפעולות הבאות:
  • מבצעים רענון מלא על ידי לחיצה ארוכה על Ctrl ואז לחיצה על הלחצן טעינה מחדש.
  • מפעילים מחדש את הדפדפן
  • מריצים את הפקודה npm cache clean משורת הפקודה.

הבא בתור

כאן מוסבר איך לצלם תמונה, לקבל את נתוני התמונה ולשתף אותם בין עמיתים מרוחקים.

9. איך מצלמים תמונה ומשתפים אותה דרך ערוץ נתונים

מה תלמדו

בשלב הזה תלמדו איך:

  • לצלם תמונה ולקבל ממנה את הנתונים באמצעות רכיב הקנבס.
  • החלפת נתוני תמונה עם משתמש מרוחק.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-06.

איך זה עובד

במאמר הקודם למדתם איך להחליף הודעות טקסט באמצעות RTCDataChannel.

בשלב הזה אפשר לשתף קבצים שלמים: בדוגמה הזו, תמונות שצולמו באמצעות getUserMedia().

החלקים העיקריים בשלב הזה הם:

  1. יצירת ערוץ נתונים. שימו לב שבשלב הזה לא מוסיפים זרמי מדיה לחיבור בין העמיתים.
  2. צילום שידור הווידאו ממצלמת האינטרנט של המשתמש באמצעות getUserMedia():
var video = document.getElementById('video');

function grabWebCamVideo() {
  console.log('Getting user media (video) ...');
  navigator.mediaDevices.getUserMedia({
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    alert('getUserMedia() error: ' + e.name);
  });
}
  1. כשמשתמש לוחץ על הלחצן Snap, צריך לקבל תמונה (פריים של סרטון) מזרם הווידאו ולהציג אותה ברכיב canvas:
var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');

function snapPhoto() {
  photoContext.drawImage(video, 0, 0, photo.width, photo.height);
  show(photo, sendBtn);
}
  1. כשהמשתמש לוחץ על הכפתור שליחה, המערכת ממירה את התמונה לבייטים ושולחת אותם דרך ערוץ נתונים:
function sendPhoto() {
  // Split data channel message in chunks of this byte length.
  var CHUNK_LEN = 64000;
  var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
    len = img.data.byteLength,
    n = len / CHUNK_LEN | 0;

  console.log('Sending a total of ' + len + ' byte(s)');
  dataChannel.send(len);

  // split the photo and send in chunks of about 64KB
  for (var i = 0; i < n; i++) {
    var start = i * CHUNK_LEN,
      end = (i + 1) * CHUNK_LEN;
    console.log(start + ' - ' + (end - 1));
    dataChannel.send(img.data.subarray(start, end));
  }

  // send the reminder, if any
  if (len % CHUNK_LEN) {
    console.log('last ' + len % CHUNK_LEN + ' byte(s)');
    dataChannel.send(img.data.subarray(n * CHUNK_LEN));
  }
}
  1. בצד המקבל, המערכת ממירה את הבייטים של ההודעה בערוץ הנתונים בחזרה לתמונה ומציגה את התמונה למשתמש:
function receiveDataChromeFactory() {
  var buf, count;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
      count = 0;
      console.log('Expecting a total of ' + buf.byteLength + ' bytes');
      return;
    }

    var data = new Uint8ClampedArray(event.data);
    buf.set(data, count);

    count += data.byteLength;
    console.log('count: ' + count);

    if (count === buf.byteLength) {
      // we're done: all data chunks have been received
      console.log('Done. Rendering photo.');
      renderPhoto(buf);
    }
  };
}

function renderPhoto(data) {
  var canvas = document.createElement('canvas');
  canvas.width = photoContextW;
  canvas.height = photoContextH;
  canvas.classList.add('incomingPhoto');
  // trail is the element holding the incoming images
  trail.insertBefore(canvas, trail.firstChild);

  var context = canvas.getContext('2d');
  var img = context.createImageData(photoContextW, photoContextH);
  img.data.set(data);
  context.putImageData(img, 0, 0);
}

קבל את הקוד

מחליפים את התוכן של התיקייה work בתוכן של התיקייה step-06. הקובץ index.html בתיקייה work אמור להיראות עכשיו כך**:**

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="/css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <h2>
    <span>Room URL: </span><span id="url">...</span>
  </h2>

  <div id="videoCanvas">
    <video id="camera" autoplay></video>
    <canvas id="photo"></canvas>
  </div>

  <div id="buttons">
    <button id="snap">Snap</button><span> then </span><button id="send">Send</button>
    <span> or </span>
    <button id="snapAndSend">Snap &amp; Send</button>
  </div>

  <div id="incoming">
    <h2>Incoming photos</h2>
    <div id="trail"></div>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>

</body>

</html>

אם אתם לא מבצעים את ה-codelab הזה מהספרייה work, יכול להיות שתצטרכו להתקין את יחסי התלות עבור התיקייה step-06 או תיקיית העבודה הנוכחית. פשוט מריצים את הפקודה הבאה מספריית העבודה:

npm install

אחרי ההתקנה, אם שרת Node.js לא פועל, מפעילים אותו באמצעות הפקודה הבאה מתוך ספריית העבודה:

node index.js

חשוב לוודא שאתם משתמשים בגרסה של index.js שמטמיעה את Socket.IO, ולזכור להפעיל מחדש את שרת Node.js אם אתם מבצעים שינויים. מידע נוסף על Node ו-Socket IO זמין בקטע 'הגדרת שירות איתות להחלפת הודעות'.

אם צריך, לוחצים על הלחצן אישור כדי לאשר לאפליקציה להשתמש במצלמת הרשת.

האפליקציה תיצור מזהה חדר אקראי ותוסיף אותו לכתובת ה-URL. פתיחת כתובת ה-URL מסרגל הכתובות בכרטיסייה חדשה או בחלון חדש בדפדפן.

לוחצים על הלחצן Snap & Send (מצלמים ושולחים) ואז מסתכלים על האזור 'נכנסות' בכרטיסייה השנייה בחלק התחתון של הדף. האפליקציה מעבירה תמונות בין כרטיסיות.

אתם אמורים לראות משהו כזה:

911b40f36ba6ba8.png

נקודות בונוס

  1. איך אפשר לשנות את הקוד כדי לאפשר שיתוף של כל סוג קובץ?

מידע נוסף

מה למדתם

  • איך מצלמים תמונה ומקבלים את הנתונים שלה באמצעות רכיב הקנבס.
  • איך להחליף את הנתונים האלה עם משתמש מרוחק.

גרסה מלאה של השלב הזה נמצאת בתיקייה step-06.

10. מזל טוב

יצרתם אפליקציה לסטרימינג של וידאו בזמן אמת ולהחלפת נתונים!

מה למדתם

ב-codelab הזה למדתם איך:

  • קבלת סרטון ממצלמת האינטרנט.
  • הזרמת סרטון באמצעות RTCPeerConnection.
  • שידור נתונים באמצעות RTCDataChannel.
  • מגדירים שירות איתות להחלפת הודעות.
  • שילוב של חיבור עמית לעמית ואיתות.
  • מצלמים תמונה ומשתפים אותה דרך ערוץ נתונים.

השלבים הבאים

מידע נוסף

  • מגוון משאבים לתחילת העבודה עם WebRTC זמינים בכתובת webrtc.org.