ارتباط بلادرنگ با WebRTC را فعال کنید

1. قبل از شروع

این لبه کد به شما می آموزد که چگونه برنامه ای بسازید تا با وب کم خود ویدیو بگیرید و عکس های فوری بگیرید و آنها را به صورت همتا با WebRTC به اشتراک بگذارید. همچنین نحوه استفاده از WebRTC APIهای اصلی و راه اندازی یک سرور پیام رسانی با Node.js را یاد می گیرید.

پیش نیازها

  • دانش اولیه HTML، CSS و جاوا اسکریپت

چیزی که خواهی ساخت

  • از وب کم خود ویدیو بگیرید.
  • پخش ویدئو با RTCPeerConnection .
  • داده ها را با RTCDataChannel .
  • یک سرویس سیگنالینگ برای تبادل پیام راه اندازی کنید.
  • اتصال همتا و سیگنالینگ را ترکیب کنید.
  • یک عکس بگیرید و از یک کانال داده برای اشتراک گذاری آن استفاده کنید.

آنچه شما نیاز دارید

  • Chrome 47 یا بالاتر
  • وب سرور برای کروم یا وب سرور انتخابی شما
  • یک ویرایشگر متن به انتخاب شما
  • Node.js

2. کد نمونه را دریافت کنید

کد را دانلود کنید

  1. اگر با Git آشنایی دارید، این دستور را اجرا کنید تا کد این کد لبه را از GitHub کلون کنید:
git clone https://github.com/googlecodelabs/webrtc-web

همچنین، برای دانلود فایل فشرده کد، روی این لینک کلیک کنید:

  1. فایل فشرده دانلود شده را باز کنید تا یک پوشه پروژه به نام webrtc-web-master باز شود که شامل یک پوشه برای هر مرحله از این کد لبه و تمام منابعی است که نیاز دارید.

شما تمام کارهای کد خود را در دایرکتوری به نام work می دهید.

پوشه های step-nn حاوی یک نسخه تمام شده برای هر مرحله از این کد لبه هستند. آنها آنجا برای مرجع هستند.

وب سرور را نصب و تأیید کنید

در حالی که شما آزاد هستید که از وب سرور خود استفاده کنید، این کد لبه برای کار با وب سرور برای Chrome طراحی شده است.

  1. اگر وب سرور برای Chrome ندارید، روی این پیوند کلیک کنید تا آن را از فروشگاه وب Chrome نصب کنید:

d0a4649b4920cf3.png

  1. روی افزودن به Chrome کلیک کنید، که سرور وب را برای Chrome نصب می‌کند و به‌طور خودکار برنامه‌های Google شما را در یک برگه جدید باز می‌کند.
  2. روی وب سرور کلیک کنید:

27fce4494f641883.png

یک گفتگو ظاهر می شود که به شما امکان می دهد وب سرور محلی خود را پیکربندی کنید:

a300381a486b9e22.png

  1. روی انتخاب پوشه کلیک کنید.
  2. پوشه work که ایجاد کردید را انتخاب کنید.

در زیر URL(های) سرور وب ، نشانی اینترنتی را می بینید که می توانید کارهای در حال انجام خود را در آن مشاهده کنید

کروم.

  1. در قسمت گزینه‌ها (ممکن است نیاز به راه‌اندازی مجدد داشته باشد) ، کادر بررسی خودکار نشان دادن index.html را انتخاب کنید.
  2. Toggle Web Server: برای توقف و راه اندازی مجدد سرور، دو بار شروع به کار کرد.

f23cafb3993dfac1.png

  1. برای مشاهده کار خود در مرورگر وب، روی URL زیر URL(های) سرور وب کلیک کنید.

شما باید صفحه‌ای را ببینید که شبیه به work/index.html :

18a705cb6ccc5181.png

بدیهی است که این برنامه هنوز کار جالبی انجام نمی دهد. برای اطمینان از اینکه سرور وب شما به درستی کار می کند، تنها یک اسکلت کوچک است. در مراحل بعدی ویژگی های عملکرد و چیدمان را اضافه می کنید.

3. ویدئو را از وب کم خود پخش کنید

نسخه کامل این مرحله در پوشه step-01 قرار دارد.

یک خط تیره HTML اضافه کنید

این کد را کپی کنید و آن را در فایل index.html در فهرست work خود قرار دهید تا یک عنصر video و script اضافه شود:

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

  <video autoplay playsinline></video>

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

</body>

</html>

کمی جاوا اسکریپت اضافه کنید

این کد را کپی کرده و در فایل main.js در پوشه js خود قرار دهید:

'use strict';

// In this codelab, you  only stream video, not audio (video: true).
const mediaStreamConstraints = {
  video: true,
};

// The video element where the stream is displayed
const localVideo = document.querySelector('video');

// The local stream that's displayed on the video
let localStream;

// Handle success and add the MediaStream to the video element
function gotLocalMediaStream(mediaStream) {
  localStream = mediaStream;
  localVideo.srcObject = mediaStream;
}

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

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

آن را امتحان کنید

فایل index.html را در مرورگر خود باز کنید و باید چیزی شبیه به این را ببینید، البته با نمای وب کم خود:

9297048e43ed0f3d.png

چگونه کار می کند

پس از فراخوانی getUserMedia() ، اگر اولین درخواست برای دسترسی دوربین برای مبدا فعلی باشد، مرورگر اجازه دسترسی به دوربین شما را درخواست می کند.

در صورت موفقیت آمیز بودن، یک MediaStream برگردانده می شود که یک عنصر media می تواند از طریق ویژگی 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, را تایپ کنید و Enter را فشار دهید ( Return on Mac). برای مشاهده کنسول در کروم، Control+Shift+J (یا Command+Option+J در Mac) را فشار دهید.
  • localStream.getVideoTracks() چه چیزی را برمی گرداند؟
  • localStream.getVideoTracks()[0].stop() فراخوانی کنید.
  • به شیء محدودیت ها نگاه کنید. وقتی آن را به {audio: true, video: true} تغییر دهید، چه اتفاقی می‌افتد؟
  • اندازه عنصر ویدیو چقدر است؟ چگونه می توانید اندازه طبیعی ویدیو را از جاوا اسکریپت دریافت کنید، برخلاف اندازه نمایش؟ برای بررسی از ابزار توسعه دهنده گوگل کروم استفاده کنید.
  • فیلترهای CSS را به عنصر ویدیو اضافه کنید، مانند این:
video {
  filter: blur(4px) invert(1) opacity(0.5);
}
  • فیلترهای SVG را مانند این اضافه کنید:
video {
   filter: hue-rotate(180deg) saturate(200%);
 }

نکات

بهترین تمرین

مطمئن شوید که عنصر ویدیوی شما از محفظه خود سرریز نمی‌شود. این کد لبه width و max-width را برای تنظیم اندازه دلخواه و حداکثر اندازه برای ویدیو اضافه کرد. مرورگر شما ارتفاع را به صورت خودکار محاسبه می کند.

video {
  max-width: 100%;
  width: 320px;
}

4. ویدئو را با RTCPeerConnection API پخش کنید

نسخه کامل این مرحله در پوشه step-2 قرار دارد.

عناصر ویدئویی و دکمه های کنترل را اضافه کنید

در فایل index.html ، عنصر video واحد را با دو عنصر video و سه button جایگزین کنید:

<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 پخش شده است. (در یک برنامه دنیای واقعی، یک عنصر video جریان محلی را نمایش می دهد و دیگری جریان از راه دور را نمایش می دهد.)

adapter.js shim را اضافه کنید

این عنصر اسکریپت را کپی کرده و بالای عنصر اسکریپت برای main.js قرار دهید:

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

فایل index.html شما اکنون باید به شکل زیر باشد:

<!DOCTYPE html>
<html>

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

<body>
  <h1>Real-time 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 جایگزین کنید.

تماس بگیرید

  1. فایل index.html را باز کنید.
  2. برای دریافت ویدیو از وب کم خود روی Start کلیک کنید.
  3. برای برقراری ارتباط همتا روی Call کلیک کنید

شما باید همان ویدیو را از وب کم خود در هر دو عنصر video ببینید.

  1. برای مشاهده گزارش WebRTC، کنسول مرورگر را مشاهده کنید.

چگونه کار می کند

این مرحله کارهای زیادی انجام می دهد.

WebRTC از RTCPeerConnection API برای راه اندازی یک اتصال برای پخش جریانی ویدیو بین مشتریان WebRTC، که به عنوان همتا شناخته می شوند، استفاده می کند.

