WebRTC / Jitsi / 架构

主要模块

模块名称 开发语言 功能
Jitsi Video-Bridge Java

Jitsi Videobridge is an XMPP server component designed to run thousands of video streams from a single server — and it’s fully open source and WebRTC compatible.

 

Jitsi Video-Bridge 是一个XMPP服务器组件,用来在单一的服务器下运行数千个视频流,并且该组件是完全开源的并兼容WebRTC。

 

Jitsi-Videobridge 用于处理视频传输,也就是视频流在各参与者之间的转发。如果没有这个组件,各参与者能文字聊天,但无法互相看见。

Jitsi Meet JavaScript

Jitsi Meet is an Open Source WebRTC JavaScript application that uses Jitsi Videobridge to provide high quality, scalable video conferences.

 

Jitsi Meet  是一个开源的 WebRTC JavaScript 应用,通过Jitsi Video-Bridge从而可以提供高质量、可伸缩的视频会议。

Jibri  

Jibri provides services for recording or streaming a Jitsi Meet conference.

 

全称:Jitsi BRoadcasting Infrastructure ,提供一个记录和流式传输一个 Jitsi Meet 会议的服务。

 

Jitsi Jicofo Java

JItsi COnference FOcus is a server side focus component used in Jitsi Meet conferences.

 

全称:Jitsi Conference Focus ,是Jitsi Meet中使用的服务器端焦点组件。

Jigasi  

Jitsi Gateway to SIP : a server-side application that links allows regular SIP clients to join Jitsi Meet conferences hosted by Jitsi Videobridge.

 

全称:Jitsi Gateway to SIP ,一个链接的服务器端应用程序,允许常规 SIP 客户端加入 Jitsi Videobridge 主持的 Jitsi Meet 会议。

Prosody Lua

XMPP Server。


XMPP 全称是 Extensible Messaging and Presence Protocol,即可扩展通信和表示协议。它是一种即时通信协议,主要是实现文字聊天。

XMPP 的前身是 Jabber,一个开源的即时通信协议。Jabber被IETF标准化为 XMPP。

 

Google Talk用的就是它。

Nginx C Web Server

支持的协议

  • SIP
  • XMPP

开发API

  • Jitsi Meet API:JavaScript 接口 SDK,将 Jitsi Meet 模块嵌入到自己的应用程序中,用于 web 客户端的开发。
  • Libjitsi先进的 Java 媒体库。用于安全地实时地音视频交流,用于服务器端开发。

 

参考:https://blog.csdn.net/xiaoluer/article/details/79088416

 

(SAW:Game Over!)

发布了278 篇原创文章 · 获赞 119 · 访问量 24万+
展开阅读全文

基于Jitsi的网页视频通话项目

02-26

