@techteamer/janus-api
Version:
Janus WebRTC-Gateway API
275 lines (274 loc) • 11.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const JanusPlugin_js_1 = __importDefault(require("../JanusPlugin.js"));
const SdpHelper_js_1 = __importDefault(require("../SdpHelper.js"));
const sdp_1 = __importDefault(require("sdp"));
class VideoRoomPublisherJanusPlugin extends JanusPlugin_js_1.default {
constructor(config, display, logger, filterDirectCandidates = false) {
if (!config) {
throw new Error('unknown config');
}
super(logger);
this.display = display;
this.pluginName = 'janus.plugin.videoroom';
this.janusRoomId = undefined;
this.janusRoomMemberId = undefined;
this.janusRoomPrivateMemberId = undefined;
this.filterDirectCandidates = !!filterDirectCandidates;
this.config = config;
this.sdpHelper = new SdpHelper_js_1.default(this.logger);
this.offerSdp = undefined;
this.answerSdp = undefined;
this.rtpForwardVideoStreamId = undefined;
this.rtpForwardAudioStreamId = undefined;
this.rtpForwardDataStreamId = undefined;
}
/**
* Usage: ffmpeg -analyzeduration 300M -probesize 300M -protocol_whitelist file,udp,rtp -i sdp.file -c:v h264 -c:a aac -ar 16k -ac 1 -g 50 -max_muxing_queue_size 9999 -preset ultrafast -tune zerolatency -f flv rtmp://127.0.0.1:1935/mytv/stream
*/
startRTPForward(host, videoPortNumber, audioPortNumber) {
const body = {
request: 'rtp_forward',
room: this.janusRoomId,
publisher_id: this.janusRoomMemberId,
host
};
if (videoPortNumber) {
body.video_port = videoPortNumber;
}
if (audioPortNumber) {
body.audio_port = audioPortNumber;
}
return this.transaction('message', { body }, 'success').then(({ data, json }) => {
if (data && data.rtp_stream && data.rtp_stream.video_stream_id) {
this.rtpForwardVideoStreamId = data.rtp_stream.video_stream_id;
}
if (data && data.rtp_stream && data.rtp_stream.audio_stream_id) {
this.rtpForwardAudioStreamId = data.rtp_stream.audio_stream_id;
}
if (data && data.rtp_stream && data.rtp_stream.data_stream_id) {
this.rtpForwardDataStreamId = data.rtp_stream.data_stream_id;
}
let rtpSdp = 'v=0\n' +
`o=- 0 0 IN IP4 ${host}\n` +
`s=janus-api:${this.janusRoomId}.${this.janusRoomMemberId}\n` +
`c=IN IP4 ${host}\n` +
't=0 0\n' +
'a=tool:janus-api\n';
sdp_1.default.splitSections(this.answerSdp).forEach((section, index) => {
if (index === 0) {
return; // session part
}
const mid = sdp_1.default.getMid(section);
const rtp = sdp_1.default.parseRtpParameters(section);
const codec = rtp.codecs[0];
if (!codec) {
return;
}
if (mid === 'video' && videoPortNumber) {
rtpSdp +=
`m=video ${videoPortNumber} RTP/AVP ${codec.payloadType}\n` +
`a=rtpmap:${codec.payloadType} ${codec.name}/${codec.clockRate}\n` +
'a=fmtp:11 packetization-mode=1\n' +
'a=rtcp-mux\n';
}
if (mid === 'audio' && audioPortNumber) {
rtpSdp +=
`m=audio ${audioPortNumber} RTP/AVP ${codec.payloadType}\n` +
`a=rtpmap:${codec.payloadType} ${codec.name}/${codec.clockRate}\n` +
'a=fmtp:11 packetization-mode=1\n' +
'a=rtcp-mux\n';
}
});
return rtpSdp;
}).catch((err) => {
this.logger.error('VideoRoomPublisherJanusPlugin, cannot RTP forward', err);
throw err;
});
}
stopRTPForward() {
const ret = [];
if (this.rtpForwardVideoStreamId) {
ret.push(this.stopRTPForwardStream(this.rtpForwardVideoStreamId));
}
if (this.rtpForwardAudioStreamId) {
ret.push(this.stopRTPForwardStream(this.rtpForwardAudioStreamId));
}
if (this.rtpForwardDataStreamId) {
ret.push(this.stopRTPForwardStream(this.rtpForwardDataStreamId));
}
return Promise.all(ret);
}
stopRTPForwardStream(streamId) {
const body = {
request: 'stop_rtp_forward',
room: this.janusRoomId,
publisher_id: this.janusRoomMemberId,
stream_id: streamId
};
return this.transaction('message', { body }, 'success').catch((err) => {
this.logger.error('VideoRoomPublisherJanusPlugin, cannot stop RTP forward', err);
throw err;
});
}
setRoomBitrate(bitrate) {
const body = {
request: 'edit',
room: this.janusRoomId,
new_bitrate: bitrate
};
return this.transaction('message', { body }, 'success').catch((err) => {
this.logger.error('VideoRoomPublisherJanusPlugin, cannot set room bitrate', err);
throw err;
});
}
connect() {
return this.transaction('message', { body: { request: 'list' } }, 'success').then((param) => {
const { data } = param || {};
if (!data || !Array.isArray(data.list)) {
this.logger.error('VideoRoomPublisherJanusPlugin, could not find roomList', data);
throw new Error('VideoRoomPublisherJanusPlugin, could not find roomList');
}
const foundRoom = data.list.find((room) => room.description === '' + this.config.id);
if (foundRoom) {
this.janusRoomId = foundRoom.room;
return this.join();
}
else {
return this.createRoom();
}
}).catch((err) => {
this.logger.error('VideoRoomPublisherJanusPlugin, cannot list rooms', err);
throw err;
});
}
join() {
const body = { request: 'join', room: this.janusRoomId, ptype: 'publisher', display: this.display };
return new Promise((resolve, reject) => {
this.transaction('message', { body }, 'event').then((param) => {
const { data } = param || {};
if (!data || !data.id || !data.private_id || !data.publishers) {
this.logger.error('VideoRoomPublisherJanusPlugin, could not join room', data);
throw new Error('VideoRoomPublisherJanusPlugin, could not join room');
}
this.janusRoomMemberId = data.id;
this.janusRoomPrivateMemberId = data.private_id;
resolve(data.publishers);
}).catch((err) => {
if (err && err.error_code === 426) { // JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM = 426
this.createRoom().then(resolve).catch(reject);
}
else {
this.logger.error('VideoRoomPublisherJanusPlugin, unknown error connecting to room', err);
reject(err);
}
});
});
}
createRoom() {
const body = Object.assign({ request: 'create' }, this.config.toJanusConfig());
return this.transaction('message', { body }, 'success').then((param) => {
const { data } = param || {};
if (!data || !data.room) {
this.logger.error('VideoRoomPublisherJanusPlugin, could not create room', data);
throw new Error('VideoRoomPublisherJanusPlugin, could not create room');
}
this.janusRoomId = data.room;
return this.join();
}).catch((err) => {
this.logger.error('VideoRoomPublisherJanusPlugin, cannot create room', err);
throw err;
});
}
configure(offer, relayAudio = true, relayVideo = true) {
if (!this.janusRoomMemberId) {
this.logger.error('VideoRoomPublisherJanusPlugin, cannot configure without janusRoomMemberId');
return;
}
const body = { request: 'configure', audio: relayAudio, video: relayVideo };
const jsep = offer;
if (this.filterDirectCandidates && jsep.sdp) {
jsep.sdp = this.sdpHelper.filterDirectCandidates(jsep.sdp);
}
this.offerSdp = jsep.sdp;
return this.transaction('message', { body, jsep }, 'event').then((param) => {
const { json } = param || {};
if (!json.jsep) {
throw new Error('cannot configure');
}
const jsep = json.jsep;
if (this.filterDirectCandidates && jsep.sdp) {
jsep.sdp = this.sdpHelper.filterDirectCandidates(jsep.sdp);
}
this.answerSdp = jsep.sdp;
return jsep;
});
}
candidate(candidate) {
if (this.filterDirectCandidates && candidate.candidate && this.sdpHelper.isDirectCandidate(candidate.candidate)) {
return;
}
return this.transaction('trickle', { candidate });
}
onmessage(data, json) {
// TODO data.videoroom === 'destroyed' handling
// TODO unpublished === 'ok' handling : we are unpublished
const { videoroom } = data || {};
if (!data || !videoroom) {
this.logger.error('VideoRoomPublisherJanusPlugin got unknown message', json);
return;
}
if (videoroom === 'slow_link') {
this.logger.debug('VideoRoomPublisherJanusPlugin got slow_link', data);
this.slowLink();
return;
}
if (videoroom === 'event') {
const { room, unpublished, leaving, publishers } = data;
if (room !== this.janusRoomId) {
this.logger.error('VideoRoomPublisherJanusPlugin got unknown roomId', this.janusRoomId, json);
return;
}
if (unpublished) {
this.emit('remoteMemberUnpublished', unpublished);
}
else if (leaving) {
this.emit('remoteMemberLeaving', leaving);
}
else if (Array.isArray(publishers)) {
this.emit('publishersUpdated', publishers);
}
else {
this.logger.error('VideoRoomPublisherJanusPlugin got unknown event', json);
}
return;
}
if (videoroom === 'keyframe') {
this.emit('keyframe', data);
return;
}
if (videoroom === 'talking') {
this.emit('talking');
return;
}
if (videoroom === 'stopped-talking') {
this.emit('stoppedTalking');
return;
}
this.logger.error('VideoRoomPublisherJanusPlugin unhandled message:', videoroom, json);
}
listParticipants() {
return this.transaction('message', { body: { request: 'listparticipants', room: this.janusRoomId } }, 'success').then((param) => {
const { data } = param || {};
if (!data || !Array.isArray(data.participants)) {
this.logger.error('VideoRoomPublisherJanusPlugin, could not list participants', data);
throw new Error('VideoRoomPublisherJanusPlugin, could not list participants');
}
return data.participants;
});
}
}
exports.default = VideoRoomPublisherJanusPlugin;