标题
FreeSWITCH 1.10.10 简单图形化界面25-JsSIP虚拟摄像头
目录
前言
在使用jssip进行视频呼叫的时候,jssip会调用本地摄像头,如果调用失败,则无法进行视频呼叫,只能进行音频呼叫。
如下图,在使用jssip时,如果没有摄像头或者摄像头调用失败会提示:
控制台提示:
(以上截图为阻止了摄像头使用权限,模拟调用摄像头失败的情况)
但是在实际应用中,并不是所有电脑都有摄像头设备的,因此在没有摄像头设备,但是需要进行视频呼叫时,可以使用通过canvas画布创建一个虚拟摄像头,模拟硬件视频流。
一、测试环境
参考安装步骤:https://blog.csdn.net/jia198810/article/details/137820796,安装一个FreeSWITCH作为测试环境。
参考使用手册:https://docs.qq.com/pdf/DVEZjSGhXVHhaUEFW?,设置一下WSS环境,并添加账号。
二、添加虚拟摄像头
1.说明
在jssip的ua的call方法中,可以通过options的mediaStream属性,设置jssip在视频呼叫时的媒体流。如下图:
(mediaConstraints 媒体约束为{'audio': true, 'video': true}才会调用摄像头设备,这个就不多说了,我们测试的就是视频呼叫)
2.示例
编写方法获取本地视频流,如果获取失败,则创建canvas画布,模拟视频流。
获取本地视频流,代码示例:
//获取本地媒体
//stream.getTracks() [0]音频 [1]视频.
//stream.getAudioTracks() stream.getVideoTracks()
// 使用canvas创建虚拟摄像头,文字内容为"未找到摄像头设备"
this.createVirtualStream = () => {
const text = "未找到摄像头设备";
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = '64px Arial';
ctx.textAlign = 'center';
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
// 将画布内容转换为MediaStream
const stream = canvas.captureStream();
return stream;
};
// 获取本地媒体流
this.getLocalMediaStream = async (mediaType) => {
try {
this.showLog("尝试获取本地媒体流");
let constraints = mediaType === "video" ? { audio: true, video: true } : { audio: true, video: false };
let stream = await navigator.mediaDevices.getUserMedia(constraints);
return stream;
} catch (error) {
this.showLog('获取本地媒体流失败, 设置虚拟摄像头(音频流和视频流):', error.name, error.message);
// 分别获取获取音频流和视频流,然后合并为一个MediaStream对象
// 获取音频流
let audioConstraints = { audio: true };
let audioStream;
try {
audioStream = await navigator.mediaDevices.getUserMedia(audioConstraints);
} catch (audioError) {
this.showLog('获取音频流也失败,仅使用虚拟摄像头:', audioError.name, audioError.message);
// 如果电脑连声卡驱动都没有,音频都获取失败,则jssip会呼叫失败,虽然音频流返回了虚拟摄像头,但是不会呼叫成功
return this.createVirtualStream();
}
// 获取虚拟摄像头视频流
let videoStream = this.createVirtualStream();
// 合并音频和视频流,返回需要的MediaStream对象
let combinedStream = new MediaStream([...audioStream.getTracks(), ...videoStream.getTracks()]);
return combinedStream;
}
};
jssip呼叫时,设置options的mediaStream属性,代码示例:
//--拨打电话
this.makeCall = async (calleeNumber, mediaType = "audio") => {
if (this.ua == null) {
this.showLog("发起呼叫:没有ua")
return false
}
if (this.ua.isRegistered() == false) {
this.showLog("发起呼叫:未注册")
return false
}
if (calleeNumber == "") {
this.showLog("发起呼叫:被叫号码不能为空")
return false;
}
if (calleeNumber === this.username) {
this.showLog("发起呼叫:不能呼叫自己")
return false;
}
let options = {
eventHandlers: {
progress: (e) => { },
failed: (e) => { },
ended: (e) => { },
confirmed: (e) => { },
},
mediaConstraints: mediaType === "video" ? { audio: true, video: true } : { audio: true, video: false },
//设置自定义的媒体流
mediaStream: await this.getLocalMediaStream(mediaType),
pcConfig: this.getPcConfig()
};
console.log("呼出OPTION:", options)
this.currentCall = this.ua.call(`sip:${calleeNumber}@${this.server}`, options);
}
三、测试
我们把浏览器的视频权限设置为阻止,模拟获取本地视频获取失败的情况,如下图:
如果打开本地摄像头的话,会显示模拟摄像头的内容,如下图:
此时,获取本地视频流失败的情况下,使用jssip进行视频呼叫,测试是可以进行视频呼叫的,如下图:
以上代码为封装的jssip库的代码,用的都是this,原理就是那样的。如果要参考代码,请稍微改改呗,祝君好运。