janus的web端开发

janus Web


1. 在线demo

通过janus的源码的html文件以及相应的js文件我们可以参考官方的demo,在上文服务端的部署中最后我们可以进行在线使用。


2. 模块开发


只有demo肯定是不够的,而且使用的是jquerybootStrap,改起来也特别麻烦。

因此我们可以参考janus 的 jsAPI

将需要将功能抽离到vue中,这里因为我使用到的是videoroom,因此代码也是以videoroom为例。


2.1 安装依赖

首先我们需要安装janus需要的依赖

npm i janus-gateway

npm i webrtc-adapter

2.2 初始化

<template>
    <div>
        <button @click="start">Startbutton>
        <button @click="stop">Stopbutton>
        <video id="myVideo" style="width: 1280px;height: 720px;border: 1px solid #ccc;margin-top: 10px;" />
    div>
template>
import Janus from 'janus-gateway';
import adapter from 'webrtc-adapter'

const server = "ws://xx.xx.xx.xx:8188"; // 这个是上文部署的服务端的IP地址
const videoId = "myVideo"; // template中用来接收流的video标签的id
const janus = ref(null); // 保存janus实例
const pluginHandler = ref(null); // 保存插件句柄, 许多交互操作都需要这个变量来操作
const room = 1234; // 连接的房间id, 默认在后端没改动的情况下为 1234
const userId = ref(null); // 需要拉流的 发布者id

const start = () => {
	// 浏览器不支持webRTC
   if (!Janus.isWebrtcSupported()) {
        return;
    }
    // 初始化并指定配置项
    Janus.init({
        debug: true,
        dependencies: Janus.useDefaultDependencies({
            adapter
        }),
        callback: () => startJanus()
    })
}

const startJanus = () => {
	// 将janus实例保存起来
    janus.value = new Janus({
    	// 指定上述的janus服务器ip连接
        server: server,
        success: function () {
            attachToStreamingPlugin(janus.value);
        }
    });
}

2.3 连接会话

连接会话前需要打开服务端http-server发布的在线demo中的videoroom,加入并且开始推送,前端才能接收到流,否则videoroom里面是空的没有流,前端也就拉不到数据了。

const attachToStreamingPlugin = (janus) => {
	// 创建一个新的会话
    janus.attach({
    	// 指定插件为videoroom, 需要使用streaming则进行切换, 插件不同代码也不同, 需要另外编写逻辑和处理回调。
        plugin: "janus.plugin.videoroom",
        // 指定房间号, 默认1234
        room,
        // 成功连接时的回调, 保存插件句柄, getParticipant获取房间内所有发布者的列表
        success: function (pluginHandle) {
            pluginHandler.value = pluginHandle;
            getParticipant()
        },
        // 接收到信息时的回调
        // 由于需要建立webrtc双向连接, 因此需要接收jsep信息并createAnswer回复本地描述来维持webrtc的连接
        // 具体逻辑在handleSDP
        onmessage: function (msg, jsep) {
            handleSDP(jsep);
        },
        // 接收到远程的流时的处理
        onremotetrack: function (stream) {
            handleStream(stream);
        },
        // 清除信息时回调
        oncleanup: function () {}
    });
}

// 获取房间内所有发布者的列表
const getParticipant = () => {
    pluginHandler.value.send({
        message: {
            request: "listparticipants",
            room,
            audio: true,
            video: true,
        },
        success: function (response) {
        	// 成功回调中可以获取所有房间内发布者的列表
            const participants = response["participants"];
            // 选择第一个用户开始订阅 可以添加业务逻辑拉第几个或多个
            const firstPart = participants[0]
            startStream(firstPart)
        },
        error: function (error) {}
    });
}

2.4 订阅发布者的流

const startStream = (selectedStream) => {
	// 获取到发布者的id
    var selectedStreamId = selectedStream?.["id"];
    if (selectedStreamId === undefined || selectedStreamId === null) {
        return console.log("::: No selected stream :::");
    }
    // 将id存到userId中
    userId.value = selectedStreamId
    // 订阅流video和audio
    pluginHandler.value.send({
        message: {
            request: "join",
            feed: selectedStreamId,
            room: room,
            ptype: "subscriber",
            audio: true,
            video: true,
        }
    })
}

const handleSDP = (jsep) => {
    if (jsep !== undefined && jsep !== null) {
    	// 处理jsep信息并创建回复,成功回复之后通过keepRTC回调保持会话
        pluginHandler.value.createAnswer({
        	// 将发送过来的jsep信息返回
            jsep,
            // 由于只发送jsep信息所以将audioSend和videoSend设置为false,不传输媒体流
            media: {audioSend: false, videoSend: false},
            // 成功后执行keepRTC并将sdp参数传入
            success: keepRTC,
            error: function (error) {
                console.error("::: WebRTC error :::" + JSON.stringify(error));
            }
        });
    }
}

const keepRTC = (sdp) => {
    // 发送start请求保持连接
    pluginHandler.value.send({
        message: {request: "start"},
        jsep: sdp
    });
}

2.5 流的处理

