janus-gateway-js
关于
Janus网关的现代JavaScript客户端。基于WebSocket。原始客户端可以在此处找到:https://janus.conf.meetecho.com/docs/rest.html。
使用示例
此示例假设Janus服务器在本地运行,并在其默认端口8188
上启用了WebSocket支持
var janus = new Janus.Client('ws://localhost:8188', {
token: 'token',
apisecret: 'apisecret',
keepalive: 'true'
});
janus.createConnection('id').then(function(connection) {
connection.createSession().then(function(session) {
session.attachPlugin('bla').then(function(plugin) {
plugin.send({}).then(function(response){});
plugin.on('message', function(message) {});
plugin.detach();
});
});
});
安装
npm i janus-gateway-js
构建
项目有一个简单的构建过程。默认情况下,npm run-script build
会构建单个文件janus.js
,其中包含Janus库及其所有依赖项。
-
默认构建。用于集成测试。
$(npm bin)/gulp
-
bluebird
和webrtc-adapter
被外部化到vendor.js
$(npm bin)/gulp external
API
该库可用于Node和浏览器环境。在浏览器中,它通过window.Janus
声明。导出的类有:
- Client
- Connection
- Session
- Plugin
- MediaPlugin
- AudiobridgePlugin
- AudioroomPlugin
- StreamingPlugin
- RtpbroadcastPlugin
- WebsocketConnection
- Error
重要提示!在Node中使用时,请阅读MediaPlugin的信息。
Client
用于创建连接的类。如果您想使用相同的选项创建多个连接到相同地址,请使用它。
-
new Client(address, [options])
创建Client的新实例。address
{string}。Janus服务器的WebSocket地址。options
{Object} 选项。可选。在new Connection
中查看其规格。此客户端创建的每个新连接都将使用options
对象。
-
client.createConnection(id)
返回一个promise,该promise解析为已经打开的Connection的新实例。
id
{string} id
Connection
表示到Janus的WebSocket连接。EventEmitter2的实例。
-
new Connection(id, address, [options])
创建Connection的新实例。在Node环境中,为创建的实例附加错误监听器非常重要。更多详情请查看https://nodejs.org/api/events.html#events_error_events。
id
{string}。address
{string}。Janus服务器的WebSocket地址。options
{Object} 选项。可选。options.token
{string}。可选。Janus令牌。options.apisecret
{string}。可选。Janus API密钥。options.keepalive
{boolean|number}。可选。如果为true
,则每30秒向Janus服务器发送一次保活消息。如果是正整数,则每[number]毫秒发送一次保活消息。
-
Connection.create(id, address, [options])
委托给上述构造函数。重写它以创建自定义Connection。
-
connection.getId()
返回连接的id。
-
connection.getAddress()
返回连接的地址。
-
connection.getOptions()
返回连接的选项。
-
connection.open()
返回一个promise,当到
options.address
的WebSocket连接打开时,该promise解析。 -
connection.close()
返回一个promise,当连接关闭时,该promise解析。
-
connection.send(message)
发送消息。返回一个promise,在消息发送后立即解析。
message
{Object}。
-
connection.sendSync(message)
发送消息。返回一个promise。如果连接有一个id等于
message['transaction']
的事务,则该promise在事务完成后解析。否则与connection.send
相同。message
{Object}。
-
connection.addTransaction(transaction)
向连接添加事务。
transaction
{Transaction}。
-
connection.executeTransaction(message)
执行id等于
message['transaction']
的事务。返回一个promise,在事务执行后解析。message
{Object}。
-
connection.createSession()
返回一个promise,该promise解析为Session的新实例。
-
connection.hasSession(sessionId)
连接是否有
sessionId
的会话。sessionId
{string}
-
connection.getSession(sessionId)
从连接返回会话,如果找不到会话则返回
undefined
。sessionId
{string}。
-
connection.getSessionList()
返回当前会话的数组。如果没有会话,则为空。
-
connection.addSession(session)
向连接添加会话。
session
{Session}。
-
connection.removeSession(sessionId)
从连接中移除会话。
sessionId
{string}。
Session
表示Janus会话。EventEmitter2的实例。
-
new Session(connection, id)
创建Session的新实例。
connection
{Connection} 已打开的Connection实例id
{string}
-
Session.create(connection, id)
委托给上述构造函数。重写它以创建自定义Session。
-
session.getId()
返回会话的id。
-
session.send(message)
将会话id添加到消息中,并将其委托给连接的
send
方法。返回连接的send
的promise。message
{Object}
-
session.attachPlugin(name)
附加插件。返回一个promise,该promise解析为Plugin的新实例。会话可能有多个同名插件。
name
{string} 插件名称
-
session.destroy()
销毁会话。返回一个promise,当会话成功销毁时,该promise解析。
-
session.cleanup()
清理会话资源。使用此方法时要非常小心。不要在活动会话上调用它。返回一个promise,当会话清理完成时,该promise解析。
-
session.hasPlugin(pluginId)
会话是否有该id的插件。
pluginId
{string}
-
session.getPlugin(pluginId)
从会话返回插件,如果找不到插件则返回
undefined
。pluginId
{string}
-
session.getPluginList()
返回已附加插件的数组。如果没有插件,则为空。
-
session.addPlugin(plugin)
向会话添加插件。
plugin
{Plugin}。
-
session.removePlugin(pluginId)
从会话中移除插件。
pluginId
{string}。
-
session.sendSync(message)
发送消息。返回一个promise。如果会话有一个id等于
message['transaction']
的事务,则该promise在事务完成后解析。否则与session.send
相同。message
{Object}。
-
session.addTransaction(transaction)
向会话添加事务。
transaction
{Transaction}。
-
session.executeTransaction(message)
执行id等于
message['transaction']
的事务。返回一个promise,在事务执行后解析。message
{Object}。
Plugin
表示Janus插件。EventEmitter2的实例。
-
new Plugin(session, name, id)
创建Plugin的新实例。
session
{Session} Session的实例name
{string} 插件名称id
{string}
-
Plugin.create(session, id)
委托给上述构造函数。重写它以创建自定义Plugin。
-
Plugin.register(name, aClass)
为名称
name
注册插件类aClass
,这样当使用name
调用Plugin.create
时,会创建aClass
的实例。 -
plugin.getId()
返回插件的id。
-
plugin.getName()
返回插件的名称。
-
plugin.getResponseAlias()
返回插件在response['plugindata']['data']中的别名。通常是插件名称的最后一部分。
-
plugin.send(message)
将插件id添加到消息中,并将其委托给会话的
send
方法。返回会话的send
的promise。message
{Object}
-
plugin.detach()
分离插件。返回一个promise,当插件成功分离时,该promise解析。
-
plugin.cleanup()
清理插件资源。使用此方法时要非常小心。不要在已附加的插件上调用它。返回一个promise,当插件清理完成时,该promise解析。
-
plugin.sendSync(message)
发送消息。返回一个promise。如果插件有一个id等于
message['transaction']
的事务,则该promise在事务完成后解析。否则与plugin.send
相同。message
{Object}。
-
plugin.addTransaction(transaction)
向插件添加交易。
transaction
{Transaction}。
-
plugin.executeTransaction(message)
执行id等于
message['transaction']
的交易。返回一个在交易执行后解决的promise。message
{Object}。
MediaPlugin
持有通用媒体方法和数据的抽象插件类。继承自Plugin
。
重要 MediaPlugin有一些方法需要可用的WebRTC,这在Node环境中是不存在的。所以在使用Node或不提供WebRTC的浏览器中使用此库时要小心。这个警告对所有继承自MediaPlugin的插件都适用。
除Plugin
外的其他方法有:
-
plugin.createPeerConnection([options])
创建并返回创建的RTCPeerConnection。同时也将其存储在插件实例上。
options
RTCConfiguration
-
plugin.getPeerConnection()
返回已创建的RTCPeerConnection实例,如果未创建则返回null。
-
plugin.addTrack(track, [...stream])
向已创建的PeerConnection添加轨道。
track
MediaStreamTrackstream
MediaStream。包含轨道的流。可重复参数。
-
plugin.getUserMedia(constraints)
封装MediaDevices.getUserMedia,并为屏幕捕获添加额外约束。返回promise。
constraints
MediaStreamConstraints
-
plugin.createOffer([options])
返回一个promise,解决为创建的offer SDP。
options
RTCOfferOptions
-
plugin.createAnswer(jsep, [options])
返回一个promise,解决为创建的answer SDP。
jsep
RTCSessionDescription offer SDPoptions
RTCAnswerOptions
-
plugin.setRemoteSDP(jsep)
在存储的PeerConnection实例上设置远程SDP。返回promise。
jsep
RTCSessionDescription
-
plugin.hangup()
发送挂断请求。返回一个promise,在插件成功挂断时解决。
AudiobridgePlugin
对应'janus.plugin.audiobridge'。继承自MediaPlugin
。下面方法参数的更多详细信息可以在 @see https://janus.conf.meetecho.com/docs/janus__audiobridge_8c.html#details 找到。除MediaPlugin
外的其他方法有:
-
plugin.create(roomId, [options])
请求创建一个音频房间。返回一个promise,在房间创建时解决。
roomId
intoptions
Object。见JSDocu。
-
plugin.destroy(roomId, [options])
请求销毁音频房间。返回一个promise,在房间销毁时解决。
roomId
intoptions
Object。见JSDocu。
-
plugin.list()
请求当前房间列表。返回一个promise,解决为插件响应。
-
plugin.listParticipants(roomId)
请求房间的参与者列表。返回一个promise,解决为插件响应。
roomId
int
-
plugin.join(roomId, [options])
请求加入音频房间。返回一个promise,在加入房间时解决。
roomId
intoptions
Object。见JSDocu。
-
plugin.leave()
请求离开当前房间。返回一个promise,在离开房间时解决。
-
plugin.change(roomId, [options])
请求更换房间。返回一个promise,在房间更换时解决。
roomId
intoptions
Object。见JSDocu。
-
plugin.configure([options], [jsep])
配置当前房间的设置。返回一个promise,在房间配置完成时解决。
options
Object。见JSDocu。jsep
RTCSessionDescription
-
plugin.offerStream(stream, [offerOptions], [configureOptions])
使用流创建一个对等连接并发送offer。返回一个promise,解决为
sendSDP
promise。stream
MediaStream。offerOptions
Object。offer的选项。configureOptions
Object。offer发送后配置房间的选项。
-
plugin.sendSDP(jsep, [configureOptions])
发送带有jsep和配置选项的offer。返回一个promise,在offer被接受后解决。
jsep
RTCSessionDescriptionconfigureOptions
Object。配置房间的选项。
AudioroomPlugin
对应'janus.plugin.cm.audioroom'。文档页面是 https://github.com/cargomedia/janus-gateway-audioroom。它提供与`AudiobridgePlugin`相同的功能,只有一些小的差异: https://github.com/cargomedia/janus-gateway-audioroom#overview。
StreamingPlugin
对应'janus.plugin.streaming'。继承自MediaPlugin
。下面方法参数的更多详细信息可以在 @see https://janus.conf.meetecho.com/docs/janus__streaming_8c.html#details 找到。除MediaPlugin
外的其他方法有:
-
plugin.create(mountpointId, [options])
请求创建一个挂载点。返回一个promise,在挂载点创建时解决。
mountpointId
intoptions
Object。见JSDocu。
-
plugin.destroy(mountpointId, [options])
请求销毁挂载点。返回一个promise,在挂载点销毁时解决。
mountpointId
intoptions
Object。见JSDocu。
-
plugin.list()
请求当前流的列表。返回一个promise,解决为插件响应。
-
plugin.watch(mountpointId, [options])
请求观看挂载点。返回一个promise,在挂载点被观看时解决。
mountpointId
intoptions
Object。见JSDocu。
-
plugin.start([jsep])
请求启动挂载点。返回一个promise,解决为来自janus的SDP。
jsep
RTCSessionDescription
-
plugin.stop()
请求停止当前挂载点。返回一个promise,在挂载点停止时解决。
-
plugin.pause()
请求暂停当前挂载点。返回一个promise,在挂载点暂停时解决。
-
plugin.switch(mountpointId, [options])
请求切换挂载点。返回一个promise,在挂载点切换时解决。
mountpointId
intoptions
Object。见JSDocu。
-
plugin.connect(mountpointId, [options])
切换方法。如果插件没有当前挂载点,则此方法调用
watch
,否则调用switch
。mountpointId
intoptions
Object。见JSDocu。
-
plugin.enable(mountpointId, [options])
请求启用挂载点。返回一个promise,在挂载点启用时解决。
mountpointId
intoptions
Object。见JSDocu。
-
plugin.disable(mountpointId, [options])
请求禁用挂载点。返回一个promise,在挂载点禁用时解决。
mountpointId
intoptions
Object。见JSDocu。
-
plugin.recording(mountpointId, [options])
请求在挂载点开始或停止录制。
mountpointId
intoptions
Object。见JSDocu。
RtpbroadcastPlugin
对应'janus.plugin.cm.rtpbroadcast'。继承自MediaPlugin
。文档页面是 https://github.com/cargomedia/janus-gateway-rtpbroadcast。除`MediaPlugin`外的其他方法有:
-
plugin.create(id, [options])
请求创建一个挂载点。返回一个promise,在挂载点创建时解决。
id
stringoptions
Object。见JSDocu。
-
plugin.destroy(id)
请求销毁挂载点。返回一个promise,在挂载点销毁时解决。
id
string
-
plugin.list([id])
请求
id
的流定义。如果省略id
,则请求当前流的流定义。返回一个promise,解决为结果。id
string
-
plugin.watch(id)
请求观看挂载点。返回一个promise,在挂载点被观看时解决。
id
string
-
plugin.watchUDP(id, streams)
将数据包从UDP服务器转发到UDP客户端。返回一个promise,在操作完成时解决。
id
stringstreams
Array
-
plugin.start()
请求启动挂载点。返回一个promise,解决为来自janus的SDP。
-
plugin.stop()
请求停止当前挂载点。返回一个promise,在挂载点停止时解决。
-
plugin.pause()
请求暂停当前挂载点。返回一个promise,在挂载点暂停时解决。
-
plugin.switch(id)
请求切换挂载点。返回一个promise,在挂载点切换时解决。
id
string
-
plugin.switchSource(index)
请求为当前会话挂载点调度切换索引为index的流。返回一个promise,在janus接受请求时解决。
index
number
-
plugin.superuser(enabled)
将当前会话升级为超级用户会话,或降级为普通会话。
enabled
boolean
WebsocketConnection
WebSocket的Promise化API。EventEmitter2的实例。
-
new WebsocketConnection([websocket])
创建一个新的WebsocketConnection实例。 -
websocket
{WebSocket} websocket。可选。可以是W3C或Node WebSocket实例。 -
websocketConnection.open(address, [protocol])
通过
protocol
创建一个到address
的新websocket连接。返回一个在websocket连接打开时解决的promise。address
{string} 地址。要连接的Websocket服务器地址。protocol
{string} 协议。Websocket协议。
-
websocketConnection.close()
返回一个在websocketConnection关闭时解决的promise。
-
websocketConnection.isOpened()
websocketConnection是否已打开。
-
websocketConnection.send(message)
发送消息。返回一个在消息发送后立即解决的promise。
message
{Object}。
-
websocketConnection.onMessage(message)
接收消息的监听器。如果需要模拟接收消息,可以调用它。
message
{Object}。
错误
自定义Janus错误。用于处理Janus消息中发生的受控错误。
JanusError
-
new JanusError(janusMessage)
创建一个新的JanusError实例。
janusMessage
{JanusMessage} 导致错误的消息。
-
error.code
Janus错误代码
-
error.janusMessage
错误的Janus消息
测试
测试位于test/
目录下。使用npm test
运行它们。
单元测试
这些测试位于test/unit/
下。使用$(npm bin)/mocha
运行它们。
集成测试
这些测试位于test/integration/
下。要运行它们,你需要:
- 有一个正常运行的Janus服务器实例。将其地址放入
test/integration/config.json
- 在运行测试的机器上安装Chrome/Chromium 51版本或更高版本。
- 使用
$(npm bin)/karma start test/karma.conf.js
运行测试。请注意,karma应该能够看到必要版本的Chrome。
发布
- 用新版本更新package.json
- 发布一个带有更新后package.json的新git标签
之后,npm发布应该会自动完成。如果没有自动完成,则手动发布:
npm publish https://github.com/sjkummer/janus-gateway-js/archive/<GitTagWithUpdatedPackageJson>.tar.gz
插件
目前该项目有四个已实现的插件:audiobridge、videostreaming、rtpbroadcast和audioroom。这是暂时的。一旦项目稳定,这些插件将被移到它们自己的仓库中。如果你需要一个未实现的插件,那么你需要自己编写它。
如何编写插件
为简单起见,让我们编写一个只做audio
的EchoTest插件。
function EchoTest() {
Janus.MediaPlugin.apply(this, arguments);
}
EchoTest.NAME = 'janus.plugin.echotest';
EchoTest.prototype = Object.create(Janus.MediaPlugin.prototype);
EchoTest.prototype.constructor = EchoTest;
Janus.Plugin.register(EchoTest.NAME, EchoTest);
/**
* @param {boolean} state
* @returns {Promise}
* @fulfilled {RTCSessionDescription} jsep
*/
EchoTest.prototype.audio = function(state) {
var self = this;
return Promise
.try(function() {
return self.getUserMedia({audio: true, video: false});
})
.then(function(stream) {
self.createPeerConnection();
stream.getTracks().forEach(function(track) {
self.addTrack(track, stream);
});
})
.then(function() {
return self.createOffer();
})
.then(function(jsep) {
var message = {body: {audio: state}, jsep: jsep};
return self.sendWithTransaction(message);
})
.then(function(response) {
var jsep = response.get('jsep');
if (jsep) {
self.setRemoteSDP(jsep);
return jsep;
}
});
};
然后我们可以这样使用它
var janus = new Janus.Client(config.url, config);
janus.createConnection('client')
.then(function(connection) {
return connection.createSession();
})
.then(function(session) {
return session.attachPlugin(EchoTest.NAME);
})
.then(function(echotestPlugin) {
return echotestPlugin.audio(true);
})