1. مقدمة
WebRTC هو مشروع مفتوح المصدر يتيح إمكانية التواصل في الوقت الفعلي باستخدام الصوت والفيديو والبيانات في تطبيقات الويب والتطبيقات الأصلية.
تتضمّن WebRTC العديد من واجهات برمجة التطبيقات JavaScript، ويمكنك النقر على الروابط للاطّلاع على العروض التوضيحية.
getUserMedia(): لتسجيل الصوت والفيديوMediaRecorder: لتسجيل الصوت والفيديو-
RTCPeerConnection: لبث الصوت والفيديو بين المستخدمين RTCDataChannel: بث البيانات بين المستخدمين
أين يمكنني استخدام WebRTC؟
في Firefox وOpera وفي Chrome على أجهزة الكمبيوتر المكتبي وأجهزة Android تتوفّر WebRTC أيضًا للتطبيقات الأصلية على أجهزة iOS وAndroid.
ما هي الإشارات؟
تستخدم WebRTC واجهة برمجة التطبيقات RTCPeerConnection لتوصيل بيانات البث بين المتصفّحات، ولكنها تحتاج أيضًا إلى آلية لتنسيق الاتصال وإرسال رسائل التحكّم، وهي عملية تُعرف باسم الإشارة. لا تحدّد WebRTC أساليب وبروتوكولات الإشارة. في هذا الدرس التطبيقي، ستستخدم Socket.IO للمراسلة، ولكن تتوفّر العديد من البدائل.
ما هما STUN وTURN؟
تم تصميم WebRTC لتعمل من نظير إلى نظير، ما يتيح للمستخدمين الاتصال بأكثر الطرق مباشرةً. ومع ذلك، تم تصميم WebRTC للتعامل مع الشبكات في العالم الحقيقي: تحتاج تطبيقات العميل إلى اجتياز بوابات NAT وجدران الحماية، وتحتاج شبكات الند للند إلى بدائل في حال تعذّر الاتصال المباشر. في إطار هذه العملية، تستخدم واجهات برمجة تطبيقات WebRTC خوادم STUN للحصول على عنوان IP لجهاز الكمبيوتر، وخوادم TURN لتعمل كخوادم وسيطة في حال تعذّر التواصل بين الأجهزة مباشرةً. (WebRTC في العالم الحقيقي يشرح هذا الموضوع بمزيد من التفصيل).
هل WebRTC آمنة؟
التشفير إلزامي لجميع مكونات WebRTC، ولا يمكن استخدام واجهات برمجة تطبيقات JavaScript إلا من مصادر آمنة (HTTPS أو localhost). لا تحدّد معايير WebRTC آليات الإشارة، لذا يعود إليك التأكّد من استخدام بروتوكولات آمنة.
2. نظرة عامة
أنشئ تطبيقًا للحصول على فيديو والتقاط لقطات باستخدام كاميرا الويب ومشاركتها مع الأجهزة الأخرى من خلال WebRTC. ستتعلّم خلال هذه العملية كيفية استخدام واجهات برمجة التطبيقات الأساسية في WebRTC وإعداد خادم مراسلة باستخدام Node.js.
أهداف الدورة التعليمية
- الحصول على فيديو من كاميرا الويب
- بث فيديو باستخدام RTCPeerConnection
- بثّ البيانات باستخدام RTCDataChannel
- إعداد خدمة إرسال إشارات لتبادل الرسائل
- الجمع بين اتصال الند للند والإشارات
- التقاط صورة ومشاركتها عبر قناة بيانات
المتطلبات
- الإصدار 47 من Chrome أو الإصدارات الأحدث
- Web Server for 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 الإلكتروني".

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

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

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

انقر على الزر اختيار مجلد، ثم اختَر مجلد العمل الذي أنشأته للتو. سيسمح لك ذلك بعرض عملك قيد التقدّم في Chrome من خلال عنوان URL المميّز في مربّع الحوار "خادم الويب" في قسم عناوين URL لخادم الويب.
ضِمن الخيارات، ضَع علامة في المربّع بجانب عرض index.html تلقائيًا كما هو موضّح أدناه:

بعد ذلك، أوقِف الخادم وأعِد تشغيله من خلال تحريك زر التبديل الذي يحمل التصنيف خادم الويب: تم البدء إلى اليسار ثم إلى اليمين مرة أخرى.

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

