Начнем с установки Meteor, следуя инструкциям, приведенным здесь.
Создайте приложение и добавьте несколько зависимостей
meteor create videochat cd videochat meteor add elmarti:video-chat accounts-ui accounts-password twbs:bootstrap meteor remove autopublish insecure
Поскольку это всего лишь базовая реализация, мы не слишком беспокоимся о пользовательском интерфейсе, поэтому Bootstrap 3 и пользовательский интерфейс учетных записей сделают свое дело. insecure
и autopublish
- это пакеты, которые Meteor использует для упрощения прототипирования, но делает приложение изначально небезопасным, поэтому мы удалили их.
Итак, теперь давайте рассмотрим структуру пользовательского интерфейса, начав с добавления следующего CSS в ваш main.css
файл. Не беспокойтесь о том, что здесь происходит.
body { padding-top: 50px; } video { width: 80%; background: black; } .app-body { padding: 40px 15px; text-align: center; } #login-buttons { padding-top: 17px; } .online { color: green; cursor: pointer; } .offline { color: red; cursor: not-allowed; } @media (max-width: 768px) { #navbar { overflow-y: initial; } #login-buttons{ padding:0px 17px; } }
А теперь давайте создадим приложение. Первый запуск: meteor
Теперь мы можем видеть приложение по адресу localhost: 3000 и наслаждаться горячим кодом. Начните с очистки файла main.html
и добавления элемента заголовка. Это гарантирует, что титровальный элемент установлен и будет правильно отображаться на большинстве устройств.
<head> <title>Video Chat</title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Meteor Video Chat</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li>{{>loginButtons}}</li> </ul> </div> </div> </nav> </body>
Если вы просмотрите localhost: 3000, вы увидите панель навигации, где вызов {{>loginButtons}}
добавлен в изящную раскрывающуюся форму входа в систему. Отлично.
Теперь давайте сделаем приложение более полезным. Под закрывающим тегом навигации добавьте следующее в нижнюю часть тега body:
<div class="container"> <div class="app-body"> {{#if currentUser}} {{> dashboard}} {{else}} <h1>Please log in</h1> {{/if}} </div> </div>
Фреймворк Meteor's Blaze любезно предоставляет помощника {{currentUser}}
, который мы можем использовать, чтобы решить, показывать или нет наши более конфиденциальные данные. {{>dashboard}}
будет искать шаблон под названием «панель управления».
Давайте начнем с удаления client/main
.js, поскольку он нам не понадобится, а затем добавим следующие файлы: dashboard.html
и dashboard.js
. Теперь откройте dashboard.html
и создайте список пользователей:
<template name="dashboard"> <div class="row"> <div class="col-md-6"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">Users</h3> </div> <ul class="list-group"> {{#each getUsers}} <li class="list-group-item user-button {{#if this.status}}online{{else}}offline{{/if}}" role="button"> {{this.email}} </li> {{/each}} </ul> </div> </div> </div> </template>
Это создаст шаблон с панелью, занимающей половину экрана и весь экран на мобильных устройствах. Теперь нам нужно реализовать помощник getUsers
. Откройте dashboard.js
и добавьте следующее:
import { Meteor } from 'meteor/meteor'; import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; import { VideoCallServices } from 'meteor/elmarti:video-chat'; Template.dashboard.helpers({ getUsers() { const users = []; Meteor.users.find({ _id: { $ne: Meteor.userId() } }).forEach(user => users.push({ email: user.emails[0].address, status: user.status.online !== undefined ? user.status.online : undefined, _id: user._id })); return users; } });
Что тут происходит? Мы находим всех пользователей, которые не вошли в систему, и возвращаем их в предпочтительном формате. Учитывая, что мы удалили autopublish
package в начале, нам нужно будет создать публикацию, чтобы сделать данные доступными.
Откройте server/main.js
и добавьте следующее (единственный серверный код, который мы будем писать).
import { Meteor } from 'meteor/meteor'; Meteor.publish(null, function() { if (this.userId) { return Meteor.users.find({}, { fields: { emails: 1, _id: 1, status: 1 } }); } });
Это автоматически опубликует всех пользователей для всех вошедших в систему пользователей, поскольку для простоты мы добавили null вместо указания имени, как обычно.
Откройте браузер в режиме инкогнито, чтобы вы могли провести 2 сеанса - один раз вы войдете в систему, вы должны увидеть зарегистрированного пользователя в любом браузере!
Итак, давайте сделаем это приложение для видеочата.
Давайте начнем с добавления нескольких вещей, которые помогут мы управляем состоянием приложения. Добавьте этот ReactiveVar
в глобальную область видимости (для примера)
const statusText = new ReactiveVar(""); //The text to display to the user
Теперь давайте создадим упрощенный способ доступа к внутреннему состоянию meteor-video-chat
и нашему тексту состояния. Добавьте следующих помощников к нашим dashboard
помощникам.
getState(key) { return VideoCallServices.getState(key); }, getStatusText() { return statusText.get(); }
Теперь, как брат другого элемента div col-md-6
класса, добавьте следующий HTML-код:
<div class="col-md-6"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">Web Cams</h3> </div> <div class="panel-body"> <div class="row"> <p>{{getStatusText}}</p> </div> <div class="row"> {{#if getState "ringing"}} <button class="btn btn-success answer-call"> Answer </button> <button class="btn btn-danger reject-call"> Reject </button> {{/if}} {{#if getState "inProgress"}} <button class="btn btn-danger end-call"> End Call </button> {{/if}} </div> <div class="row"> <video id="localVideo"></video> <div class="row"> Local </div> </div> <div class="row"> <video id="remoteVideo"></video> <div class="row"> Remote </div> </div> </div> </div> </div>
---
Вот где происходит волшебство.
Мы собираемся создать Template.dashboard.onCreated
обратный вызов и обработать все elmarti:video-chat
события, которые нам нужны.
Несколько слов о iceServers
Приведенный ниже пример будет работать с данными, предоставленными функции init, однако вам может быть сложно заставить ваше приложение работать через Интернет без хорошего STUN / TURN. серверы. Многие из них легко доступны через списки, которые легко найти через Google, но я не рекомендую использовать их для серьезных проектов.
Template.dashboard.onCreated(function() { VideoCallServices.init({ 'iceServers': [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun2.l.google.com:19302' }, { urls: 'stun:stun3.l.google.com:19302' }, { urls: 'stun:stun4.l.google.com:19302' } ] }); VideoCallServices.onReceiveCall = (userId) => { const user = Meteor.users.findOne({ _id: userId }); statusText.set(`Recieving call from ${user.emails[0].address}`); }; VideoCallServices.onTargetAccept = () => { statusText.set('Target accepted'); }; VideoCallServices.onTerminateCall = () => { statusText.set('Call terminated'); }; VideoCallServices.onCallRejected = () => { statusText.set('Call rejected'); }; VideoCallServices.setOnError(err => { console.error(err); statusText.set(err); }); });
---
Но как мы называем?
Это последний отрезок! Останься со мной! Нам нужно обрабатывать все события щелчка, прикрепляя их к элементам HTML. По пути я тайно добавляю именованные классы, чтобы мы могли продолжить работу с обратными вызовами.
Начните с добавления блока событий и события вызова:
Template.dashboard.events({ "click .user-button" () { if (this.status) { VideoCallServices.call({ id: this._id, localElement: document.querySelector("#localVideo"), remoteElement: document.querySelector("#remoteVideo"), video: true, audio: true }); statusText.set(`Calling ${this.email}`); } });
Здесь мы проверяем, что пользователь вошел в систему, получаем видеоэлементы и указываем типы мультимедиа. Это вызовет onRecieveCall
callback на стороне целевого пользователя.
Если для состояния звонка установлено значение true, будет показана кнопка ответа, давайте обработаем событие, при котором пользователь нажимает кнопку ответа:
"click .answer-call" () { VideoCallServices.answerCall({ localElement: document.querySelector("#localVideo"), remoteElement: document.querySelector("#remoteVideo"), video: true, audio: true }); statusText.set("Connected"); },
В этом случае мы также можем отклонить звонок
"click .reject-call" () { VideoCallServices.rejectCall(); statusText.set("Call rejected"); },
Наконец, позвольте пользователю завершить звонок
"click .end-call" () { VideoCallServices.endCall(); statusText.set("Call ended"); },
И вот оно, полнофункциональное приложение для видеочата!
Спасибо за проверку, примите участие и предложите улучшения / функции как для RTCFly, так и для видеочата Meteor :)
Исходный код
Пример приложения - на данный момент у вас могут возникнуть проблемы с подключением к пользователям за пределами вашего NAT, поскольку серверы STUN / TURN не настроены. Я работаю над этим и обновлю это, если / когда я что-то получу.