| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- import {
- Room,
- RoomEvent,
- createLocalTracks,
- createLocalAudioTrack,
- createLocalVideoTrack,
- } from 'livekit-client'
- console.log("Room 构造函数:", Room)
- class Trtc {
- constructor() {
- this.room = null
- this.localTracks = []
- }
- // 安全 attach 远端轨道
- attachTrack(track, participant) {
- const container = document.getElementById('video-' + participant.identity)
- if (!container) return
- // 已经 attach 过就跳过,避免闪屏
- if (track.attachedElements.length > 0) return
- const element = track.attach()
- element.style.width = '100%'
- element.style.height = '100%'
- element.style.objectFit = 'cover'
- container.appendChild(element)
- }
- async joinRoom(token, url, userId, onRemoteTrack) {
- try {
- // 如果已有房间,先退出
- if (this.room) {
- await this.leaveRoom()
- }
- this.room = new Room()
- // 监听远端轨道
- this.room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
- console.log('📡 TrackSubscribed:', track.kind, participant.identity)
- if (track.kind === 'video' || track.kind === 'audio') {
- this.attachTrack(track, participant)
- if (typeof onRemoteTrack === 'function') {
- onRemoteTrack(track, participant)
- }
- }
- })
- // 检查设备
- this.localTracks = []
- if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
- const devices = await navigator.mediaDevices.enumerateDevices()
- const hasMic = devices.some(d => d.kind === 'audioinput')
- const hasCam = devices.some(d => d.kind === 'videoinput')
- if (hasMic || hasCam) {
- this.localTracks = await createLocalTracks({
- audio: hasMic,
- video: hasCam,
- })
- console.log('本地轨道列表:', this.localTracks)
- } else {
- console.warn('未检测到摄像头或麦克风,将以静默方式加入房间')
- }
- } else {
- console.warn('当前环境不支持 mediaDevices,将以纯接收模式加入房间')
- }
- console.log('发起连接到 LiveKit ...', url)
- // 监听连接状态
- const connectedPromise = new Promise((resolve, reject) => {
- const timeout = setTimeout(() => {
- reject(new Error('连接 LiveKit 超时'))
- }, 15000)
- try {
- this.room.once(RoomEvent.Connected, () => {
- clearTimeout(timeout)
- console.log('成功连接到 LiveKit 房间')
- resolve()
- // 主动挂载已有轨道
- if (this.room.participants && typeof this.room.participants.values === 'function') {
- for (const participant of this.room.participants.values()) {
- participant.tracks.forEach(publication => {
- if (publication.isSubscribed && publication.track) {
- this.attachTrack(publication.track, participant)
- if (typeof onRemoteTrack === 'function') {
- onRemoteTrack(publication.track, participant)
- }
- }
- })
- }
- }
- })
- this.room.once(RoomEvent.ConnectionError, (err) => {
- clearTimeout(timeout)
- console.error('LiveKit 连接错误:', err)
- reject(err)
- })
- } catch (e) {
- console.error('LiveKit连接失败:', e)
- }
- })
- this.room.on(RoomEvent.Disconnected, () => {
- console.warn('与 LiveKit 断开连接')
- })
- // 发起连接
- await this.room.connect(url, token, {
- autoSubscribe: true,
- })
- await connectedPromise
- // 发布本地轨道
- for (const track of this.localTracks) {
- console.log('正在发布轨道:', track.kind)
- await this.room.localParticipant.publishTrack(track)
- }
- return this.room
- } catch (err) {
- console.error('joinRoom 出错:', err)
- throw err
- }
- }
- getLocalVideoTrack() {
- return this.localTracks.find(t => t.kind === 'video') || null
- }
- async leaveRoom() {
- if (this.room) {
- console.log("Trtc 离开房间")
- // 停止并释放本地轨道
- this.localTracks.forEach(track => {
- try {
- track.stop()
- track.detach()
- } catch (e) {
- console.warn("轨道释放失败:", e)
- }
- })
- try {
- await this.room.disconnect()
- } catch (e) {
- console.error("房间断开异常:", e)
- }
- this.room = null
- this.localTracks = []
- } else {
- this.room = null
- console.warn("离开房间 时 room 为 null")
- }
- }
- }
- export {
- createLocalAudioTrack,
- createLocalVideoTrack,
- }
- export default new Trtc()
|