前言音视频通话的功能之前做过但不是在小程序上而是通过调用zoom、海信这类公司的SDK在Android、iOS的APP上实现我想小程序上再困难还有这个难吗结果调试了两天尼妹的其实也不是很容易呀场景一小程序内纯1v1视频通话效果差但不要钱为什么不推荐呢因为这个视频通话的要求比苛刻当小程序中发起会议接听方必须同时在小程序中的指定页面里否则收不到来电请求参考微信社区智能回答给出的方案1、申请类目工具-视频客服2、开通接口权限app.json中添加语音和视频权限permission: { scope.camera: { desc: 小程序需要您的授权才能使用摄像头进行视频通话 }, scope.record: { desc: 小程序需要您的授权才能使用麦克风进行语音交流 } },3、购买套餐https://fuwu.weixin.qq.com/service/detail/00048c0090477065ad6b475ee56415选择免费款的试用期999个月收费情况如下4、小程序代码view classcontainer !-- 顶部状态显示 -- view classstatus-bar text classstatus-text{{statusText}}/text text classtimer wx:if{{isInCall}}{{formatTime(callDuration)}}/text /view !-- 视频区域 -- view classvideo-container !-- 本地视频画面 -- view classlocal-video {{isInCall ? small : full}} voip-room wx:if{{roomId}} idvoip-room openCamera{{openCamera}} mode{{isInCall ? camera : none}} devicePosition{{devicePosition}} binderroronVoipError bindstatechangeonVoipStateChange /voip-room view classvideo-placeholder wx:else text classplaceholder-text等待连接.../text /view /view !-- 远程视频画面通话中显示 -- view classremote-video wx:if{{isInCall}} text classremote-text对方画面/text /view /view !-- 控制按钮区域 -- view classcontrol-panel !-- 通话前输入和呼叫按钮 -- view classpre-call-controls wx:if{{!isInCall}} view classinput-group text classlabel对方OpenID:/text input classinput typetext placeholder请输入对方OpenID value{{targetOpenId}} bindinputonInputChange / /view view classbutton-group button classbtn video-call bindtapstartVideoCall text classbtn-icon/text text classbtn-text视频通话/text /button button classbtn voice-call bindtapstartVoiceCall text classbtn-icon/text text classbtn-text语音通话/text /button /view /view !-- 通话中控制按钮 -- view classin-call-controls wx:if{{isInCall}} button classcontrol-btn {{muteAudio ? active : }} bindtaptoggleAudio text classcontrol-icon{{muteAudio ? : }}/text text classcontrol-text{{muteAudio ? 取消静音 : 静音}}/text /button button classcontrol-btn {{muteVideo ? active : }} bindtaptoggleVideo text classcontrol-icon{{muteVideo ? ❌ : }}/text text classcontrol-text{{muteVideo ? 开启视频 : 关闭视频}}/text /button button classcontrol-btn switch-camera bindtapswitchCamera text classcontrol-icon/text text classcontrol-text切换摄像头/text /button button classcontrol-btn hangup bindtaphangUp text classcontrol-icon/text text classcontrol-text挂断/text /button /view /view !-- 调试信息开发时显示 -- view classdebug-info wx:if{{debugMode}} text房间ID: {{roomId || 无}}/text text状态: {{voipState}}/text /view /view.container { height: 100vh; display: flex; flex-direction: column; background-color: #1a1a1a; color: white; } /* 状态栏 */ .status-bar { height: 60rpx; padding: 20rpx; display: flex; justify-content: space-between; align-items: center; background-color: rgba(0, 0, 0, 0.5); } .status-text { font-size: 28rpx; } .timer { font-size: 28rpx; font-weight: bold; } /* 视频容器 */ .video-container { flex: 1; position: relative; background-color: #000; } .local-video { position: absolute; background-color: #333; overflow: hidden; transition: all 0.3s; } .local-video.full { width: 100%; height: 100%; } .local-video.small { width: 240rpx; height: 320rpx; top: 40rpx; right: 40rpx; border-radius: 10rpx; border: 2rpx solid rgba(255, 255, 255, 0.5); z-index: 10; } .remote-video { width: 100%; height: 100%; background-color: #222; display: flex; justify-content: center; align-items: center; } .video-placeholder { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; background-color: #333; } .placeholder-text { color: #999; font-size: 32rpx; } .remote-text { color: #999; font-size: 36rpx; } /* 控制面板 */ .control-panel { padding-top: 40rpx; padding-bottom: 40rpx; background-color: rgba(0, 0, 0, 0.7); } /* 通话前控制 */ .pre-call-controls .input-group { margin-bottom: 40rpx; } .label { margin-left: 2.5%; display: block; margin-bottom: 20rpx; font-size: 28rpx; } .input { width: 90%; margin-left: 2.5%; height: 80rpx; background-color: #333; border-radius: 10rpx; padding: 0 20rpx; color: white; font-size: 28rpx; } .button-group { display: flex; } .btn { display: flex; flex-direction: column; align-items: center; height: 200rpx; border: none; justify-content: center; } .video-call { background: linear-gradient(135deg, #ff5e7d, #ff9500); } .voice-call { background: linear-gradient(135deg, #5ac8fa, #007aff); } .btn-icon { font-size: 50rpx; margin-bottom: 10rpx; } .btn-text { font-size: 24rpx; } /* 通话中控制 */ .in-call-controls { display: flex; justify-content: space-around; align-items: center; } .control-btn { display: flex; flex-direction: column; align-items: center; background-color: rgba(255, 255, 255, 0.2); border: none; border-radius: 50%; width: 140rpx; height: 140rpx; justify-content: center; } .control-btn.active { background-color: rgba(255, 255, 255, 0.4); } .control-btn.hangup { background-color: #ff3b30; } .control-icon { font-size: 40rpx; margin-bottom: 10rpx; } .control-text { font-size: 20rpx; } /* 调试信息 */ .debug-info { padding: 20rpx; background-color: rgba(0, 0, 0, 0.8); font-size: 24rpx; color: #ccc; }Page({ /** * 页面的初始数据 */ data: { // 状态相关 statusText: 准备就绪, isInCall: false, callDuration: 0, // 通话时长秒 timer: null, // 通话配置 targetOpenId: , // 对方OpenID roomId: null, // 房间ID isVideoCall: true, // 是否为视频通话 openCamera: true, // 是否开启摄像头 muteAudio: false, // 是否静音 muteVideo: false, // 是否关闭视频 devicePosition: front, // 摄像头位置front/back // VoIP状态 voipState: idle, // idle, calling, connected, ended // 调试 debugMode: true, // 开发阶段可开启上线前关闭 }, /** * 生命周期函数--监听页面加载 */ onLoad(options) { console.log(wmpfVoip) // 有结果即表示引入插件成功 // 初始化VoIP能力 this.initVoIP(); this.setData({ myOpenId:拨打方openid, targetOpenId: 接听方openid }); }, // 初始化VoIP功能 initVoIP() { wx.setEnable1v1Chat({ enable: true, success: (res) { console.log(VoIP初始化成功, res); this.setData({ statusText: VoIP已就绪 }); // 注册VoIP事件监听 this.registerVoIPEvents(); }, fail: (err) { console.error(VoIP初始化失败, err); this.setData({ statusText: VoIP初始化失败 }); wx.showToast({ title: 通话功能初始化失败, icon: none }); } }); }, // 注册VoIP事件监听 registerVoIPEvents() { // 监听成员变化 wx.onVoIPChatMembersChanged((result) { console.log(房间成员变化:, result); this.handleMembersChange(result); }); // 监听通话中断 wx.onVoIPChatInterrupted((result) { console.log(通话被中断, result); this.setData({ statusText: 通话被中断, voipState: ended }); this.cleanUp(); wx.showToast({ title: 通话被中断, icon: none }); }); }, // 处理成员变化 handleMembersChange(result) { const { memberCount, selfOpenId, memberList } result; if (memberCount 2) { // 双方都已进入房间 this.setData({ statusText: 通话中, voipState: connected }); this.startCallTimer(); } else if (memberCount 1) { // 只有一方在房间 this.setData({ statusText: 等待对方接听... }); } else if (memberCount 0) { // 房间已空 this.setData({ statusText: 通话结束 }); this.cleanUp(); } }, // 开始通话计时 startCallTimer() { this.setData({ callDuration: 0 }); this.data.timer setInterval(() { this.setData({ callDuration: this.data.callDuration 1 }); }, 1000); }, // 格式化时间显示 formatTime(seconds) { const mins Math.floor(seconds / 60); const secs seconds % 60; return ${mins.toString().padStart(2, 0)}:${secs.toString().padStart(2, 0)}; }, // 输入框变化 onInputChange(e) { this.setData({ targetOpenId: e.detail.value }); }, // 开始视频通话 startVideoCall() { if (!this.data.targetOpenId) { wx.showToast({ title: 请输入对方OpenID, icon: none }); return; } this.setData({ isVideoCall: true, openCamera: true, statusText: 发起视频通话..., voipState: calling }); this.joinChatRoom(); }, // 开始语音通话 startVoiceCall() { if (!this.data.targetOpenId) { wx.showToast({ title: 请输入对方OpenID, icon: none }); return; } this.setData({ isVideoCall: false, openCamera: false, statusText: 发起语音通话..., voipState: calling }); this.joinChatRoom(); }, /** * 加入聊天房间 * myOpenId:拨打方 * targetOpenId:接听方 */ joinChatRoom() { wx.join1v1Chat({ caller: { openid: this.data.myOpenId, nickname: 拨打方 }, listener: { openid: this.data.targetOpenId, nickname: 接听方 }, video: this.data.isVideoCall, success: (res) { console.log(加入房间成功, res); this.setData({ roomId: res.roomId, isInCall: true }); }, fail: (err) { console.error(加入房间失败, err); this.setData({ statusText: 加入房间失败, voipState: idle }); wx.showToast({ title: 加入房间失败: err.errMsg, icon: none }); } }); }, // 切换音频静音 toggleAudio() { this.setData({ muteAudio: !this.data.muteAudio }); // 实际应该调用VoIP API控制音频 // 这里简化处理实际开发中需要调用相应API console.log(音频状态:, this.data.muteAudio ? 静音 : 取消静音); }, // 切换视频开关 toggleVideo() { this.setData({ muteVideo: !this.data.muteVideo, openCamera: !this.data.muteVideo }); // 实际应该调用VoIP API控制视频 console.log(视频状态:, this.data.muteVideo ? 关闭 : 开启); }, // 切换摄像头 switchCamera() { this.setData({ devicePosition: this.data.devicePosition front ? back : front }); }, // 挂断通话 hangUp() { wx.exitVoIPChat({ success: (res) { console.log(退出通话成功, res); }, fail: (err) { console.error(退出通话失败, err); }, complete: () { this.cleanUp(); } }); }, // VoIP错误处理 onVoipError(e) { console.error(VoIP错误:, e.detail); this.setData({ statusText: VoIP错误: e.detail.errMsg }); }, // VoIP状态变化 onVoipStateChange(e) { console.log(VoIP状态变化:, e.detail); this.setData({ voipState: e.detail.state }); }, // 清理资源 cleanUp() { if (this.data.timer) { clearInterval(this.data.timer); this.setData({ timer: null }); } this.setData({ isInCall: false, callDuration: 0, roomId: null, statusText: 通话结束, voipState: ended }); }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, /** * 生命周期函数--监听页面隐藏 */ onHide() { }, /** * 生命周期函数--监听页面卸载 */ onUnload() { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom() { }, /** * 用户点击右上角分享 */ onShareAppMessage() { } })场景二使用腾讯云插件这个公司没有开通 live-player、live-pusher 的权限要什么证书以后有机会再更新