תקשורת בזמן אמת עם 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 של המחשב, ומפעילים את השרתים כדי שיפעלו כשרתי ממסר במקרה של כשל בתקשורת 'מקצה לקצה'. (הסבר מפורט על WebRTC בעולם האמיתי זמין במאמר).

האם WebRTC מאובטח?

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

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

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

מה תלמדו

  • קבלת וידאו ממצלמת האינטרנט
  • שידור וידאו באמצעות RTCPeerConnection
  • שידור נתונים באמצעות RTCDataChannel
  • הגדרה של שירות איתות לשליחת הודעות
  • לשלב חיבור בין רשתות שכנות (peering) ואותות
  • צילום תמונה ושיתוף שלה דרך ערוץ נתונים

למה תזדקק?

  • Chrome 47 ומעלה
  • שרת אינטרנט עבור 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

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

1d2b4aa977ab7e24.png

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

27fce4494f641883.png

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

צילום מסך מתאריך 2016-02-18 בשעה 11.48:14 בבוקר.png

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

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

צילום מסך מתאריך 18-02-2016 בשעה 11:56:30 בבוקר.

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

צילום מסך מתאריך 18-02-2016 בשעה 12.22.18.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>

...וצביטה של 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() והשני יציג את אותו סרטון שמועבר בסטרימינג באמצעות RTCPeerconnect. (באפליקציה בעולם האמיתי, רכיב וידאו אחד יציג את השידור המקומי והשני את הסטרימינג המרוחק).

הוספת ספריית ה-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, לוחצים על הלחצן התחלה כדי לצפות בסרטון ממצלמת האינטרנט, ולוחצים על התקשרות כדי לבצע את החיבור דרך האינטרנט. אותו סרטון (ממצלמת האינטרנט) אמור להופיע בשני רכיבי הווידאו. במסוף הדפדפן אפשר לראות רישום ביומן של WebRTC.

איך זה עובד

השלב הזה מבצע הרבה...

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

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

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

  • יוצרים RTCPeerConnection לכל סוף השיחה, ובכל קצה מוסיפים את השידור המקומי מ-getUserMedia().
  • קבלה ושיתוף של מידע על הרשת: נקודות קצה (endpoint) פוטנציאליות של חיבור נקראות 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. ה-handler של 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 צריכות למצוא ולהחליף מידע לגבי מדיה של אודיו ווידאו מקומי או מרוחק, כמו רזולוציה ויכולות קודק. איתות כדי לשלוח מידע על הגדרות מדיה מתבצע על ידי העברת אובייקטי blob של מטא-נתונים, שנקראים הצעה ותשובה, באמצעות הפורמט של פרוטוקול תיאור הסשן, שמכונה SDP:

  1. ליאל מפעילה את שיטת ה-method createOffer() של RTCPeerConnection. ההבטחה שהוחזרה כוללת RTCSessionDescription: תיאור הסשן המקומי של אליס:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. אם הפעולה בוצעה ללא שגיאות, אליס מגדירה את התיאור המקומי באמצעות setLocalDescription() ולאחר מכן שולחת את תיאור הסשן הזה אל יוסי דרך ערוץ האיתות שלו.
  2. יוסי מגדיר את התיאור שאליס שלחה לו כתיאור מרחוק באמצעות setRemoteDescription().
  3. יוסי מפעיל את שיטת RTCPeerConnection createAnswer() ומעביר אליה את התיאור המרוחק שהוא קיבל מאלי, כדי שניתן יהיה ליצור סשן מקומי שתואם לשלה. ההבטחה 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.
  • אפשר למצוא מידע נוסף על ספריית ה-shim של Adapter.js במאגר GitHub של adapter.js.
  • רוצה לראות איך נראית אפליקציית הווידאו צ'אט הטובה בעולם? כדאי להציץ ב-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, לוחצים על התחלה כדי להגדיר את החיבור בין האפליקציות, מזינים טקסט ב-textarea מימין ואז לוחצים על שליחה כדי להעביר את הטקסט באמצעות ערוצי נתונים של 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, עם method 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 נמצאים באותו הדף, לכן 'אותות' הוא פשוט עניין של העברת מטא-נתונים בין אובייקטים.

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

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

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

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

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

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

על היישום

WebRTC משתמש ב-JavaScript API בצד הלקוח, אבל לשימוש בעולם האמיתי נדרש גם שרת איתות (העברת הודעות), וגם שרתי 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 Package Manager (npm) אילו יחסי תלות של הפרויקט צריך להתקין.

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

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.

למידע נוסף

השלב הבא

איך להשתמש באיתות כדי לאפשר לשני משתמשים ליצור חיבור בין רשתות שכנות (peering).

8. לשלב חיבור בין רשתות שכנות (peering) ואותות

מה תלמדו

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

  • הפעלת שירות איתות של 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. למידע נוסף על קלט/פלט של צמתים ו-Socket, אפשר לעיין בקטע 'הגדרת שירות איתות לשליחת הודעות'.

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

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

אפשר להציג את הרישום ביומן במסוף הדפדפן.

נקודות בונוס

  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 לא פועל, מפעילים אותו באמצעות הפקודה הבאה מספריית work:

node index.js

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

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

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

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

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

911b40f36ba6ba8.png

נקודות בונוס

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

למידע נוסף

  • MediaStream Image capture API: API לצילום תמונות ולשליטה במצלמות. בקרוב זמין בדפדפן בקרבת מקום!
  • ממשק ה-API של MediaRecorder, להקלטת אודיו ווידאו: הדגמה, תיעוד.

מה למדתם

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

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

10. מזל טוב

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

מה למדתם

ב-Codelab הזה למדנו איך:

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

השלבים הבאים

מידע נוסף

  • באתר webrtc.org אפשר למצוא מגוון משאבים לתחילת העבודה עם WebRTC.