^ 正の文、
Published on 2025-04-01 / 3 Visits
0
0

用OpenAI做一个可视频通话的聊天机器人

准备工作

  1. 安装AI操作系统
    首先,你需要在你的机器上安装一个适合运行AI应用的操作系统。这将为后续步骤提供必要的环境基础。

  2. 设置LiveKit Cloud账户
    LiveKit提供了强大的实时通信服务,可以用于实现视频通话功能。访问LiveKit Cloud注册账号,并设置好相关服务。

  3. 创建应用模板和虚拟Python环境
    接下来,创建一个新的应用模板,并确保为你的项目设置了一个独立的Python虚拟环境,以便更好地管理依赖包。

  4. 添加API密钥
    为了使你的应用能够与各种服务交互,别忘了添加所需的API密钥。

相关资源和服务

  • DigitalOcean GitHub授权
    DigitalOcean 提供了经济实惠的GPU droplets,且新用户享有$200的免费额度。

  • LiveKit Cloud GitHub授权
    LiveKit Cloud不仅提供5万分钟的免费试用,还支持Webhook通知,方便集成到现有系统中。了解更多信息

  • Deepgram STT
    使用Deepgram的语音转文字服务,可以获得$200的免费额度,非常适合需要处理音频输入的应用。

  • OpenAI API
    最后,不要忘记利用OpenAI的API来赋予你的聊天机器人智能对话的能力。

参考链接

部分关键命令

livekit 安装

curl -sSL https://get.livekit.io | bash

启动服务命令

livekit-server --config ./livekit.yaml --node-ip=8.218.***.** --bind 0.0.0.0

或者是新建run.sh

#!/bin/sh
nohup livekit-server --config ./livekit.yaml > livekit.log 2>&1 &

livekit.yaml 配置文件参数

port: 7880
rtc:
    udp_port: 7882
    tcp_port: 7881
    use_external_ip: true
    enable_loopback_candidate: false
keys:
    APIbxDWetqcjHaa: RlZfytYLmdMOgV2u6fSFAbMhrYQok9B4aVWq48eIE1aa
logging:
    json: false
    level: info

依赖jar包

<dependency>
    <groupId>io.livekit</groupId>
    <artifactId>livekit-server</artifactId>
    <version>0.5.9</version>
</dependency>

livekit 前端演示demo 页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LiveKit Example</title>
 
 
</head>
<body>
<div id="videoTestDiv"></div>
<div id="videoDiv" style="width: 80%"></div>
<input type="text" id="tokenInput" value=""/>
<br/>
<button onclick="user1Fun()" style="margin: 10px;">用户90</button>
<button onclick="user2Fun()" style="margin: 10px;">用户92</button>
<button onclick="user3Fun()" style="margin: 10px;">用户96</button>
<button onclick="user4Fun()" style="margin: 10px;">用户98</button>
<br/>
<button onclick="connect()" style="margin: 10px;">视频链接</button>
<button onclick="connect()" style="margin: 10px;">语音链接</button>
<button onclick="ext()" style="margin: 10px;">登出</button>
<button onclick="openVideo()" style="margin: 10px;">打开摄像头</button>
 
<script src="livekit-client.umd.min.js"></script>
<script src="eruda.js"></script>
 
