الاتصال في الوقت الفعلي باستخدام WebRTC

1. مقدمة

WebRTC هو مشروع مفتوح المصدر يتيح الاتصال في الوقت الفعلي بالصوت والفيديو والبيانات في الويب والتطبيقات الأصلية.

تحتوي WebRTC على العديد من واجهات برمجة تطبيقات JavaScript. يمكنك النقر على الروابط للاطّلاع على عروض توضيحية.

أين يمكنني استخدام WebRTC؟

في Firefox وOpera وفي Chrome على أجهزة الكمبيوتر المكتبي وAndroid. تتوفر WebRTC أيضًا للتطبيقات الأصلية على نظامي التشغيل iOS وAndroid.

ما المقصود بالإشارة؟

تستخدم WebRTC بروتوكول RTCPeerConnection لتوصيل بيانات البث بين المتصفِّحات، ولكنها تحتاج أيضًا إلى آلية لتنسيق الاتصال وإرسال رسائل التحكّم، وهي عملية تُعرف بالإشارة. لا يحدِّد WebRTC طرق وبروتوكولات الإشارة. في هذا الدرس التطبيقي حول الترميز، ستستخدم Socket.IO للمراسلة، ولكن هناك العديد من البدائل.

ما هما STUN و0؟

تم تصميم WebRTC للعمل على شبكة الند للند، بحيث يمكن للمستخدمين الاتصال باستخدام أكثر مسار مباشر ممكن. ومع ذلك، تم تصميم WebRTC للتعامل مع الشبكات الفعلية: تحتاج تطبيقات العميل إلى اجتياز بوابات NAT وجدران الحماية، وتحتاج إلى شبكات نظير إلى نظير إلى إجراءات احتياطية في حال تعذُّر الاتصال المباشر. كجزء من هذه العملية، تستخدم واجهات برمجة تطبيقات WebRTC خوادم STUN للحصول على عنوان IP الخاص بجهاز الكمبيوتر الخاص بك، وخوادم turn لتعمل كخوادم إرسال في حال تعذُّر الاتصال من نظير إلى نظير. (يوضّح WebRTC في العالم الفعلي بمزيد من التفاصيل).

هل WebRTC آمن؟

يكون التشفير إلزاميًا لجميع مكوّنات WebRTC، ولا يمكن استخدام واجهات برمجة تطبيقات JavaScript الخاصة به إلا من مصادر آمنة (HTTPS أو مضيف محلي). لا يتم تحديد آليات الإشارة وفقًا لمعايير WebRTC، لذا عليك التأكّد من استخدام بروتوكولات آمنة.

2. نظرة عامة

يمكنك إنشاء تطبيق للحصول على فيديو وأخذ لقطات باستخدام كاميرا الويب الخاصة بك ومشاركتها عبر شبكة WebRTC. بالإضافة إلى ذلك، ستتعلّم كيفية استخدام واجهات برمجة تطبيقات WebRTC الأساسية وإعداد خادم مراسلة باستخدام Node.js.

ما ستتعرَّف عليه

  • الحصول على فيديو من كاميرا الويب
  • بث الفيديو باستخدام RTCPeerConnection
  • بث البيانات باستخدام RTCDataChannel
  • إعداد خدمة إشارات لتبادل الرسائل
  • الجمع بين اتصال الزملاء وإرسال الإشارات
  • التقاط صورة ومشاركتها عبر قناة بيانات

المتطلبات

  • الإصدار 47 من Chrome أو الإصدارات الأحدث
  • خادم الويب للمتصفح Chrome، أو استخدام خادم الويب الخاص بك الذي تختاره
  • نموذج التعليمات البرمجية
  • محرر النصوص
  • معرفة أساسية بـ HTML وCSS وJavaScript

3- الحصول على الرمز النموذجي

تنزيل الرمز

إذا كنت على دراية بمنصة git، يمكنك تنزيل الرمز الخاص بهذا الدرس التطبيقي من GitHub من خلال استنساخه:

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

وكإجراء بديل، انقر فوق الزر التالي لتنزيل ملف zip .الخاص بالرمز:

افتح ملف ZIP الذي تم تنزيله. سيؤدي ذلك إلى فك ضغط مجلد المشروع (Adaptive-web-media) الذي يحتوي على مجلد واحد لكل خطوة من هذا الدرس التطبيقي، بالإضافة إلى جميع الموارد التي ستحتاج إليها.

ستُنفّذ جميع أعمال الترميز في الدليل الذي يحمل اسم work.

تحتوي مجلدات step-nn على نسخة نهائية لكل خطوة من هذا الدرس التطبيقي. إنها موجودة كمرجع.

تثبيت خادم الويب وإثبات ملكيته

على الرغم من أنّه يمكنك استخدام خادم الويب الخاص بك بحرية، صُمِّم هذا الدرس التطبيقي ليعمل بشكل جيد مع خادم الويب Chrome. إذا لم يكن هذا التطبيق مثبّتًا، يمكنك تثبيته من "سوق Chrome الإلكتروني".

6ddeb4aee53c0f0e.png

بعد تثبيت تطبيق خادم الويب لمتصفح Chrome، انقر على اختصار تطبيقات Chrome من شريط الإشارات أو صفحة "علامة تبويب جديدة" أو من "مشغّل التطبيقات":

1d2b4aa977ab7e24.png

انقر فوق رمز خادم الويب:

27fce4494f641883.png

بعد ذلك، سيظهر لك مربع الحوار هذا، الذي يتيح لك ضبط خادم الويب المحلي:

لقطة شاشة يوم 18-02-2016 في الساعة 11.48.14 صباحًا.png

انقر على زر اختيار مجلد، واختَر مجلد العمل الذي أنشأته للتو. سيتيح لك هذا عرض عملك قيد التقدم في Chrome عبر عنوان URL المظلل في مربع حوار خادم الويب في القسم عناوين URL لخادم الويب.

ضمن الخيارات، ضَع علامة في المربّع بجانب عرض index.html تلقائيًا كما هو موضّح أدناه:

لقطة شاشة يوم 18-02-2016 في الساعة 11.56.30 صباحًا.png

ثم أوقف الخادم وأعد تشغيله من خلال تمرير مفتاح التبديل المسمى خادم الويب: بدء إلى اليسار ثم الرجوع إلى اليمين.

لقطة شاشة يوم 18-02-2016 في الساعة 12.22.18 مساءً.png

انتقل الآن إلى موقع عملك في متصفح الويب بالنقر على عنوان URL لخادم الويب المميز. من المفترض أن تظهر لك صفحة بالشكل التالي، تتوافق مع work/index.html:

18a705cb6ccc5181.png

من الواضح أن هذا التطبيق لم ينفّذ أي إجراء مثير للاهتمام حتى الآن - وما هو حتى الآن سوى هيكل بسيط نستخدمه للتأكد من عمل خادم الويب بشكل صحيح. ستضيف الوظائف وميزات التنسيق في الخطوات اللاحقة.

4. بث الفيديو من كاميرا الويب

ما ستتعرَّف عليه

في هذه الخطوة، ستتعرف على كيفية:

  • الحصول على فيديو مضمَّن من كاميرا الويب
  • التلاعب بتشغيل البث.
  • استخدام CSS وSVG لمعالجة الفيديو

تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-01.

شرطة من HTML...