من الواضح أنّ هذا التطبيق لا يفعل أي شيء مثير للاهتمام حتى الآن، فهو مجرد هيكل بسيط نستخدمه للتأكّد من أنّ خادم الويب يعمل بشكل صحيح. ستضيف ميزات الوظائف والتصميم في الخطوات اللاحقة.
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 في متصفّحك، وسيظهر لك ما يلي (مع عرض الفيديو من كاميرا الويب، بالطبع):

آلية العمل
بعد إجراء مكالمة 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 لبث الفيديو.
- التحكّم في تسجيل الوسائط وبثها
يمكنك العثور على نسخة كاملة من هذه الخطوة في المجلد 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. (في تطبيق حقيقي، سيعرض أحد عناصر الفيديو البث المحلي والآخر البث البعيد).
إضافة أداة محاكاة 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 لإعداد اتصال لبث الفيديو بين عملاء WebRTC، المعروفين باسم الأجهزة المتصلة.
في هذا المثال، يظهر عنصران من RTCPeerConnection على الصفحة نفسها: pc1 وpc2. لا يمكن الاستفادة منه كثيرًا، ولكنّه مفيد لتوضيح طريقة عمل واجهات برمجة التطبيقات.
يتضمّن إعداد مكالمة بين نظراء WebRTC ثلاث مهام:
- أنشئ RTCPeerConnection لكل طرف من المكالمة، وفي كل طرف، أضِف البث المحلي من
getUserMedia(). - الحصول على معلومات الشبكة ومشاركتها: تُعرف نقاط نهاية الاتصال المحتملة باسم مرشّحات ICE.
- الحصول على أوصاف محلية وبعيدة ومشاركتها: بيانات وصفية حول الوسائط المحلية بتنسيق SDP
لنفترض أنّ "أليس" و"بوب" يريدان استخدام RTCPeerConnection لإعداد محادثة فيديو.
في البداية، يتبادل "علي" و"أحمد" معلومات الشبكة. يشير مصطلح "العثور على عناوين مرشّحة" إلى عملية العثور على منافذ وواجهات شبكة باستخدام إطار عمل ICE.
- تنشئ "أليس" عنصر RTCPeerConnection باستخدام معالج
onicecandidate (addEventListener('icecandidate')). يتوافق ذلك مع الرمز التالي من main.js:
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
'iceconnectionstatechange', handleConnectionChange);
- تتصل "أليس" بـ
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.');
- يتم استدعاء المعالج
onicecandidateمن الخطوة 1 عندما تصبح الشبكات المرشّحة متاحة. - ترسل نبيلة بيانات المرشحين المسلسلة إلى يوسف. في تطبيق حقيقي، تتم هذه العملية (المعروفة باسم الإشارة) من خلال خدمة مراسلة، وستتعرّف على كيفية إجراء ذلك في خطوة لاحقة. بالطبع، في هذه الخطوة، يكون العنصران RTCPeerConnection على الصفحة نفسها ويمكنهما التواصل مباشرةً بدون الحاجة إلى مراسلة خارجية.
- عندما يتلقّى "بوب" رسالة مرشّح من "أليس"، يتّصل بـ
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:
- تنفّذ نبيلة الطريقة
createOffer()في RTCPeerConnection. تعرض القيمة التي تم إرجاعها RTCSessionDescription: وصف جلسة Alice المحلية:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
.then(createdOffer).catch(setSessionDescriptionError);
- في حال نجاح العملية، تضبط "أليس" الوصف المحلي باستخدام
setLocalDescription()ثم ترسِل وصف الجلسة هذا إلى "بوب" عبر قناة الإشارة. - يضبط يوسف الوصف الذي أرسلته إليه نبيلة كوصف عن بُعد باستخدام
setRemoteDescription(). - يشغّل "بوب" طريقة RTCPeerConnection
createAnswer()، ويمرّر إليها الوصف البعيد الذي حصل عليه من "أليس"، حتى يمكن إنشاء جلسة محلية متوافقة مع جلستها. تتضمّن عمليّة غير مكتملةcreateAnswer()RTCSessionDescription: يضبط يوسف هذا الوصف كالوصف المحلي ويرسله إلى نبيلة. - عندما تتلقّى "لمى" وصف جلسة "هيثم"، تضبطه كوصف عن بُعد باستخدام
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);
}
- Ping!
نقاط المكافأة
- اطّلِع على chrome://webrtc-internals. توفّر هذه الأداة إحصاءات WebRTC وبيانات تصحيح الأخطاء. (تتوفّر قائمة كاملة بعناوين URL في Chrome على chrome://about).
- تنسيق الصفحة باستخدام CSS:
- ضَع الفيديوهات جنبًا إلى جنب.
- اجعل الأزرار بالعرض نفسه مع نص أكبر.
- تأكَّد من أنّ التنسيق يعمل على الأجهزة الجوّالة.
- من وحدة تحكّم "أدوات مطوّري البرامج في Chrome"، اطّلِع على
localStreamوlocalPeerConnectionوremotePeerConnection. - من وحدة التحكّم، اطّلِع على
localPeerConnectionpc1.localDescription. كيف يبدو تنسيق SDP؟
ما تعلّمته
في هذه الخطوة، تعرّفت على كيفية:
- إخفاء اختلافات المتصفّح باستخدام WebRTC shim، أي adapter.js
- استخدِم واجهة برمجة التطبيقات RTCPeerConnection لبث الفيديو.
- التحكّم في تسجيل الوسائط وبثها
- مشاركة الوسائط ومعلومات الشبكة بين الأجهزة النظيرة لإجراء مكالمة WebRTC
يمكنك العثور على نسخة كاملة من هذه الخطوة في المجلد step-2.
نصائح
- هناك الكثير لتعلُّمه في هذه الخطوة. للعثور على مراجع أخرى تشرح RTCPeerConnection بتفصيل أكبر، يمكنك الاطّلاع على webrtc.org. تتضمّن هذه الصفحة اقتراحات بشأن أُطر عمل JavaScript، إذا كنت تريد استخدام WebRTC ولكن لا تريد التعامل مع واجهات برمجة التطبيقات.
- يمكنك الاطّلاع على مزيد من المعلومات عن adapter.js shim من مستودع adapter.js GitHub.
- هل تريد معرفة شكل أفضل تطبيق لمحادثات الفيديو في العالم؟ يمكنك الاطّلاع على AppRTC، وهو التطبيق الأساسي لمشروع WebRTC لإجراء مكالمات WebRTC: app، code. يستغرق إعداد المكالمة أقل من 500 مللي ثانية.
أفضل ممارسة
- لضمان توافق الرمز مع الإصدارات المستقبلية، استخدِم واجهات برمجة التطبيقات الجديدة المستندة إلى Promise وفعِّل التوافق مع المتصفّحات التي لا تتوافق معها باستخدام adapter.js.
التالي
توضّح هذه الخطوة كيفية استخدام Web Real-Time Communication (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، واضغط على بدء (Start) لإعداد اتصال الجهاز النظير، وأدخِل بعض النص في textarea على اليمين، ثم انقر على إرسال (Send) لنقل النص باستخدام قنوات بيانات WebRTC.
آلية العمل
يستخدم هذا الرمز RTCPeerConnection وRTCDataChannel لتفعيل تبادل الرسائل النصية.
معظم الرموز في هذه الخطوة هي نفسها المستخدَمة في مثال RTCPeerConnection.
تتضمّن الدالتان sendData() وcreateConnection() معظم الرمز الجديد:
function createConnection() {
dataChannelSend.placeholder = '';
var servers = null;
pcConstraint = null;
dataConstraint = null;
trace('Using SCTP based data channels');
// For SCTP, reliable and ordered delivery is true by default.
// Add localConnection to global scope to make it visible
// from the browser console.
window.localConnection = localConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created local peer connection object localConnection');
sendChannel = localConnection.createDataChannel('sendDataChannel',
dataConstraint);
trace('Created send data channel');
localConnection.onicecandidate = iceCallback1;
sendChannel.onopen = onSendChannelStateChange;
sendChannel.onclose = onSendChannelStateChange;
// Add remoteConnection to global scope to make it visible
// from the browser console.
window.remoteConnection = remoteConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created remote peer connection object remoteConnection');
remoteConnection.onicecandidate = iceCallback2;
remoteConnection.ondatachannel = receiveChannelCallback;
localConnection.createOffer().then(
gotDescription1,
onCreateSessionDescriptionError
);
startButton.disabled = true;
closeButton.disabled = false;
}
function sendData() {
var data = dataChannelSend.value;
sendChannel.send(data);
trace('Sent Data: ' + data);
}
تم تصميم بنية RTCDataChannel لتكون مشابهة عن قصد لبنية WebSocket، مع توفّر طريقة send() وحدث message.
لاحظ استخدام dataConstraint. يمكن ضبط قنوات البيانات لتفعيل أنواع مختلفة من مشاركة البيانات، مثل تحديد أولوية التسليم الموثوق به على الأداء. يمكنك الاطّلاع على مزيد من المعلومات حول الخيارات المتاحة في شبكة مطوّري Mozilla.
نقاط المكافأة
- باستخدام SCTP، وهو البروتوكول الذي تستخدمه قنوات بيانات WebRTC، يتم تلقائيًا تفعيل إمكانية تسليم البيانات بشكل موثوق ومنظَّم. متى قد تحتاج قناة RTCDataChannel إلى توفير تسليم موثوق للبيانات، ومتى قد يكون الأداء أكثر أهمية، حتى لو كان ذلك يعني فقدان بعض البيانات؟
- استخدِم CSS لتحسين تخطيط الصفحة، وأضِف سمة العنصر النائب إلى مساحة النص "dataChannelReceive".
- اختبِر الصفحة على جهاز جوّال.
ما تعلّمته
في هذه الخطوة، تعرّفت على كيفية:
- إنشاء اتصال بين جهازَين نظيرَين في WebRTC
- تبادُل بيانات نصية بين الأجهزة المتصلة
يتوفّر إصدار كامل من هذه الخطوة في المجلد step-03.
مزيد من المعلومات
- قنوات بيانات WebRTC (قديمة بعض الشيء، ولكن لا يزال من المفيد قراءتها)
- لماذا تم اختيار بروتوكول SCTP لقناة البيانات في WebRTC؟
التالي
لقد تعلّمت كيفية تبادل البيانات بين الأجهزة النظيرة على الصفحة نفسها، ولكن كيف يمكنك إجراء ذلك بين أجهزة مختلفة؟ عليك أولاً إعداد قناة إرسال إشارات لتبادل رسائل البيانات الوصفية. تعرَّف على كيفية إجراء ذلك في الخطوة التالية.
7. إعداد خدمة إرسال إشارات لتبادل الرسائل
أهداف الدورة التعليمية
في هذه الخطوة، سنتعرّف على كيفية:
- استخدِم
npmلتثبيت تبعيات المشروع كما هو محدّد في package.json - شغِّل خادم Node.js واستخدِم node-static لعرض الملفات الثابتة.
- إعداد خدمة مراسلة على Node.js باستخدام Socket.IO
- يمكنك استخدامها لإنشاء "غرف" وتبادل الرسائل.
يمكنك العثور على نسخة كاملة من هذه الخطوة في المجلد step-04.
المفاهيم
من أجل إعداد مكالمة WebRTC والحفاظ عليها، على عملاء WebRTC (الأجهزة المتصلة) تبادل البيانات الوصفية:
- معلومات المرشح (الشبكة)
- رسائل العرض والردّ التي تقدّم معلومات عن الوسائط، مثل الدقة وبرامج الترميز
بعبارة أخرى، يجب تبادل البيانات الوصفية قبل أن يتمكن المستخدمون من بث الصوت أو الفيديو أو البيانات من جهاز إلى آخر. تُعرف هذه العملية باسم الإشارة.
في الخطوات السابقة، يكون كل من كائنَي RTCPeerConnection الخاصين بالمرسِل والمستلِم على الصفحة نفسها، لذا فإنّ "الإشارة" هي ببساطة مسألة تمرير البيانات الوصفية بين الكائنات.
في تطبيق عملي، يتم تشغيل RTCPeerConnections للمرسل والمستلم في صفحات ويب على أجهزة مختلفة، وتحتاج إلى طريقة لتبادل البيانات الوصفية بينهما.
لإجراء ذلك، يمكنك استخدام خادم إشارات، وهو خادم يمكنه تمرير الرسائل بين عملاء WebRTC (الأجهزة المتصلة). الرسائل الفعلية هي نص عادي: كائنات JavaScript محوَّلة إلى سلسلة.
المتطلّبات الأساسية: تثبيت Node.js
لتنفيذ الخطوات التالية من هذا الدرس التطبيقي حول الترميز (المجلدات من step-04 إلى step-06)، عليك تشغيل خادم على localhost باستخدام Node.js.
يمكنك تنزيل Node.js وتثبيته من هذا الرابط أو من خلال أداة إدارة الحِزم المفضّلة لديك.
بعد التثبيت، ستتمكّن من استيراد الاعتماديات المطلوبة للخطوات التالية (تشغيل npm install)، بالإضافة إلى تشغيل خادم محلي صغير لتنفيذ الدرس التطبيقي حول الترميز (تشغيل node index.js). سيتم توضيح هذه الأوامر لاحقًا عند الحاجة إليها.
لمحة عن التطبيق
تستخدم WebRTC واجهة برمجة تطبيقات JavaScript من جهة العميل، ولكنها تتطلّب أيضًا خادم إشارات (مراسلة) وخادمي STUN وTURN لاستخدامها في العالم الحقيقي. يمكنك الاطّلاع على مزيد من المعلومات هنا.
في هذه الخطوة، ستنشئ خادم إشارات بسيطًا باستخدام Node.js، وذلك باستخدام وحدة Socket.IO Node.js ومكتبة JavaScript للرسائل. ستكون لديك خبرة في Node.js وSocket.IO، ولكنّها ليست ضرورية، لأنّ مكوّنات المراسلة بسيطة جدًا.
في هذا المثال، يتم تنفيذ الخادم (تطبيق Node.js) في index.js، ويتم تنفيذ العميل الذي يعمل عليه (تطبيق الويب) في index.html.
يتضمّن تطبيق Node.js في هذه الخطوة مهمتَين.
أولاً، يعمل كجهاز ترحيل للرسائل:
socket.on('message', function (message) {
log('Got message: ', message);
socket.broadcast.emit('message', message);
});
ثانيًا، يدير هذا التطبيق "غرف" محادثات الفيديو في WebRTC:
if (numClients === 0) {
socket.join(room);
socket.emit('created', room, socket.id);
} else if (numClients === 1) {
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
} else { // max two clients
socket.emit('full', room);
}
سيسمح تطبيق WebRTC البسيط لعدد أقصاه شخصان بمشاركة غرفة.
HTML وJavaScript
عدِّل الملف index.html ليصبح على النحو التالي:
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<script src="/socket.io/socket.io.js"></script>
<script src="js/main.js"></script>
</body>
</html>
لن يظهر لك أي شيء على الصفحة في هذه الخطوة، إذ يتم تسجيل جميع البيانات في وحدة تحكّم المتصفّح. (لعرض وحدة التحكّم في Chrome، اضغط على Ctrl-Shift-J أو Command-Option-J إذا كنت تستخدم جهاز Mac).
استبدِل js/main.js بما يلي:
'use strict';
var isInitiator;
window.room = prompt("Enter room name:");
var socket = io.connect();
if (room !== "") {
console.log('Message from client: Asking to join room ' + room);
socket.emit('create or join', room);
}
socket.on('created', function(room, clientId) {
isInitiator = true;
});
socket.on('full', function(room) {
console.log('Message from client: Room ' + room + ' is full :^(');
});
socket.on('ipaddr', function(ipaddr) {
console.log('Message from client: Server IP address is ' + ipaddr);
});
socket.on('joined', function(room, clientId) {
isInitiator = false;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
إعداد Socket.IO للتشغيل على Node.js
في ملف HTML، ربما لاحظت أنّك تستخدم ملف Socket.IO:
<script src="/socket.io/socket.io.js"></script>
في المستوى الأعلى من دليل work، أنشئ ملفًا باسم package.json يتضمّن المحتوى التالي:
{
"name": "webrtc-codelab",
"version": "0.0.1",
"description": "WebRTC codelab",
"dependencies": {
"node-static": "^0.7.10",
"socket.io": "^1.2.0"
}
}
هذا هو بيان تطبيق يخبر مدير حزم Node (npm) بتبعيات المشروع التي يجب تثبيتها.
لتثبيت التبعيات (مثل /socket.io/socket.io.js)، شغِّل ما يلي من وحدة طرفية لسطر الأوامر، في دليل العمل:
npm install
من المفترض أن يظهر لك سجلّ تثبيت ينتهي على النحو التالي:

كما ترى، ثبّت 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 أعلاه.
نقاط المكافأة
- ما هي آليات المراسلة البديلة التي قد تكون ممكنة؟ ما هي المشاكل التي قد تواجهها عند استخدام WebSocket "الخالص"؟
- ما هي المشاكل التي قد تنشأ عند توسيع نطاق هذا التطبيق؟ هل يمكنك تطوير طريقة لاختبار آلاف أو ملايين طلبات الغرف المتزامنة؟
- يستخدم هذا التطبيق طلب JavaScript للحصول على اسم غرفة. ابحث عن طريقة للحصول على اسم الغرفة من عنوان URL. على سبيل المثال، سيؤدي localhost:8080/foo إلى ظهور اسم الغرفة
foo.
ما تعلّمته
في هذه الخطوة، تعرّفت على كيفية:
- استخدِم npm لتثبيت تبعيات المشروع كما هو محدّد في package.json.
- تشغيل خادم Node.js لعرض الملفات الثابتة
- إعداد خدمة مراسلة على Node.js باستخدام socket.io
- يمكنك استخدامها لإنشاء "غرف" وتبادل الرسائل.
يمكنك العثور على نسخة كاملة من هذه الخطوة في المجلد step-04.
مزيد من المعلومات
- مستودع chat-example على Socket.io
- WebRTC في العالم الحقيقي: STUN وTURN والإشارات
- مصطلح "الإشارة" في WebRTC
التالي
تعرَّف على كيفية استخدام الإشارات لتمكين مستخدمَين من إنشاء اتصال بين نظيرين.
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
إذا كنت لا تتبع هذا الدرس التطبيقي حول الترميز من دليل العمل، قد تحتاج إلى تثبيت الاعتماديات للمجلد step-05 أو مجلد العمل الحالي. نفِّذ الأمر التالي من دليل العمل:
npm install
بعد التثبيت، إذا لم يكن خادم Node.js قيد التشغيل، ابدأ تشغيله من خلال استدعاء الأمر التالي في دليل العمل:
node index.js
تأكَّد من استخدام إصدار index.js من الخطوة السابقة الذي ينفّذ Socket.IO. لمزيد من المعلومات عن Node وSocket IO، راجِع القسم "إعداد خدمة إرسال إشارات لتبادل الرسائل".
من المتصفّح، افتح localhost:8080.
افتح localhost:8080 مرة أخرى في علامة تبويب أو نافذة جديدة. سيعرض أحد عناصر الفيديو البث المحلي من getUserMedia()، بينما سيعرض العنصر الآخر الفيديو "عن بُعد" الذي يتم بثه عبر RTCPeerconnection.
عرض التسجيل في وحدة تحكّم المتصفّح
نقاط المكافأة
- يتيح هذا التطبيق محادثات فيديو بين شخصين فقط. كيف يمكن تغيير التصميم للسماح لأكثر من شخص بمشاركة غرفة محادثة الفيديو نفسها؟
- يحتوي المثال على اسم الغرفة foo مبرمَجًا بشكل ثابت. ما هي أفضل طريقة لتفعيل أسماء الغرف الأخرى؟
- كيف يمكن للمستخدمين مشاركة اسم الغرفة؟ حاوِل إنشاء بديل لمشاركة أسماء الغرف.
- كيف يمكن تغيير التطبيق؟
ما تعلّمته
في هذه الخطوة، تعرّفت على كيفية:
- تشغيل خدمة إرسال إشارات WebRTC باستخدام Socket.IO على Node.js
- استخدِم هذه الخدمة لتبادل بيانات تعريف WebRTC بين الأجهزة المتصلة.
تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-05.
نصائح
- تتوفّر إحصاءات WebRTC وبيانات تصحيح الأخطاء من chrome://webrtc-internals.
- يمكن استخدام test.webrtc.org للتحقّق من بيئتك المحلية واختبار الكاميرا والميكروفون.
- إذا واجهت مشاكل غريبة في التخزين المؤقت، جرِّب ما يلي:
- إجراء إعادة تحميل إجبارية من خلال الضغط مع الاستمرار على Ctrl والنقر على زر إعادة التحميل
- أعِد تشغيل المتصفّح
- نفِّذ الأمر
npm cache cleanمن سطر الأوامر.
التالي
تعرَّف على كيفية التقاط صورة والحصول على بيانات الصورة ومشاركتها بين الأجهزة البعيدة.
9- التقاط صورة ومشاركتها عبر قناة بيانات
أهداف الدورة التعليمية
في هذه الخطوة، سنتعرّف على كيفية:
- التقاط صورة والحصول على البيانات منها باستخدام عنصر اللوحة
- تبادل بيانات الصور مع مستخدم بعيد
تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-06.
آلية العمل
لقد تعلّمت سابقًا كيفية تبادل الرسائل النصية باستخدام RTCDataChannel.
تتيح لك هذه الخطوة مشاركة الملفات بأكملها، مثل الصور التي تم التقاطها باستخدام getUserMedia() في هذا المثال.
في ما يلي الأجزاء الأساسية لهذه الخطوة:
- إنشاء قناة بيانات يُرجى العِلم أنّه لن تتم إضافة أي وسائط إلى اتصال الند للند في هذه الخطوة.
- التقط فيديو من كاميرا الويب باستخدام
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);
});
}
- عندما ينقر المستخدم على الزر لقطة، احصل على لقطة (إطار فيديو) من بث الفيديو واعرضها في عنصر
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);
}
- عندما ينقر المستخدم على الزر إرسال، حوِّل الصورة إلى وحدات بايت وأرسِلها عبر قناة بيانات:
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));
}
}
- يحوّل الجهاز المستلِم وحدات بايت رسالة قناة البيانات إلى صورة ويعرضها للمستخدم:
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);
}
الحصول على الشفرة
استبدِل محتويات مجلد العمل بمحتويات الخطوة 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 & 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>
إذا كنت لا تتبع هذا الدرس التطبيقي حول الترميز من دليل العمل، قد تحتاج إلى تثبيت الاعتماديات للمجلد step-06 أو مجلد العمل الحالي. ما عليك سوى تنفيذ الأمر التالي من دليل العمل:
npm install
بعد التثبيت، إذا لم يكن خادم Node.js قيد التشغيل، ابدأه عن طريق استدعاء الأمر التالي من دليل العمل:
node index.js
تأكَّد من أنّك تستخدم إصدار index.js الذي ينفّذ Socket.IO، وتذكَّر إعادة تشغيل خادم Node.js إذا أجريت تغييرات. لمزيد من المعلومات عن Node وSocket IO، راجِع القسم "إعداد خدمة إرسال إشارات لتبادل الرسائل".
إذا لزم الأمر، انقر على الزر السماح للسماح للتطبيق باستخدام كاميرا الويب.
سينشئ التطبيق معرّف غرفة عشوائيًا ويضيفه إلى عنوان URL. افتح عنوان URL من شريط العناوين في علامة تبويب أو نافذة متصفّح جديدة.
انقر على الزر التقاط وإرسال، ثم اطّلِع على قسم "الوارد" في علامة التبويب الأخرى في أسفل الصفحة. ينقل التطبيق الصور بين علامات التبويب.
ينبغي أن تظهر لك على النحو التالي:

نقاط المكافأة
- كيف يمكنك تغيير الرمز البرمجي لإتاحة مشاركة أي نوع من الملفات؟
مزيد من المعلومات
- واجهة برمجة التطبيقات MediaStream Image Capture API: هي واجهة برمجة تطبيقات لالتقاط الصور والتحكّم في الكاميرات، وستتوفّر قريبًا في متصفحك.
- واجهة برمجة التطبيقات MediaRecorder API لتسجيل الصوت والفيديو: عرض توضيحي، مستندات
ما تعلّمته
- كيفية التقاط صورة والحصول على البيانات منها باستخدام عنصر لوحة الرسم
- كيفية تبادل هذه البيانات مع مستخدم بعيد
تتوفّر نسخة كاملة من هذه الخطوة في المجلد step-06.
10. تهانينا
لقد أنشأت تطبيقًا لبث الفيديو وتبادل البيانات في الوقت الفعلي.
ما تعلّمته
في هذا الدرس العملي، تعلّمت كيفية:
- الحصول على فيديو من كاميرا الويب
- بث الفيديو باستخدام RTCPeerConnection
- بث البيانات باستخدام RTCDataChannel
- إعداد خدمة إرسال إشارات لتبادل الرسائل
- الجمع بين اتصال الأقران والإشارات
- التقاط صورة ومشاركتها عبر قناة بيانات
الخطوات التالية
- يمكنك الاطّلاع على الرمز البرمجي والبنية لتطبيق الدردشة الأساسي AppRTC المستند إلى WebRTC: app، code.
- جرِّب العروض التوضيحية المباشرة من github.com/webrtc/samples.
مزيد من المعلومات
- تتوفّر مجموعة من المراجع لبدء استخدام WebRTC على webrtc.org.