در این مثال، دو شیء RTCPeerConnection در یک صفحه قرار دارند: pc1 و pc2 .

تنظیم تماس بین همتایان WebRTC شامل سه کار است:

  1. یک RTCPeerConnection برای هر انتهای تماس ایجاد کنید و در هر پایان، جریان محلی را از getUserMedia() اضافه کنید.
  2. اطلاعات شبکه را دریافت و به اشتراک بگذارید.

نقاط پایانی اتصال بالقوه به عنوان نامزدهای ICE شناخته می شوند.

  1. توضیحات محلی و راه دور را دریافت و به اشتراک بگذارید.

فراداده درباره رسانه محلی در قالب پروتکل شرح جلسه (SDP) است.

تصور کنید که آلیس و باب می خواهند از RTCPeerConnection برای راه اندازی یک چت تصویری استفاده کنند.

ابتدا آلیس و باب اطلاعات شبکه را تبادل می کنند. عبارت finding kandidats به فرآیند یافتن رابط‌ها و پورت‌های شبکه با استفاده از چارچوب ICE اشاره دارد.

  1. آلیس یک شی RTCPeerConnection با یک کنترل کننده 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 از مرحله اول زمانی فراخوانی می شود که نامزدهای شبکه در دسترس قرار گیرند.
  2. آلیس داده های نامزد سریالی را برای باب می فرستد.

در یک اپلیکیشن واقعی، این فرآیند که به عنوان سیگنالینگ شناخته می شود، از طریق یک سرویس پیام رسانی انجام می شود. در مرحله بعد یاد می گیرید که چگونه این کار را انجام دهید. البته در این مرحله دو شیء RTCPeerConnection در یک صفحه قرار دارند و می توانند بدون نیاز به پیام خارجی مستقیماً با هم ارتباط برقرار کنند.

  1. هنگامی که باب یک پیام نامزد از آلیس دریافت می کند، 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 نیاز به کشف و تبادل اطلاعات رسانه های صوتی و تصویری محلی و راه دور مانند قابلیت های وضوح و کدک دارند. سیگنال دهی برای تبادل اطلاعات پیکربندی رسانه با مبادله حباب های ابرداده، که به عنوان پیشنهاد و پاسخ شناخته می شود، با استفاده از فرمت SDP ادامه می یابد.

  1. آلیس createOffer() RTCPeerConnection را اجرا می کند.

وعده بازگشتی یک RTCSessionDescription ارائه می‌کند - شرح جلسه محلی آلیس:

trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. در صورت موفقیت آمیز بودن، آلیس توضیحات محلی را با استفاده از setLocalDescription() می کند و سپس این توضیحات جلسه را از طریق کانال سیگنالینگ آنها برای Bob ارسال می کند.
  2. Bob توضیحاتی را که آلیس برای او ارسال کرده بود به عنوان توضیحات راه دور با setRemoteDescription() می کند.
  3. Bob متد createAnswer() RTCPeerConnection را اجرا می‌کند و توضیحات راه دوری را که از آلیس دریافت کرده است به آن می‌فرستد تا یک جلسه محلی ایجاد شود که با جلسه او سازگار باشد.
  4. وعده createAnswer() یک RTCSessionDescription را ارسال می کند، که باب آن را به عنوان توضیحات محلی تنظیم می کند و به Alice می فرستد.
  5. وقتی آلیس توضیحات جلسه باب را دریافت می کند، آن را به عنوان توضیحات راه دور با 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. به chrome://webrtc-internals بروید.