أضِف عنصر video وعنصر 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 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() هو في نطاق عام، لذا يمكنك فحصه من وحدة تحكّم المتصفّح: افتح وحدة التحكّم، واكتب ساحة المشاركات ثم اضغط على "إرجاع". (لعرض وحدة التحكم في 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، adapter.js.
  • استخدِم واجهة برمجة التطبيقات RTCPeerConnection API لبث الفيديو المباشر.
  • التحكّم في التقاط الوسائط وبثها

تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-2.

ما هو RTCPeerConnection؟

RTCPeerConnection هي واجهة برمجة تطبيقات لإجراء طلبات 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. (في تطبيق فعلي، يعرض أحد عناصر الفيديو البث المحلي والآخر البث البعيد.)

أضِف ملف البيانات المترابطة filter.js shim.

أضِف رابطًا إلى الإصدار الحالي من 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. ليس هناك استخدام عملي كبير، ولكنه جيد لتوضيح كيفية عمل واجهات برمجة التطبيقات.

يتضمن إعداد مكالمة بين نظراء WebRTC ثلاث مهام:

  • أنشِئ اتصال RTCPeerConnection لكل نهاية مكالمة وأضِف البث المحلي من "getUserMedia()" في كل نهاية.
  • الحصول على معلومات الشبكة ومشاركتها: تُعرف نقاط نهاية الاتصال المحتملة باسم ICE.
  • يمكنك الحصول على الأوصاف المحلية وتلك عن بُعد ومشاركتها: البيانات الوصفية عن الوسائط المحلية بتنسيق SDP.

تخيل أن نبيلة ويوسف يريدون استخدام RTCPeerConnection لإعداد محادثة فيديو.

أولاً، يتبادل أليس ويوسف معلومات الشبكة. عبارة "العثور على مرشحين" يشير إلى عملية العثور على واجهات ومنافذ الشبكات باستخدام إطار العمل 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 من الخطوة 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 أيضًا الاطّلاع على معلومات محلية وبعيدة عن ملفات وسائط الصوت والفيديو وتبادلها، مثل إمكانيات برنامج الترميز ودرجة الدقة. تستمر الإشارة لتبادل معلومات إعداد الوسائط عن طريق تبادل ملفات ثنائية كبيرة من البيانات الوصفية، المعروفة باسم offer وanswer، باستخدام تنسيق بروتوكول وصف الجلسة، والمعروف باسم SDP:

  1. تشغّل نبيلة طريقة RTCPeerConnection createOffer(). يوفر الوعد الوارد وصف 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، adapter.js.
  • استخدِم واجهة برمجة التطبيقات RTCPeerConnection API لبث الفيديو المباشر.
  • التحكّم في التقاط الوسائط وبثها
  • مشاركة معلومات الوسائط والشبكة بين التطبيقات المشابهة لتفعيل اتصال WebRTC

تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-2.

نصائح

  • هناك الكثير لتتعلمه في هذه الخطوة! للعثور على موارد أخرى تشرح RTCPeerConnection بمزيد من التفاصيل، يمكنك الاطّلاع على webrtc.org. تتضمن هذه الصفحة اقتراحات لأُطر عمل JavaScript - إذا أردت استخدام WebRTC، ولكنك لا تريد تنسيق واجهات برمجة التطبيقات.
  • يمكنك الاطّلاع على مزيد من المعلومات عن ملف البيانات asset.js shim من مستودع adapter.js GitHub.
  • هل تريد معرفة الشكل الذي يبدو عليه أفضل تطبيق لمحادثة الفيديو في العالم؟ ألقِ نظرة على AppRTC، وهو التطبيق الأساسي لمشروع WebRTC لطلبات WebRTC: التطبيق، الرمز. وقت إعداد المكالمة أقل من 500 ملي ثانية.

أفضل ممارسة

  • لحماية الرمز البرمجي للمستقبل، يمكنك استخدام واجهات برمجة التطبيقات الجديدة المستنِدة إلى Promise وتفعيل التوافق مع المتصفّحات غير المتوافقة باستخدام adapter.js.

التالي

توضّح هذه الخطوة كيفية استخدام WebRTC لبث الفيديو بين التطبيقات المشابهة، إلا أنّ هذا الدرس التطبيقي يدور حول البيانات أيضًا.

تعرَّف في الخطوة التالية على كيفية بث البيانات العشوائية باستخدام 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، باستخدام طريقة send() وحدث message.

لاحِظ استخدام "dataConstraint". يمكن ضبط قنوات البيانات لتفعيل أنواع مختلفة من مشاركة البيانات، على سبيل المثال، إعطاء الأولوية للعرض الموثوق على الأداء. يمكنك التعرّف على مزيد من المعلومات حول الخيارات في شبكة مطوّري برامج موزيلا.

نقاط المكافأة

  1. في حال استخدام SCTP، يتم تلقائيًا تفعيل البروتوكول الذي تستخدمه قنوات بيانات WebRTC وتسليم البيانات الموثوق بها والمطلوبة. متى قد تحتاج RTCDataChannel إلى توفير تسليم موثوق للبيانات، ومتى يكون الأداء أكثر أهمية، حتى إذا يعني ذلك فقدان بعض البيانات؟
  2. استخدام CSS لتحسين تنسيق الصفحة، وإضافة سمة عنصر نائب إلى "dataChannelremove" منطقة النص.
  3. اختبِر الصفحة على جهاز جوّال.

ما تعلمته

تعلمت في هذه الخطوة كيفية:

  • إنشاء اتصال بين اثنين من أقران WebRTC.
  • يمكنك تبادل البيانات النصية بين النظراء.

تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-03.

التعرف على المزيد

التالي

لقد تعلمت كيفية تبادل البيانات بين الزملاء في نفس الصفحة، ولكن كيف تفعل ذلك بين الأجهزة المختلفة؟ أولاً، يجب إعداد قناة إشارات لتبادل رسائل البيانات الوصفية. تعرَّف على كيفية إجراء ذلك في الخطوة التالية.

7. إعداد خدمة إشارات لتبادل الرسائل

ما ستتعرَّف عليه

في هذه الخطوة، ستتعرف على كيفية:

  • استخدام npm لتثبيت تبعيات المشروع على النحو المحدّد في package.json
  • يمكنك تشغيل خادم Node.js واستخدام العقدة الثابتة لعرض الملفات الثابتة.
  • عليك إعداد خدمة مراسلة على Node.js باستخدام Socket.IO.
  • استخدِم ذلك لإنشاء "غرف" وتبادل الرسائل.

تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-04.

المفاهيم

لإعداد مكالمة WebRTC والحفاظ عليها، يحتاج عملاء WebRTC (التطبيقات المشابهة) إلى تبادل البيانات الوصفية، وذلك على النحو التالي:

  • معلومات المرشح (الشبكة)
  • رسائل العرض والإجابات التي تقدّم معلومات حول الوسائط، مثل برامج الدقة وبرامج الترميز

بعبارة أخرى، يجب تبادل البيانات الوصفية قبل بث الصوت أو الفيديو أو البيانات من نظير إلى نظير. تُعرف هذه العملية باسم الإشارة.

في الخطوات السابقة، كانت كائنات RTCPeerConnection للمرسل والمستلم على نفس الصفحة، وبالتالي فإن "الإشارة" هي مجرد مسألة تمرير بيانات التعريف بين الكائنات.

في أي تطبيق فعلي، يعمل المرسِل والمستلم RTCPeerConnections في صفحات الويب على أجهزة مختلفة، وتحتاج إلى طريقة لتوصيل البيانات الوصفية.

ولإجراء ذلك، يمكنك استخدام خادم إشارات: خادم يمكنه تمرير الرسائل بين عملاء WebRTC (الزملاء). وتكون الرسائل عبارة عن نص عادي: كائنات JavaScript التي تتكوّن من سلسلة.

المتطلبات الأساسية: تثبيت Node.js

لتنفيذ الخطوات التالية من هذا الدرس التطبيقي حول الترميز (المجلدات step-04 إلى step-06)، ستحتاج إلى تشغيل خادم على المضيف المحلي باستخدام Node.js.

يمكنك تنزيل Node.js وتثبيتها من هذا الرابط أو من خلال مدير الحزم المفضّل لديك.

بعد التثبيت، ستتمكّن من استيراد الاعتماديات المطلوبة للخطوات التالية (تشغيل npm install)، فضلاً عن تشغيل خادم مضيف محلي صغير لتنفيذ الدرس التطبيقي حول الترميز (تشغيل node index.js). ستتم الإشارة إلى هذه الأوامر لاحقًا، عندما تكون مطلوبة.

لمحة عن التطبيق

تستخدم WebRTC واجهة برمجة تطبيقات JavaScript من جهة العميل، ولكن للاستخدام الفعلي، فإنّها تتطلّب أيضًا خادم إشارات (مراسلة)، بالإضافة إلى خادمَي STUN و0. يمكنك الاطّلاع على المزيد من المعلومات هنا.

في هذه الخطوة، ستُنشئ خادم إشارة بسيطًا 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. ما آليات المراسلة البديلة الممكنة؟ ما المشكلات التي قد تواجهها عند استخدام كلمة "pure" أو 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

إذا لم تكن تتابع هذا الدرس التطبيقي حول الترميز من دليل work، قد تحتاج إلى تثبيت الاعتماديات لمجلد step-05 أو مجلد العمل الحالي. شغِّل الأمر التالي من دليل العمل:

npm install

بعد تثبيت الخادم، إذا لم يكن خادم Node.js قيد التشغيل، ابدأ باستخدام الأمر التالي في دليل work:

node index.js

تأكد من استخدام إصدار index.js من الخطوة السابقة التي تستخدم Socket.IO. للحصول على مزيد من المعلومات عن إدخال الإدخال في العقدة والمقبس، راجِع القسم "إعداد خدمة إشارات لتبادل الرسائل".

من المتصفّح، افتح localhost:8080.

افتح localhost:8080 مرة أخرى في علامة تبويب أو نافذة جديدة. سيعرض أحد عناصر الفيديو البث المحلي من getUserMedia()، بينما سيعرض العنصر الآخر البث "عن بُعد". فيديو يتم بثه عبر اتصال RTCPeer.

عرض تسجيل الدخول في وحدة تحكّم المتصفّح

نقاط إضافية

  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);
}