const handleStream = (stream) => {
	// 接收到远程流之后新建一个媒体流 将远程流加入轨道
    const streamRemote = new MediaStream();
    streamRemote.addTrack(stream);
    const videoElement = document.getElementById(videoId);
    // 在video标签显示内容
    videoElement.srcObject = streamRemote;
    videoElement.oncanplay = () => {
        videoElement.play()
    };
}

const stopStream = () => {
    pluginHandler.value.send({message: {request: "stop"}});
    pluginHandler.value.hangup();
}

完整示例代码(vue3)

<template>
    <div>
        <button @click="start">Start</button>
        <button @click="stop">Stop</button>
        <video id="myVideo" style="width: 1280px;height: 720px;border: 1px solid #ccc;margin-top: 10px;"></video>
    </div>
</template>

<script setup>
import {ref} from 'vue'

import Janus from 'janus-gateway';
import adapter from 'webrtc-adapter'

const server = "ws://xxx.xx.xxx.xx:8188";
const videoId = "myVideo"; // 接收流的video标签的id
const janus = ref(null); // 保存janus实例
const pluginHandler = ref(null); // 保存插件句柄, 许多交互操作都需要这个变量来操作
const room = "1234"; // 连接的房间id, 默认在后端没改动的情况下为 1234
const userId = ref(null); // 需要拉流的发布者id

const start = () => {
    init()
}

const stop = () => {
    stopStream()
}

const init = () => {
    // 浏览器不支持webRTC
    if (!Janus.isWebrtcSupported()) {
        console.error("::: No webrtc support :::");
        return;
    }
    // 初始化并指定配置项
    Janus.init({
        debug: true,
        dependencies: Janus.useDefaultDependencies({
            adapter
        }),
        callback: () => startJanus()
    })
}

const startJanus = () => {
    // 创建一个新的会话
    janus.value = new Janus({
        server: server,
        success: function () {
            attachToStreamingPlugin(janus.value);
        },
        error: function (error) {
            console.log("::: error :::", error);
        },
        destroyed: function () {
            console.log("::: destroyed :::");
        }
    });
}

const attachToStreamingPlugin = (janus) => {
    janus.attach({
        plugin: "janus.plugin.videoroom",
        room,
        // 成功连接时的回调, 保存插件句柄, 获取房间内所有发布者的列表
        success: function (pluginHandle) {
            pluginHandler.value = pluginHandle;
            getParticipant()
        },
        /*
            接收到信息时的回调,
            由于需要建立webrtc双向连接, 因此需要接收jsep信息并createAnswer回复本地描述来维持webrtc的连接,
            具体逻辑在handleSDP。
        */
        onmessage: function (msg, jsep) {
            console.log(" ::: message :::", JSON.stringify(msg));
            console.log(" ::: jsep :::", jsep);
            handleSDP(jsep);
        },
        // 接收到远程的流时的处理
        onremotetrack: function (stream) {
            console.log(" ::: remote stream :::", stream);
            handleStream(stream);
        },
        oncleanup: function () {
            console.log(" ::: cleanup notification :::");
        },
        error: function (error) {
            console.log("::: Error attaching plugin :::" + error);
        },
    });
}

const getParticipant = () => {
    pluginHandler.value.send({
        message: {
            request: "listparticipants",
            room: +room,
            audio: true,
            video: true,
        },
        success: function (response) {
            // 成功回调中可以获取所有房间内发布者的列表
            const participants = response["participants"];
            // 选择第一个用户开始订阅 默认只有一个流
            const firstPart = participants[0]
            startStream(firstPart)
        },
        error: function (error) {
            console.error("::: Error getting participant list :::", error);
        }
    });
}

const startStream = (selectedStream) => {
    // 获取到发布者的id
    var selectedStreamId = selectedStream?.["id"];
    if (selectedStreamId === undefined || selectedStreamId === null) {
        return console.log("::: No selected stream :::");
    }
    userId.value = selectedStreamId
    // 订阅流video和audio
    pluginHandler.value.send({
        message: {
            request: "join",
            feed: selectedStreamId,
            room: +room,
            ptype: "subscriber",
            audio: true,
            video: true,
        }
    })
}

const handleSDP = (jsep) => {
    if (jsep !== undefined && jsep !== null) {
        pluginHandler.value.createAnswer({
            jsep,
            media: {audioSend: false, videoSend: false},
            success: keepRTC,
            error: function (error) {
                console.error("::: WebRTC error :::" + JSON.stringify(error));
            }
        });
    }
}

const keepRTC = (sdp) => {
    console.log("::: sdp :::", sdp);
    pluginHandler.value.send({
        message: {request: "start"},
        jsep: sdp
    });
}

const handleStream = (stream) => {
    const streamRemote = new MediaStream();
    streamRemote.addTrack(stream);
    const videoElement = document.getElementById(videoId);
    videoElement.srcObject = streamRemote;
    videoElement.oncanplay = () => {
        videoElement.play()
    };
}

const stopStream = () => {
    pluginHandler.value.send({message: {request: "stop"}});
    pluginHandler.value.hangup();
}
</script>

你可能感兴趣的:(Audio,前端,javascript,html,vue.js,音视频)