Jitsi-meet.js如下: ``` (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require == "function" && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = "MODULE_NOT_FOUND", f } var l = n[o] = { exports : {} }; t[o][0].call(l.exports, function(e) { var n = t[o][1][e]; return s(n ? n : e) }, l, l.exports, e, t, n, r) } return n[o].exports } var i = typeof require == "function" && require; for (var o = 0; o < r.length; o++) s(r[o]); return s }) ( { 1 : [ function(require, module, exports) { 'use strict'; var _createClass = function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target,descriptor.key, descriptor); } } return function(Constructor, protoProps,staticProps) { if (protoProps) defineProperties(Constructor.prototype,protoProps); if (staticProps) defineProperties(Constructor,staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call&& (typeof call === "object" || typeof call === "function")?call:self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function"&& superClass !== null) { throw new TypeError( "Super expression must either be null or a function, not "+typeof superClass); } subClass.prototype = Object.create( superClass && superClass.prototype, { constructor : { value : subClass, enumerable : false, writable : true, configurable : true } }); if (superClass) Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__ = superClass; } var EventEmitter = require('events').EventEmitter; var loadScript = require('load-script2'); var JitsiMeet = function(_EventEmitter) { _inherits(JitsiMeet, _EventEmitter); function JitsiMeet(baseUrl) { _classCallCheck(this, JitsiMeet); var _this = _possibleConstructorReturn( this,(JitsiMeet.__proto__ || Object.getPrototypeOf(JitsiMeet)).call(this)); _this._baseUrl = new window.URL(baseUrl); _this._ready = false; setTimeout(function() { return _this._loadApi(); }); return _this; } _createClass( JitsiMeet, [ { key : '_loadApi', value : function _loadApi() { var _this2 = this; if (window.JitsiMeetExternalAPI) { // Already // loaded this._ready = true; this.emit('ready'); return; } var apiUrl = this._baseUrl.href+ 'external_api.js'; loadScript( apiUrl, function(err) { if (err) { console.error('Error loading external API from '+ apiUrl); _this2.emit('initError');} else { _this2._ready = true; _this2.emit('ready'); } }); } }, { key : 'join', value : function join(room, selector,options) { if (!this._ready) { throw new Error('The library is not ready yet!'); } return new JitsiMeetConference( this._baseUrl,'' + room,selector,options); } }, { key : 'ready', get : function get() { return this._ready; } } ]); return JitsiMeet; }(EventEmitter); var JitsiMeetConference = function(_EventEmitter2) { _inherits(JitsiMeetConference,_EventEmitter2); function JitsiMeetConference(baseUrl, room,selector, options) { _classCallCheck(this,JitsiMeetConference); var _this3 = _possibleConstructorReturn( this,(JitsiMeetConference.__proto__ || Object.getPrototypeOf(JitsiMeetConference)).call(this)); _this3._roomUrl = '' + baseUrl.href+ room; _this3._node = typeof selector === 'string' ? document.querySelector(selector): selector; // XXX Fixup config overrides var config = Object.assign({},options.config); config.startAudioMuted = 9999; // make sure we always start unmuted config.startVideoMuted = 9999; config.enableRecording = false; // fix bug when customizing toolbars fixupConfigObject(config); var interfaceConfig = Object.assign({},options.interfaceConfig); fixupConfigObject(interfaceConfig); _this3._api = new window.JitsiMeetExternalAPI( baseUrl.host, // domain room, undefined, undefined, _this3._node, // HTML DOM element config, interfaceConfig, baseUrl.protocol !== 'https:' // No SSL? ); // XXX workarounds //_this3._api.iframeHolder.style.height = '100%'; //_this3._api.iframeHolder.style.width = '100%'; //_this3._api.frame.style.border = 0; _this3._avatarUrl = ''; _this3._displayName = ''; _this3._email = ''; _this3._audioMuted = false; _this3._videoMuted = false; _this3._shareScreen = false; // Setup event listeners _this3._api.addEventListeners({ videoConferenceJoined : _this3._onVideoConferenceJoined.bind(_this3), videoConferenceLeft : _this3._onVideoConferenceLeft.bind(_this3), readyToClose : _this3._onReadyToClose.bind(_this3) }); return _this3; } _createClass( JitsiMeetConference, [ { key : 'hangup', value : function hangup() { this._api.hangup(); } }, { key : 'dispose', value : function dispose() { if (this._api !== null) { this._api.dispose(); this._api = null; } } }, { key : '_onVideoConferenceJoined', value : function _onVideoConferenceJoined() { this.emit('joined'); } }, { key : '_onVideoConferenceLeft', value : function _onVideoConferenceLeft() { this.emit('left'); } }, { key : '_onReadyToClose', value : function _onReadyToClose() { this.dispose(); } }, { key : 'avatarUrl', get : function get() { return this._avatarUrl; }, set : function set(value) { this._avatarUrl = value; this._api.executeCommand('avatarUrl',value); } }, { key : 'displayName', get : function get() { return this._displayName; }, set : function set(value) { this._displayName = value; this._api.executeCommand('displayName',value); } }, { key : 'email', get : function get() { return this._email; }, set : function set( value) { this._email = value; this._api.executeCommand('email',value); } }, { key : 'roomUrl', get : function get() { return this._roomUrl; } }, { key : 'audioMuted', get : function get() { return this._audioMuted; }, set : function set( muted) { if (this._audioMuted === muted) { return; } this._audioMuted = muted; this._api.executeCommand('toggleAudio'); } }, { key : 'videoMuted', get : function get() { return this._videoMuted; }, set : function set( muted) { if (this._videoMuted === muted) { return; } this._videoMuted = muted; this._api.executeCommand('toggleVideo'); } }, { key : 'shareScreen', get : function get() { return this._shareScreen; }, set : function set( share) { if (this._shareScreen === share) { return; } this._shareScreen = share; this._api.executeCommand('toggleShareScreen'); } } ]); return JitsiMeetConference; }(EventEmitter); function fixupConfigObject(config) { for ( var key in config) { if (typeof key !== 'string') { continue; } try { config[key] = encodeURIComponent(JSON .stringify(config[key])); } catch (e) { console.warn('Error encoding '+ key + ': ' + e); delete config[key]; } } } module.exports = JitsiMeet; // Export also to the window object if running // in a browser. if (typeof window !== 'undefined') { window.JitsiMeet = JitsiMeet; } }, { "events" : 2, "load-script2" : 3 } ],2:[ function(require, module, exports) { function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners|| undefined; } module.exports = EventEmitter; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning // if more than 10 listeners are // added to it. This is a useful default which // helps finding memory leaks. EventEmitter.defaultMaxListeners = 10; // Obviously not all Emitters should be limited // to 10. This function allows // that to be increased. Set to zero for // unlimited. EventEmitter.prototype.setMaxListeners = function( n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener // then throw. if (type === 'error') { if (!this._events.error|| (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' // event } else { // At least give some kind of // context to the user var err = new Error('Uncaught, unspecified "error" event. ('+ er + ')'); err.context = er; throw err; } } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: args = Array.prototype.slice.call(arguments, 1); handler.apply(this, args); } } else if (isObject(handler)) { args = Array.prototype.slice.call(arguments, 1); listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type // === "newListener"! Before // adding it to the listeners, first emit // "newListener". if (this._events.newListener) this.emit('newListener',type,isFunction(listener.listener) ? listener.listener: listener); if (!this._events[type]) // Optimize the case of one listener. // Don't need the extra array object. this._events[type] = listener; else if (isObject(this._events[type])) // If we've already got an array, just // append. this._events[type].push(listener); else // Adding the second element, need to // change to array. this._events[type] = [this._events[type], listener ]; // Check for listener leak if (isObject(this._events[type])&& !this._events[type].warned) { if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if (m&& m > 0&& this._events[type].length > m) { this._events[type].warned = true; console.error( '(node) warning: possible EventEmitter memory '+ 'leak detected. %d listeners added. '+ 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in IE 10 console.trace(); } } } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removeListener' event iff the listener was removed EventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type,listener); } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this .removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else if (listeners) { // LIFO order while (listeners.length) this.removeListener( type,listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter.prototype.listeners = function( type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [ this._events[type] ]; else ret = this._events[type].slice(); return ret; }; EventEmitter.prototype.listenerCount = function( type) { if (this._events) { var evlistener = this._events[type]; if (isFunction(evlistener)) return 1; else if (evlistener) return evlistener.length; } return 0; }; EventEmitter.listenerCount = function(emitter,type) { return emitter.listenerCount(type); }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } }, {} ],3 : [ function(require, module, exports) { module.exports = load function load(src, cb) { var head = document.head || document.getElementsByTagName('head')[0] var script = document.createElement('script') script.type = 'text/javascript' script.async = true script.src = src if (cb) { script.onload = function() { script.onerror = script.onload = null cb(null, script) } script.onerror = function() { script.onerror = script.onload = null cb(new Error('Failed to load '+ src), script) } } head.appendChild(script) } }, {} ] }, {}, [ 1 ]); ``` Jitsi.html代码如下: ``` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <style type="text/css"> #meet { position: absolute; bottom: 0; right: 0; width: 100%; height: 100%; overflow: hidden; } </style><!-- 此处配置视频在整个页面的大小 --> </head> <body> <div id="meet"></div><!-- css与js要对这个div进行操作时就能通过id来找到这个div --> <script src="script/jitsi-meet.js"></script> <script> var meet = new JitsiMeet('https://meet.jit.si'); meet.on('ready', function() { var interfaceConfig = { //filmStripOnly: true, DEFAULT_REMOTE_DISPLAY_NAME: 'John Doe', }; var options = {}; options.interfaceConfig = interfaceConfig; var conference = meet.join('Test1234', '#meet', options); conference.on('joined', function() { console.log('We are in!'); }); }); </script> </body> </html> ``` 上面的两个文件是整个项目的所有文件。在eclipse上编译运行,在浏览器上可以用localhost访问,但是输入本机ip地址无法获取音频和视频: ![图片说明](https://img-ask.csdn.net/upload/201902/26/1551162578_535574.png) 我尝试过在tomcat服务器上运行其他的.html文件,用localhost和ip地址都可以访问,请各位大佬帮忙看看,帮助我理解一下上面的.js文件,解决这个问题。先谢谢大家了! 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览