این صفحه آمار WebRTC و داده های اشکال زدایی را ارائه می دهد. (می توانید لیست کامل URL های کروم را در chrome://about پیدا کنید.)

  1. صفحه را با CSS استایل کنید:
  2. ویدیوها را کنار هم قرار دهید.
  3. با متن بزرگتر دکمه ها را هم عرض کنید.
  4. مطمئن شوید که چیدمان روی موبایل کار می کند.
  5. از کنسول Chrome Developer Tools، به localStream ، localPeerConnection ، و remotePeerConnection کنید.
  6. از کنسول، به localPeerConnectionpc1.localDescription نگاه کنید.

فرمت SDP چگونه به نظر می رسد؟

نکات

  • برای اطلاعات بیشتر در مورد adapter.js shim، به مخزن adapter.js GitHub مراجعه کنید.
  • نگاهی به AppRTC و کد آن بیندازید، برنامه متعارف پروژه WebRTC برای تماس‌های WebRTC. زمان تنظیم تماس کمتر از 500 میلی ثانیه است.

بهترین تمرین

برای اثبات کدهای خود در آینده، از APIهای جدید مبتنی بر Promise استفاده کنید و سازگاری با مرورگرهایی را فعال کنید که آنها را با adapter.js پشتیبانی نمی‌کنند.

5. از یک کانال داده برای تبادل داده استفاده کنید

نسخه کامل این مرحله در پوشه step-03 قرار دارد.

HTML خود را به روز کنید

برای این مرحله، از کانال های داده WebRTC برای ارسال متن بین دو عنصر textarea در یک صفحه استفاده می کنید. این خیلی مفید نیست، اما نشان می‌دهد که چگونه می‌توان از WebRTC برای اشتراک‌گذاری داده‌ها و همچنین پخش ویدئو استفاده کرد.

عناصر video و button را از 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>

یک textarea برای وارد کردن متن است، در حالی که دیگری برای نمایش متن به صورت جریانی بین همتایان است.

فایل index.html شما اکنون باید به شکل زیر باشد:

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time 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>

جاوا اسکریپت خود را به روز کنید

  1. main.js را با محتویات step-03/js/main.js جایگزین کنید.
  1. پخش جریانی داده بین همتایان را امتحان کنید:
  2. index.html را باز کنید.
  3. برای تنظیم اتصال همتا بر روی Start کلیک کنید.
  4. متنی را در قسمت textarea سمت چپ وارد کنید.
  5. برای انتقال متن با استفاده از کانال داده WebRTC روی Send کلیک کنید.

چگونه کار می کند

این کد از 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 توجه کنید. کانال های داده را می توان به گونه ای پیکربندی کرد که انواع مختلفی از اشتراک گذاری داده ها را فعال کند، مانند اولویت دادن به تحویل قابل اعتماد بر عملکرد.

امتیاز جایزه بگیرید

  1. با SCTP ، پروتکلی که توسط کانال های داده WebRTC استفاده می شود، تحویل داده های قابل اطمینان و سفارش داده شده به طور پیش فرض روشن است. چه زمانی ممکن است RTCDataChannel نیاز به ارائه تحویل قابل اعتماد داده داشته باشد و چه زمانی ممکن است عملکرد مهم تر باشد، حتی اگر این به معنای از دست دادن برخی از داده ها باشد؟
  2. از CSS برای بهبود طرح‌بندی صفحه استفاده کنید و یک ویژگی نگهدارنده مکان به textarea dataChannelReceive اضافه کنید.
  3. صفحه را روی دستگاه تلفن همراه تست کنید.

اطلاعات بیشتر

6. یک سرویس سیگنالینگ برای تبادل پیام راه اندازی کنید

شما یاد گرفتید که چگونه داده ها را بین همتایان در همان صفحه مبادله کنید، اما چگونه این کار را بین ماشین های مختلف انجام می دهید؟ ابتدا باید یک کانال سیگنالینگ برای تبادل پیام های ابرداده راه اندازی کنید.

نسخه کامل این مرحله در پوشه step-04 قرار دارد.

درباره برنامه

WebRTC از یک API جاوا اسکریپت سمت کلاینت استفاده می کند، اما برای استفاده در دنیای واقعی نیز به سرور سیگنالینگ (پیام رسانی) و همچنین سرورهای STUN و TURN نیاز دارد. در اینجا می توانید اطلاعات بیشتری کسب کنید .

در این مرحله، یک سرور سیگنالینگ Node.js ساده با استفاده از ماژول Socket.IO Node.js و کتابخانه جاوا اسکریپت برای پیام رسانی می سازید.

در این مثال، سرور (برنامه 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 و جاوا اسکریپت

  1. index.html را به‌روزرسانی کنید تا به شکل زیر درآید:
<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

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

</html>

در این مرحله چیزی در صفحه نمی بینید. تمام ثبت نام در کنسول مرورگر انجام می شود. برای مشاهده کنسول در کروم، Control+Shift+J (یا Command+Option+J در Mac) را فشار دهید.

  1. 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>
  1. در سطح بالای فهرست 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 ) می گوید که چه پروژه ای دارد

وابستگی ها برای نصب

  1. برای نصب وابستگی ها، مانند /socket.io/socket.io.js ، موارد زیر را از ترمینال خط فرمان در فهرست work خود اجرا کنید:
npm install

شما باید یک گزارش نصب را ببینید که با چیزی شبیه به این به پایان می رسد:

3ab06b7bcc7664b9.png

همانطور که می بینید، npm وابستگی های تعریف شده در package.json را نصب کرد.

  1. یک فایل جدید 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);
        }
      });
    }
  });

});
  1. از ترمینال خط فرمان، دستور زیر را در پوشه work اجرا کنید:
node index.js
  1. از مرورگر خود به http://localhost:8080 بروید.

هر بار که به این URL می روید، از شما خواسته می شود نام اتاق را وارد کنید.

برای پیوستن به یک اتاق، هر بار نام اتاق یکسانی را وارد کنید، مانند foo .

  1. یک برگه جدید باز کنید و دوباره به http://localhost:8080 بروید و دوباره همان نام اتاق را وارد کنید.
  2. یک برگه جدید دیگر باز کنید، دوباره به http://localhost:8080 بروید و دوباره همان نام اتاق را وارد کنید.
  3. کنسول را در هر یک از تب ها بررسی کنید.

شما باید ورود به سیستم را از جاوا اسکریپت ببینید.

امتیاز جایزه بگیرید

  • چه مکانیسم های جایگزین پیام ممکن است ممکن باشد؟ با استفاده از WebSocket خالص با چه مشکلاتی ممکن است مواجه شوید؟
  • چه مسائلی ممکن است در مقیاس این برنامه دخیل باشد؟ آیا می توانید روشی برای آزمایش هزاران یا میلیون ها درخواست اتاق همزمان ایجاد کنید؟
  • این برنامه از یک درخواست جاوا اسکریپت برای دریافت نام اتاق استفاده می کند. نحوه دریافت نام اتاق را از URL پیدا کنید. برای مثال، http://localhost:8080/foo نام اتاق را foo می‌دهد.

اطلاعات بیشتر

7. اتصال همتا و سیگنالینگ را ترکیب کنید

نسخه کامل این مرحله در پوشه step-05 قرار دارد.

HTML و JavaScript را جایگزین کنید

  1. محتوای index.html را با موارد زیر جایگزین کنید:
<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time 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>
  1. js/main.js را با محتویات step-05/js/main.js جایگزین کنید.

سرور Node.js را اجرا کنید

اگر این کد لبه را از فهرست work خود دنبال نمی کنید، ممکن است لازم باشد وابستگی ها را برای پوشه step-05 یا پوشه کاری فعلی خود نصب کنید.

  1. دستور زیر را از پوشه کاری خود اجرا کنید:
npm install
  1. پس از نصب، اگر سرور Node.js شما در حال اجرا نیست، آن را با اجرای دستور زیر در پوشه work شروع کنید:
node index.js

مطمئن شوید که از نسخه index.js در مرحله قبل که Socket.IO را پیاده سازی می کند، استفاده می کنید. برای اطلاعات بیشتر در مورد Node and Socket IO، بخش راه اندازی سرویس سیگنالینگ برای تبادل پیام را مرور کنید.

  1. از مرورگر خود به http://localhost:8080 بروید.
  2. یک تب جدید باز کنید و دوباره به http://localhost:8080 بروید.

یک عنصر video جریان محلی را از getUserMedia() نمایش می دهد و دیگری ویدیوی راه دور پخش شده از طریق RTCPeerconnection را نشان می دهد.

  1. مشاهده ورود به سیستم در کنسول مرورگر.

امتیاز b**** امتیاز کسب کنید

  • این برنامه فقط از چت تصویری یک به یک پشتیبانی می کند. چگونه می‌توانید طراحی را تغییر دهید تا بیش از یک نفر بتوانند اتاق چت ویدیویی یکسانی را به اشتراک بگذارند؟
  • در مثال نام اتاق foo سخت کدگذاری شده است. بهترین راه برای فعال کردن نام اتاق های دیگر چیست؟
  • کاربران چگونه نام اتاق را به اشتراک می گذارند؟ سعی کنید جایگزینی برای به اشتراک گذاری نام اتاق بسازید.
  • چگونه می توانید برنامه را تغییر دهید؟

نکات

  • آمار WebRTC و داده‌های اشکال‌زدایی را در chrome://webrtc-internals پیدا کنید.
  • از WebRTC Troubleshooter برای بررسی محیط محلی خود و تست دوربین و میکروفون خود استفاده کنید.
  • اگر مشکلات عجیبی در کش دارید، موارد زیر را امتحان کنید:
  1. Control را فشار دهید و روی بارگذاری مجدد این صفحه کلیک کنید.
  2. مرورگر را مجددا راه اندازی کنید.
  3. npm cache clean کنید.

8. یک عکس بگیرید و آن را از طریق یک کانال داده به اشتراک بگذارید

نسخه کامل این مرحله در پوشه step-06 قرار دارد.

چگونه کار می کند

قبلاً، نحوه تبادل پیام های متنی را با استفاده از RTCDataChannel یاد گرفتید. این مرحله امکان اشتراک گذاری کل فایل ها را فراهم می کند. در این مثال، عکس‌ها با getUserMedia() گرفته می‌شوند.

بخش های اصلی این مرحله به شرح زیر است:

  1. یک کانال داده ایجاد کنید.

در این مرحله هیچ جریان رسانه ای را به اتصال همتا اضافه نمی کنید.

  1. با 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. برای دریافت یک عکس فوری (فریم ویدیویی) از جریان ویدئو و نمایش آن در یک عنصر canvas روی Snap کلیک کنید:
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. برای تبدیل تصویر به بایت و ارسال آنها از طریق یک کانال داده، روی Send کلیک کنید:
function sendPhoto() {
  // Split the 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 approximately 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 applicable.
  if (len % CHUNK_LEN) {
    console.log('last ' + len % CHUNK_LEN + ' byte(s)');
    dataChannel.send(img.data.subarray(n * CHUNK_LEN));
  }
}

سمت دریافت کننده بایت های پیام کانال داده را به تصویر تبدیل می کند و تصویر را به کاربر نمایش می دهد:

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');
  // The trail is the element that holds 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);
}

کد را دریافت کنید

  1. محتویات پوشه work خود را با محتویات step-06 جایگزین کنید.

فایل index.html شما اکنون work به این شکل باشد**:**

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time 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>
  1. اگر این کد لبه را از فهرست work خود دنبال نمی کنید، ممکن است لازم باشد وابستگی ها را برای پوشه step-06 یا پوشه کاری فعلی خود نصب کنید. به سادگی دستور زیر را از پوشه کاری خود اجرا کنید:
npm install
  1. پس از نصب، اگر سرور Node.js شما در حال اجرا نیست، آن را با اجرای دستور زیر از فهرست work خود شروع کنید:
node index.js
    Make sure that you're using the version of `index.js` that implements Socket.IO and 

به یاد داشته باشید که در صورت ایجاد تغییرات، سرور Node.js خود را مجددا راه اندازی کنید.

برای اطلاعات بیشتر در مورد Node و Socket.IO، بخش Set up a signaling را مرور کنید

خدمات تبادل پیام

  1. در صورت لزوم، روی Allow کلیک کنید تا به برنامه اجازه دهید از وب کم شما استفاده کند.

برنامه یک شناسه اتاق تصادفی ایجاد می کند و شناسه را به URL اضافه می کند.

  1. URL را از نوار آدرس در برگه یا پنجره مرورگر جدید باز کنید.
  2. روی Snap & Send کلیک کنید، سپس به عکس‌های ورودی در برگه دیگر در پایین صفحه نگاه کنید.

این برنامه عکس ها را بین برگه ها منتقل می کند.

شما باید چیزی شبیه به این را ببینید:

911b40f36ba6ba8.png

امتیاز جایزه بگیرید

چگونه می توانید کد را تغییر دهید تا امکان اشتراک گذاری هر نوع فایلی وجود داشته باشد؟

اطلاعات بیشتر

9. تبریک می گویم

شما یک برنامه برای پخش ویدئو و تبادل داده در زمان واقعی ساخته اید!

بیشتر بدانید