🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

janus-manager

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

janus-manager

An adapter for Janus client library.

latest
npmnpm
Version
1.3.11
Version published
Weekly downloads
22
37.5%
Maintainers
1
Weekly downloads
 
Created
Source

janus-manager

An adapter manager for Janus client library.

Janus 原文档地址可参考

基于 janus-manager Vue实现的 janus-connector 组件库可用于快速构建通讯应用。

Usage

  • install
npm install janus-manager --save
  • step 1:引入类库,初始化
import JanusManager from "janus-manager";

const manager = new JanusManager();
// 查看当前版本
const version = JanusManager.version;
  • step 2:服务器初始化,建立服务器连接
// 传入服务器地址,进行服务连接初始化,返回连接上下文对象
const context = await manager.init({ 
    server: this.server,
    // 设置连接超时时间,默认6000,60s
    longPollTimeout: 60000,
    // iceServers config
    /* iceServers: [{
        "urls": ["turn:xxxxx:xxxx"],
        "username": "username",
        "credential": "credential"
    }]*/
}).catch((err) => { 
    // 可处理兼容性及连接异常错误
    throw err 
});
  • step 3:传入 handle 插件配置,通过已建立的连接通道,将插件与通过建立关联,建立插件状态Session
// 创建一个可用于拨打电话的插件处理器,将插件与服务器建立关联,返回插件处理器对象,并进行消息监听
const handler = await manager.createCallHandle({
    // plugin options
});
handler.on("message", (msg, jsep) => {
    // handle events. 
    // eg: registered / hangup / accepted / progress / hangup | incomingcall

    // 处理接通事件
    const event = msg?.result?.event;
    if (event === "progress" || event === "accepted") {
        jsep && handler.handleRemoteJsep(jsep);
    }
}

// 处理音频流播放事件
handler.on("remotestream", (stream) => {
    manager.attachMediaStream(remoteMediaDom, stream);
});
  • step 4:发送注册消息,建立webrtc通讯能力
handler.sendMessage("register", {
    authuser: this.authuser,
    proxy: this.sipServer,
    secret: this.password,
    username: this.username,
    display_name: this.displayname,
});
  • step 5:拨打/接听/挂断/拒接 电话
// 拨打电话,通话地址 格式: sip:{phone}@ip:port
handler.makeCall(uri);

// 发送数字键盘命令信息
// 参数: tones - DTMF tones 
handler.sendDtmf({ tones: '1'});

// 挂断电话
handler.hangup();

// 接听电话 - 监听 incomingcall 事件
handler.on("message", (msg, jsep) => {
    const event = msg?.result?.event;
    if (event === "incomingcall") {
        handler.answerCall(jsep, { audio: true });
    }
}

// 拒接电话
handler.declineCall();

// 呼出端禁用音频输入
handler.muteAudio();
// 呼出端重新启用音频输入
handler.unmuteAudio();
// 判断呼出端是否启用音频输入,返回boolean
handler.isAudioMuted(); // true/false
  • step 6:页面退出前,销毁管理器实例
// 等同于  manager.destory();
context.destory();

其他说明

音视频环境调用检测

const isSupported = manager.isSupportedUserMedia();

可用于在通话前判断当前系统兼容性,是否支持音视频调用

异步方法使用

在如下方法调用时,注意为异步操作,同时根据逻辑决定是否需要捕获异常,若不catch,执行错误时,会默认抛出异常

  • 连接上下文创建:
await manager.init({ }).catch()
  • 通话对象创建:
await manager.createCallHandle({ }).catch()
  • 发送消息:
await handler.sendMessage({ }).catch()
  • 通话相关操作
// 拨打电话
await handler.makeCall({ }).catch()
// 挂断通话
await handler.hangup({ }).catch()
// 拒绝来电
await handler.declineCall({ }).catch()
// 接听来电
await handler.answerCall({ }).catch()

异常处理

  • 常规异常:所有Promise类型的方法调用异常,都可使用 catch 来捕获
  • 特殊异常:处理janus运行时的异常,如超时异常
const context = await manager.init({ 
    server: this.server,
    // 设置连接超时时间,默认6000,60s
    longPollTimeout: 60000,
})
.catch(error => {
    // 可处理兼容性及连接异常错误
});

context.error = (err: Error) => {
    // 一般网络连接超时,可在此处处理
}

调用主流程

  • 初始化管理器对象manager
  • 通过管理器对象创建服务连接上下文对象context
  • 使用manager创建业务操作对象handler
  • 使用操作对象handler进行通话操作(可多次调用相关方法)
  • 流程结束,释放相关连接资源 context.destorymanager.destory

以上操作除对handler对象进行相关操作外,其他流程只需要执行一次。

兼容性问题

  • 麦克风权限

    • Android中创建webview时,需先获取android.permission.RECORD_AUDIOandroid.permission.MODIFY_AUDIO_SETTINGS权限
    • 同样IOS中也需获取麦克风权限,可通过 AVAudioSession.sharedInstance().recordPermission 获取麦克风权限
  • 音视频自动播放

    • Android中可禁用自动播放需人工操作 webView.getSettings().setMediaPlaybackRequiresUserGesture(false)
    • IOS中也可进行相关配置,WKWebViewConfiguration().mediaTypesRequiringUserActionForPlayback 根据客户端使用语言或版本设置为 []WKAudiovisualMediaTypeNonfalse参考
  • 因系统支持问题,在Android系统版本 >=7.0 及 IOS系统版本 >=14.3才支持在webview中获取系统mediaDevices,才可正常使用库基础功能, 可通过实例方法 isSupportedUserMedia 来检测。

Demo

see call-demo

<template>
    <div class="page">
        <div class="home-container">
            <!-- 第一部分 启动服务 -->
            <div class="start-btn">
                <button type="primary" @click="start">启动服务</button>
            </div>

            <!-- 第二部分 注册服务配置 -->
            <div v-if="running" class="register-infor">
                <div>
                    <input
                        v-model="sipServer"
                        type="text"
                        placeholder="SIP服务器(例如, sip:10.10.10.10:5688)"
                    />
                </div>
                <div>
                    <input
                        v-model="username"
                        type="text"
                        placeholder="SIP身份(例如,sip:0000@10.10.10.10:5688)"
                    />
                </div>
                <div>
                    <input
                        v-model="authuser"
                        type="text"
                        placeholder="SIP号(例如, 1001)"
                    />
                </div>
                <div>
                    <input
                        v-model="password"
                        type="password"
                        placeholder="SIP密码"
                    />
                </div>
                <div>
                    <input
                        v-model="displayname"
                        type="text"
                        placeholder="显示名称 (例如, 806100)"
                    />
                </div>

                <div class="start-btn">
                    <button type="primary" @click="connect">建立连接</button>
                </div>
            </div>

            <!-- 第三部分 通话 -->
            <div v-if="connected" class="register-infor">
                <div>
                    <input
                        placeholder="请输入手机号 如: 13688886666"
                        v-model="phoneNumber"
                    />
                </div>
                <div class="start-btn">
                    <button
                        :class="{ primary: !calling, danger: calling }"
                        type="primary"
                        @click="callHandler"
                    >
                        {{ calling ? "挂断" : "拨打" }}
                    </button>
                </div>
            </div>

            <!-- 远程音频,接听电话时音频输入,需在 remotestream 事件中处理 -->
            <video ref="remoteMedia" class="hide" autoplay playsinline />
        </div>
    </div>
</template>
<script lang="ts">
import Vue from "vue";
import JanusManager from "janus-manager";

const manager = new JanusManager();

export default Vue.extend({
    data() {
        return {
            running: false,

            server: [
                "https://servername.com/janus",
                "wss://servername.com/janus",
            ],
            sipServer: "sip:server_ip:port", // SIP服务器
            username: "sip:user_name@server_ip:port", // SIP身份
            authuser: "user", // SIP号
            password: "password",
            displayname: "", // 显示名称

            connected: false,
            calling: false,
            phoneNumber: "",
        };
    },
    computed: {
        callNumber() {
            return `sip:${this.phoneNumber}@server_ip:port`;
        },
    },
    methods: {
        async start() {
            const context = await manager
                .init({ server: this.server })
                .catch((err) => {
                    console.error(err);
                    this.running = false;
                    throw err;
                });
            this.context = context;
            this.running = true;
        },
        async connect() {
            const handler = await manager.createCallHandle({
                mediaState: function (medium, on) {
                    const status = on ? "started" : "stopped";
                    const msg = `Janus ${status}  receiving our ${medium}`;
                    console.info("Janus mediaState:", msg);
                },
                webrtcState: function (on) {
                    const status = on ? "up" : "down";
                    const msg = `Janus says our WebRTC PeerConnection is ${status}`;
                    console.info("Janus webrtcState:", msg);
                },
                iceState: function (state) {
                    // state: 'connected' | 'failed'
                    console.info("iceState", state);
                },
            });

            handler.on("message", (msg, jsep) => {
                console.info("message event:", msg?.result?.event, jsep);
                const event = msg && msg.result && msg.result.event;
                // 注册成功
                if (event === "registered") {
                    this.connected = true;
                } else if (event === "hangup") {
                    // 通话挂断
                    console.warn("通话已挂断:", msg, jsep);
                    this.calling = false;
                    handler.hangup();
                } else if (event === "progress" || event === "accepted") {
                    // 接听
                    jsep && handler.handleRemoteJsep(jsep);
                }
            });

            handler.on("remotestream", (stream) => {
                manager.attachMediaStream(this.$refs.remoteMedia, stream);
            });

            console.log("connect success....");
            this.handler = handler;

            handler.sendMessage("register", {
                authuser: this.authuser,
                proxy: this.sipServer,
                secret: this.password,
                username: this.username,
                display_name: this.displayname,
            });
        },
        callHandler() {
            if (this.calling) {
                this.handler.hangup();
                this.calling = false;
                return;
            }

            if (this.phoneNumber === "") return;
            this.handler.makeCall(this.callNumber).catch(error => {
                throw error;
            });
        },
    },
    beforeDestroy() {
        this.context && this.context.destroy();
    },
});
</script>
<style lang="less" scoped>
.page {
    text-align: center;
    padding: 20px;
    display: flex;
    justify-content: center;

    .text-center {
        text-align: center;
    }

    .home-container {
        text-align: left;
        width: 300px;

        .hide {
            display: none;
        }

        input {
            height: 32px;
            line-height: 32px;
            border-radius: 4px;
            border: 1px solid #dcdfe6;
            padding: 0 15px;
            box-sizing: border-box;
            width: 100%;
        }
        button {
            border: none;
            color: #fff;
            padding: 10px 20px;
            border-radius: 4px;
            background-color: #357bff;
            cursor: pointer;
            &:focus {
                outline: none;
            }
            &:active {
                background-color: #2671fc;
            }
        }

        .primary {
            background-color: #357bff;
        }

        .danger {
            background-color: #f8483b;
        }

        .register-infor {
            margin-top: 10px;
            > div {
                margin-bottom: 10px;
            }
        }
    }
}
</style>

Keywords

janus

FAQs

Package last updated on 21 Nov 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts