更新记录
1.0.16(2026-08-29)
新增语音通话以及新接口
1.0.15(2026-08-29)
框架重构,原1对1,改为 以1对多,语音通话下一版本
1.0.14(2026-04-23)
修复
查看更多平台兼容性
uni-app x(3.8.5)
| Chrome | Safari | Android | Android插件版本 | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|
| × | × | 7.0 | 1.0.15 | × | × | × |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
tc-webrtc 多人视频通话插件接口文档
📌 概述
tc-webrtc 支持 多人 同时视频通话,无需媒体服务器,通过信令服务器协调连接
特别注意!!
试用前请先【导入示例项目】 导入完成后,再申请试用插件并导入到示例项目中!
目前支持 :多人视频通话 、多人语音通话
支持:蒸汽模式
WebSocket (官方X版本)
当前版本:tc-webrtc(v2.0)(1对多)
如需其他插件可联系我定制。
接口分类
| 分类 | 方法 |
|---|---|
| 初始化 | WebRTCapi.setIceServers(), WebRTCapi.initializeFactory(), WebRTCapi.destroy() |
| 视图绑定 | WebRTCapi.bindLocalView(), WebRTCapi.bindRemoteView() |
| 信令 | WebRTCapi.setSignalingCallback(), WebRTCapi.handleOffer(), WebRTCapi.handleAnswer(), WebRTCapi.handleIce() |
| 连接管理 | WebRTCapi.connectToUser(), WebRTCapi.removeUser(), WebRTCapi.leaveRoom() |
| 音视频控制 | WebRTCapi.setVideoEnabled(), WebRTCapi.setAudioEnabled(), WebRTCapi.switchCamera(), WebRTCapi.setSpeakerphoneOn(), WebRTCapi.setVoiceOnlyMode() |
接口速查表
| 方法调用 | 参数 | 作用 |
|---|---|---|
WebRTCapi.setIceServers(configJson) |
JSON数组详见示例工程 | 配置 STUN/TURN 服务器 |
WebRTCapi.setSignalingCallback(callback) |
callback: (type, data, sdpMid, mLineIdx, targetUid) => void | 设置信令发送回调 |
WebRTCapi.initializeFactory() |
无 | 初始化引擎(摄像头/麦克风) |
WebRTCapi.destroy() |
无 | 彻底销毁引擎,释放全部资源 |
WebRTCapi.bindLocalView(view) |
view: any (native-view element) | 绑定本地摄像头预览视图 |
WebRTCapi.bindRemoteView(uid, view) |
uid: string, view: any | 绑定远端用户ID视频渲染视图 |
WebRTCapi.connectToUser(uid) |
uid: string | 主动向指定用户ID发起 Offer |
WebRTCapi.handleOffer(fromUid, sdp) |
fromUid: string, sdp: string | 处理 Offer,自动回复 Answer |
WebRTCapi.handleAnswer(fromUid, sdp) |
fromUid: string, sdp: string | 处理 Answer,完成连接 |
WebRTCapi.handleIce(fromUid, candidate, sdpMid, mLineIdx) |
fromUid: string, candidate: string, sdpMid: string, mLineIdx: number | 添加 ICE 候选 |
WebRTCapi.removeUser(uid) |
uid: string | 移除远端用户ID(断开连接,释放渲染器) |
WebRTCapi.leaveRoom() |
无 | 离开房间,断开连接,保留引擎 |
WebRTCapi.setVideoEnabled(enabled) |
boolean | true=启用/false=禁用摄像头 |
WebRTCapi.setAudioEnabled(enabled) |
boolean | true=启用/false=禁用麦克风 |
WebRTCapi.switchCamera() |
无 | 切换 前(默认)/后 摄像头 |
WebRTCapi.setSpeakerphoneOn(true) |
boolean | true=扬声器(默认),false=听筒 |
WebRTCapi.setVoiceOnlyMode() |
boolean | false=视频通话(默认),true=仅语音通话 |
注意事项
WebRTCapi.setVoiceOnlyMode()
在视频通话页面可以不用
在语音通话时如下所示
onLoad() {
WebRTCapi.setVoiceOnlyMode(true);
}
加入房间
例如:用户ID+房间号
uni.navigateTo({
url:"/pages/vd_webrtc/vd_webrtc_room?myuid=22&roomid=asd123"
});
更多查考示DEMO
一、配套信令服务端代码(PHP)(仅为快速跑通本插件,服务器安全请自行优化处理)
<?php
$ws = new Swoole\WebSocket\Server("0.0.0.0", 9505);//服务器放行9505
$ws->set([
'open_tcp_keepalive' => 1,
'tcp_fastopen' => true,
'max_conn' => 1000,
'dispatch_mode' => 1,
'daemonize' => false,
'user' => 'www',
'group' => 'www',
'heartbeat_check_interval' => 30,
'heartbeat_idle_time' => 60,
'enable_signalfd' => true,
'log_file' => '/tmp/swoole_ws.log',
]);
$redis = new Redis();
$redis->connect('127.0.0.1', 6379, 60);
$redis->auth('123456') or die("redis 连接失败");//Redis密码
function getRoomUsers($redis, $roomid) {
$users = [];
$entries = $redis->hGetAll("rtc_roomid_{$roomid}");
foreach ($entries as $uid => $serialized) {
$data = unserialize($serialized);
if (isset($data['uid'])) $users[] = $data['uid'];
}
return $users;
}
function broadcastToRoom($ws, $redis, $roomid, $myuid, $message, $excludeFd = null) {
$entries = $redis->hGetAll("rtc_roomid_{$roomid}");
foreach ($entries as $uid => $serialized) {
if ($uid == $myuid) continue;
$fd = $redis->get("rtc_uid_{$uid}");
if ($fd && $fd != $excludeFd) {
$ws->push($fd, json_encode($message));
}
}
}
function unicastToUser($ws, $redis, $targetUid, $message) {
$fd = $redis->get("rtc_uid_{$targetUid}");
if ($fd) {
$ws->push($fd, json_encode($message));
return true;
}
return false;
}
function registerFdMapping($redis, $fd, $uid, $roomid) {
$redis->hset("rtc_fd_{$fd}", 'uid', $uid);
$redis->hset("rtc_fd_{$fd}", 'roomid', $roomid);
}
function clearFdMapping($redis, $fd) {
$redis->del("rtc_fd_{$fd}");
}
$ws->on('open', function ($ws, $request) {
//echo "New connection fd={$request->fd}\n";
});
$ws->on('message', function ($ws, $frame) use ($redis) {
$data = json_decode($frame->data);
if (!$data) {
return;
}
$type = $data->type ?? '';
$roomid = $data->roomid ?? '';
$myuid = $data->myuid ?? '';
$targetUid = $data->targetUid ?? null;
switch ($type) {
case 'addroom':
$redis->hset("rtc_roomid_{$roomid}", $myuid, serialize(['uid' => $myuid]));
$redis->set("rtc_uid_{$myuid}", $frame->fd);
registerFdMapping($redis, $frame->fd, $myuid, $roomid);
$users = getRoomUsers($redis, $roomid);
$ws->push($frame->fd, json_encode(['type' => 'room_users', 'users' => $users]));
broadcastToRoom($ws, $redis, $roomid, $myuid, ['type' => 'new_peer', 'uid' => $myuid]);
break;
case 'leave':
$redis->hdel("rtc_roomid_{$roomid}", $myuid);
$redis->del("rtc_uid_{$myuid}");
clearFdMapping($redis, $frame->fd);
broadcastToRoom($ws, $redis, $roomid, $myuid, ['type' => 'peer_left', 'uid' => $myuid]);
break;
case 'ping':
// 忽略,仅保持连接
break;
case 'offer':
case 'answer':
case 'ice':
// 优先单播
if ($targetUid) {
unicastToUser($ws, $redis, $targetUid, (array)$data);
} else {
// 降级广播(保留兼容性)
broadcastToRoom($ws, $redis, $roomid, $myuid, (array)$data);
}
break;
default:
broadcastToRoom($ws, $redis, $roomid, $myuid, (array)$data);
break;
}
});
$ws->on('close', function ($ws, $fd) use ($redis) {
$mapping = $redis->hGetAll("rtc_fd_{$fd}");
if (empty($mapping)) return;
$uid = $mapping['uid'] ?? null;
$roomid = $mapping['roomid'] ?? null;
if ($uid && $roomid) {
$redis->hdel("rtc_roomid_{$roomid}", $uid);
$redis->del("rtc_uid_{$uid}");
clearFdMapping($redis, $fd);
broadcastToRoom($ws, $redis, $roomid, $uid, ['type' => 'peer_left', 'uid' => $uid]);
}
});
// 定时清理僵尸连接(每60秒)
Swoole\Timer::tick(60000, function () use ($ws, $redis) {
$keys = $redis->keys("rtc_fd_*");
foreach ($keys as $key) {
$fd = str_replace("rtc_fd_", "", $key);
$info = $ws->getClientInfo($fd);
if (!$info || $info['websocket_status'] != WEBSOCKET_STATUS_ACTIVE) {
$mapping = $redis->hGetAll($key);
if (isset($mapping['uid']) && isset($mapping['roomid'])) {
$uid = $mapping['uid'];
$roomid = $mapping['roomid'];
$redis->hdel("rtc_roomid_{$roomid}", $uid);
$redis->del("rtc_uid_{$uid}");
$redis->del($key);
broadcastToRoom($ws, $redis, $roomid, $uid, ['type' => 'peer_left', 'uid' => $uid]);
}
}
}
});
$ws->start();
二、STUN/TURN 服务器架设(仅为快速跑通本插件,服务器安全请自行优化处理)
以CentOS/RHEL 系统为例使用 YUM 包管理器安装
1.安装 EPEL 仓库
sudo yum install epel-release
2.安装 Coturn
sudo yum install coturn
装完成后,需要编辑配置文件
配置文件通常在 【/etc/coturn/turnserver.conf】
找到并编辑【turnserver.conf】在最后一行添加如下配置
no-loopback-peers
response-origin-only-with-rfc5780
listening-port=3478
tls-listening-port=5349
relay-ip=【你服务器IP地址】
relay-threads=50
lt-cred-mech
cert=/etc/coturn/ca.crt
pkey=/etc/coturn/ca.key
realm=【你的域名】(重要。例如:st.myrtc.cn)
min-port=49152
max-port=65535
user=cs123:cs456
注意user=cs123:cs456
分别为
cs123:cs456
账号:密码
配置完成后保存 防火墙也要开放TCP/UDP 3478和 49152-65535端口
coturn相关命令
sudo systemctl daemon-reload # 重新加载 systemd 配置
sudo systemctl enable coturn # 设置开机自启
sudo systemctl start coturn # 立即启动服务
sudo systemctl status coturn # 检查服务状态 (coturn安装完成后一定要使用此命令检查)。
返回
确保状态是active (running)
然后前往
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
测试结果
URL (if present) 显示以下就可以使用了
stun:st.myrtc.cn:3478
turn:st.myrtc.cn:3478?transport=tcp
三、wss地址以及其他(以宝塔面板为例)
假如我们有一个域名:myrtc.cn (以下均已这个域名为示例)
准备一个二级域名
例如:rtc.myrtc.cn
1.创建一个站点 rtc.myrtc.cn 》打开网站目录》创建成一个名字为 myrtc.php文件、将上方的PHP代码 复制并保存
具体请自行操作
2.设置网站伪静态(注意域名rtc.myrtc.cn 需要开启 ssl 也就是https://)
location /wsss {
proxy_pass http://wsss;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
3.配置文件
在server{}前添加代码
map $http_upgrade $connection_upgrade {
default upgrade; '' close;
}
upstream wsss {
server localhost:9505;
}
server
{
listen 80;
listen 443 ssl ;
........//原配置不要动
}
保存就得到了这样的地址: wss://rtc.myrtc.cn/wsss

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 71
赞赏 0
下载 12564065
赞赏 1949
赞赏
京公网安备:11010802035340号