الحصول على الرمز‏

استبدِل محتوى مجلد العمل بمحتوى 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>

إذا لم تكن تتابع هذا الدرس التطبيقي حول الترميز من دليل work، قد تحتاج إلى تثبيت الاعتماديات لمجلد step-06 أو مجلد العمل الحالي. ما عليك سوى تشغيل الأمر التالي من دليل العمل:

npm install

بعد التثبيت، إذا لم يكن خادم Node.js قيد التشغيل، ابدأ باستخدام الأمر التالي من دليل work:

node index.js

تأكد من استخدام إصدار index.js الذي ينفِّذ Socket.IO، وتذكر إعادة تشغيل خادم Node.js إذا أجريت أي تغييرات. للحصول على مزيد من المعلومات عن إدخال الإدخال في العقدة والمقبس، راجِع القسم "إعداد خدمة إشارات لتبادل الرسائل".

إذا لزم الأمر، انقر على الزر السماح للسماح للتطبيق باستخدام كاميرا الويب.

سينشئ التطبيق رقم تعريف غرفة عشوائيًا ويضيف هذا المعرّف إلى عنوان URL. افتح عنوان URL من شريط العناوين في علامة تبويب أو نافذة متصفِّح جديدة.

انقر على زر محاذاة "إرسال" ثم انظر إلى منطقة "الوارد" في علامة التبويب الأخرى أسفل الصفحة. ينقل التطبيق الصور بين علامات التبويب.

ينبغي أن تظهر لك على النحو التالي:

911b40f36ba6ba8.png

نقاط المكافأة

  1. كيف يمكنك تغيير التعليمات البرمجية لإتاحة مشاركة أي نوع من الملفات؟

التعرف على المزيد

ما تعلمته

  • كيفية التقاط صورة والحصول على البيانات منها باستخدام عنصر لوحة الرسم.
  • طريقة تبادل هذه البيانات مع مستخدم بعيد

تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-06.

10. تهانينا

لقد أنشأت تطبيقًا للقيام ببث الفيديو في الوقت الفعلي وتبادل البيانات!

ما تعلمته

في هذا الدرس التطبيقي حول الترميز، تعلمتَ كيفية:

  • الحصول على فيديو من كاميرا الويب.
  • بث الفيديو باستخدام RTCPeerConnection
  • يمكنك بث البيانات باستخدام RTCDataChannel.
  • إعداد خدمة إشارة لتبادل الرسائل.
  • الجمع بين اتصال الأقران والإشارة.
  • التقط صورة وشاركها عبر قناة البيانات.

الخطوات التالية

مزيد من المعلومات

  • تتوفّر مجموعة من الموارد لبدء استخدام WebRTC من webrtc.org.