<script>
 
    // 手机上的调试插件
    eruda.init();
    const tokenInput = document.getElementById("tokenInput");
    // 获取页面的高度和宽度
    let pageHeight = document.documentElement.clientHeight;
    let pageWidth = document.documentElement.clientWidth;
    user1Fun();
    // 检查是否支持webrtc和是否存在麦克风和摄像头
    // 如果手机上访问必须是https,不是则调用不了麦克风和摄像头
    // checkVideo();
 
    // const wsURL = "wss://gscs.yuming.com:7443"
    const wsURL = "ws://8.218.***.**:7880"
    const room = new LivekitClient.Room({
        // 自动管理订阅的视频质量
        adaptiveStream: true,
        // 优化已发布曲目的发布带宽和CPU
        dynacast: true,
        // 默认捕获设置
        videoCaptureDefaults: {
            resolution: LivekitClient.VideoPresets.h720.resolution,
        },
    });
 
    // 链接livekit
    async function connect() {
        let token = tokenInput.value;
        console.log(token);
        console.log(wsURL);
        if (token == "") {
            alert("请输入token");
            return;
        }
        // 预热连接,这可以在页面加载后立即调用
        // room.prepareConnection(wsURL, token);
        // 设置事件侦听器
        allHandle();
        // 链接
        await room.connect(wsURL, token);
        console.log('链接成功', room.roomInfo);
        alert('链接成功');
        // 发布本地相机和麦克风曲目 下面可以单独开麦克风和相机
        // await room.localParticipant.enableCameraAndMicrophone();
        const p = room.localParticipant;
        // 打开本地用户的相机和麦克风,这可能会触发浏览器提示
        // 以确保授予权限
        await p.setCameraEnabled(true);
        await p.setMicrophoneEnabled(true);
        // 开始共享用户的屏幕,这将触发浏览器提示选择
        // 要共享的屏幕。
        // await p.setScreenShareEnabled(true);
    }
 
    // 所有监听事件
    function allHandle() {
        // 房间有人加入事件
        room.on(LivekitClient.RoomEvent.ParticipantConnected, function () {
            console.log("一个远程参与者 在本地参与者之后加入。");
        })
        room.on(LivekitClient.RoomEvent.ParticipantDisconnected, function () {
            console.log("远程参与者离开");
        })
        room.on(LivekitClient.RoomEvent.Reconnecting, function () {
            console.log("与服务器的连接已中断,它正在尝试重新连接。");
        })
        room.on(LivekitClient.RoomEvent.Reconnected, function () {
            console.log("重新连接已成功");
        })
        room.on(LivekitClient.RoomEvent.Disconnected, function () {
            console.log("由于房间关闭或无法恢复的故障而与房间断开连接");
        })
        room.on(LivekitClient.RoomEvent.TrackPublished, function () {
            console.log("本地参与者加入后,新曲目将发布到房间");
        })
        room.on(LivekitClient.RoomEvent.TrackUnpublished, function () {
            console.log("远程参与者已取消发布轨道");
        })
        room.on(LivekitClient.RoomEvent.TrackMuted, function () {
            console.log("轨道被静音,本地轨道和远程轨道都会触发");
        })
        room.on(LivekitClient.RoomEvent.TrackUnmuted, function () {
            console.log("轨道未静音,本地轨道和远程轨道都会触发");
        })
        room.on(LivekitClient.RoomEvent.LocalTrackPublished, function () {
            console.log("本地曲目已成功发布");
        })
        room.on(LivekitClient.RoomEvent.LocalTrackUnpublished, function (track, publication, participant) {
            console.log("本地曲目未发布");
            publication.track.detach();
        })
        room.on(LivekitClient.RoomEvent.ActiveSpeakersChanged, function () {
            console.log("当前活动的发言人已更改");
        })
        room.on(LivekitClient.RoomEvent.IsSpeakingChanged, function () {
            console.log("当前参与者已更改发言状态");
        })
        // 参与者的连接质量已更改
        room.on(LivekitClient.RoomEvent.ConnectionQualityChanged, function () {
            console.log("参与者的连接质量已更改");
        })
        room.on(LivekitClient.RoomEvent.ParticipantMetadataChanged, function () {
            console.log("通过服务器API更新了参与者的元数据");
        })
        room.on(LivekitClient.RoomEvent.RoomMetadataChanged, function () {
            console.log("与房间相关联的元数据已更改");
        })
        room.on(LivekitClient.RoomEvent.DataReceived, function () {
            console.log("从另一个参与者或服务器接收的数据");
        })
        // 指示订阅的曲目是否因带宽而暂停
        room.on(LivekitClient.RoomEvent.TrackStreamStateChanged, function () {
            console.log("指示订阅的曲目是否因带宽原因而暂停");
        })
        room.on(LivekitClient.RoomEvent.TrackSubscriptionPermissionChanged, function () {
            console.log("订阅的曲目之一已更改当前参与者的曲目级别权限");
        })
        room.on(LivekitClient.RoomEvent.ParticipantPermissionsChanged, function () {
            console.log("当前参与者的权限更改时");
        })
 
        // 有新成员加入是会访问这个方法
        room.on(LivekitClient.RoomEvent.TrackSubscribed, handleTrackSubscribed)
        // 曲目取消订阅
        room.on(LivekitClient.RoomEvent.TrackUnsubscribed, function (track, publication, participant) {
            track.detach();
            let videoDiv = document.getElementById("videoDiv");
            videoDiv.innerHTML="";
            console.log("有人退出了房间!")
        })
    }
 
    // 从服务器接收曲目始于订阅。有新成员加入是会访问这个方法
    function handleTrackSubscribed(track, publication, participant) {
        console.log("有人加入了房间!")
        // 将音轨附加到新的HTMLVideoElement或HTMLAudioElement
        const element = track.attach();
        let videoDiv = document.getElementById("videoDiv");
        videoDiv.appendChild(element);
        let video = videoDiv.querySelector("video");
        if (video) {
            video.style.width = pageWidth + "px";
            video.style.height = (pageHeight - 200) + "px";
        }
        // 或附加到现有元素
        // track.attach(element)
    }
 
    // 退出登录
    function ext() {
        room.disconnect();
    }
 
    function user1Fun() {
        // 我是90
        tokenInput.value = ""
    }
 
    function user2Fun() {
        // 我是92
        tokenInput.value = ""
    }
 
    function user3Fun() {
        // 我是96
        tokenInput.value = ""
    }
 
    function user4Fun() {
        // 我是98
        tokenInput.value = ""
    }
 
    // 打开摄像头
    function openVideo() {
        let videoTestDiv = document.getElementById("videoTestDiv");
        // 创建一个新的 video 元素
        const videoElement = document.createElement('video');
        videoElement.style.width = pageWidth + "px";
        videoElement.style.height = (pageHeight - 200) + "px";
        videoElement.setAttribute('playsinline', '');
        videoElement.setAttribute('autoPlay', '');
        videoTestDiv.appendChild(videoElement);
        navigator.mediaDevices.getUserMedia({video: true, audio: true}).then(function (stream) {
            console.log(stream);
            videoElement.srcObject = stream;
            // videoTest.src = window.webkitURL.createObjectURL(stream);
        });
    }
 
    /**
     * 检查摄像头和麦克风是否可用
     */
    function checkVideo() {
        // 检查是否支持 getUserMedia 方法
        if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
            // 请求访问摄像头和麦克风
            navigator.mediaDevices.getUserMedia({video: true, audio: true})
                .then(function (stream) {
                    // 摄像头和麦克风设备可用
                    console.log('摄像头和麦克风可用');
                })
                .catch(function (error) {
                    // 摄像头和麦克风设备不可用
                    console.error('摄像头或麦克风不可用', error);
                });
        } else {
            alert('浏览器不支持webRtc');
        }
    }
 
 
</script>
</body>
</html>


Comment