2026-04-29 15:18:30 +08:00
/ * *
2026-05-24 01:46:57 +08:00
* 闂傚倸鍊搁崐鐑芥嚄閸撲礁鍨濇い鏍 亹閳ь剨绠撳畷濂稿Ψ閵夛附袣闂備礁鎼 粙渚 € 宕㈡總鍛婂 € 块柛顭戝亖娴滄粓鏌熸潏鍓у埌闁告梻鏁婚弻娑滅疀閹惧瓨鎷遍梺闈涙搐鐎氫即鐛 Ο灏栧亾濞戞 顏堝焵椤掍礁濮嶉柡 ?
* 婵犵數濮烽弫鎼佸磻閻樿 绠垫い蹇撴 缁 € 濠囨煃瑜滈崜姘辨崲濞戞瑥绶為悗锝庡亞椤 ︿ 即鎮楀 ▓ 鍨 珮闁稿 锕ユ穱濠囧醇閺囩偟鍊為梺瀹犮 € € 閸嬫挾绱掑Δ鈧 ˇ闈涱潖濞差亝鐒婚柣鎰 蔼鐎氭澘顭胯 椤曨參鍩 € 椤掑喚娼愭繛娴嬫櫇閹广垹鈹戦崱鈺佹 闂佸湱铏庨崰妤呭磻閹邦喒鍋撶憴鍕 婵炶 绠戦埢 ? Observable 濠电姷鏁告慨鐑姐 € 傞挊澹 ╋ 綁宕ㄩ弶鎴濈 € 銈呯箰閻楀棝鎮為崹顐犱簻闁瑰搫妫楁禍鍓х磼閸撗嗘 闁告ɑ鍎抽埥澶愭偨缁嬭法鍔 ﹀ 銈嗗笒鐎氼參鍩涢幋锔界厵缂佸 瀵ч幑锝夋煃閽樺 妯 € 闁哄被鍊曠叅閻犲洩灏欐禒鎾 ⒑闂堟稒澶勯柛鏃 € 鐟ラ 锝嗙節濮橆儵銊 ╂ 煏婢跺牆鍔氱紒渚囧櫍濮婄粯鎷呴崨濠呯 濡炪値鍘奸悧濠囥 € 冮妷鈺佸窛濠电姴鍟 崝鍛 節閻㈤潧孝闁稿 ﹪ 娼у嵄 ? Redux / Vuex / Pinia
2026-04-29 15:18:30 +08:00
* /
import { mockCallSession } from './models.js' ;
2026-05-24 01:46:57 +08:00
import { RenderStreaming } from "../../module/renderstreaming.js" ; // WebRTC闂傚倸鍊风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺鍝勬缁捇骞冨Δ鍛棃婵炴垶鐟﹂崰鎰箾閹寸偞灏紒澶婄秺瀵濡搁妷銏☆潔濠碘槅鍨拃锔界妤e 啯鈷?
import { getServerConfig , getRTCConfiguration } from "../js/config.js" ; //闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢敂钘変罕闂佺硶鍓濋悷褔鎯岄幘缁樺€垫繛鎴烆伆閹达箑鐭楅煫鍥ㄧ⊕閻撶喖鏌¢崘銊モ偓鍝ユ暜閸洘鈷掗柛灞诲€曢悘锕傛煛鐏炵偓绀冪紒缁樼洴閹瑩顢楁担鍝勭到闂備焦鐪归崺鍕垂閹殿喖顥氭い鎾跺У椤洟鏌熼幑鎰靛殭缂佲偓閸愵喗鐓欐い鏍ф鐎氼喗绂嶉鍫熲拻濞达絽鎽滅粔鐑樸亜閵夛附灏柍璇茬Ч瀹曠娀鎯勭€n喗鈷掗柛灞捐壘閳ь剛鍏橀幃鐐烘晝閳ь剟鈥﹂崹顔ョ喖鎳栭埡鍐帬闂備礁澹婇崑鍛哄鈧畷?
import { showNotification , generateId } from './utils.js' ; // 闂傚倸鍊峰ù鍥敋瑜嶉湁闁绘垼妫勯弸渚€鏌熼梻瀵割槮闁稿被鍔庨幉鎼佸棘鐠恒劍娈鹃梺鎸庣箓椤︻垶鐛姀锛勭闁瑰鍋熼幊鍛存煕閺冨牊娑ч柍瑙勫灴椤㈡瑧绮电€n剙濮煎┑鐐茬摠缁娀宕滈悢椋庢殾闁挎繂顦悞鍨亜閹烘垵顏柍閿嬪灴閺屾稑鈽夊鍫濅紣闂佽绻愬畷顒勫煘閹达富鏁婇柛婵嗗閸嬫挸鈹戦崱鈺佹?
2026-04-29 15:18:30 +08:00
import chatMessage from './chatmessage.js' ;
2026-05-24 00:54:58 +08:00
import {
DEFAULT _PARTICIPANT _AVATAR ,
DEFAULT _PARTICIPANT _NAME ,
buildParticipantsSyncData ,
omitParticipant ,
removeParticipant ,
upsertParticipant
} from './participants.js' ;
2026-05-24 01:01:28 +08:00
import {
AUDIO _CONFIG ,
VAD _CONFIG ,
VIDEO _ONLY _CONSTRAINT ,
buildVideoConstraints ,
getAdaptiveVideoBitrate ,
getResolutionLabel ,
getTargetResolutionBitrate
} from './media-config.js' ;
2026-05-24 01:29:34 +08:00
import { buildStatsLogPayload , createAudioAnalyser , getAudioLevel } from './media-monitoring.js' ;
2026-05-24 01:46:57 +08:00
import {
bindInviteSocketEvents ,
buildSocketUserInfoPayload ,
createSignalingInstance ,
ensureSignalingStarted ,
getActiveSignalingInstance ,
sendInviteSignal ,
sendSocketUserInfo
} from './signaling-session.js' ;
2026-05-24 01:01:28 +08:00
import { getNetworkQualityFromSummary , summarizeInboundStats } from './webrtc-stats.js' ;
2026-04-29 15:18:30 +08:00
class CallStateManager {
constructor ( ) {
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢妶鍥╃厠闂佸壊鍋呭ú宥夊焵椤掑﹦鐣电€规洖銈告慨鈧柕蹇嬪灩椤︹晛鈹戞幊閸娧呭緤娴犲鐤い鏍仜绾惧鏌熼幍顔碱暭闁绘挾濞€閺屾稑鈹戦崟顐㈠Ф闂佸搫妫崹鍫曞蓟?
2026-04-29 15:18:30 +08:00
this . state = {
id : generateId ( ) ,
session : {
... mockCallSession ,
2026-05-24 01:46:57 +08:00
status : 'idle' // 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹濠德板€曢崯顖氱暦閺屻儲鐓曠€光偓閳ь剟宕曢幋鐘电闁哄稁鍘介悡娆撴煙濞堝灝鏋涙い锝呫偢閺屾稓鈧綆鍋勬慨宥夋煛鐏炶鈧繂鐣烽锕€唯妞ゆ棁濮ら惁婊堟⒒娴d警鐒炬い鎴濇楠炴劖绻濆顒傤唵闂佸憡绋戦悺銊╁磻椤忓懌浜滈柡宥冨妿閻倖淇婇幓鎺撹础缂佽鲸鎹囧畷鎺戔枎閹搭厽袦闂備礁鎼悧婊堝礈濞嗘挴鈧棃宕橀钘夌檮婵?
2026-04-29 15:18:30 +08:00
} ,
2026-05-24 01:46:57 +08:00
localStream : null , // MediaStream 闂傚倸鍊峰ù鍥敋瑜嶉湁闁绘垼妫勯弸渚€鏌熼梻鎾闁逞屽厸閻掞妇鎹㈠┑瀣倞闁靛鍎冲Ο?
remoteStream : null , // 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩顔瑰亾閸愵喖骞㈡俊鐐存礀閹碱偊鍩為幋鐘亾閿濆骸浜滈柛鎾崇秺濮婅櫣绱掑Ο 鍝勑曢梺鍛婃尰绾板秶绮嬪澶娢ч柛鈩冪懅閻﹀牊绻濋悽闈浶㈤柛濠咁潐閸掑﹪鎮¢獮鐔风秺閹剝鎯旈埦鈧崑鎾诲捶椤撶喎搴婂┑鐐村灦濮樸劎绮堥崟顖涚厽婵☆垰鍚嬮弳鈺呮煟閳╁喚鐒界紒杈ㄦ尰閹峰懏绂掔€n 亝鎳欓梻浣藉吹閸熷潡寮插☉銏″仼鐎瑰嫭瀚堥弮鍫濆窛妞ゆ棃妫跨花鐢告⒑閻熸澘鎮戦柟顖氱焸瀹曚即寮介鐐垫煣闂佹寧绻傞ˇ浼村煕閹达附鐓曟繝闈涙椤忊晠鏌℃担鐟扳枙闁哄本鐩俊鐑筋敍濠婂啫鐓傚┑鐐茬摠缁挾绮婚弽顓炶摕闁跨喓濮寸粈瀣亜閹扳晛鈧顢欐繝鍥ㄧ厽闁靛繆鏅涢悘鐘充繆椤愶絿绠炵€殿喖顭锋俊鎼佸Ψ閵忊槅娼旀繝娈垮枟椤ㄥ懎螞濡ゅ懎鏋佺紒顖滄畽ticipant缂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏇楀亾閾荤偤鐓崶銊р 槈闁搞劌鍊归妵鍕冀閵娧佲偓鎺旂磼閻樿崵鐣洪柡灞剧洴婵$ 兘顢欓悡搴浇闂備胶顭堥鍐礉瀹ュ桅闁告洦鍨奸弫鍥煟閺冨牜妫戞い鎴濆€荤槐鎾存媴閹绘帊澹?
remoteStreams : { } , // 婵犵數濮烽弫鍛婃叏娴兼潙鍨傚┑鍌滎焾閺勩儵鏌″鍐ㄥ闁崇懓绉电换娑橆啅椤旇崵鍑归梺绋垮椤ㄥ﹪寮婚敐澶婄疀闂傚牊绋戦~顏呯箾鐎涙ê娈犻柛濠冪墱閹广垹鈹戦崶鈺冪槇闂佺鏈粙鎴︻敁濞戞瑧绠鹃悗鐢殿焾鐢爼鏌涙繝鍌涜础闁?Map: { connectionId: MediaStream }闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樻噽閻瑩鏌熺€涙绠ラ柣鎺曞Г缁绘繈寮撮悩铏彎t缂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏇楀亾閾荤偤鐓崶銊р 槈闁搞劌鍊归妵鍕冀閵娧佲偓鎺旂磼閻樿崵鐣洪柡灞剧洴婵$ 兘顢欓悡搴浇闂備胶顭堥鍐礉瀹ュ桅闁告洦鍨奸弫鍥煟閺冨牜妫戞い鎴濆€荤槐鎾存媴閹绘帊澹?
participants : { } // 婵犵數濮烽弫鍛婃叏娴兼潙鍨傚┑鍌滎焾閺勩儵鏌″搴″箺闁稿鍊楅埀顒傛嚀鐎氼參顢楅悷鍍玞ipant闂傚倸鍊搁崐鐑芥倿閿曗偓椤啴宕归鍛姺闂佺鍕垫當缂佲偓婢跺备鍋撻獮鍨姎妞わ富鍨跺浼村Ψ閿斿墽顔曢梺鐟邦嚟閸嬬喖骞婇崨瀛樼厓闁荤喐澹嗘晥闂佸搫鑻粔褰掑春閳╁啯濯撮柛娑橈攻椤撹法绱?Map: { participantId: { id, name, avatar, mediaState, status } }闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樻噽閻瑩鏌熺€涙绠ラ柣鎺曞Г缁绘繈寮撮悩铏彎t缂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏇楀亾閾荤偤鐓崶銊р 槈闁搞劌鍊归妵鍕冀閵娧佲偓鎺旂磼閻樿崵鐣洪柡灞剧洴婵$ 兘顢欓悡搴浇闂備胶顭堥鍐礉瀹ュ桅闁告洦鍨奸弫鍥煟閺冨牜妫戞い鎴濆€荤槐鎾存媴閹绘帊澹?
2026-04-29 15:18:30 +08:00
} ;
this . listeners = [ ] ;
2026-05-18 23:03:28 +08:00
this . socketEventHandlers = { } ;
2026-05-24 01:46:57 +08:00
this . _inviteEventSignaling = null ;
2026-04-29 15:18:30 +08:00
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊峰ù鍥х暦閸偅鍙忛柟鎯板Г閳锋梻鈧箍鍎遍ˇ顖炲垂閸岀偞鐓㈡俊顖滃皑缁辨岸鏌ㄥ┑鍡╂Ц缂佲偓鐎n偁浜滈柡宥冨妿閳藉霉濠婂啰绉烘慨濠冩そ閹剝鎯旈姀鈥虫瀾婵$偑鍊х徊鑲╁垝濞嗗繒鏆﹂柟杈剧畱缁犲鎮楀☉娅亪顢撻幘鍓佺=濞达絽澹婇崕鎾寸箾婢跺绀堢紒顔芥椤㈡岸鍩€椤掆偓椤?
2026-04-29 15:18:30 +08:00
subscribe ( callback ) {
this . listeners . push ( callback ) ;
return ( ) => {
this . listeners = this . listeners . filter ( cb => cb !== callback ) ;
} ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鎼佸磹妞嬪孩顐介柨鐔哄Т绾惧鏌涘☉鍗炲季婵炲皷鏅犻弻鏇熺箾閻愵剚鐝曢梺绋款儏閸婂潡寮婚妸鈺傚亜闁告繂瀚呴姀銏㈢<闁绘﹩鍠栭崝锕傛煛鐏炵晫啸妞ぱ傜窔閺屾盯骞樼捄鐑樼€诲銈嗘穿缂嶄礁鐣疯ぐ鎺濇晝闁靛繈鍨婚悰顕€姊虹拠鑼闁稿绋掗弲鑸电鐎n偅娅栭梺鎼炲労閸撴岸鍩涢幋锔界厱婵炴垶锕銉╂煕閵堝骸澧撮柡灞剧洴楠炴鈧潧鎲¢崳褔姊?
2026-04-29 15:18:30 +08:00
notify ( changes ) {
this . listeners . forEach ( cb => cb ( this . state , changes ) ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹濠德板€曢崯顖氱暦閺屻儲鐓曠€光偓閳ь剟宕曢幋鐘电闁哄稁鍘介悡娆撴煟濡も偓閻楀﹦娆㈤懠顒傜<闁?
2026-04-29 15:18:30 +08:00
async init ( ) {
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹濠德板€曢崯顖氱暦閺屻儲鐓曠€光偓閳ь剟宕曢幋鐘电闁哄稁鍘介悡娆撴煟濡も偓閻楀﹦娆㈤懠顒傜<闁逞屽墮閻f繈宕熼鍌氬箰闁诲骸绠嶉崕杈殽閹间胶宓侀柡宥庡幗閸嬨劍銇勯弽銊︾殤闁挎稑绉剁槐?
2026-04-29 15:18:30 +08:00
await this . setupConfig ( ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鍨鹃幇浣圭稁缂傚倷鐒﹁摫闁告瑥绻橀弻鐔碱敍閿濆洣姹楅悷婊呭鐢帡鎮欐繝鍐︿簻闁瑰搫绉堕ˇ锕€霉閻樿櫕銇濇慨濠冩そ濡啫鈽夋潏鈺佸綃缂傚倷鑳舵慨鐢稿垂閸ф绠栭柨鐔哄Т閸楁娊鏌曡箛濞惧亾閸忓懎瀵叉繝鐢靛仩閹活亞寰婇崸妞烩偓锕傚醇閵夈儳鍘遍梺鍝勫暙閻楀﹪鎮?
2026-04-29 15:18:30 +08:00
this . loadUserSettings ( ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓瀹曘儳鈧綆鍠栫壕鍧楁煙閹増顥夐幖鏉戯躬閺屻倝鎳濋幍顔肩墯婵炲瓨绮岀紞濠囧蓟濞戙垹唯妞ゆ梻鍘ч~鈺呮煟鎼淬垼澹樻い锔垮嵆婵$敻宕熼姘鳖唺闂佺硶鍓濋妵鐐寸珶閺囩喓绡€闁汇垽娼цⅴ闂佺顑嗛幑鍥蓟閻旇櫣纾奸柕蹇曞У閻忓牏绱撴担鍝勑f俊鐐扮矙瀵鈽夐姀鐘插祮闂侀潧顭堥崕鎵姳婵犳碍鈷戦柛娑橈攻鐏忕敻鏌涘Ο鐘叉礌閳ь剨绠撳畷绋课旈埀顒傜矆閸縿鈧帒顫濋浣割槱濡炪們鍎查幐鎼佲€旈崘顔嘉ч幖绮光偓鑼泿婵$偑鍊栭崹闈浳涘┑瀣瀬鐎广儱顦粻娑㈡煛婢跺孩纭舵い鎾存そ濮婅櫣绱掑Ο鍝勵潕闂佺顑戠紞浣哥暦?
2026-04-29 15:18:30 +08:00
await this . getLocalStream ( ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鍨鹃幇浣圭稁缂傚倷鐒﹁摫闁告瑥绻橀弻鐔碱敍閿濆洣姹楅悷婊呭鐢帡鎮欐繝鍐︿簻闁瑰搫绉堕ˇ锕€霉閻樿櫕銇濇慨濠冩そ濡啫鈽夋潏鈺佸綃缂傚倷鑳舵慨鐢稿垂閸ф绠栭柨鐔哄Т閸楁娊鏌曡箛濞惧亾閸忓懎瀵叉繝鐢靛仩閹活亞寰婇崸妞烩偓锕傚醇閵夈儳鍘遍梺鍝勫暙閻楀﹪鎮?
2026-04-29 15:18:30 +08:00
loadUserSettings ( ) {
const userSettings = localStorage . getItem ( 'userSettings' ) ;
if ( userSettings ) {
try {
const settings = JSON . parse ( userSettings ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢埛姘そ婵¤埖寰勭€n亙妲愰梻渚€娼ц墝闁哄懏鐩幏鎴︽偄鐏忎焦鏂€闂佺粯锚瀵爼骞栭幇鐗堝€垫慨姗嗗墻濡插綊鏌曢崶褍顏鐐村浮瀹曞崬顪冮幆褜妫滄繝鐢靛Х閺佸憡鎱ㄩ銏犵;闁瑰墽绮悡娆戠磽娴i潧鐏╅柡瀣枛閺屾稓鈧絻鍔屾慨鍌炴煛鐏炵偓绀冪紒缁樼箚缁犳盯寮撮悩铏啅闂傚倷鑳剁划顖炲箰鐠囪娲冀椤掆偓缁插綊姊绘担瑙勫仩闁稿氦宕靛濠囨嚍閵夛附鐝烽梺鍦帛瀹?
2026-04-29 15:18:30 +08:00
if ( settings . name || settings . avatar ) {
this . state . session . localUser = {
... this . state . session . localUser ,
id : settings . userId || this . state . session . localUser . id ,
name : settings . name || this . state . session . localUser . name ,
avatar : settings . avatar || this . state . session . localUser . avatar
} ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鎼佸磹妞嬪孩顐介柨鐔哄Т绾惧鏌涘☉鍗炲季婵炲皷鏅犻弻鏇熺箾閻愵剚鐝曢梺绋款儏閸婂綊濡甸崟顖氱労闁告劏鏅滅欢婊堟⒒閸屾艾鈧绮堟笟鈧獮鏍敃閳锋碍妞芥俊鑸靛緞鐎n亙妲愰梻渚€娼ц墝闁哄懏鐩幏?
2026-04-29 15:18:30 +08:00
this . notify ( { type : 'USER_SETTINGS_UPDATED' , user : this . state . session . localUser } ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐宄懊归崶顒夋晪闁哄稁鍘奸崒銊ф喐閻楀牆绗掗柛銊ュ€婚幉鎼佹偋閸繂鎯為梺鎼炲労閸撴瑩鎯屽Δ鈧…璺ㄦ崉閸濆嫷妲甸梺琛″亾闁告劖绁撮弨浠嬫煟濡偐甯涙繛鎳峰嫪绻嗘い鎰剁悼濞插瓨顨ラ悙鎼疁闁诡喒鏅濈槐鎺懳熼悡搴$闂傚倷绶氬褔鈥﹂崼銉ョ?闂侇剙绋侀弫鍌炴煃閸濆嫭鍣洪柣鎾存礋閺屾洘绻涢崹顔煎濡炪倖甯囬崹铏规崲濞戙垺鍋傞幖杈剧悼椤旀帗绻濈喊妯哄⒉闁烩剝娲熻棟闁哄被鍎查悡鐘绘煟閹寸伝顏堝煝閺囥垺鐓欐い鏃€顑欏鎰版煙瀹勭増鍣界紒顔界懄閹棃骞橀弶鎴犳Ш闂?
2026-04-29 15:18:30 +08:00
if ( settings . resolution ) {
this . _savedResolution = settings . resolution ;
2026-05-24 01:46:57 +08:00
console . log ( ` 闂傚倷娴囬褎顨ョ粙鍖¤€块梺顒€绉埀顒婄畵瀹曠厧鈹戦幇顒侇吙闂備胶鍘ч幗婊堝极閹间降鈧懘鏌ㄧ€c劋绨婚梺鍝勫暙濞诧箓藟婢跺瞼纾奸柛鎾茬娴犙囨煃瑜滈崜娆戠不瀹ュ纾块梺顒€绉寸粻鐘诲箹濞n剙濡介柛濠囨涧閳规垿鎮╃€圭姴顥濈紓浣哄珡閸ャ劎鍘卞銈嗗姧缁插潡鍩ユ径濞炬斀闂勫洤鈻旈弴銏犵劦妞ゆ帒鍠氬鎰版煟閳╁啯绀嬬€规洘鍨块獮鍥级鐠侯煈鍞甸梺璇插嚱缂嶅棝宕伴弽顐ょ焼闁割偁鍨洪崰鎰扮叓閸ャ劎鈽夐柛? ${ settings . resolution . width } x ${ settings . resolution . height } ` ) ;
2026-04-29 15:18:30 +08:00
}
} catch ( error ) {
console . error ( 'Error loading user settings:' , error ) ;
}
}
}
async setupConfig ( ) {
const res = await getServerConfig ( ) ;
this . useWebSocket = res . useWebSocket ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓瀹曘儳鈧綆鍠栫壕鍧楁煙閹増顥夐幖鏉戯躬閺屻倝鎳濋幍顔肩墯婵炲瓨绮岀紞濠囧蓟濞戙垹唯妞ゆ梻鍘ч~鈺呮煟鎼淬垼澹樻い锔垮嵆婵$敻宕熼姘鳖唺闂佺硶鍓濋妵鐐寸珶閺囩喓绡€闁汇垽娼цⅴ闂佺顑嗛幑鍥蓟閻旇櫣纾奸柕蹇曞У閻忓牏绱撴担鍝勑f俊鐐扮矙瀵鈽夐姀鐘插祮闂侀潧顭堥崕鎵姳婵犳碍鈷戦柛娑橈攻鐏忕敻鏌涘Ο鐘叉礌閳ь剨绠撳畷绋课旈埀顒傜矆閸縿鈧帒顫濋浣割槱濡炪們鍎查幐鎼佲€旈崘顔嘉ч幖绮光偓鑼泿婵$偑鍊栭崹闈浳涘┑瀣瀬鐎广儱顦粻娑㈡煛婢跺孩纭舵い鎾存そ濮婅櫣绱掑Ο鍝勵潕闂佺顑戠紞浣哥暦?
2026-04-29 15:18:30 +08:00
async getLocalStream ( ) {
try {
console . log ( 'Requesting camera permission...' ) ;
2026-05-24 01:46:57 +08:00
// 濠电姷鏁告慨鐑姐€傞挊澹╋綁宕ㄩ弶鎴狅紱闂侀€炲苯澧撮柡灞剧〒閳ь剨缍嗛崑鍛暦瀹€鍕厸鐎光偓鐎n 剛锛熸繛瀵稿婵″洭骞忛悩璇茬闁圭儤鍩堝銉х 磽閸屾艾鈧嘲顪冮幒鎳ㄥ綊宕惰閺嗭箓鏌i 姀銏╃劸缂佺姷鍠栭弻鐔虹磼閵忕姵鐏堥梻浣稿船濞诧妇鎹㈠┑瀣棃婵炴垶鐟ョ粣娑㈡偡濠婂懎顣肩紒顔芥尭椤繐煤椤忓嫪绱堕梺鍛婃处閸嬫帗瀵煎畝鍕拺闁硅偐鍋涙俊娲煕濡や礁鈻曢柣娑卞櫍瀹曞崬螣閼测晜鍤岄梻渚€鈧偛鑻晶顔姐亜閺囶亞绉┑鈩冩倐閸┾剝绻濋崘鈺傜彨濠电姷鏁搁崑鐐哄垂閸洍鈧箓宕堕‖顒婄悼缁艾顕欑粊宥秙erMedia
2026-04-29 15:18:30 +08:00
if ( ! navigator . mediaDevices || ! navigator . mediaDevices . getUserMedia ) {
console . error ( 'getUserMedia is not supported' ) ;
throw new Error ( 'getUserMedia is not supported' ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊峰ù鍥х暦閸偅鍙忛柡澶嬪殮濞差亶鏁囬柕蹇曞Х閸濇姊绘笟鍥у缂佸鏁诲畷鏇㈠箣濠㈡繂缍婂畷妤呭礂閼测晝鈻忕紓鍌欒兌婵潧顪冩禒瀣摕婵炴垯鍨归崡鎶芥煏婵炲灝鍔欑紒銊ヮ煼濮婃椽宕ㄦ繝鍐ㄦ殫闂佸憡顭嗛崶锝傚亾閿旂偓宕夐柕濠忕畱绾绢垶姊洪幆褏绠烘い顐㈩樀楠炲鎮℃惔妯绘杸闂佺粯鍔欏褏鏁崼鏇熺厽婵°倓鐒︾亸顓熴亜椤愩垻绠茬紒鍌涘笧閳ь剨缍嗘禍鐐烘偪閳ь剚淇婇悙顏勨偓鏍暜閹烘鐤い鎰堕檮閸嬬喐銇勯弽顐沪闁抽攱鍨块弻娑滅疀閺囩偛浠橀梺鍛婃煟閸婃繈寮诲☉銏狀潊闁挎繂鎳撻弫鍧楁⒑闂堟稒澶勯柛鏃€鐟ラ悾鐑藉醇閺囩倣鈺呮煏婢跺牆鍔ゅù婊勭墵濮婄粯鎷呴悜妯烘畬闂佸湱鈷堥崑鍡欏垝閺冨牆绀堝ù锝堟閻撳姊虹粙璺ㄧ闁稿鍔楃划濠氬礈瑜忕壕浠嬫煕椤愮姴鐏╅崯鍝ョ磽娴e湱鈽夌紒缁樺笧濡叉劙骞掗幘瀵哥Ф闂侀潧臎閸涱垱婢栭梺璇叉唉椤煤韫囨稑纾块柣銏℃偠閳ь兛绀侀埥澶婎潨閸℃瑥寮抽梻浣告啞濞诧箓宕滃棰濇晣鐟滅増甯楅埛鎺懨归敐鍫綈闁稿濞€閺屾稒鎯旈姀鐘灆閻庤娲橀崹鐢稿煡婢舵劕顫呴柍鈺佸暞閻濐偊姊绘担鍝ョШ闁稿锕畷妤€螣娴f洩缍佸畷濂稿Ψ閿旀儳骞堟俊鐐€栭崝妤佹叏閹绢喖绀夋繝濠傜墛閻?
// 婵犵數濮烽弫鎼佸磻閻樿绠垫い蹇撴缁€濠囨煃瑜滈崜姘辨崲濞戞瑥绶為悗锝庡亞椤︿即鎮楀▓鍨珮闁稿锕ユ穱濠囧醇閺囩偛鑰垮┑鐐叉閺堫剟寮崹顐ょ瘈闁汇垽娼ч埢鍫熺箾娴e啿鍚樺☉妯锋闁靛繒濮烽鎴︽⒑缂佹﹩鐒界紒顕呭灦閹繝鎮㈤崗鑲╁帾婵犵數鍋涢悘婵嬪礉濮橆厹浜滈煫鍥э攻濞呭﹪鏌$仦鍓ф创濠碘剝鎮傛俊鐑藉Ψ椤旂厧唯濠电姷鏁搁崑鐔煎储瑜忛幑銏犖熺拋宕囩畾濠殿喗绻傞惌鍫澪f繝姘拺闁兼祴鏅涢崝瀣攽閻愯韬€殿喖顭锋俊鎼佸Ψ閵忊槅娼旀繝纰樻閸ㄦ娊宕㈣閵嗗倿宕崟鍨瘜闂侀潧鐗嗛崐褰掑汲濮椻偓閺屾盯濡烽幋婵嗘殶鐟滄澘顦埞鎴︽偐椤旇偐浼囬梺绯曟櫆閻楁粎鍒掑▎鎾崇闁告挷鑳堕悞濂告⒑缁洖澧茬紒瀣浮閹繝濡烽敂鍓ь啎闂佺懓顕崕鎰版倿妤e啯鍊垫慨姗嗗亜瀹撳棝鏌″畝鈧崰鏍€佸☉妯锋瀻闁瑰濮峰畷鏌ユ煟鎼淬値娼愭繛鍙夛耿閹虫繃銈i崘銊у幒闁瑰吋鐣崹娲磿閻斿吋鐓冮梺娆惧灠娴滈箖姊洪崫鍕靛剰缂佺粯锚椤繘宕崝鍊熸缁辨帒螣鐞涒剝鐎奸梻鍌欑閹测€愁潖瑜版帒鍨傜憸鐗堝笒缁€鍡涙煙閻戞﹩娈㈤柡浣哥У缁绘繃绻濋崒姘间患濡炪倕瀛╅惄顖氼潖濞差亝顥堟繛鎴炶壘椤e搫鈹戦悙鑼勾闁稿﹥绻堥妴浣糕枎閹邦喚鐦堥梺鎼炲劘閸斿酣宕?
2026-05-24 01:01:28 +08:00
const videoConstraints = buildVideoConstraints ( this . _savedResolution ) ;
2026-04-29 15:18:30 +08:00
const stream = await navigator . mediaDevices . getUserMedia ( {
video : videoConstraints ,
audio : AUDIO _CONFIG
} ) ;
console . log ( 'Stream obtained successfully:' , stream ) ;
console . log ( 'Video tracks:' , stream . getVideoTracks ( ) ) ;
console . log ( 'Audio tracks:' , stream . getAudioTracks ( ) ) ;
this . state . localStream = stream ;
this . state . session . localUser . mediaState . video = true ;
this . state . session . localUser . mediaState . audio = true ;
console . log ( 'Local stream stored, notifying UI...' ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鐑芥嚄閸洍鈧箓宕奸姀鈥冲簥闂佽澹嗘晶妤呭磻鐎n喗鐓欓柟瑙勫姦閸ゆ瑩鏌i幒鎴犱粵闁靛洤瀚伴獮鎺戭吋閸パ冾瀴闂備焦鐪归崝宥夊垂閸ф钃熼柨婵嗩槸缁犲ジ鏌涢弴銊ュ箻闁挎稓鍠栧娲偡閺夎法楠囬梺鍦焾閸熷潡鎮鹃悜钘夌闁绘劏鏅滈~宥夋煛婢跺﹦澧戦柛鏂跨Ч椤㈡瑩寮埀顒傛崲濠靛鍋ㄩ梻鍫熺◥閹撮绱撴担铏瑰笡缂佽瀚崚鎺楀醇閻旇櫣鎳濋梺閫炲苯澧柣锝呭槻閳诲酣骞樼€涙ɑ顏熼梻浣虹帛閿氶柛鐔风仢閳绘捁顦归柟顔筋殜閹兘寮跺▎鍙ョ棯婵犵數濮伴崹娲€﹂崶褜鍤?
2026-04-29 15:18:30 +08:00
this . notify ( { type : 'LOCAL_STREAM_OBTAINED' , stream } ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鑼槷闂佸搫娲ㄦ慨鐑芥儗閹捐埖鍠愰柣妤€鐗嗙粭鎺楁煟閹烘垹浠涢柕鍥у楠炴帒顓奸崶褍顬夐梻浣圭湽閸斿秹宕归崸妤€钃熼柨婵嗩槸缁犵粯銇勯弽銊ㄥ闁冲嘲顦靛娲川婵犲倻鐟ㄩ梺鎸庢处娴滎亪鎮伴鈧獮鎺楀箣椤撶偞鍊梻浣规偠閸庮噣寮埡鍜佹晜闁割偆鍟块幏铏圭磽閸屾瑧鍔嶉柨鏇楁櫊閹偞銈i崘鈺冨幈闂佸啿鎼崐缁樻櫠閻㈠憡鐓曢柍瑙勫劤娴滅偓淇婇悙顏勨偓鏍垂閻撳簶鏋栭柡鍥f嚍閸ヮ剚鏅滈柣鎰靛墮閺嬫垿姊洪崫鍕垫Ч妞ゆ垶鐟ヨ灒闁逞屽墴閹?
2026-04-29 15:18:30 +08:00
this . notify ( { type : 'LOCAL_MEDIA_CHANGE' , mediaType : 'video' , value : true } ) ;
this . notify ( { type : 'LOCAL_MEDIA_CHANGE' , mediaType : 'audio' , value : true } ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鍐蹭画闂佹寧娲栭崐鎼佸垂閸岀偞鐓曠憸搴ㄣ€冮崨瀛樺€块柛顭戝亖娴滄粓鏌熸潏鍓хɑ缁绢叀鍩栭妵鍕晜閼测晝鏆ら梺鍝勭焿缁蹭粙鍩ユ径濠庢僵妞ゆ帊鑳堕埀顒勭畺濮婃椽鎮烽弶鎸幮╅梺纭呮珪閿曘垽鎮伴鍢夌喖宕楅悡搴o紡闂備胶鍎甸弲婊呮暜椤忓棛涓嶉柟鎹愵嚙閽冪喖鏌i弮鍌楁嫛闁轰礁瀚伴幃瑙勭瑹椤栨粌甯ュ┑鈥虫▕閸o絽顫忛搹鐟板闁哄洨鍠愰悵鏃堟⒑濞茶骞栭柛濠冩倐椤㈡岸鏁愭径瀣垫濠电偞鍨靛畷顒勫箖閹达附鈷戦柛娑橈梗缁堕亶鏌涘▎蹇涱€楁い顓炴喘楠炲酣鎳為妷銏″?
2026-04-29 15:18:30 +08:00
this . emitMediaStateChange ( ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鑼槱閻熸粎澧楃敮鎺楀垂閸岀偞鐓熸俊銈傚亾闁绘锕畷锝堢疀濞戞瑧鍘撻梺鍛婄箓鐎氼參宕宠ぐ鎺撳€垫慨姗嗗墻濡插綊鏌曢崶褍顏鐐村浮瀹曞崬顪冮幆褜妫滄繝鐢靛Х閺佸憡鎱ㄩ銏犵;闁瑰墽绮悡娆戠磽娴i潧鐏╅柡瀣枑閵囧嫰濡烽妷顖濆惈闂佸搫鐬奸崰鏍х暦濠婂嫭濯撮柣鎴炆戦崯鎺楁⒒娴e憡鎯堟俊顐㈤叄瀹曟洟鎳犻浣稿簥濠电偞鍨崹娲吹閹存惊娑㈡偋閸垻鐣靛┑鐐茬墱閸樼晫鎹㈠┑鍡忔灁闁割煈鍠楅悘鈧梻浣告惈閹冲繒绮欓弽顓熷€堕悗锝庡枟閳锋垿姊洪銈呬粶闁兼椿鍨遍弲鍫曨敋閳ь剙螞?
2026-04-29 15:18:30 +08:00
this . startActivityDetection ( this . state . localStream , { isLocal : true } ) ;
} catch ( error ) {
console . error ( 'Error getting local stream:' , error ) ;
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽弫鍛婃叏閻戝鈧倹绂掔€n亞鍔﹀銈嗗坊閸嬫捇鏌涢悢閿嬪仴闁糕斁鍋撳銈嗗坊閸嬫挾绱撳鍜冭含妤犵偛鍟灒閻犲洩灏欑粣鐐烘⒑瑜版帒浜伴柛妯煎帶閳绘捁顦归柟顔筋殜閹兘寮跺▎鍙ョ棯婵犵數濮伴崹娲€﹂崶褜鍤楀┑鐘叉搐闁裤倖淇婇妶鍕槮濞寸媭鍘奸埞鎴︽倷閸欏妫炵紓浣虹帛閸ㄨ儻妫㈤梺闈涚返妫颁胶鐩庨梻浣瑰缁诲倿鎮ч崱娑欏亗闁归偊鍠掗崑鎾诲垂椤愶絿鍑¢柣搴㈠嚬閸樺ジ鈥﹂崶顏嗙杸婵炴垼椴搁弲婵嬫⒑闂堟侗妲归柛鏃€鐗曠叅闁绘梻鍘ч拑鐔兼煏婵炲灝鍔楁俊鎻掔墛娣囧﹪顢涘▎鎺濆妳濠电偛鐗滈崢鍓ф閹惧瓨濯撮柧蹇曟嚀缁楋繝鎮楅崗澶婁壕闂佸綊妫块懗璺虹暤娓氣偓閺岀喖鏌囬敃鈧獮妤€鈹戦姘煎殶缂佽鲸甯掗埥澶婎潨閸℃澹夌紓鍌氬€哥粔鎾晝閵夛妇鈹嶅┑鐘叉祩閺佸啴鏌曢崼婵囧闁哄棭鍙冨娲箹閻愭彃顬嬮梺杞版祰椤曆囨偩閻戣姤鏅查柛顐亝閳诲本绻濆▓鍨灈闁挎洏鍊濋垾锕傛倻婵劏鍋撴担鍓叉建闁逞屽墴楠炲啴鍩℃担鍙夌亖闁诲函绲芥晶搴ㄦ偩閻㈠憡鐓?
2026-04-29 15:18:30 +08:00
this . state . session . localUser . mediaState . video = false ;
this . state . session . localUser . mediaState . audio = false ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鎼佸磹妞嬪孩顐介柨鐔哄Т绾惧鏌涘☉鍗炲季婵炲皷鏅犻弻鏇熺箾閻愵剚鐝曢梺绋款儏閸婂潡寮诲澶婄厸濞达絽鎲″▓顓㈡⒑閹肩偛濡芥俊顐㈠暣瀵鎮㈤悡搴濈炊闂佸憡娲﹂崢婊堝Χ閸モ晝锛滄繝銏f硾椤牓宕戦姀銈嗙厸鐎光偓鐎n剙鍩岄柧浼欑秮閺屾盯鈥﹂幋婵囩亾闂佸憡锕╅崑濠傤潖濞差亝鐒婚柣鎰蔼鐎氫即鏌涘Ο鐑樻喐缂佽鲸甯¢崺鈧い鎺嶇劍婵潙鈹戦钘夊婵顨嗙换婵嬪閿濆懐鍘梺鍛婃⒐濞叉粎鍒?
2026-04-29 15:18:30 +08:00
this . notify ( { type : 'LOCAL_MEDIA_CHANGE' , mediaType : 'video' , value : false } ) ;
this . notify ( { type : 'LOCAL_MEDIA_CHANGE' , mediaType : 'audio' , value : false } ) ;
}
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢埛姘そ婵¤埖寰勭€n亙妲愰梻渚€娼ц墝闁哄懏鐩幏鎴︽偄鐏忎焦鏂€闂佺粯锚瀵爼骞栭幇鐗堝€垫慨姗嗗墻濡插綊鏌曢崶褍顏鐐村浮瀹曞崬顪冮幆褜妫滄繝鐢靛Х閺佸憡鎱ㄩ銏犵;闁瑰墽绮崐鍨箾閹寸儐浠炬い蹇撶墕閸ㄥ倿鏌¢崘銊у闁绘挻鐟╅弻娑樷攽閸℃浼€闁煎弶鐗滅槐鎾存媴閸撳弶笑闂侀潧鐗婇幃鍌氼嚕婵犳碍鏅查柛娑樺€婚崰鏍х暦瑜版帩鏁婇柟顖嗗啰绱伴梻?
2026-04-29 15:18:30 +08:00
async updateLocalMedia ( mediaType , value ) {
2026-05-24 01:18:27 +08:00
await this . _updateLocalMediaRefactored ( mediaType , value ) ;
return ;
}
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
async _updateLocalMediaRefactored ( mediaType , value ) {
2026-04-29 15:18:30 +08:00
if ( mediaType === 'video' && value ) {
2026-05-24 01:18:27 +08:00
await this . _enableLocalVideo ( ) ;
this . _notifyUserListUpdate ( ) ;
return ;
}
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
this . state . session . localUser . mediaState [ mediaType ] = value ;
this . _notifyLocalMediaChange ( mediaType , value ) ;
this . emitMediaStateChange ( ) ;
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
if ( mediaType === 'video' && ! value ) {
this . _disableLocalVideoTracks ( ) ;
}
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
if ( mediaType === 'audio' ) {
this . _setLocalAudioTrackEnabled ( value ) ;
}
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
this . _notifyUserListUpdate ( ) ;
}
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
async _enableLocalVideo ( ) {
try {
const newVideoTrack = await this . _requestNewVideoTrack ( ) ;
this . _replaceLocalVideoTrack ( newVideoTrack ) ;
await this . _updateOutgoingVideoTrack ( newVideoTrack ) ;
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
this . state . session . localUser . mediaState . video = true ;
this . notify ( { type : 'LOCAL_STREAM_OBTAINED' , stream : this . state . localStream } ) ;
this . _notifyLocalMediaChange ( 'video' , true ) ;
2026-04-29 15:18:30 +08:00
this . emitMediaStateChange ( ) ;
2026-05-24 01:18:27 +08:00
this . startActivityDetection ( this . state . localStream , { isLocal : true } ) ;
} catch ( error ) {
console . error ( 'Error reopening video:' , error ) ;
this . state . session . localUser . mediaState . video = false ;
this . _notifyLocalMediaChange ( 'video' , false ) ;
2026-04-29 15:18:30 +08:00
}
2026-05-24 01:18:27 +08:00
}
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
async _requestNewVideoTrack ( ) {
const newVideoStream = await navigator . mediaDevices . getUserMedia ( VIDEO _ONLY _CONSTRAINT ) ;
const newVideoTrack = newVideoStream . getVideoTracks ( ) [ 0 ] ;
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
if ( ! newVideoTrack ) {
throw new Error ( 'Failed to get video track' ) ;
}
return newVideoTrack ;
}
_replaceLocalVideoTrack ( newVideoTrack ) {
if ( this . state . localStream ) {
const oldVideoTracks = this . state . localStream . getVideoTracks ( ) ;
oldVideoTracks . forEach ( track => {
track . stop ( ) ;
this . state . localStream . removeTrack ( track ) ;
2026-04-29 15:18:30 +08:00
} ) ;
2026-05-24 01:18:27 +08:00
this . state . localStream . addTrack ( newVideoTrack ) ;
return ;
}
this . state . localStream = new MediaStream ( [ newVideoTrack ] ) ;
}
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
async _updateOutgoingVideoTrack ( newVideoTrack ) {
if ( ! this . renderstreaming ) {
return ;
2026-04-29 15:18:30 +08:00
}
2026-05-24 01:18:27 +08:00
console . log ( 'Updating video track in WebRTC connection' ) ;
if ( this . role === 'host' ) {
const participantIds = Object . keys ( this . state . remoteStreams ) ;
for ( const participantId of participantIds ) {
await this . _updateVideoTrackForPeer ( newVideoTrack , participantId ) ;
}
return ;
2026-04-29 15:18:30 +08:00
}
2026-05-24 01:18:27 +08:00
await this . _updateVideoTrackForPeer ( newVideoTrack ) ;
}
async _updateVideoTrackForPeer ( newVideoTrack , participantId = undefined ) {
const transceivers = this . renderstreaming . getTransceivers ( participantId ) ;
if ( ! transceivers ) {
return ;
}
const videoTransceivers = transceivers . filter ( transceiver =>
transceiver . sender && transceiver . sender . track && transceiver . sender . track . kind === 'video'
) ;
if ( videoTransceivers . length > 0 ) {
await this . _replaceVideoTrackOnTransceivers ( videoTransceivers , newVideoTrack , participantId ) ;
} else {
this . _addVideoTransceiver ( newVideoTrack , participantId ) ;
}
this . _scheduleVideoSenderUpdate ( participantId ) ;
}
async _replaceVideoTrackOnTransceivers ( videoTransceivers , newVideoTrack , participantId ) {
for ( const transceiver of videoTransceivers ) {
try {
await transceiver . sender . replaceTrack ( newVideoTrack ) ;
console . log ( participantId
? ` Replaced video track for participant ${ participantId } `
: 'Successfully replaced video track' ) ;
} catch ( error ) {
console . error (
participantId
? ` Error replacing video track for ${ participantId } : `
: 'Error replacing video track:' ,
error
) ;
}
}
}
_addVideoTransceiver ( newVideoTrack , participantId ) {
try {
if ( participantId ) {
this . renderstreaming . addTransceiver ( newVideoTrack , { direction : 'sendonly' } , participantId ) ;
console . log ( ` Added new video transceiver for participant ${ participantId } ` ) ;
return ;
}
this . renderstreaming . addTransceiver ( newVideoTrack , { direction : 'sendonly' } ) ;
console . log ( 'Added new video transceiver' ) ;
} catch ( error ) {
console . error (
participantId
? ` Error adding video transceiver for ${ participantId } : `
: 'Error adding video transceiver:' ,
error
) ;
}
}
_scheduleVideoSenderUpdate ( participantId ) {
setTimeout ( ( ) => { this . setCodecPreferences ( participantId ) ; } , 100 ) ;
setTimeout ( ( ) => { this . setVideoEncodingParameters ( participantId ) ; } , 200 ) ;
}
_disableLocalVideoTracks ( ) {
if ( ! this . state . localStream ) {
return ;
}
this . state . session . localUser . mediaState . video = false ;
this . state . localStream . getTracks ( ) . forEach ( track => {
if ( track . kind === 'video' ) {
track . stop ( ) ;
}
} ) ;
}
_setLocalAudioTrackEnabled ( value ) {
if ( ! this . state . localStream ) {
return ;
}
this . state . session . localUser . mediaState . audio = value ;
this . state . localStream . getTracks ( ) . forEach ( track => {
if ( track . kind === 'audio' ) {
track . enabled = value ;
}
} ) ;
}
_notifyLocalMediaChange ( mediaType , value ) {
this . notify ( { type : 'LOCAL_MEDIA_CHANGE' , mediaType , value } ) ;
}
2026-04-29 15:18:30 +08:00
2026-05-24 01:18:27 +08:00
_notifyUserListUpdate ( ) {
this . notify ( {
type : 'USER_LIST_UPDATE' ,
localUser : this . state . session . localUser ,
remoteUser : this . state . session . remoteUser
} ) ;
2026-04-29 15:18:30 +08:00
}
2026-05-24 01:46:57 +08:00
onSocketEvent ( eventName , handler ) {
this . socketEventHandlers [ eventName ] = handler ;
}
2026-05-16 21:26:19 +08:00
async connectSignaling ( ) {
await this . setupConfig ( ) ;
2026-05-24 01:46:57 +08:00
const { signaling , reused } = await ensureSignalingStarted ( this . _signaling , this . useWebSocket ) ;
this . _signaling = signaling ;
this . _inviteEventSignaling = bindInviteSocketEvents (
this . _signaling ,
this . socketEventHandlers ,
this . _inviteEventSignaling
) ;
if ( reused ) {
2026-05-16 21:26:19 +08:00
console . log ( 'Signaling already connected, reusing existing instance' ) ;
return this . _signaling ;
}
console . log ( 'Signaling connected (WebSocket only, no room yet)' ) ;
return this . _signaling ;
}
2026-05-18 23:03:28 +08:00
getActiveSignaling ( ) {
2026-05-24 01:46:57 +08:00
return getActiveSignalingInstance ( this . _signaling , this . renderstreaming ) ;
2026-05-18 23:03:28 +08:00
}
sendInviteCall ( payload ) {
2026-05-24 01:46:57 +08:00
sendInviteSignal ( this . getActiveSignaling ( ) , 'sendInviteCall' , payload ) ;
2026-05-18 23:03:28 +08:00
}
sendInviteAccepted ( payload ) {
2026-05-24 01:46:57 +08:00
sendInviteSignal ( this . getActiveSignaling ( ) , 'sendInviteAccepted' , payload ) ;
2026-05-18 23:03:28 +08:00
}
sendInviteRejected ( payload ) {
2026-05-24 01:46:57 +08:00
sendInviteSignal ( this . getActiveSignaling ( ) , 'sendInviteRejected' , payload ) ;
2026-05-18 23:03:28 +08:00
}
2026-05-16 23:07:08 +08:00
syncSocketUserInfo ( userInfo = null ) {
2026-05-24 01:46:57 +08:00
const payload = buildSocketUserInfoPayload ( userInfo , this . state . session . localUser ) ;
2026-05-16 23:07:08 +08:00
this . state . session . localUser = {
... this . state . session . localUser ,
id : payload . id ,
name : payload . name ,
avatar : payload . avatar
} ;
2026-05-24 01:46:57 +08:00
sendSocketUserInfo ( this . getActiveSignaling ( ) , payload ) ;
2026-05-16 23:07:08 +08:00
}
2026-04-29 15:18:30 +08:00
async _createSignalingAndRTC ( connectionId ) {
2026-05-24 01:46:57 +08:00
this . connectionId = connectionId ;
2026-04-29 15:18:30 +08:00
this . state . session . status = 'connecting' ;
this . notify ( { type : 'CALL_STATUS_CHANGE' , status : 'connecting' } ) ;
if ( ! this . state . localStream ) {
console . log ( 'Local stream not available, waiting for initialization...' ) ;
await new Promise ( ( resolve ) => {
const checkStream = ( ) => {
if ( this . state . localStream ) {
resolve ( ) ;
} else {
setTimeout ( checkStream , 100 ) ;
}
} ;
checkStream ( ) ;
} ) ;
}
2026-05-24 01:46:57 +08:00
const signaling = this . _signaling || createSignalingInstance ( this . useWebSocket ) ;
const config = getRTCConfiguration ( ) ;
2026-04-29 15:18:30 +08:00
this . renderstreaming = new RenderStreaming ( signaling , config ) ;
2026-05-24 01:46:57 +08:00
this . _signaling = null ;
2026-04-29 15:18:30 +08:00
}
async setUp ( connectionId ) {
await this . _createSignalingAndRTC ( connectionId ) ;
this . _registerCallbacks ( ) ;
await this . _startConnection ( connectionId ) ;
}
/ * *
2026-05-24 01:46:57 +08:00
* 濠电姷鏁告慨鐑藉极閹间礁纾绘繛鎴旀嚍閸ヮ剦鏁囬柕蹇曞Х椤 ︻ 噣鎮楅崗澶婁壕闂佸憡娲 ﹂ 崑澶愬春閻愬 绠鹃悗鐢殿焾瀛濆 銈嗗灥閹虫劗鍒掓繝姘 ㄩ柍鍝勫 € 婚崢鎾绘偡濠婂嫮鐭掔 € 规洘绮撻幃銏 $ 附婢跺 ﹥ 顓块梻浣稿 閻撳牓宕戦崱娆戜笉婵炲棙鍔楃粻鍓р偓鍏夊亾闁逞屽墴閸 ┾ 偓妞ゆ巻鍋撴い顭戞 C闂傚倸鍊搁崐鐑芥倿閿曞倸绠栭柛顐f 礀绾惧潡鏌熼幆鐗堫棄缁炬儳顭烽弻锝呂熼懖鈺佺 闂 ?
2026-04-29 15:18:30 +08:00
* /
_registerCallbacks ( ) {
this . renderstreaming . onNewPeer = ( participantId ) => {
console . log ( ` New peer created for ${ participantId } , adding local tracks ` ) ;
if ( this . state . localStream ) {
const tracks = this . state . localStream . getTracks ( ) ;
for ( const track of tracks ) {
this . renderstreaming . addTransceiver ( track , { direction : 'sendonly' } , participantId ) ;
}
this . setCodecPreferences ( participantId ) ;
this . setVideoEncodingParameters ( participantId ) ;
}
} ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺鍝勬缁捇寮婚埄鍐ㄧ窞閻庯綆浜為崝鎼佹⒑閹肩偛鈧洟鎮ч幘璇茶摕鐟滄垹绮诲☉銏犵缁炬儳顑囬、鍛存煟閻斿摜鐭嬫繝銏★耿閹矂宕掗悙鑼舵憰濠电偞鍨崹鐟版暜闁荤喐绮岀粔鐢电矉閹烘鐓涢柛娑卞枤閸?
2026-04-29 15:18:30 +08:00
this . renderstreaming . onConnect = ( connectionId , data ) => {
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽弫鎼佸磿閹寸姴绶ら柦妯侯棦濞差亝鏅滈柣鎰靛墮鎼村﹪姊虹粙璺ㄧ伇闁稿鍋ゅ畷鎴﹀Χ婢跺鍘繝鐢靛€崘顭戜患闂佸搫顑呴崐鍨潖濞差亜鎹舵い鎾跺仜婵″搫鈹戦悙鎻掔骇闁绘濮惧Λ銏ゆ椤愩垺澶勯柟鏋€栭幆鏃堝Ω閵壯冣偓鐐烘⒑閹稿海绠撻柟鍐查叄閸╂稒寰勭€n剛顔曢柣搴f暩椤牏鏁崼鏇熺厵闁惧浚鍋撻懓璺ㄢ偓娈垮櫘閸嬪嫰顢橀崗鐓庣窞濠电姴娲﹂弳鍛存⒒娴e鈧偓闁稿鎹囬弻鐔碱敇閻樿尙浜?participant闂?
2026-04-29 15:18:30 +08:00
if ( data && data . role ) {
this . role = data . role ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢埛姘そ婵¤埖寰勭€n 亙妲愰梻渚€娼ц墝闁哄懏鐩幏鎴︽偄鐏忎焦鏂€闂佺偨鍎抽崑銊╁焵椤掆偓閻涙瓫lUser闂傚倸鍊搁崐鐑芥倿閿曞倹鍎戠憸鐗堝笒缁€澶屸偓鍏夊亾闁告洦鍋呭Σ顒勬⒑闂堚晝绋婚柛妯兼畮st闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢妶鍥╃厠闂佸搫顦伴崵姘洪鍕幯囨煕閵夛絽濡奸幖?
2026-04-29 15:18:30 +08:00
this . state . session . localUser . isHost = ( this . role === 'host' ) ;
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽弫鎼佸磿閹寸姴绶ら柦妯侯棦濞差亝鏅滈柣鎰靛墮鎼村﹪姊虹粙璺ㄧ伇闁稿鍋ゅ畷鎴﹀Χ 婢跺鍘繝鐢靛仧閸嬫挸鈻嶉崱娑欑厱闁靛牆鎷戦弨濠氭煏閸パ冾伂缂佺姵鐩獮姗€寮堕幋鐐典悍闂傚倷鑳剁划顖炲箰閹绢喖鐤鹃柣妯款嚙閽冪喖鏌ㄥ┑鍡╂Ц閹喖姊洪棃娑辨Ф闁稿孩濞婂畷锝夊磽椤$ icipantId闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樻噽閻瑩鏌熸潏楣冩闁稿孩鏌ㄩ埞鎴﹀磼濠婂海鍔搁梺鍝勵儎缁舵岸寮婚弴銏犻唶婵犻潧娴傚Λ銈咁渻閵堝倹娅嗛柣鎿勭節閻涱噣寮介鐔蜂壕婵炴垶鐟悞鐣岀磼閻樼鑰块柡宀嬬秮椤㈡顦虫い顐ゅ壃rticipants-sync婵犵數濮烽弫鎼佸磻閻愬搫鍨傞柛顐f 礀缁犲綊鏌嶉崫鍕櫣闁活厽顨婇弻宥堫檨闁告挻绋撳Σ鎰板箳閺冣偓鐎氭岸鏌熺紒妯虹瑲婵炲牜鍘奸—鍐Χ 鎼粹€崇闂佸憡姊归崹鑸电┍婵犲洤閱囬柡鍥╁仱閸炶泛鈹戦悩缁樻锭婵炴潙鍊垮畷顒勫醇閺囩啿鎷?
2026-04-29 15:18:30 +08:00
if ( data . participantId ) {
this . selfParticipantId = data . participantId ;
}
console . log ( ` Connected as ${ this . role } , participantId: ${ this . selfParticipantId } ` ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺鍝勬缁捇寮婚埄鍐ㄧ窞閻庯綆浜為崝鎼佹⒑閹肩偛鈧洟鎮ч幘璇茶摕鐟滄垹绮诲☉銏犵缁炬儳顑囬、鍛存煟閻斿摜鐭嬫繝銏★耿閹矂宕掗悙鑼舵憰濠电偞鍨堕敃鈺侇焽閳哄倶浜滈柟鍝勬娴滈箖姊洪幖鐐测偓娑欑椤掑倹顫曢柟鐑樻尰缂嶅洭鏌曟繛褍鎳愰弶浠嬫煟鎼淬埄鍟忛柛鐘崇墵椤㈡岸顢橀姀鐘靛姦濡炪倖甯婇懗鍫曞煀閺囥垺瀚呴梺顒€绉甸悡鐔兼煙缂併垹鐏犲ù婊堢畺濮婂宕掑▎鎺戝帯缂備緡鍣崹鍫曞灳閿曞倸閱囬柕澶堝劜濡差剟姊洪崫鍕枆闁告ê銈搁幃鐐哄垂椤愮姳绨婚梺鐟邦嚟閸嬫盯顢氬鍕╀簻闁挎梹鍎抽崢鎾煙椤旇偐绉烘鐐叉喘椤㈡瑩鎮¢獮顒夋磮going
2026-04-29 15:18:30 +08:00
this . state . session . status = 'ongoing' ;
this . notify ( { type : 'CALL_STATUS_CHANGE' , status : 'ongoing' } ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鍐蹭画闂侀潧顦弲娑氬閻熼偊娓婚悗锝庝簼閹癸絿绱掗悩铏拻闁逞屽墮閸樻粓宕戦幘缁樼厱闁哄洢鍔屾晶顔界箾閸繄鐒告慨濠冩そ瀹曘劍绻濇担铏圭畳闂備礁鎲¢敋闁靛牊鎮傞弫鎰版倷閻戞ɑ娅滄繝銏f硾椤戝洭宕㈤柆宥嗩棅妞ゆ劑鍨洪幖鎰亜閹存繃鍤囩€规洘绻傞…銊╁礋椤忓棛鐣鹃梻浣虹帛閸旀牞銇愰崘顔肩劦妞ゆ巻鍋撶紓宥咃躬閸ㄩ箖寮崼婵堝姦濡炪倖甯婂鎺旀崲閸℃ǜ浜滈柡宥冨妺缁堕亶鏌涙惔娑樺姦闁哄本绋戦~婵嬵敆娴e憡鐏庢繝娈垮枛閿曘倝鈥﹀畡鎵殾濠靛倸鎲¢崑鍕煟閹寸伝顏勨枍濮樿埖鈷掑ù锝堟鐢盯鏌熼崨濠冨€愰柟顔哄劜缁虹晫绮欓幐搴b偓顒勬倵楠炲灝鍔氶柟宄邦儏椤洭鍩℃担鍕剁悼娴狅箓宕滆椤囨⒑濞茶骞楁い銊ワ工椤繘宕崝鍊熸缁辨帒螣鐞涒剝鐎奸梻鍌欑閹测€愁潖瑜版帒鍨傞柛褎顨呯粻鏍煥閻斿搫校闁搞倕顑囬惀顏堝级濞嗙偓楔闂佹椿鍘介〃濠傤潖閾忚鍏滈柛娑卞枛濞懷呯磽娴h棄鐨洪柡鍜佸亞濡叉劙骞樼拠鑼啋缂傚倷鐒﹁彜闁瑰嘲鎼—鍐Χ閸℃鐟愰梺缁樺釜婵″洦绂嶉幖渚囨晣闁靛骏绱曢崢楣冩⒑閻撳寒娼熼柛濠冩礋瀵鈽夊锝呬壕婵炲牆鐏濋弸娑欍亜閺囥劌寮柛鈹惧亾濡炪倖甯婇懗鍫曞煀閺囥垺瀚呴梺顒€绉甸悡鐔兼煙缂併垹鐏犲ù婊堢畺濮婂宕掑▎鎺戝帯缂備緡鍣崹鍫曞灳閿曞倸閱囬柕澶堝劜濡差剟姊洪崫鍕枆闁告ê銈搁幃?
2026-04-29 15:18:30 +08:00
if ( this . role === 'participant' ) {
if ( this . state . localStream ) {
this . state . localStream . getAudioTracks ( ) . forEach ( track => {
track . enabled = false ;
} ) ;
}
this . state . session . localUser . mediaState . audio = false ;
this . notify ( { type : 'LOCAL_MEDIA_CHANGE' , mediaType : 'audio' , value : false } ) ;
console . log ( 'Participant joined with audio muted by default' ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺鍝勬缁捇寮婚埄鍐ㄧ窞閻庯綆浜為崝鎼佹⒑閹肩偛鈧洟鎮ч幘璇茶摕鐟滄垹绮诲☉銏犵缁炬儳顑囬、鍛存煟閻斿摜鐭嬫繝銏★耿閹矂宕掗悙鑼舵憰濠电偞鍨堕敃鈺侇焽閳哄倶浜滈柟鐑樺灥椤忔挳鏌嶈閸撴岸銆冮崼銉ョ疅闁归棿绀佺痪褔鏌涢…鎴濇灀闁稿鎹囧畷绋课旈埀顒勬嫅閻斿吋鐓曟繛鎴濆船閺嬬喎鈹戦娑欏唉闁诡喖鍢查埢搴ょ疀閹垮啩鐥梺璇插绾板秵绻涙繝鍌ゆ綎婵炲樊浜濋ˉ鍫ユ煃閸濆嫭鍣烘い搴$У缁绘繈濮€閵忊€虫畬濠碘槅鍋呯换鍌烆敋閿濆鏁冮柨鏇楀亾閸ュ瓨绻濋姀锝嗙【闁挎洩绠撻、妯好洪鍛嫼闂佺粯鍔﹂崜婵囩珶濮椻偓閺屾稒绻濋崶鑸殿棖闂佺懓绠嶉崹钘夌暦閸楃偐妲堟繛鍡樺灥楠?
2026-04-29 15:18:30 +08:00
this . sendMessage ( 'user-info' , {
id : this . state . session . localUser . id ,
name : this . state . session . localUser . name ,
avatar : this . state . session . localUser . avatar
} ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鍐蹭画闂佹寧娲栭崐鎼佸垂閸岀偞鐓曠憸搴ㄣ€冮崨瀛樺€块柛顭戝亖娴滄粓鏌熸潏鍓хɑ缁绢叀鍩栭妵鍕晜閻撳寒娲紓浣介哺閹告悂顢樻總绋挎そ濞达絿顭堟竟搴ㄦ煟鎼淬値娼愭繛鍙夘焽閸掓帒鐣濋崟鍨櫍婵犻潧鍊婚…鍫濐啅濠靛洢浜滈柡宥冨妽閻ㄦ垿鏌$仦鐣屽闁宠鍨块幃娆撴嚋闂堟稒閿紓鍌欑劍閻擄紕绮婚弽顓犲祦闁规壆澧楅崐閿嬨亜閹哄秶顦︾€殿喖娼″楦裤亹閹烘搫绱电紓浣插亾濞达綀娅f稉宥夋煙鐎涙缂氶柍褜鍓氱敮鎺楋綖濠靛鏁嗗┑鐘插€搁~鐘电磽閸屾瑨顔夐柛瀣尭椤法鎹勯搹鍦紘濠碘槅鍋掗崹閬嶅Φ閸曨垰绠绘俊銈傚亾闁哥噥鍨堕幃姗€宕卞▎鎴狅紳婵炶揪缍€濞咃絿鏁☉姘辨/闁哄娉曞瓭濡炪値鍘煎ú銊╁箯閻樺樊鍟呮い鏃€鍎冲鎶芥⒒娴e憡鍟炵紒顔肩墕椤啴骞掗弮鈧~鏇㈡煙閸撗呭笡闁抽攱鍨块幃璺衡槈閹哄棗浜鹃柛蹇撴噹椤ユ岸姊绘担钘夊惞濠殿喗鎸抽獮鏍敃閿曗偓缁犺銇勯幇鍫曟闁稿鍊块弻娑氫沪閸撗€濮囬梺鍛婄懃缁绘垹鎹㈠┑瀣瀭妞ゆ劧绲介弳妤呮⒑鐠団€虫灈闁靛牊鎮傚璇差吋婢跺á銊╂煥閺冨洤顥嶉柍褜鍓欏锟犲蓟閻旂⒈鏁嶆慨姗嗗墯濞堝鎮楃憴鍕闁哥姵鐗犻妴渚€寮撮姀鐙€娼婂銈庡亽閸樿棄螣婵犲啰绡€闁汇垽娼ф牎濡炪倖姊归悧鐘茬暦娴兼潙鍐€妞ゆ挆鍕珗闂備焦鎮堕崕顖炲礉瀹ュ鐭楅煫鍥ㄧ⊕閻撴瑩姊婚崒姘煎殶妞わ讣濡囩槐?
2026-04-29 15:18:30 +08:00
this . emitMediaStateChange ( ) ;
if ( this . state . localStream ) {
// const tracks = this.state.localStream.getTracks();
// for (const track of tracks) {
// this.renderstreaming.addTransceiver(track, { direction: 'sendonly' });
// }
// this.setCodecPreferences();
this . showStatsMessage ( ) ;
} else {
console . error ( 'Local stream is not available' ) ;
2026-05-24 01:46:57 +08:00
showNotification ( 'Local video stream is not available' , 'error' ) ;
2026-04-29 15:18:30 +08:00
}
} ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺鍝勬缁捇寮婚悢铏圭<闁靛繒濮甸悘鍫濃攽閳藉棗浜濋拑閬嶆煏閸パ冾伃妤犵偞顭囬幑鍕姜閻楀牆鐓曠紓鍌欒兌閸嬫捇宕曢幎瑙b偓锕傛倻閽樺鎽曞┑鐐村灟閸ㄧ懓鏁柣鐔哥矊缁夌數绮嬮幒妤€鐓涢柛娑卞枤閸樹粙姊洪棃娴ゆ盯宕橀埡鍐ч偗闂傚倷娴囧銊х矆娴h櫣鐭撻柣鐔稿閺嗭箑鈹戦崒姘暈闁稿瀚伴弻娑樷槈濮楀牊鏁鹃梺浼欓檮閸ㄥ灝顫忕紒妯诲闁告稑锕ら弳鍫ユ煟閵忊晛鐏i柛瀣仱瀵尙鎹勬担鏇熸閸┾偓妞ゆ帒瀚弲婵嬫煏韫囧鈧洟鎮欐繝鍐︿簻闁瑰搫妫楁禍鍓х磽娴f彃浜炬繝銏e煐閸旀牠鎮¢崘顔界厪濠㈣泛鐗嗘俊浠嬫煟濠垫劒閭柡宀嬬到閳规垿宕熼鐔蜂壕婵犻潧顑呴拑?disconnect 濠电姷鏁告慨鐑藉极閹间礁纾婚柣鎰惈閸ㄥ倿鏌i姀鐘冲暈闁稿顑呴埞鎴︽偐閹绘帗娈銈嗘礋娴滃爼寮诲☉妯锋婵☆垰鍚嬮幉濂告⒑閸濆嫭濯奸柛鎾寸洴閸┾偓妞ゆ巻鍋撻柛妯荤矒瀹曟垿骞樼紒妯煎幈闂佸搫娲﹂敋濠碉紕鍘ч湁婵犲﹤鎳庢禍楣冩煙娓氬灝濡兼い顐g矒瀹曞崬螖閳ь剟锝為锔解拺缂侇垱娲樺▍鍡樸亜閵娿儻韬€?host 缂傚倸鍊搁崐鎼佸磹妞嬪海鐭嗗〒姘e亾鐎规洘鍔栫粋鎺斺偓锝呯仛閺咁亪鎮峰鍐閸楅亶鏌熼悧鍫熺凡缂佺媴缍侀弻锝咁潨閸℃ぞ绨绘繛瀛樼矋閸庢娊婀侀梺鎸庣箓閹冲酣寮冲▎鎰╀簻闁靛鍎虫晥闂佸搫鏈惄顖涗繆閻㈢纭€闁绘劕鐡ㄩ幃娆戠磽娴e搫顎撶紓宥勭窔瀵鈽夊搴⑿俊鐐€戦崝灞轿涘┑瀣瀬鐎广儱顦伴崑鍕煕韫囨艾浜归柛姗€浜跺Λ鍛搭敃閵忊€愁槱闂佸搫琚崝鎴﹀春閵忋倕鍗抽柣鏂垮缁犳艾顪冮妶鍡楀Ё缂佹彃澧芥禍鎼佹晝閸屾稓鍘?
2026-04-29 15:18:30 +08:00
this . renderstreaming . onDisconnect = ( ) => {
console . log ( 'Received disconnect from server, host left or room closed' ) ;
2026-05-24 01:46:57 +08:00
this . hangUp ( ) ; // 闂傚倸鍊搁崐宄懊归崶顒婄稏濠㈣泛顭悞鑺ョ箾閸℃绂嬮柣鎺嶇矙閺屽秹濡烽敂鍓х嵁濠电偞鍨崹褰掑垂閸屾稏浜滈柡鍥╁仦閸e綊鏌¢崨顖氣枅婵﹥妞藉畷鐑筋敇閻樿尙顐奸梺姹囧焺閸ㄨ京鏁悢鐓幬﹂柟鎵閸嬫劗绱撴担楠ㄦ岸骞忔繝姘拺缂佸鍎婚~锕傛煕婵犲啯绀€妞ゆ洩绲块幑鍕Ω瑜忛敍婊堟煛婢跺﹦澧戦柛鏂挎捣缁鎮欓幖顓燁啍闂佺粯鍔栧娆愭叏瀹ュ洠鍋撶憴鍕閻㈩垱甯¢崺銉﹀緞婵炵偓鐎婚梺鐟扮摠缁诲倹顨欓梻鍌氬€风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺?
2026-04-29 15:18:30 +08:00
} ;
2026-05-24 01:46:57 +08:00
// SDP Answer 闂傚倸鍊搁崐宄懊归崶顒婄稏濠㈣泛顑囬々鎻捗归悩宸剰缁炬儳娼¢弻锛勪沪鐠囨彃濮庨梺鍝勵儎閼冲爼骞夐幖浣瑰亱闁割偅绻勯悷銊╂⒑缂佹ê濮囬柨鏇ㄤ邯瀵鏁愭径濠勭杸濡炪倖宸婚崑鎾淬亜閿濆牊鐝紒缁樼洴瀹曠厧鈹戦崼婵堝幗闂備礁鎼悮顐﹀礉瀹€鍕叀濠㈣泛艌閺嬪酣鐓崶銊﹀鞍闁硅櫕鐟╁缁樼瑹閳ь剙顭囬懡銈呯筏濡わ絽鍟粻鏍煕鐏炴儳鍤柛銈嗘礃閵囧嫰骞掗幋婵愪患缂備胶濯崹鎶藉焵椤掆偓閸樻粓宕戦幘鏂ユ斀闁绘ɑ褰冮埀顒傛嚀閳诲秴螖閸愵亞锛濋梺绋挎湰閼归箖鍩€椤掍焦鍊愮€规洘婢橀~婵堟崉閾忓湱宕舵繝娈垮枟椤牊銇旈幖渚婄稏闁哄洨鍠嗘禍婊堢叓閸ャ劍灏靛褎鐩弻娑欐償閳锋凹浜﹢渚€姊洪幐搴g畵婵炴潙鍊块幃鐐烘倷椤戣法绠氬銈嗗姧缂嶅洭鎳撻崸妤佺厸閻忕偟顭堟晶鑼磼濡ゅ啫鏋涢柛鈹惧亾濡炪倖甯掔€氥劑鍩€椤掆偓閸婂潡寮崒鐐村仼鐎光偓婵犲啴鏁滈梻鍌欑濠€閬嶅磿閵堝绠板Δ锝呭暙閻撴鈧箍鍎卞Λ娑氬姬閳ь剟姊婚崒姘卞濞撴碍顨婂畷鏇㈠箛椤撴粈绨婚梺鍝勫€圭€笛囧窗濮椻偓閺屸€崇暆鐎n剛袦婵犳鍠掗崑鎾绘⒑閺傘儲娅呴柛鐘宠壘椤洭鏁撻悩鍐测偓?
2026-04-29 15:18:30 +08:00
this . renderstreaming . onGotAnswer = ( connectionId ) => {
console . log ( 'SDP Answer received, resetting encoding parameters for connectionId:' , connectionId ) ;
if ( this . role === 'host' ) {
const allParticipantIds = Object . keys ( this . state . remoteStreams || { } ) ;
for ( const pid of allParticipantIds ) {
setTimeout ( ( ) => { this . setVideoEncodingParameters ( pid ) ; } , 50 ) ;
}
} else {
setTimeout ( ( ) => { this . setVideoEncodingParameters ( ) ; } , 50 ) ;
}
} ;
this . renderstreaming . onParticipantJoined = ( participantId ) => {
console . log ( ` Participant joined: ${ participantId } ` ) ;
2026-05-24 00:54:58 +08:00
this . _upsertParticipant ( participantId ) ;
this . _notifyParticipantsUpdate ( ) ;
2026-04-29 15:18:30 +08:00
this . broadcastParticipantsList ( ) ;
} ;
2026-05-24 01:46:57 +08:00
// participant缂傚倸鍊搁崐鎼佸磹妞嬪海鐭嗗〒姘e 亾鐎规洘鍔栫粋鎺斺偓锝呯仛閺咁亪鎮峰鍐閸楅亶鏌熼悧鍫熺凡缂佺媴缍侀弻銊╁即濡も偓娴滈箖姊虹紒妯哄闁挎洦浜濠氭晲婢跺﹦鐤€濡炪倖宸婚崑鎾淬亜閿濆牊鐝紒缁樼洴瀹曠厧鈹戦崼婵堝幗闂備礁鎼悮顐﹀礉瀹€鍕叀濠㈣泛谩閻斿吋鍤掗柕鍫濇礌閺岊洉st闂傚倸鍊搁崐宄懊归崶顒€违闁逞屽墴閺屾稓鈧綆鍋呭畷宀勬煙椤旇偐绉虹€规洦鍋婂畷鐔碱敆娴g 澹嶉梻鍌欒兌缁垶骞愰幖浣哥9 闁秆勵殔閽冪喖鏌曟繛鍨姉婵℃彃鐗婃穱濠囶敍濠婂懎绗¢梺鍦厴娴滆泛顫忛搹鐟板闁哄洨鍠愬▓顒勬⒑閹肩偛鍔橀柛鏂款樀楠炲繘鎳¢妶鍥╋紳闂佺鏈銊︽櫠閵忕姭鏀芥い鏃€鍎抽崢鎾煙椤栨俺瀚伴柍瑙勫灩閳ь剨缍嗛崑鍡涘储娴犲鈷戦柛锔诲幖閸斿绻涢崣澶樼劷婵炴垵鐏氶妶锝夊礃閳哄啫骞楅梺鍦劋婵炲﹤鐣峰┑瀣婵犻潧鐗婂▓楣冩⒑缂佹ê鐏辩悮娆撴煃瑜滈崜銊х 不閹捐崵宓侀柟鐑橆殔缁秹鏌涚仦璇测偓鏍х 摥
2026-04-29 15:18:30 +08:00
this . renderstreaming . onParticipantLeft = ( participantId ) => {
console . log ( ` Participant left: ${ participantId } , room still active ` ) ;
this . updateRemoteUserStatus ( 'offline' ) ;
this . updateRemoteUserNetworkQuality ( 'no_signal' ) ;
2026-05-24 01:46:57 +08:00
showNotification ( 'The other participant left the call' , 'warning' ) ;
// 濠电姷鏁告慨鐑藉极閹间礁纾婚柣鎰惈缁犱即鏌熼梻瀵割槮缂佺姷濞€閺岀喖鎮ч崼鐔哄嚒缂備胶濮甸悧鏇㈠煘閹达附鍋愰柟缁樺俯娴犻箖鏌?participant 闂傚倸鍊搁崐鐑芥倿閿曞倹鍎戠憸鐗堝笒缁€澶屸偓鍏夊亾闁逞屽墴閸┾偓妞ゆ帊绀侀崵顒勬煕閵婏箑鈻曟鐐村灴婵偓闁绘﹩鍋呴悗濠氭⒑閻熼偊鍤熷┑顔芥綑閻g兘濡烽妷顔藉瘜闂侀潧鐗嗗Λ娑欐櫠椤掑倻纾奸悗锝庡亝瀹曞苯鈹?
2026-04-29 15:18:30 +08:00
if ( this . state . remoteStreams [ participantId ] ) {
this . state . remoteStreams [ participantId ] . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
delete this . state . remoteStreams [ participantId ] ;
}
if ( this . state . remoteStream ) {
this . state . remoteStream . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
this . state . remoteStream = null ;
}
2026-05-24 00:54:58 +08:00
this . _removeParticipant ( participantId ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鎼佸磹妞嬪孩顐介柨鐔哄Т 绾惧鏌涘☉鍗炲季婵炲皷鏅犻弻鏇熺箾閻愵剚鐝曢梺绋款儏閸婂綊濡甸崟顖氱労闁告劏鏅滅欢婊堟⒒閸屾艾鈧绮堟笟鈧獮鏍敃閳锋碍妞芥俊鑸靛緞鐎n 亙妲愰梻渚€娼ц墝闁哄懏鐩幏鎴︽偄鐏忎焦鏂€闂佺粯锚瀵埖寰勯崟顖涚厸闁告粈绀佹晶浼存煃瑜滈崜娆撳储濠婂牆纾婚柟鍓х 帛閻撳啰鎲稿鍫濈闁绘柨顨庨崵鏇熴亜閹板洩顕у ú顓烆嚕閻㈠灚顫曢柛宥囧交cipantId婵犵數濮烽弫鎼佸磻閻樿绠垫い蹇撴缁躲倝鏌涢幘妤€鍟悘濠囨⒑閸撴彃浜栭柛銊ㄦ閳ь剚鑹鹃…鐑藉蓟閿濆绠抽柟瀵稿Х 瀹曠垔nectionId婵犵數濮烽弫鎼佸磻閻斿澶愬箛閺夎法锛涢梺褰掑亰閸樹粙宕h 箛鎿冩富閻庯綆浜妤呮煕閵婏妇绠為柡宀嬬秮婵℃悂濡烽埗鈺佷壕妞も晜鏀rer
2026-04-29 15:18:30 +08:00
this . notify ( { type : 'PARTICIPANT_LEFT' , connectionId : participantId } ) ;
2026-05-24 00:54:58 +08:00
this . _notifyParticipantsUpdate ( ) ;
2026-04-29 15:18:30 +08:00
this . broadcastParticipantsList ( ) ;
} ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊风粈渚€骞栭位鍥焼瀹ュ懐锛涢梺纭呮彧缁犳垹绮堟径鎰厵閻庣數顭堟禒褎銇勯埡鍌滃弨闁哄矉缍侀獮鍥濞戞﹩娼炬俊鐐€愰弲婊堟偂閿熺姴绠栨俊銈呮噺閺呮煡鏌涚仦鍓р姇闁绘繃娲栭—鍐Χ閸℃顫堢紓渚囧枟閻熲晛顕g拠娴嬫闁靛繒濮堥妸褎鍠愮€广儱妫涢々鐑芥煃閸濆嫭鍣洪柣?
2026-04-29 15:18:30 +08:00
this . renderstreaming . onTrackEvent = ( data ) => {
2026-05-24 00:54:58 +08:00
this . _handleTrackEvent ( data ) ;
2026-04-29 15:18:30 +08:00
} ;
this . renderstreaming . onMessage = ( data ) => {
2026-05-24 00:54:58 +08:00
this . _handleRenderStreamingMessage ( data ) ;
2026-04-29 15:18:30 +08:00
} ;
}
/ * *
2026-05-24 01:46:57 +08:00
* 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲 鏁撻悩鑼 槱閻熸粎澧楃敮鎺楀垂閸岀偞鐓熸俊銈傚亾闁绘 锕 畷锝堢疀濞戞瑧鍙嗛梺缁樻磻閻掞箓锝為幁鍢 C闂傚倸鍊风粈渚 € 骞栭 位鍥 敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺鍝勬 缁 捇寮婚悢铏圭 < 闁靛繒濮甸悘鍫濃攽閿涘嫬浠掔紒顔界懇楠炲啫顫滈埀顒勫箖濞嗘劖濯撮柛鎰ㄦ櫓閳ь剚顨呴埞鎴 ︻ 敊绾 嘲濮涢梺绋款儐閹搁箖骞 ?
2026-04-29 15:18:30 +08:00
* @ async
2026-05-24 01:46:57 +08:00
* @ param { string } connectionId - 闂傚倸鍊风粈渚 € 骞栭 位鍥 敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺鍝勬 缁 酣鎯 € 椤忓牊鍋嬮柛顐ゅ枑閸 ?
2026-04-29 15:18:30 +08:00
* @ returns { Promise < void > }
* /
async _startConnection ( connectionId ) {
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鑼槱閻熸粎澧楃敮鎺楀垂閸岀偞鐓熸俊銈傚亾闁绘锕畷锝堢疀濞戞瑧鍙嗛梺缁樻磻閻掞箓锝為幁鍢C闂傚倸鍊风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺?
2026-04-29 15:18:30 +08:00
await this . renderstreaming . start ( ) ;
await this . renderstreaming . createConnection ( connectionId ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鑼槱閻熸粎澧楃敮鎺楀垂閸岀偞鐓熸俊銈傚亾闁绘锕畷锝堢疀濞戞瑧鍘撻梺鍛婄箓鐎氼剟寮抽悢铏规/濡わ絽鍟伴悾娲煛瀹€鈧崰鎾诲焵椤掑倹鏆╂い顓炵墕閺嗏晠姊绘担渚敯妞ゎ偄顦叅闁绘梻鍘х粻鏍喐閻楀牆绗掔紒鐘崇洴閺屽秵娼幍顕呮М濡炪値鍋勫ú顓烆潖濞差亝顥堥柍鍝勫暙閸╁矂姊洪崷顓涙嫛闁稿顦甸幃妯尖偓锝庡枟閳锋垿姊洪銈呬粶闁兼椿鍨遍弲鍫曨敋閳ь剙螞?
2026-04-29 15:18:30 +08:00
this . startNetworkQualityDetection ( ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鑼槱閻熸粎澧楃敮鎺楀垂閸岀偞鐓熸俊銈傚亾闁绘锕畷锝堢疀濞戞瑧鍘撻梺鍛婄箓鐎氼參宕宠ぐ鎺撳€垫慨姗嗗墻濡插綊鏌曢崶褍顏鐐村浮瀹曞崬顪冮幆褜妫滄繝鐢靛Х閺佸憡鎱ㄩ銏犵;闁瑰墽绮悡娆戠磽娴i潧鐏╅柡瀣枑閵囧嫰濡烽妷顖濆惈闂佸搫鐬奸崰鏍х暦濠婂嫭濯撮柣鎴炆戦崯鎺楁⒒娴e憡鎯堟俊顐㈤叄瀹曟洟鎳犻浣稿簥濠电偞鍨崹娲吹閹存惊娑㈡偋閸垻鐣靛┑鐐茬墱閸樼晫鎹㈠┑鍡忔灁闁割煈鍠楅悘鈧梻浣告惈閹冲繒绮欓弽顓熷€堕悗锝庡枟閳锋垿姊洪銈呬粶闁兼椿鍨遍弲鍫曨敋閳ь剙螞?
2026-04-29 15:18:30 +08:00
this . startActivityDetection ( this . state . localStream , { isLocal : true } ) ;
2026-05-24 01:46:57 +08:00
//闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鑼槱閻熸粎澧楃敮鎺楀垂閸岀偞鐓熸俊銈傚亾闁绘锕畷锝堢疀濞戞瑧鍘撻梺鍛婄箓鐎氼剟鍩€椤掑喚娼愮紒鍌氱Ч婵″爼宕卞▎鎴犳濠电姷鏁告慨鎾磹鐟欏嫬鍨旈柣鎾崇瘍瑜版帗鍋傞幖绮规閸嬫捇宕归鍛稁缂傚倷鐒﹂…鍥╁姬閳ь剟姊洪崨濠傚闁告柨顦靛绋库枎韫囷絾瀵岄梺闈涚墕缁绘帞绮旈浣虹濠㈣泛顑囬妴鎺楁煕濞嗗繑鍤囬柡宀嬬秮閹晠宕f径濠庢П闁荤喐绮嶅姗€宕幎閾扮兘宕熼鍌滅槇缂佸墽澧楄摫妞ゎ偄锕弻娑氣偓锛卞啩澹曢梻鍌欒兌椤牓顢栭崱娑樼闁煎鍊栧畷?
2026-04-29 15:18:30 +08:00
this . startActivityDetection ( this . state . remoteStream , { isLocal : false } ) ;
}
/ * *
2026-05-24 01:46:57 +08:00
* 闂傚倸鍊搁崐椋庣矆娴i潻鑰块梺顒 € 绉 埀顒婄畵瀹曞ジ濡烽妷褝绱 柣鐔哥矌婢ф 鏁 幒妤佸珔闁绘柨鍚嬮悡鏇熴亜椤愵偄鍘撮柛瀣 崌瀹曨偆绮垫總顤夐梻鍌氬 € 风粈渚 € 骞栭 位鍥 敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺 ?
* Host闂傚倸鍊搁崐椋庣矆娴i 潻鑰块梺顒 € 绉 埀顒婄畵瀹曞ジ濡烽妷褝绱 柣鐔哥矌婢ф 鏁 幒妤佸珔闁绘柨鍚嬮悡鏇㈡煏婢舵ê鏋涘 褜鍨堕弻锟犲椽娴g 顫 梺閫涚 ┒ 閸斿矂锝炲 鍫濆耿婵 ° 倐鍋撶紒鐘茬秺濮婅櫣绱掑Ο缁樺創缂傚倸绉崇粈渚 € 锝炶箛鏃傜瘈婵 ﹩ 鍓涢敍婊冣攽閻愭潙鐏 ﹂ 柣鐔濆懐鐭欓柛銉 墯閳锋垿鏌熼懖鈺佷粶濠德ゅ亹缁辨挸顓奸崪鍐ㄤ紣濡炪値鍋勭换鎴犵矉閹烘柡鍋撻敐搴樺亾椤撳 ﹤ 娲ㄩ崣鎾绘煕閵夛絽濡界紒鈧 崼鐔翠簻闁挎棁顕у ▍ 宥嗘叏婵犲嫮甯涚紒妤冨枛瀹曟儼顦辩紒銊у帶閳规垿顢欑憴鎺撶矒瀹曟繈骞嬮敃鈧 粻鏍 煥閻斿搫校闁哄懏鎮傞弻锝呂熼崹顔炬 濡炪倖鎸搁妶绋款潖濞差亝鐒婚柣鎰 蔼鐎氭澘顭胯 椤曨參鍩 € 椤掍緡鍟忛柛鐘崇墵閹 ê顫濇潏銊ュ簥闂佸憡渚楅崹鎶藉础濮樿埖鍊甸柛锔诲幘濮e 攨cipants
* Participant闂傚倸鍊搁崐椋庣矆娴i 潻鑰块梺顒 € 绉 埀顒婄畵瀹曞ジ濡烽妷褝绱 柣鐔哥矌婢ф 鏁 幒妤佸珔闁绘柨鍚嬮悡鏇㈡煏婢舵ê鏋涘 褜鍨堕弻锟犲椽娴g 顫 梺閫涚 ┒ 閸斿矂锝炲 鍫濆耿婵 $ 偛澧介弫鏍 ⒒娴h 銇熼柛妯恒偢閹 矂宕掗悙鑼舵憰闂侀潧鐗嗗Λ娑㈡⒔閸曨偒鐔嗛悹杞拌 濡叉椽鏌i埡浣规崳缂佽鲸鎸婚幏鍛 驳鐎n亝鏆 梻浣哄帶閸熸寧鏅舵惔銊ョ 闁圭儤鏌 ¢ 崑鎾绘晲鎼粹 € 愁潽闁诲孩纰嶅畝鎼佸蓟閵堝洤鏋堥柛妤冨仜椤 偊姊洪幐搴b姇闁烩晩鍨伴 ~ 蹇撁洪 鍕 唶闁硅壈鎻 徊鍝勎i崼銉 ︹ 拺闁告繂瀚 晶閬嶆煕閹捐泛鏋庨柣锝夋敱閵堬箑鈻庨悙顑芥瀸濠电姵顔栭崰鏍 晝閵娿儳鏆 ﹂ 柣銏犵仛椤 洘绻涢崱妯诲鞍闁稿 鍔欓弻锝夊 閳藉棗鏅遍梺鍝ュ枎缁夌懓顫 ?
2026-04-29 15:18:30 +08:00
* @ async
* @ returns { Promise < void > }
* /
async hangUp ( ) {
2026-05-24 01:46:57 +08:00
this . clearStatsMessage ( ) ; // 濠电姷鏁告慨鐑藉极閹间礁纾婚柣鎰惈缁犱即鏌熼梻瀵割槮缂佺姷濮垫穱濠囶敍濠靛嫧鍋撻埀顒勬煛鐎n亞效妤犵偞鐗楀蹇涘礈瑜庨崑褏绱掗悙顒€鍔ら柕鍫熸倐瀵鏁愭径濠勭潉闂侀€炲苯澧い顏勫暣瀹曠螖閳ь剛鎲撮敃鍌涚厓鐟滄粓宕滃杈ㄥ床婵炴垯鍨洪弲鏌ユ煕濞戝崬鏋ゆい锕備憾濮婃椽宕ㄦ繝鍐ㄩ瀺缂備浇顕ч崯浼村焵?
this . stopNetworkQualityDetection ( ) ; // 闂傚倸鍊搁崐鐑芥嚄閸洍鈧箓宕奸妷顔芥櫈闂佺硶鍓濋悷銉╁垂濠靛牃鍋撻獮鍨姎妞わ缚绮欏顐﹀幢濡偐顔曢梺鐟邦嚟閸嬬偤鎯冮幋鐘垫/濡わ絽鍟伴悾娲煛瀹€鈧崰鎾诲焵椤掑倹鏆╂い顓炵墕閺嗏晠姊绘担渚敯妞ゎ偄顦叅闁绘梻鍘х粻鏍喐閻楀牆绗掔紒鐘崇洴閺屽秵娼幍顕呮М濡炪値鍋勫ú顓烆潖濞差亝顥堥柍鍝勫暙閸╁矂姊洪崷顓涙嫛闁稿顦甸幃妯尖偓锝庡枟閳锋垿姊洪銈呬粶闁兼椿鍨遍弲鍫曨敋閳ь剙螞? // 闂傚倸鍊搁崐鐑芥嚄閸洍鈧箓宕奸妷顔芥櫈闂佺硶鍓濋悷銉╁垂濠靛牃鍋撻獮鍨姎妞わ缚绮欏顐﹀幢濡偐顔曢梺鐟扮摠閻熴儵鎮橀鍫熺厽闁圭儤姊荤敮娑㈡煙娓氬灝濡奸摶锝夋煣韫囨洘顏熺紒杈╁仜閳规垿鍩ラ崱妤冧哗闂佺粯顨嗛幑鍥ь嚕婵犳碍鏅搁柣妯垮皺椤︺劑姊洪棃娴ゆ盯宕橀鍕闂傚倸鍊风粈渚€骞栭鈶芥稑顭ㄩ崟顓ф锤濡炪倖甯掔€氼剛绮婚鐐村€甸柨婵嗛閺嬫盯鏌i幒鎾淬仢闁哄本鐩獮鍥Ω閿旂晫褰熸繝纰樷偓铏枙闁搞劏娉涢~?
2026-04-29 15:18:30 +08:00
if ( this . durationInterval ) {
clearInterval ( this . durationInterval ) ;
this . durationInterval = null ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏇氶檷娴滃綊鏌涢幇鍏哥敖闁活厽鎹囬弻娑㈩敃閿濆棛顦ㄩ梺绋款儛娴滎亪寮诲☉銏犵労闁告劦浜栨慨鍥⒑缂佹ê绗х紒顕呭灦楠炲牓濡搁妷搴e枛閹煎綊鎯傞崫銉ь槸濠电姷顣藉Σ鍛村垂娴煎瓨鍋嬮柟鎹愵嚙閽冪喖鏌ㄩ悢鍝勑㈢紒鈧崘顔界厪濠电倯鍐ㄦ殶闁告ü绮欏缁樻媴閸涘﹥鍎撳┑鈽嗗亝缁诲啰绮嬪鍥ㄥ磯闁惧繗顫夊▓楣冩⒑闂堟冻绱¢柛灞剧☉婵壆绱撻崒姘偓鐑芥⒔瀹ュ鍨傞悷娆忓缁诲棝姊洪鈧粔鐢稿煕閹达附鐓曟繝闈涙椤忣偆鈧稒绻傞埞鎴︽倷閼碱剚鍕鹃梺绋匡攻椤ㄥ﹪鐛?
2026-04-29 15:18:30 +08:00
this . durationSynced = false ;
const isHost = this . role === 'host' ;
console . log ( ` Disconnect peer on ${ this . connectionId } . Role: ${ this . role } ` ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹闂佺粯鍔曢顓犵不妤e 啯鐓冪憸婊堝礈濮樿鲸宕叉繛鎴欏灩瀹告繃銇勯幘璺哄壉闁告柨顦埞鎴︽倷闂堟稑浠樺銈庡幘閸忔﹢鐛崘銊㈡瀻闁规儳纾鍥⒑閻熸壆鎽犵紒璇茬Т 鍗辩紓浣姑肩换鍡涙煏閸繃鎼愰崯鎼佹⒑缁嬫鍎戝┑顔芥尦閸┿垽寮惔鎾搭潔濠电偛妫欓崺鍫澝归崟顖涒拺缂備焦鈼ら鍕庣喐绻濋崒銈囧姺闂佺粯顨呴悧鍕濠婂牊鐓忛煫鍥ь儏閻忣喗銇勯埡鍐簮RTC
2026-04-29 15:18:30 +08:00
if ( this . renderstreaming ) {
try {
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鍐蹭画闂佹寧娲栭崐鎼佸垂閸岀偞鐓曠憸搴ㄣ€冮崨瀛樺€块柛顭戝亖娴滄粓鏌熸潏鍓хɑ缁绢厼鐖奸弻娑㈠棘鐠恒剱褔鏌″畝瀣?濞寸媴绠撻幊鐐哄Ψ閿旇棄鈧攱淇婇悙顏勨偓鏍洪埡鍐濞撴埃鍋撶€规洘妞介崺鈧い鎺嶉檷娴滄粓鏌熼崫鍕ら柛鏂跨Т闇夋繝褍鐏濋埀顒佺箞瀵鏁愭径濠勭杸濡炪倖鎸鹃崰鎾剁矙閸ャ劎绡€闁靛骏绲剧涵鍓х磼婢跺矈妫庣憸棰佺窔濮婃椽骞栭悙鎻掑Х缂備礁寮跺钘夌暦閹达箑绠婚悹鍥皺閻撴垶绻濋姀锝嗙【閼煎懘鏌熺€电啸缁惧彞绮欓弻娑氫沪閸撗勫櫘闂佸憡鏌ㄧ粔褰掑蓟閿濆鍋嗛柛灞剧矌閺嗙娀姊洪幐搴㈢8闁搞劏妫勯悾鐑藉醇閺囥劍鏅㈤梺閫炲苯澧扮紒顔肩墛瀵板嫰骞囬鐘插笚闂備線娼чˇ顓㈠礉瀹ュ鍋熸い蹇撶墛閻?
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢敂钘変罕闂佺硶鍓濋悷褔鎯岄幘缁樺€垫繛鎴烆伆閹达箑鐭楅煫鍥ㄧ⊕閻撶喖鏌¢崘銊モ偓鍝ユ暜閸洘鈷掗柛灞诲€曢悘锕傛煛鐏炵偓绀冪紒缁樼洴瀹曞綊顢欓悷棰佸濠德板€曢崯浼存偝缂佹ü绻嗛柕鍫濇噺閸f椽鏌i幘瀵搞€掗柍褜鍓欑粻宥夊磿闁秵鍋嬫繝濠傚枤閻掍粙鏌熼柇锕€鏋撻柛瀣尵閹叉挳宕熼鈧惌顕€姊洪悡搴℃毐闁绘牕銈搁悰顔藉緞閹邦剛顔掑銈嗘閸嬫劙顢欐繝鍥ㄢ拺闁告繂瀚婵嬫煕鎼淬垹濮囬摶鐐碘偓鍏夊亾闁告洦鍏橀幏娲⒑閸涘﹦鈽夐柨鏇樺€栭幈銊╁炊椤掍胶鍘遍梺缁樻閺€閬嶅磻閵夆晜鐓欐い鏃€鍎虫禍楣冩煏閸剛绉€规洘锕㈤崺锟犲磼濞戞艾寤?
// - host闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢敃鈧壕濠氭煕閳╁啰鈽夐柛灞诲姂閺屻倕霉鐎n 偅鐝曞銈庡亝濞茬喖寮婚悢鍛婄秶濡わ絽鍟宥夋⒑閸濆嫭濯奸柛鎾村哺楠炲牓濡搁妷搴e 枛閹煎綊鏌呭☉銏犱粣闂傚倷鑳堕…鍫ヮ敄閸℃稑绠伴柟闂寸筏缂嶆牗绻濋棃娑欏缂傚秴娲弻宥嗘姜閹殿噮妲紓鍌欒閺呯姴顫忕紒妯诲闁告稑锕ㄧ涵鈧俊鐐€х 徊鑲╁垝濞嗗繒鏆﹂柟杈剧畱缁犺崵绱撴担璇$ 劷闁告ɑ 鎮傚娲礈閹绘帊绨肩紓浣告惈濞硷繝宕洪埀顒併亜閹哄棗浜鹃梺浼欑悼閺咁暁icipants闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樻噽閻瑩鏌熸潏楣冩闁搞倖鍔栭妵鍕冀椤愵澀绮堕梺鎼炲妼閸婂骞夐幖浣瑰亱闁割偅绻勯悷鏌ユ⒑閹惰姤鏁辨俊顐㈠暣瀵寮撮姀鐘诲敹濠电娀娼у ù鍌毼涢悙鐑樷拺缂備焦蓱閹牏绱撳鍕槮妞?
// - participant闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢敃鈧壕濠氭煕閳╁啰鈽夐柛灞诲姂閺屻倕霉鐎n 偅鐝曞銈庡亝濞茬喖寮婚悢鍛婄秶濡わ絽鍟宥夋⒑閸濆嫭濯奸柛鎾村哺楠炲牓濡搁妷顔藉缓闂佺硶鍓濋妵鐐佃姳娴犲鈷戞繛鑼额嚙楠炴牠鏌i 鐐测偓鎼侊綖韫囨拋娲敂瀹ュ棙娅囬梻浣瑰缁诲倸螞濞戞ǚ鏋旈柕鍫濐槹閳锋垹绱撴担鑲℃垿骞嗛崟顖涚厵缂佹稑顑嗙壕鐥祎闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樻噽閻瑩鏌熺€电浠ч梻鍕閺岋繝宕橀妸銉㈠亾閻熸壆鏆﹂柛娆忣槹閸欏繑淇婇姘变虎闁绘挻鍔欓弻娑氣偓锛卞嫭鐝曠紓浣虹帛閻╊垰鐣烽崡鐐嶇喖宕崟鍨秼闂傚倷鑳剁划顖炲箹椤愩倗绀婂ù锝呭濞?
2026-04-29 15:18:30 +08:00
await this . renderstreaming . deleteConnection ( ) ;
await this . renderstreaming . stop ( ) ;
} catch ( error ) {
console . error ( 'Error during hangUp:' , error ) ;
}
this . renderstreaming = null ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢埛姘そ婵¤埖寰勭€n亙妲愰梻渚€娼ц墝闁哄懏鐩幏鎴︽偄鐏忎焦鏂€闂佺粯蓱瑜板啴寮抽悙瑁佺懓鈹冮崹顔瑰亾閺嶎偅宕叉繝闈涙川缁♀偓闂佺鏈粙鎴濃枍濠靛鈷戦柛婵嗗椤箓鏌涙繝鍐疄鐎殿喖顭烽弫鎰緞濞戞氨鈼ゆ俊鐐€栧濠氬磻閹炬枼鏀介柍銉ㄥ皺閻瑩鏌$仦鍓ф创闁糕晪绻濆畷鎺戭煥閸曨偄鐏¢梻鍌欐祰椤曟牠宕伴幒妤€鐤鹃柣妯款嚙缁犵姵绻濇繝鍌涘櫣闁哄鐗婃穱濠囶敍濠靛洢鈧啫顭跨憴鍕缂佺粯绋掑蹇涘礈瑜庨崑褔姊洪崫鍕妞わ箒浜崚鎺楀醇閳垛晛浜鹃柨婵嗛婢ф壆鐥幆褋鍋㈤柡灞剧〒娴狅箓宕滆閻g敻姊洪崨濠呭缂傚秴锕?
2026-04-29 15:18:30 +08:00
this . updateRemoteUserStatus ( 'offline' ) ;
this . updateRemoteUserNetworkQuality ( 'no_signal' ) ;
2026-05-24 01:46:57 +08:00
// 濠电姷鏁告慨鐑藉极閹间礁纾婚柣鎰惈缁犱即鏌熼梻瀵割槮缂佺姷濞€閺岀喖鎮ч崼鐔哄嚒缂備胶濮甸悧鏇㈠煘閹达箑鐏抽悗鍦Х 濡澁ticipants
2026-04-29 15:18:30 +08:00
this . state . participants = { } ;
this . selfParticipantId = null ;
this . connectionId = null ;
this . role = null ;
this . state . session . status = 'ended' ;
this . notify ( { type : 'CALL_ENDED' , reason : isHost ? 'host_hangup' : 'participant_hangup' } ) ;
}
2026-05-24 00:54:58 +08:00
_handleTrackEvent ( data ) {
const direction = data . transceiver . direction ;
if ( direction === 'sendrecv' || direction === 'recvonly' ) {
this . _handleIncomingTrack ( data ) ;
return ;
}
if ( direction === 'sendonly' && data . track . kind === 'audio' ) {
this . startActivityDetection ( this . state . localStream , { isLocal : true } ) ;
}
}
_handleIncomingTrack ( data ) {
const trackParticipantId = data . participantId || this . connectionId ;
const isHost = this . role === 'host' ;
const targetStream = this . _getOrCreateRemoteStream ( trackParticipantId , isHost ) ;
this . _replaceTrackOfSameKind ( targetStream , data . track ) ;
console . log ( 'Added new track:' , data . track . kind , 'for participant:' , trackParticipantId ) ;
if ( isHost && ! this . state . participants [ trackParticipantId ] ) {
this . _upsertParticipant ( trackParticipantId ) ;
this . _notifyParticipantsUpdate ( ) ;
this . broadcastParticipantsList ( ) ;
}
this . _notifyRemoteStreamUpdate ( targetStream , trackParticipantId , isHost , data . track . kind ) ;
if ( this . state . session . remoteUser . status !== 'online' ) {
this . updateRemoteUserStatus ( 'online' ) ;
this . updateRemoteUserNetworkQuality ( 'good' ) ;
this . sendMessage ( 'user-info' , {
id : this . state . session . localUser . id ,
name : this . state . session . localUser . name ,
avatar : this . state . session . localUser . avatar
} ) ;
this . _startDurationTimer ( ) ;
}
if ( data . track . kind === 'audio' ) {
this . startActivityDetection ( this . state . remoteStream , { isLocal : false } ) ;
}
}
_getOrCreateRemoteStream ( trackParticipantId , isHost ) {
if ( isHost ) {
if ( ! this . state . remoteStreams [ trackParticipantId ] ) {
this . state . remoteStreams [ trackParticipantId ] = new MediaStream ( ) ;
}
return this . state . remoteStreams [ trackParticipantId ] ;
}
if ( this . state . remoteStream == null ) {
this . state . remoteStream = new MediaStream ( ) ;
}
return this . state . remoteStream ;
}
_replaceTrackOfSameKind ( targetStream , track ) {
const existingTracks = targetStream . getTracks ( ) . filter ( existingTrack => existingTrack . kind === track . kind ) ;
existingTracks . forEach ( existingTrack => {
targetStream . removeTrack ( existingTrack ) ;
console . log ( 'Removed old track:' , existingTrack . kind ) ;
} ) ;
targetStream . addTrack ( track ) ;
}
_notifyRemoteStreamUpdate ( targetStream , trackParticipantId , isHost , trackKind ) {
const notifyStreamUpdate = ( ) => {
this . notify ( {
type : 'REMOTE_STREAM_OBTAINED' ,
stream : targetStream ,
connectionId : trackParticipantId ,
isHost
} ) ;
console . log ( 'Notified UI about remote stream update' ) ;
} ;
if ( trackKind === 'audio' && targetStream . getVideoTracks ( ) . length === 0 ) {
console . log ( 'Audio track arrived first, delaying stream notification for video track...' ) ;
setTimeout ( ( ) => {
const nowHasVideo = targetStream . getVideoTracks ( ) . length > 0 ;
console . log ( ` After delay, stream has video: ${ nowHasVideo } ` ) ;
notifyStreamUpdate ( ) ;
} , 200 ) ;
return ;
}
notifyStreamUpdate ( ) ;
}
_handleRenderStreamingMessage ( data ) {
2026-05-24 01:46:57 +08:00
console . log ( '闂傚倸鍊搁崐宄懊归崶顒€违闁逞屽墴閺屾稓鈧綆鍋呭畷宀勬煙椤旇偐绉虹€规洦鍋婂畷鐔碱敆娴g澹嶉梻鍌欒兌缁垶骞愰幖浣哥9闁革富鍘藉畷鍙夌箾閹存瑥鐏╃紒鐙呯稻缁绘繈妫冨☉姘暫濡炪們鍊愰崑鎾寸節?' , data ) ;
2026-05-24 00:54:58 +08:00
switch ( data . type ) {
case 'chat-message' :
this . _handleChatMessage ( data ) ;
break ;
case 'media-state-changed' :
this . _handleMediaStateChangedMessage ( data ) ;
break ;
case 'user-info' :
this . _handleUserInfoMessage ( data ) ;
break ;
case 'participants-sync' :
this . _handleParticipantsSyncMessage ( data ) ;
break ;
default :
break ;
}
}
_handleChatMessage ( data ) {
const chatPayload = data . data || data . message ;
if ( ! chatPayload ) {
return ;
}
chatMessage . handleChatMessage ( chatPayload ) ;
if ( data . participantId && this . role === 'host' && this . state . participants [ data . participantId ] ) {
this . _upsertParticipant ( data . participantId , {
id : chatPayload . senderId ,
... ( chatPayload . senderName ? { name : chatPayload . senderName } : { } ) ,
... ( chatPayload . senderAvatar ? { avatar : chatPayload . senderAvatar } : { } )
} ) ;
this . _notifyParticipantsUpdate ( ) ;
this . broadcastParticipantsList ( ) ;
return ;
}
if ( ! this . role || this . role !== 'host' ) {
if ( data . participantId && this . state . participants [ data . participantId ] ) {
this . _upsertParticipant ( data . participantId , {
... ( chatPayload . senderName ? { name : chatPayload . senderName } : { } ) ,
... ( chatPayload . senderAvatar ? { avatar : chatPayload . senderAvatar } : { } )
} ) ;
this . _notifyParticipantsUpdate ( ) ;
} else if ( chatPayload . senderId !== this . state . session . localUser . id ) {
this . _updateRemoteUserProfile ( {
id : chatPayload . senderId ,
name : chatPayload . senderName ,
avatar : chatPayload . senderAvatar
} ) ;
}
}
}
_handleMediaStateChangedMessage ( data ) {
2026-05-24 01:46:57 +08:00
console . log ( '闂傚倸鍊搁崐宄懊归崶顒€违闁逞屽墴閺屾稓鈧綆鍋呭畷宀勬煙椤旇偐绉虹€规洦鍋婂畷鐔碱敆娴g澹嶉梻鍌欒兌缁垶骞愰幖浣哥9闁归棿绀侀悡姗€鏌熸潏鎯х槣闁轰礁绉电换娑㈠箣閻戝棛鍔烽梺鑽ゅ枑閸旀妲愰幘璇茬<婵ɑ鐦烽姀掳浜滈柟瀛樼箥濡偓濡ょ姷鍋涢崯鎾春閿熺姴宸濇い鏂垮悑閻ゅ倻绱撴担绋库挃濠⒀勵殜閺佸绻涚€涙鐭嬮柛搴㈠▕濠€渚€姊洪幐搴g畵婵炴潙鍊块幃鐐哄礈瑜忕壕鐣屸偓骞垮劚鐎氼喚绮i弮鍫熺厸?' , data . data , 'from participant:' , data . participantId ) ;
2026-05-24 00:54:58 +08:00
if ( this . role === 'host' ) {
if ( data . participantId && this . state . participants [ data . participantId ] ) {
this . _upsertParticipant ( data . participantId , {
mediaState : data . data
} ) ;
}
this . updateRemoteMedia ( data . data , data . participantId ) ;
this . _notifyParticipantsUpdate ( ) ;
this . broadcastParticipantsList ( ) ;
return ;
}
if ( data . participantId && data . participantId !== this . selfParticipantId && this . state . participants [ data . participantId ] ) {
this . _upsertParticipant ( data . participantId , {
mediaState : data . data
} ) ;
this . _notifyParticipantsUpdate ( ) ;
return ;
}
if ( data . participantId === this . selfParticipantId ) {
return ;
}
console . log ( 'Received media-state-changed from Host, updating remoteUser:' , data . data ) ;
this . updateRemoteMedia ( data . data , data . participantId ) ;
this . _notifyParticipantsUpdate ( ) ;
}
_handleUserInfoMessage ( data ) {
2026-05-24 01:46:57 +08:00
console . log ( '闂傚倸鍊搁崐宄懊归崶顒€违闁逞屽墴閺屾稓鈧綆鍋呭畷宀勬煙椤旇偐绉虹€规洦鍋婂畷鐔碱敆娴g澹嶉梻鍌欒兌缁垶骞愰幖浣哥9闁秆勵殔閽冪喖鏌ㄥ┑鍡╂缂傚秵鐗楅妵鍕箳閸℃ぞ澹曞┑鐘殿暯閳ь剝灏欓惌娆撴煛鐏炲墽娲撮柛鈺嬬節瀹曟帒鈹冮幆褜娼撶紓鍌氬€风粈渚€鎯岄崒娑氼洸闁割偅娲栭弰銉╂煕閺囥劌鐏犵紒鈧崘顏呭枑闊洦娲滈惌鍡涙煃?' , data . data , 'from participant:' , data . participantId ) ;
2026-05-24 00:54:58 +08:00
if ( ! data . data ) {
return ;
}
if ( data . participantId && this . role === 'host' ) {
this . _upsertParticipant ( data . participantId , {
id : data . data . id || '' ,
name : data . data . name || DEFAULT _PARTICIPANT _NAME ,
avatar : data . data . avatar || DEFAULT _PARTICIPANT _AVATAR
} ) ;
this . _notifyParticipantsUpdate ( ) ;
this . broadcastParticipantsList ( ) ;
return ;
}
this . _updateRemoteUserProfile ( {
id : data . data . id || this . state . session . remoteUser . id ,
name : data . data . name || this . state . session . remoteUser . name ,
avatar : data . data . avatar || this . state . session . remoteUser . avatar
} ) ;
}
_handleParticipantsSyncMessage ( data ) {
if ( this . role === 'host' || ! data . data ) {
return ;
}
2026-05-24 01:46:57 +08:00
console . log ( '闂傚倸鍊搁崐宄懊归崶顒€违闁逞屽墴閺屾稓鈧綆鍋呭畷宀勬煙椤旇偐绉虹€规洦鍋婂畷鐔碱敆娴g澹嶉梻鍌欒兌缁垶骞愰幖浣哥9闁秆勵殔閽冪喖鏌i弮鍥モ偓鈧柛瀣尭閳藉鈻嶉褌绨奸柟渚垮姂瀹曞爼顢楁担鍝勫箥闂備礁鎲¢悷銉┧囬鐐茬厺闁哄洨濮崑鎾舵喆閸曨剛顦ㄩ梺鑹邦潐瀹曟﹢鎮橀崘顔解拺闁告稑锕ョ壕鐢告煛閸屾瑧绐旂€规洘鍨块獮妯兼嫚閼碱剦妲版俊鐐€栧Λ浣圭珶閸綆鏉洪梻鍌欐祰椤曆呮崲閹烘纾婚柣鏂垮悑閹偤骞栧ǎ顒€濡肩紒鈧?' , data . data ) ;
2026-05-24 00:54:58 +08:00
this . state . participants = omitParticipant ( data . data , this . selfParticipantId ) ;
this . _notifyParticipantsUpdate ( ) ;
this . _syncCallDuration ( data . callDuration ) ;
}
_updateRemoteUserProfile ( profile ) {
2026-05-24 01:29:34 +08:00
this . _setRemoteUserState ( profile ) ;
this . _notifyRemoteUserChange ( { mediaState : this . state . session . remoteUser . mediaState } ) ;
2026-05-24 00:54:58 +08:00
}
_syncCallDuration ( callDuration ) {
if ( this . durationSynced || typeof callDuration !== 'number' ) {
return ;
}
this . state . session . duration = callDuration ;
this . durationSynced = true ;
this . _startDurationTimer ( ) ;
this . notify ( { type : 'DURATION_UPDATE' , duration : this . state . session . duration } ) ;
2026-05-24 01:46:57 +08:00
console . log ( ` Call duration synced: ${ callDuration } seconds ` ) ;
2026-05-24 00:54:58 +08:00
}
_startDurationTimer ( ) {
if ( this . durationInterval ) {
return ;
}
this . durationInterval = setInterval ( ( ) => {
this . state . session . duration ++ ;
this . notify ( { type : 'DURATION_UPDATE' , duration : this . state . session . duration } ) ;
} , 1000 ) ;
}
2026-05-24 01:29:34 +08:00
_setRemoteUserState ( patch ) {
this . state . session . remoteUser = {
... this . state . session . remoteUser ,
... patch
} ;
}
_setRemoteUserMediaState ( mediaState ) {
this . _setRemoteUserState ( {
mediaState : {
... this . state . session . remoteUser . mediaState ,
... mediaState
}
} ) ;
}
_notifyRemoteUserChange ( changes = { } ) {
this . notify ( {
type : 'REMOTE_MEDIA_CHANGE' ,
... changes ,
localUser : this . state . session . localUser ,
remoteUser : this . state . session . remoteUser
} ) ;
this . _notifyUserListUpdate ( ) ;
}
2026-04-29 15:18:30 +08:00
/ * *
2026-05-24 01:46:57 +08:00
* 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲 鏁撻悩鍐蹭画闂佹寧娲栭崐鎼佸垂閸岀偞鐓曠憸搴ㄣ € 冮崨瀛樺 € 块柛顭戝亖娴滄粓鏌熸潏鍓хɑ缁绢厼鐖奸弻娑㈠棘鐠恒剱銈囩磼鏉堛劌绗氱 € 垫澘瀚 禒锕傛寠婢跺 ﹦ 顓哄 ┑ ? * @ param { string } type - 濠电姷鏁告慨鐑藉极閹间礁纾婚柣鎰 惈閸ㄥ倿鏌i姀鐘冲暈闁稿 顑呴埞鎴 ︽ 偐閹绘帗娈 銈嗘礋娴滃爼寮诲 ☉ 妯锋 闁告 鍋熸禒顖炴煟韫囨挾绠抽柡浣割煼楠炲啳銇愰幒鎴犲 € 炲 銈呯箰鐎氼喖袙閵忋倖鈷 ?
* @ param { Object } data - 濠电姷鏁告慨鐑藉极閹间礁纾婚柣鎰 惈閸ㄥ倿鏌i姀鐘冲暈闁稿 顑呴埞鎴 ︽ 偐閹绘帗娈 銈嗘礋娴滃爼寮诲 ☉ 妯锋 婵炲棙鍔楃粙鍥 ⒑閸濆嫷鍎庣紒鑸靛哺瀵 鏁愰崨鍌涙 閸 ┾ 偓妞ゆ帒瀚 崑瀣 煕閳 ╁ 啰鎳呴柣 ?
2026-04-29 15:18:30 +08:00
* /
sendMessage ( type , data ) {
if ( this . renderstreaming ) {
this . renderstreaming . sendMessage ( {
type : type ,
data : data
} ) ;
}
}
2026-05-24 00:54:58 +08:00
/ * *
* Participant state helpers
* /
_notifyParticipantsUpdate ( ) {
this . notify ( { type : 'PARTICIPANTS_UPDATE' , participants : this . state . participants } ) ;
}
_upsertParticipant ( participantId , patch = { } ) {
return upsertParticipant ( this . state . participants , participantId , patch ) ;
}
_removeParticipant ( participantId ) {
return removeParticipant ( this . state . participants , participantId ) ;
}
2026-04-29 15:18:30 +08:00
/ * *
2026-05-24 01:46:57 +08:00
* Host缂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏇楀亾閾荤偤鐓 崶銊р槈闁搞劌鍊块弻鐔封枔閸 喗鐏撴繛鎴炴尭缁夊爼鍩 € 椤掆偓缁犲秹宕曢柆宓ュ洭顢涘 鍛 瑝缂傚倷鐒 ﹁ 彠濞存粍绮撻弻鐔煎箲閹邦厾銆愭繛瀵稿 閸ャ劎鍘搁梺绯曞墲閿氶柛婵囨そ閺岋紕浠 ︾ 粙鍨 拤闂佺懓鍢查幊妯虹暦椤愶箑唯闁挎棁妫勯弸娑㈡⒒閸屾瑧鍔嶉柟顔肩埣瀹曟繂顓奸崨顖涙畷缂備礁顑堝 ▔ 鏇㈠汲閿曞倹鐓忓 ┑ 鐐茬仢閸撳 鏌i弮鍌氬付濞磋偐濞 € 閺屾盯寮撮妸銉ョ 缂備緡鍠楅幐鍐差潖濞差亝鍤掗柕鍫濇噸濞岊亞绱撴担鍓插剰缂佺粯锚閻e嘲鈹戦崶銊ュ妳闂佹寧绻傞幊搴ㄦ倶婵犲啰绠鹃柡澶嬪灥閹 垶绻涢崗鑲 ╂ 噮缂侇喖顭锋俊鐑藉煛閸屾粌甯楅柣鐔哥矋缁 挸鐣峰 鍫熷亜濠靛倸顦 ▓ 銊 ╂ ⒑閸撹尙鍘涢柛瀣 缁 鈻庨幘瀵稿幍闁荤娀缂氬 ▍ 锝呯暤閸嶇彻cipant
* 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲 鏁撻悩鍐蹭罕闂佸搫娲㈤崹鍦 不閻樿 绠规繛锝庡墮婵 ¤ 偐绱掗悩鍐插摵闁哄本鐩 弫鍌滅驳鐎n 亜顫噑t闂傚倸鍊搁崐鐑芥嚄閸 洖鍌ㄧ憸鏃堝Υ閸愨晜鍎熼柕蹇嬪焺濞叉悂姊洪幐搴g畵妞わ缚鍗宠棢濠电姴娲 ﹂ 悡鍐 煕濠靛棗顏 ╅ 柍褜鍓欓 … 鐑藉春閻愬樊鍚嬪 璺侯儑閸樻捇姊虹 € 圭姵銆冪紒鈧 笟鈧 鎶藉即閻樼數锛 ? + 闂傚倸鍊搁崐椋庣矆娴i潻鑰块梺顒 € 绉查埀顒 € 鍊圭粋鎺斺偓锝庝簽閿涙盯姊洪悷鏉库挃缂侇噮鍨堕幃鈥斥槈閵忥紕鍘卞 銈嗗姧缁茶法绮婚妷鈺傜厸濞达絽鎲 ¢ 幊姊 icipant婵犵數濮烽弫鎼佸磿閹寸姴绶ら柦妯侯棦濞差亝鍋愰悹鍥 皺椤 ︻ 厼鈹戦悩缁樻锭婵炲眰鍊濋 、 ?
* Participant闂傚倸鍊搁崐宄懊归崶顒 € 违闁逞屽墴閺屾稓鈧 綆鍋呭畷宀勬煙椤旇偐绉虹 € 规洦鍋婂畷鐔碱敆娴g 澹嶉梻鍌欒兌缁 垶骞愰幖浣哥9闁秆勵殔閽冪喐绻涢幋鐑囦緵婵炲皷鏅滈妵鍕 箳閹存繍浼岄梺閫炲苯澧 い銊ョ墦楠炲 骞栨担鍝ヮ唵闁诲繒鍋熼弲顐⑿掗姀锛勭 閻庣數顭堥 鎾剁磼閻樿櫕灏 い鏇 秮楠炴 ﹢ 顢欓挊澶夋睏婵 $ 偑鍊栧Λ浣哥暦閸 偁浠氶柟鎯板Г閳锋垿鎮归崶褍绾ч柟鐧哥秮閺岀喖鎼归 锝呯3閻庢 鍠氶弫濠氥 € 佸Δ鍛 妞ゆ劧缍 € 缁躲垽姊绘担鐟邦嚋缂佽 鍊胯棟濞寸厧鐡ㄩ崐璺ㄧ磽娴h偂鎴 ﹀ 矗韫囨柧绻嗘い鏍ㄧ箖椤忕娀鏌ㄥ ☉ 妯夹eǎ鍥э躬椤㈡稑顫濋崡鐐 ╁ 徍闂備礁鎼 惉濂稿窗閹捐 鐒垫い鎺嶈兌閳 洟鏌ㄥ 顑芥斀妞ゆ柨鍚嬮崰姗 € 鏌 ″ 畝鈧 崰鏍х暦閿濆棗绶炲 ┑ 鐐靛亾閻忓棝鏌f惔銈庢綈婵炲弶锕㈤幊婵嬪箲閹邦喕绨烽梻鍌欑 閹测 € 趁哄 澶婃 濞撴埃鍋撶 € ?
2026-04-29 15:18:30 +08:00
* /
broadcastParticipantsList ( ) {
if ( this . role !== 'host' || ! this . renderstreaming ) return ;
2026-05-24 00:54:58 +08:00
const memberList = buildParticipantsSyncData ( this . state . session . localUser , this . state . participants ) ;
2026-04-29 15:18:30 +08:00
this . renderstreaming . sendMessage ( {
type : 'participants-sync' ,
data : memberList ,
callDuration : this . state . session . duration
} ) ;
console . log ( 'Broadcast participants list:' , Object . keys ( memberList ) ) ;
}
/ * *
2026-05-24 01:46:57 +08:00
* 闂傚倸鍊峰ù鍥х暦閸 偅鍙忕 € 规洖娲ㄩ惌鍡椕归敐鍫 綈婵炲懐濮撮湁闁绘ê妯婇崕鎰版煕鐎e吀閭 柡灞剧洴閸 ╁ 嫰宕橀 鍛 珮缂備焦鍎宠ぐ鐐靛垝濞嗘挸钃熼柍銉 ﹀ 墯閸氬 骞栫划鍏夊亾閸愭畫鐐烘⒒娴e憡璐 ¢ 柛搴涘 € 濋妴鍐 幢濞戞 锛熷 ┑ 鈽嗗灠閵堝 € 熴亹閹烘挻娅滈梺鍛婁緱閸犳牠寮抽崼銉 ︹ 拺缂備焦顭囩紓姘舵煕婵犲啰绠炵 € 殿喛顕ч埥澶婎潩閿濆懍澹曢梺鎸庣箓缁ㄨ偐鑺遍悾宀 € 纾奸柣妯哄船瀹撳棝鏌 ?
* 婵犵數濮烽弫鎼佸磻閻愬樊鐒芥繛鍡樻尭鐟欙箓鎮楅敐搴 ℃ 灍闁哄拋浜 缁樻媴閸涘 ﹤ 鏆堥柦鍐 憾閺岋綁鍩 ℃ 繝鍌滀桓閻庢 鍠栭 … 宄扮暦閵娾晩鏁婇柤娴嬫櫇閻涱噣姊虹拠鎻掑毐缂傚秴妫欑粋宥夋嚋閻㈡ 娴 ? VP9 / AV1闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樻噽閻 瑩鏌熸潏楣冩 闁稿 顑呴埞鎴 ︽ 偐闊 厾绀 € 濠电偛妯婃禍婊呯不娴兼潙绠归柟纰卞幖閺嬬喖鏌i敐鍫 殭闁宠 鍨块崺銉 ╁ 幢濡ゅ啩鍖栫紓鍌欑贰閸n噣宕规导鏉戠厺鐎广儱鐗忛悿鈧 ┑ 鐐村灦閻熝囧储娴犲 鈷戦梻鍫熶緱閻掗箖鏌涙惔銊ゆ喚妞ゃ垺妫冮崺锟犲川椤旈棿缂撴俊鐐 € 栭悧妤冨枈瀹ュ拋鍟呮繝闈涚墢绾惧ジ寮堕崼娑樺 鐎规洖鐭傞弻娑㈠煛鐎n剛锛熼梺閫炲苯澧剧紓宥呮 瀹曟粌鈽夐埗鍝勬喘椤㈡洟鏁傜憴锝嗗 闂備礁婀遍 … 鍫澝归悜钘夌叀濠㈣埖鍔栭悡鐘崇箾閼奸 鍤欓柣蹇曞 █ 閹 繝濡舵径瀣 幐閻庡箍鍎辨 鎼佺嵁濡ゅ懏鐓熼柟鐑樺焾閻撳ジ鏌 $ 仦鐣屝ユい褌绶氶弻娑㈠箻鐠虹儤鐎婚梺 ? H264 High Profile
2026-04-29 15:18:30 +08:00
* /
setCodecPreferences ( participantId ) {
const capabilities = RTCRtpSender . getCapabilities ( 'video' ) ;
if ( ! capabilities || ! capabilities . codecs || capabilities . codecs . length === 0 ) return ;
const { codecs } = capabilities ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢敂缁樻櫈闂佸憡渚楅崹顏堝磻閹炬剚娼╅柣鎾抽椤偆绱撴担浠嬪摵闁圭懓娲悰顔碱潨閳ь剙顕i崼鏇炵闁绘鏁稿畵浣糕攽閻樻剚鍟忛柛鐘愁殘閹广垽宕橀鍡楁殫閻庣懓瀚槐纰挎繝鐢靛Х閺佹悂宕戦悙宸劷婵炲棙鎸哥憴锕傛倵閿濆骸鏋熼柡鍜佷邯濮婄粯鎷呴崨濠傛殘闁藉啳浜槐鎺斺偓锝庡亝瀹曞矂鏌熼妤€浜炬俊鐐€曠换鎰归崒姣硷絾绻濆顓涙嫼闂佸憡鎹佺亸娆撳储濞戞◤鐟邦煥閸涱厺妲愬Δ鐘靛仜閸燁垳鈧絻鍋愰崚鎺楀礂婢跺﹣澹曢梺鎸庢礀閸婂摜绮婚幎鑺ョ厸闁告劑鍔屾禍鐐翠繆閺屻儰鎲炬慨濠勭帛閹峰懐绮欓幐搴♀偓顖氣攽閻橆喖鐏柨鏇樺灲楠炲棗鐣濋崟顐わ紲濠殿喗顭堥崑鎰板礉瀹勯偊娓婚柕鍫濇噽缁犱即鏌eΔ鈧Λ婵嬪箚鐏炶娇鏃€鎷呴崗鍝ョ泿闂傚鍋勫ú锕傚箰閻愵剚娅犻柟鎵閻撴洟鎮楅敐搴′簼閻忓繑澹嗛埀顒€鐏氬妯尖偓姘煎幖椤曘儵宕熼銈嗘畷婵犵數濮抽懗鍓佺矆閸愨晝绡€闁汇垽娼ф禒锕傛煕閵娿儳鍩f鐐寸墵閸╋繝宕掑搴㈤敜濠碉紕鍋涢鍛偓娑掓櫊閿?
2026-04-29 15:18:30 +08:00
let selectedCodecs = [ ] ;
const av1Codec = codecs . find ( c => c . mimeType === 'video/AV1' ) ;
const vp9Codec = codecs . find ( c => c . mimeType === 'video/VP9' ) ;
const h264HighCodec = codecs . find ( c =>
c . mimeType === 'video/H264' &&
c . sdpFmtpLine && c . sdpFmtpLine . includes ( 'profile-level-id=6400' )
) ;
const h264Codec = codecs . find ( c => c . mimeType === 'video/H264' ) ;
if ( av1Codec ) selectedCodecs . push ( av1Codec ) ;
if ( vp9Codec ) selectedCodecs . push ( vp9Codec ) ;
if ( h264HighCodec ) selectedCodecs . push ( h264HighCodec ) ;
if ( h264Codec && ( ! h264HighCodec || h264Codec !== h264HighCodec ) ) selectedCodecs . push ( h264Codec ) ;
if ( selectedCodecs . length === 0 ) return ;
if ( this . renderstreaming ) {
const transceivers = this . renderstreaming . getTransceivers ( participantId ) ;
if ( transceivers && transceivers . length > 0 ) {
const videoTransceivers = transceivers . filter ( t => {
if ( t . sender && t . sender . track ) {
return t . sender . track . kind === 'video' ;
}
return t . mid !== null && t . receiver && t . receiver . track && t . receiver . track . kind === 'video' ;
} ) ;
if ( videoTransceivers && videoTransceivers . length > 0 ) {
videoTransceivers . forEach ( t => {
try {
t . setCodecPreferences ( selectedCodecs ) ;
} catch ( e ) {
console . error ( 'Error setting codec preferences:' , e ) ;
}
} ) ;
console . log ( ` Codec preferences set: ${ selectedCodecs . map ( c => c . mimeType ) . join ( ' > ' ) } ` ) ;
}
}
}
}
/ * *
2026-05-24 01:46:57 +08:00
* 闂傚倸鍊峰ù鍥х暦閸 偅鍙忕 € 规洖娲ㄩ惌鍡椕归敐鍫 綈婵炲懐濮撮湁闁绘ê妯婇崕鎰版煕鐎e吀閭 柡灞剧洴閸 ╁ 嫰宕橀 鍛 珬闂備礁鎽滄慨鏉懳涘 ┑ 瀣 摕婵炴垯鍨归悞娲 煕閹板苯鎳愮粻鎺楁⒒娴e憡鎯堟俊顐㈤叄瀹曟洟鎮界粙鑳 憰濠电偞鍨惰彜闁哄 閰i弻鐔兼焽閿曗偓婢ф澘霉閻撳海鐒告慨濠冩そ閺屽懘鎮欓懠璺侯伃闂佸憡蓱閹 倿寮婚妶澶婄 闁圭粯甯 ╅ 弳銏犫攽閳ュ啿绾ч柛鏃 € 鐟 ╅ 妴渚 € 寮 崼婵嬪敹闂佺粯鏌ㄩ崲鍙夌珶閹炬枼鏀介柣鎰 煐瑜把呯磼閹绘帗鍋ラ柍銉 畵瀹曠厧顭块 鍛 槈妞ゎ偅绮撻崺鈧 い鎺戝 閽 ?
* 闂傚倸鍊搁崐椋庣矆娴i潻鑰块弶鍫氭櫅閸ㄦ繃銇勯弽顐 粶缂佲偓婢舵劖鐓涢柛銉㈡櫅娴犙囨煛 ? maxBitrate 婵犵數濮烽弫鎼佸磻濞戙埄鏁嬫い鎾跺枑閸欏繐霉閸忓吋缍戠痪鎯ф健閺岋紕浠 ︾ 拠鎻掑 闂佸搫顑勭粈渚 € 鍩為幋锕 € 纾兼繝濠傛捣閸斿摜绱撴担鐟板 妞ゃ劌锕 璇测槈閵忕姴宓嗛梺闈涱焾閸庢煡鎮橀幘鍓佺 = 濞达絽鎼 瀷闂佺 顑嗛崝妤 € 危閹版澘绠虫俊銈傚亾缂侇偄绉归弻鐔衡偓娑欘焽缁犳ê霉閻樿櫕缍戞い顏勫暣婵 ¤ 埖鎯旈垾鑼 泿婵 $ 偑鍊栭崹闈浳涘 ┑ 瀣 瀬鐎广儱顦 粻娑㈡煛婢跺孩纭舵い鎾存そ濮婅櫣绱掑 鍡欏姼濠电偛鎳忓ú鏍 暰闁瑰吋鐣 崝宥夋偂 ?
* @ param { string } [ participantId ] - 闂傚倸鍊搁崐鐑芥嚄閸 洖纾块柣銏㈩焾閻ら箖鏌嶉崫鍕 櫣缂佹劖顨嗘穱濠囧Χ閸涱喖娅ら梺绋款儏椤 ︿ 即濡甸崟顖毼ч柛銉ュ 閺嗘涪icipant闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樻噽閻 瑩鏌熺 € 涙 绠ラ柣鎺曞Г缁绘繈寮撮悩铏 彎t缂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏇楀亾閾荤偤鐓 崶銊р槈闁搞劌鍊归妵鍕 冀閵娧佲偓鎺旂磼閻樿崵鐣洪柡灞剧洴婵 $ 兘顢欓悡搴 浇闂備胶顭堥 鍐 礉瀹ュ 桅闁告洦鍨奸弫鍥 煟閺冨牜妫戞い鎴濆 € 荤槐鎾存媴閹绘帊澹 ?
2026-04-29 15:18:30 +08:00
* /
setVideoEncodingParameters ( participantId ) {
if ( ! this . renderstreaming ) return ;
const transceivers = this . renderstreaming . getTransceivers ( participantId ) ;
if ( ! transceivers || transceivers . length === 0 ) return ;
const videoTransceivers = transceivers . filter ( t =>
t . sender && t . sender . track && t . sender . track . kind === 'video'
) ;
for ( const transceiver of videoTransceivers ) {
try {
const sender = transceiver . sender ;
const params = sender . getParameters ( ) ;
if ( ! params . encodings || params . encodings . length === 0 ) {
params . encodings = [ { } ] ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢妶鍥╃厠闂佺粯鍨堕弸鑽ょ礊閺嵮岀唵閻犺櫣灏ㄩ崝鐔兼煛閸℃劕鈧洟婀侀梺鎸庣箓閻楀嫰鍩€椤戣棄浜鹃梻浣侯焾椤戝棝骞戦崶褏鏆﹂柣鎴犵摂閺佸洭鏌i 幇顔碱暭妞ゎ偅娲樼换婵堝枈婢跺瞼锛熼梺鎯х 箰闁帮絽鐣疯ぐ鎺戦唶闁哄洨鍋犻幗鏇㈡倵閸忓浜鹃梺鍛婃处閸嬪棝宕愰悙宸富闁靛牆妫楃粭鍌涚箾閺夋垶鍠樼€规洘鍨剁粭鐔煎焵椤掆偓椤繐煤椤忓嫬绐涙繝鐢靛Т 閸婂鎮烽妸銉㈡斀闁宠棄妫楁禍婵堢磼椤旇偐效濠碉紕鏁诲顕€宕煎┑鍫晣濠电偠鎻徊鍧楀箠閹惧顩烽柕蹇嬪€栭埛鎴︽煙閼测晛浠滈柛鏂哄亾闂備礁鎲¢崝鏇㈠疮椤栫偑鈧啯绻濋崶褑鎽曢梺缁樻⒒閳峰牓寮崟顖涚厵缂佸瀵ч幑锝夋煏閸喐绶叉い顏勫暣婵″爼宕卞Ο 閿嬪婵犵數鍋涘鍓佸垝鎼粹垾锝夊箛閺夎法鐫勯梺鍓插亞閸犲孩绂掗搹鍦= 濞达絽鎼崝瀣⒑閸欏鐛媔trate
2026-04-29 15:18:30 +08:00
const videoTrack = sender . track ;
const settings = videoTrack ? videoTrack . getSettings ( ) : { } ;
const height = settings . height || 1080 ;
2026-05-24 01:01:28 +08:00
const maxBitrate = getAdaptiveVideoBitrate ( height ) ;
2026-04-29 15:18:30 +08:00
params . encodings [ 0 ] . maxBitrate = maxBitrate ;
params . encodings [ 0 ] . scaleResolutionDownBy = 1.0 ;
params . encodings [ 0 ] . xGoogleMinBitrate = Math . floor ( maxBitrate * 0.5 ) ;
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽弫鎼佸磻閻愬樊鐒芥繛鍡樻尭鐟欙箓鎮楅敐搴℃灍闁哄拋浜缁樻媴閸涘﹤鏆堥柦鍐含缁辨帞鈧綆鍋勭粭褏绱掗钘夊摵鐎规洩绻濋幃娆撳煛閸屾稒婢戦梻鍌欑劍閹爼宕曞鍫濆窛妞ゆ枮浣稿姦婵﹦绮幏鍛村川婵犲倹娈橀梺姹囧焺閸ㄨ京鏁幒鏇犱罕婵犵數濮撮敃銈団偓姘ュ妽缁傛帡宕奸妷锔惧幍闂傚倸鍊搁顓㈠礉瀹ュ鐓曢悗锝庡亞缁犵偤鏌$仦鍓ф创濠碉紕鍏橀崺鈩冪節閸愮偓顥涚紓鍌氬€风拋鏌ュ磻閹炬剚鐔嗛柤鎼佹涧婵牓鏌嶉柨瀣诞闁哄本鐩鎾Ω閵夈倗鏁栭柣蹇撶箰缁绘帞妲愰幘璇茬<婵ɑ鐦烽姀鈥茬箚妞ゆ劧绲垮ú瀵糕偓娈垮櫘閸嬪﹪銆佸▎蹇婃瀻闁绘劦鍎烽鍕厽閹肩补鍓濈拹鈩冦亜椤撶偟澧﹀┑鈥冲缁瑥鈻庨幆褎顓块梻浣稿閸嬪懎煤閺嵮勬殰闂傚倷绀侀悿鍥綖婢舵劕纾块柧蹇e亞椤╂彃螖閿濆懎鏆為柣鎾存礋閹﹢鎮欓棃娑楀闂佹眹鍔嶉崹鍧楀箖濡ゅ懏鍋¢梺顓ㄩ檮閳诲牓姊虹拠鈥虫灆缂侇喗鐟╅妴浣糕槈閵忕姈褔鏌涢妷銏℃珕婵炲牆鎲℃穱?
// 'maintain-resolution' 闂傚倸鍊搁崐椋庢濮橆剦鐒介柤濮愬€栫€氬鏌i弮鍌氬付缂佲偓婢跺备鍋撻崗澶婁壕闂佸憡娲﹂崜娆愮閸濆嫷娓婚柕鍫濇婵倿鏌涙繝鍐ㄥ鐎规洘鍨块獮姗€鎳滈棃娑欑€梻浣告啞濞诧箓宕戦崱妯碱洸闁规鍠氱壕钘壝归敐鍫殐婵炲牊姊归妵鍕晝閸屾瑦鍠氬Δ鐘靛仦閻楁濡堕敐澶婄闁冲搫鍊搁崝鎺撲繆閻愵亜鈧牠宕濊缁辩偤宕卞顫秮椤㈡稑鈽夊槌栧晭闂備焦瀵х换鍌炲箠韫囨柨绶為柛鏇ㄥ幘绾惧ジ鎮归崶銊ョ祷闁哄棛鍠栭弻宥堫檨闁告挻鐟╅幃妯侯潩鐠洪缚鎽曢梺闈浥堥弲娑滅箽闂備礁婀遍崕銈夈€冮崨顓у晠婵犻潧娲㈡禍婊堟煛閸愩劌鈧懓鈻嶉弴鐘冲枑闁绘鐗婄亸锕傛煛?
2026-04-29 15:18:30 +08:00
if ( params . degradationPreference !== undefined ) {
params . degradationPreference = 'maintain-resolution' ;
}
sender . setParameters ( params ) ;
console . log ( ` Set video encoding: maxBitrate= ${ maxBitrate / 1000000 } Mbps, scaleResolutionDownBy=1.0, xGoogleMinBitrate= ${ Math . floor ( maxBitrate * 0.5 ) } ${ participantId ? ` for ${ participantId } ` : '' } ` ) ;
} catch ( error ) {
console . error ( 'Error setting video encoding parameters:' , error ) ;
}
}
}
/ * *
2026-05-24 01:46:57 +08:00
* 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲 鏁撻悩鑼舵憰闂佹寧绻傞ˇ顖滅矆婢跺备鍋撻獮鍨 姎妞わ富鍨堕幃鐐哄垂椤愮姳绨婚梺纭呮彧缁插墽娑甸懜鐐逛簻闁挎棁鍋愰悾鐢告煛鐏炲墽娲村 ┑ 鈩冩倐婵 $ 柉顦存い鏃 € 甯掕灃闁绘 ﹢ 娼ф禒锕傛煕閵婏箑顕滈柟渚垮姂閸 ┾ 偓妞ゆ帒瀚 悡銉 ︾ 節闂堟稒顥滄い鎺嬪灮缁辨挸顓奸崪鍐 ╂ 暰婵烇絽娲ら敃顏堛 € 侀弴銏犖ч柛婊 € 鐒 ﹀ В澶愭⒒娴h 鍋犻柛鏂块叄瀵 偆鎷犻懠顒佹 闁荤喐鐟ョ € 氼參宕曢悢鍏肩厪闊 洢鍎崇壕璺ㄧ磼婢跺 ﹦ 鍩f慨濠冩そ濡 啫霉閵堝棛娲寸 € 规洘锕㈤ 、 姗 € 鎮 ?
* 婵犵數濮烽弫鎼佸磻閻樿 绠垫い蹇撴 缁 € 濠囨煃瑜滈崜姘辨崲濞戞瑥绶為悗锝庡亞椤 ︿ 即鎮 ? MediaStreamTrack . applyConstraints ( ) 闂傚倸鍊搁崐椋庢 濮橆剦鐒介柤濮愬 € 栫 € 氬 鏌i弮鍌氬付缂佲偓婢舵劖鐓欓弶鍫濆⒔閻i亶鏌i幒鎴犱粵闁靛洤瀚伴獮鎺戭吋閸 ♀ 晜鈻婂 ┑ 鐐茬摠閸ゅ酣宕规禒瀣 摕闁挎繂顦伴弲婵喢归敐鍡楀Τ闁规儳澧庣壕濂告倵閿濆簼鎲炬俊顖楀亾婵 $ 偑鍊戦崹娲 晝閵忋倕绠栨繛鍡樻尭閸ㄥ倹銇勯幇鍓佹偧闂佽姤甯 ″ 濠氬磼濞嗘埈妲 梺鍦 拡閸嬪 ﹤ 鐣烽 鐑嗘晬闁绘劘灏欓敍鐔哥節闂堟稑鈧 悂骞夐敓鐘茬 ? 鐎广儱顦伴悡鐔兼煛閸愩劌鈧 摜鏁 崼鏇熺厸 ?
* 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲 鏁撻悩鍐叉疄婵 ° 倧绲介崯顐も偓姘 槹閵囧嫰骞掗崱妞惧 婵 $ 偑鍊ゆ禍婊堝疮閺夋垹鏆 ﹂ 柟鐑橆殕閸婄兘鏌熺紒妯虹瑨闁轰降鍊濆 娲 嚒閵堝懏鐎鹃梺鑽ゅ枂閸庢娊鍩 € 椤掍礁鍤 柛鎾寸 〒 閸掓帗绻濋崶銊 ︽ 珖闂佺 鏈 粙鎾诲储闁 秵鈷戦柛锔诲幖閸斿 鏌涢妸銉у煟閽樼喖鏌嶉妷锕 € 澧 繛鎾愁煼閹 鈽夊 ▍ 铏 灴瀹曟劙骞囬悧鍫㈠幍濡炪倖妫佸Λ鍕 煝閺囩姭鍋撶憴鍕 闁绘牕銈搁妴渚 € 寮 崼婵嗚 € 垮 ┑ 顔筋殔濡 寮插 ▎ 鎾粹拻闁稿本鐟 ︾ 粊鐗堜繆濡炵厧濡跨紒顔肩墛缁楃喖鍩 € 椤掑嫨鈧 礁顫濇0婵囨櫍闂佺粯蓱閸撴艾顭囬悢濂夋富闁靛牆鎳愮粻浼存煟濡も偓閿曨亜鐣烽弴鐐嶇喐绺介挊澶岀Ш闁轰焦鍔欏畷銊 ╊ 敊鐠侯煈鏀ㄥ ┑ 鐘茬棄閵堝懐姣㈤梺绋跨箲閿曘垹顕f繝姘 櫜闁告稑鍊瑰Λ鍐 春閳ь剚銇勯幒鍡椾壕闂佹寧娲忛崹钘夌暦閿熺姵鍊婚柛鈩冾殕濞 ?
* @ param { number } width - 闂傚倸鍊搁崐鐑芥嚄閸 洖纾块柣銏㈩焾閻ら箖鏌嶉崫鍕 櫣缂佹劖顨嗘穱濠囧Χ閸涱喖娅ら梺绋款儏椤 ︾ 敻寮婚妸銉㈡斀闁糕剝顭囬ˇ閬嶆⒑缁嬫 鍎愰柟鍛婂劤閳藉 鎮界粙鍨 獩濡炪倖娲栭幊蹇浰夋径鎰 拺 ?
* @ param { number } height - 闂傚倸鍊搁崐鐑芥嚄閸 洖纾块柣銏㈩焾閻ら箖鏌嶉崫鍕 櫣缂佹劖顨嗘穱濠囧Χ閸涱喖娅ら梺绋款儏椤 ︾ 敻寮婚妸銉㈡斀闁糕剝锕 槐鐐测攽閻愯尙澧涙い銊ユ 缁岃鲸绻濋崶鑸垫櫖濠殿喗锕 ╅ 崢浠嬪磿閹炬剚娓 ?
2026-04-29 15:18:30 +08:00
* /
async changeResolution ( width , height ) {
if ( ! this . state . localStream ) {
2026-05-24 01:46:57 +08:00
showNotification ( 'Local video stream is not available' , 'error' ) ;
2026-04-29 15:18:30 +08:00
return ;
}
const videoTracks = this . state . localStream . getVideoTracks ( ) ;
if ( videoTracks . length === 0 ) {
2026-05-24 01:46:57 +08:00
showNotification ( 'Failed to switch resolution' , 'error' ) ;
2026-04-29 15:18:30 +08:00
return ;
}
const track = videoTracks [ 0 ] ;
2026-05-24 01:01:28 +08:00
const label = getResolutionLabel ( height ) ;
2026-04-29 15:18:30 +08:00
try {
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽弫鎼佸磻閻樿绠垫い蹇撴缁€濠囨煃瑜滈崜姘辨崲濞戞瑥绶為悗锝庡亞椤︿即鎮?applyConstraints 闂傚倸鍊搁崐椋庢濮橆剦鐒介柤濮愬€栫€氬鏌i弮鍌氬付缂佲偓婢舵劖鍊甸柨婵嗛婢ф壆绱掗悩铏叆妞ゎ厼娼¢幊婊堟濞戞﹩娼旈梻浣虹帛閹告悂宕愭繝姘劦妞ゆ巻鍋撶紒鐘茬Ч瀹曟洟宕¢悙宥嗙☉閳诲酣骞橀弶鎴滅暗闂備線鈧偛鑻晶瀛樻叏婵犲啯銇濈€规洘顨婇幃鈩冩償閳ユ壙鎴︽⒒娴h鍋犻柛銊ㄥ亹娴滄悂顢旈崼婢箓鏌熼悧鍫熺凡闁绘劕锕ラ妵鍕敄濠靛嫬鈧倕顭囬幇鐗堢厵鐎瑰嫮澧楅崵鍥煙椤旂晫鐭婇摶锝夋煕韫囨挸鎮戞い鏂跨箻濮婂宕掑▎鎰偘濡炪倖娲橀悧鐘茬暦鐟欏嫬顕遍柡澶嬪灩閻ゅ洭鏌熼崗鑲╂殬闁告柨鐬肩划锝呂旈崨顔尖偓鍫曟煟閹邦厼绲婚柍閿嬫閺屽秶绱掑Ο娲绘闂佽鍠楅〃濠囧极閹邦厽鍎熼柍銉ョ-椤旀帗绻濋悽闈涗粶闁绘妫濋幃銉︾附缁嬭儻鎽曢梺鏂ユ櫅閸燁垱鍒婇幘顔藉仭婵炲棗绻愰顐ょ磼鐎n亝鍠樻慨濠冩そ楠炴牠鎮欓幓鎺濈€抽梺璇插缁嬪牓寮插☉鈶┾偓锕傚炊椤忓棛鏉稿┑鐐村灦椤倿鍩¢崘鈺佹瀾閻庡箍鍎遍ˇ浼村吹?
2026-04-29 15:18:30 +08:00
await track . applyConstraints ( {
width : { ideal : width , max : width } ,
height : { ideal : height , max : height } ,
frameRate : { ideal : 30 , max : 30 }
} ) ;
2026-05-24 01:46:57 +08:00
console . log ( ` 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹闂佸湱鍎ら崵锕€鈽夊鍡欏弳闂佸憡娲嶉弲娆戝垝閹剧粯鈷戠憸鐗堝笚閿涚喖鏌i幒鐐电暤鐎规洘鍨甸埞鎴犫偓锝庡亞閸橀亶姊洪棃娑辨Ч闁搞劎鏁诲畷顖烆敃閳垛晜鐏侀梺闈涱槴閺呮粓鍩涢幋锔界厱婵炴垶锕銉х磼濡や礁绗氱紒缁樼〒閹风姾顦撮柣锝変憾閺岋綁鏁愰崶褍骞嬮悗瑙勬穿缁叉儳顕ラ崟顓濇勃闁告稑锕︽禍? ${ width } x ${ height } ` ) ;
2026-04-29 15:18:30 +08:00
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢妶鍥╃厠闂佺粯鍨堕弸鑽ょ礊閺嵮岀唵閻犺櫣灏ㄩ崝鐔兼煛閸℃劕鈧洟濡撮幒鎴僵闁挎繂鎳嶆竟鏇㈡煟鎼淬値娼愭繛鍙壝悾婵堢矙鐠恒劍娈鹃梺鍛婎殘閸庢劕顭囬埡鍛仯闁惧繒鎳撻崝瀣煙閾忣偆鎳囨慨濠勭帛閹峰懘鎼归悷鎵偧缂傚倷绶¢崰鏍嚐椤栨粌寮查梻浣稿暱閹碱偊骞婅箛娑樼?鐎广儱顦伴悡鐔兼煛閸愩劌鈧摜鏁崼鏇熺厸閻庯絺鏅濈粣鏃堟煙椤旂瓔娈滄俊顐㈠暙閳藉鈻庨幇顒佺€惧┑鐘殿暜缁辨洟宕戝Ο鐓庡灊婵炲棙鎸搁拑鐔兼煥濠靛棭妯堥柡浣革躬閺屻倖鎱ㄩ幇顑藉亾閺囩喍绻嗗ù鐘差儐閳锋垵霉閸忚偐鎳冮柣锔界矒閺屾盯鏁愯箛鏇犘滃Δ鐘靛仜閸熸娊藝閸︻厸鍋撶憴鍕闁稿繑锕㈤妴浣割潨閳ь剟骞冨▎鎴炲磯闁?
2026-04-29 15:18:30 +08:00
// 480p: ~1Mbps, 720p: ~2.5Mbps, 1080p: ~4Mbps, 2K: ~6Mbps
2026-05-24 01:01:28 +08:00
const maxBitrate = getTargetResolutionBitrate ( height ) ;
2026-04-29 15:18:30 +08:00
this . _applyMaxBitrate ( maxBitrate ) ;
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽弫鎼佸磿閹寸姴绶ら柦妯侯棦濞差亝鏅滈柣鎰靛墮鎼村﹪姊虹粙璺ㄧ伇闁稿鍋ゅ畷鎴﹀Χ婢跺鍘繝鐢靛仜閻忔繃淇婄粙妫电懓顭ㄩ崘顏喰ㄩ梺鍝勭灱閸犳牠鐛崱姘兼Щ闂佸搫妫滄ご鎼佸Φ閸曨垰围闁告侗鍠栧▓妤呮⒑閸濆嫭婀伴柣鈺婂灦閻涱噣骞掑Δ鈧粻锝夋煟閹存繃顥為柨娑樻噹閳规垿鏁嶉崟顐$捕闂佸鏉垮妤犵偛鍟村畷鎺楁倷閼碱剛鏆梻浣筋潐閸庣厧螞閸曨垱鍊峰┑鐘插暔娴滄粓鐓崶銊﹀暗鐎涙繈姊洪崫鍕闁告ê澧藉Σ鎰板箻鐠囪尙锛滃┑顔斤供閸撴岸鎮橀幘缁樷拺闁告縿鍎辨牎闂佺粯顨堟慨鎾偩閻戣棄浼犻柛鏇ㄥ幗濞堟洟姊洪崨濠冨闁稿瀚伴幃闈浳熸笟顖涘瘜闂侀潧鐗嗗Λ妤呭锤婵犲洦鐓曢悗锝庡亝鐏忔壆绱掔紒妯肩疄闁糕斁鍋撳銈嗗笒閸婄敻宕戦幘缁樺仺闁汇垻鍋i埀顒€锕弻娑氣偓锝庡亝鐏忣參鏌嶉挊澶樻Ц闁宠绉归、妯款槺闂侇収鍨抽埀?
2026-04-29 15:18:30 +08:00
const userSettings = JSON . parse ( localStorage . getItem ( 'userSettings' ) || '{}' ) ;
userSettings . resolution = { width , height } ;
localStorage . setItem ( 'userSettings' , JSON . stringify ( userSettings ) ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鎼佸磹妞嬪孩顐介柨鐔哄Т绾惧鏌涘☉鍗炲季婵炲皷鏅犻弻鏇熺箾閻愵剚鐝曢梺?UI 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢埛姘そ婵¤埖寰勭€n亙妲愰梻渚€娼ц墝闁哄懏鐩幏?
2026-04-29 15:18:30 +08:00
this . notify ( { type : 'RESOLUTION_CHANGED' , resolution : { width , height , label } } ) ;
2026-05-24 01:46:57 +08:00
showNotification ( 'Switched to ' + label , 'success' ) ;
2026-04-29 15:18:30 +08:00
} catch ( error ) {
2026-05-24 01:46:57 +08:00
console . error ( '闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹闂佸湱鍎ら崵姘洪鍛珖闂侀€炲苯澧撮柟顕€绠栭幃婊堟寠婢跺孩鎲伴梻渚€娼чˇ顓㈠磿閹跺壙鍥敃閿旇В鎷虹紓渚囧灡濞叉牗鏅堕懠顑藉亾閸忓浜鹃梺褰掓?缁€浣瑰閻樺磭绠剧€瑰壊鍠曠花濂告煟閹捐泛鏋戠紒缁樼箖缁绘繈宕掑顓燁唹闂備胶鎳撻崥瀣礉濞嗘挸钃熼柡鍥ュ灩閻愬﹪鏌曟繛鍨姢濞寸姴銈稿?' , error ) ;
showNotification ( 'Failed to switch resolution. The camera may not support it.' , 'error' ) ;
2026-04-29 15:18:30 +08:00
}
}
/ * *
2026-05-24 01:46:57 +08:00
* 闂傚倸鍊峰ù鍥 敋瑜忛懞閬嶆嚃閳轰胶绛忔繝鐢靛У閻旑剛绱為弽褜鐔嗛悹杞拌 閸庢劖绻涢崨顖毿ラ柍褜鍓欑粻宥夊磿闁 单鍥 敍濠ф儳浜炬慨姗嗗墻濡插憡銇勯 鈩冪 《 闁圭懓瀚板畷顐 ﹀ 礋椤撶啘鐐寸節閻㈤潧浠滈柛姘 儑閺侇噣骞掗弴鐘辩綍 ? sender 闂傚倸鍊风粈浣革耿闁 秴鍌ㄧ憸鏃堝箖濞差亜惟闁靛 鍠楃紞搴㈢節閻㈤潧校闁煎綊绠栧 ? maxBitrate
* @ param { number } maxBitrate - 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢敂钘変罕闂佸憡鍔 ﹂ 崰鏍 婵犳碍鐓欓柣鎰靛墮婢ь垶鏌i弬鎸庮棦闁哄本鐩 、 鏇㈠ 閳藉棙顥i梺璇插 閸戝綊宕伴弽褍绁 梻浣虹帛閸ㄥ吋鎱ㄩ妶澶嬪亗闁绘棃鏅茬换鍡涙煏閸 繂鈧 憡绂嶉幆褉鏀介柣姗嗗亜娴滈箖姊洪崨濠庢畼闁稿孩鍔欏畷顖炲川椤撴稒鏂 € 闂佺 鏈 喊宥夊疮閻愮儤鐓涢柛婊 € 绀佹晶鎾 煛鐏炲墽顬兼い锔界叀閺屸剝鎷呴幓鎺嶅 s闂 ?
2026-04-29 15:18:30 +08:00
* /
_applyMaxBitrate ( maxBitrate ) {
if ( ! this . renderstreaming ) return ;
const isHost = this . role === 'host' ;
const participantIds = isHost ? Object . keys ( this . state . participants ) : [ null ] ;
for ( const pid of participantIds ) {
const transceivers = this . renderstreaming . getTransceivers ( pid ) ;
if ( ! transceivers ) continue ;
const videoTransceivers = transceivers . filter ( t =>
t . sender && t . sender . track && t . sender . track . kind === 'video'
) ;
for ( const transceiver of videoTransceivers ) {
try {
const sender = transceiver . sender ;
const params = sender . getParameters ( ) ;
if ( ! params . encodings || params . encodings . length === 0 ) {
params . encodings = [ { } ] ;
}
params . encodings [ 0 ] . maxBitrate = maxBitrate ;
sender . setParameters ( params ) ;
console . log ( ` Updated maxBitrate to ${ maxBitrate } for ${ pid || 'self' } ` ) ;
} catch ( error ) {
console . error ( 'Error updating maxBitrate:' , error ) ;
}
}
}
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢埛姘そ婵¤埖寰勭€n亙妲愰梻渚€娼ц墝闁哄懏鐩幏鎴︽偄鐏忎焦鏂€闂佺粯蓱瑜板啴寮抽悙瑁佺懓鈹冮崹顔瑰亾閺嶎偅宕叉繝闈涙川缁♀偓闂佺鏈粙鎴︻敁濞戞瑧绠鹃悗鐢殿焾鐢爼鏌涙繝鍐╁€愰柛鈺冨仱楠炲鏁傜紒妯绘珫婵犵數鍋為崹鍫曗€﹂崶顒佸仭闁归偊鍠氱壕浠嬫煕鐏炵偓鐨戠€涙繂顪冮妶鍡楃仴妞わ妇鏁婚悰顕€宕橀鑲╁幐闂佸憡渚楅崰鏍р枔濠靛牏纾奸柛鎾楀喚鏆梺鎸庤壘闇?(闂?WebSocket 闂傚倸鍊峰ù鍥х暦閻㈢绐楅柟鎵閸嬶繝寮堕崼姘珖濞戞挸绉归弻鐔告綇閸撗呫偡婵?
2026-04-29 15:18:30 +08:00
updateRemoteMedia ( mediaState , participantId ) {
2026-05-24 01:29:34 +08:00
this . _setRemoteUserMediaState ( mediaState ) ;
this . _notifyRemoteUserChange ( { mediaState , participantId } ) ;
2026-04-29 15:18:30 +08:00
}
updateRemoteUserStatus ( status ) {
2026-05-24 01:29:34 +08:00
this . _setRemoteUserState ( { status } ) ;
this . _notifyRemoteUserChange ( ) ;
2026-04-29 15:18:30 +08:00
}
updateRemoteUserNetworkQuality ( networkQuality ) {
2026-05-24 01:29:34 +08:00
this . _setRemoteUserState ( { networkQuality } ) ;
this . _notifyRemoteUserChange ( ) ;
}
_setSpeakingState ( isLocal , isSpeaking ) {
if ( isLocal ) {
this . state . session . localUser . mediaState . isSpeaking = isSpeaking ;
this . _notifyLocalMediaChange ( 'isSpeaking' , isSpeaking ) ;
this . emitMediaStateChange ( ) ;
return ;
}
this . updateRemoteMedia ( { isSpeaking } ) ;
2026-04-29 15:18:30 +08:00
}
2026-05-24 01:46:57 +08:00
// 缂傚倸鍊搁崐鎼佸磹閹间礁纾归柣鎴eГ閸婂潡鏌ㄩ弴鐐测偓鍝ョ不娴煎瓨鍋i柛銉戝嫧鏋欓梺缁樺笩婵倝濡甸崟顖氱疀闁割偅娲橀宥夋⒑缂佹ê绗х紒顕呭灦楠炲牓濡搁妷搴e枛閹煎綊鎯傞崫銉ь槸濠电姷顣藉Σ鍛村垂娴煎瓨鍋嬮柟鎹愵嚙閽冪喖鏌曟繛鍨姉婵℃彃鐗婃穱濠囶敍濠婂啫浠橀梺鎰佷邯娴滆泛顫忛悜妯诲濞寸厧鐡ㄩ鏍⒑缁嬪尅鏀荤紒璇茬墦閹即顢欓柨顖氫壕闁挎繂楠搁弸鐔兼煙鐏炲倸鍔﹂柡灞剧〒娴狅箓鎮欓鍌涱吇婵犲痉銈庡殝缂傚秳绀侀~蹇撁洪鍕獩婵犵數濮撮崯浼村储娴犲鈷戦柛娑橈攻閻撱儵姊虹敮顔惧埌闁伙絿鍏橀獮瀣晜閼恒儲鐝冲┑鐘灱濞夋盯鏁冮敃鍋瑰洭鍩¢崘顏嗭紳闂佺鏈悷褔宕濆澶嬬叆婵鍩栭悡鐔兼煥閺冨浂鍤欓柣蹇ョ畵閺屸€崇暆鐎n剛鐦堥悗瑙勬礃鐢剝淇婇崼鏇炲窛妞ゆ挾濯崯鍫ユ⒒娴g瓔鍤欏Δ鐘虫倐閹ê顫濋崡鐐茬亰闂佽宕橀褔鎷戦悢鍏肩叆闁哄洨鍋涙禍鐐烘煛娴e壊鍎旈柡灞剧洴閸╁嫰宕橀鍛珮婵?
2026-04-29 15:18:30 +08:00
async endCall ( ) {
console . log ( ` endCall called. Role: ${ this . role } ` ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊峰ù鍥х暦閸偅鍙忛柟缁㈠櫘閺佸嫰鏌涘☉娆愮稇闁汇値鍠栭湁闁稿繐鍚嬬紞鎴︽煛?hangUp() 濠电姷鏁告慨鐢割敊閺嶎厼绐楁俊銈呭暞瀹曟煡鏌熼柇锕€鏋涚紒韬插€濋弻娑滎槼妞ゃ劌鎳橀幃姗€鎼归锝呭伎濠碉紕鍋犻褎绂嶆ィ鍐╁€甸悷娆忓缁€鍐┿亜閵娿儻韬柣娑卞枟閹棃濡搁敃鈧▓鐔兼⒑闂堟侗妯堥柛銊ュ暱椤?WebRTC 闂傚倸鍊风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺鍝勬缁捇寮婚悢铏圭<闁靛繒濮甸悘鎾绘倵鐟欏嫭灏紒鑸靛哺楠炲啳銇愰幒鎴滅炊闂佸憡娲﹂崜姘跺磿閹惧墎纾藉ù锝囶焾缁狙勪繆閻愯埖顥夋い顐㈢箰鐓ゆい蹇撶У閺呮繈姊洪幐搴b槈閻庢凹鍓氭穱濠冪鐎n偀鎷洪梺鍛婄箓鐎氼垳鈧氨澧楃换娑氫沪閸屾埃鍋撳┑瀣ㄢ偓浣割潩閼稿灚娅滄繝銏f硾閿曪箓宕濋敃鈧—鍐Χ閸℃鐟愰梺鐓庡暱閻栧ジ宕洪悙宸悑濠㈣泛顑囬崢鎾绘⒑闂堟侗妲堕柛搴や含閻ヮ亣顦查柍?
// hangUp 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鑼槷闂佸搫娲㈤崹鍦不閻樿櫕鍙忔俊鐐额嚙娴滈箖鎮楃憴鍕婵炲弶绮撻崺鈧い鎺嶈兌閳洟鏌ㄥ顓滀簻闁哄浄绻濋崫娲煙娓氬灝濡兼い顐g矒瀹曞崬螖閳ь剛绮荤紒妯肩瘈闁靛繈鍨洪崵鈧梺娲诲幖閸婂灝顕f繝姘亜闁绘垶锚閻濅即姊洪悙钘夊姤婵炲懏娲熼幃姗€寮撮悜鍡樺瘜闂侀潧鐗嗘鍛婄濠靛洢浜滈柕濞垮劵闊剚顨ラ悙璇у伐闁宠鍨归埀顒婄秵娴滅偤顢欏畝鍕拺闁革富鍘奸崝瀣煕閵娿儳浠㈤柍缁樻崌椤㈡宕熼鑺ュ闂備礁鎲$粙鎴︽晝閵堝洨绠旈柟鐑樻尫缁诲棙銇勯幇鈺佺労闁搞倗鍠愭穱?
// - host: 闂傚倸鍊搁崐鎼佸磹妞嬪孩顐介柨鐔哄Т 绾惧鏌涘☉鍗炲季婵炲皷鏅犻弻鏇熺箾閻愵剚鐝曢梺绋款儏閸婂潡寮婚妸鈺傚亜闁告繂瀚呴姀銏㈢< 闁绘﹩鍠栭崝锕傛煛鐏炵晫啸妞ぱ傜窔閺屾盯骞樼捄鐑樼€诲銈嗘穿缂嶄礁鐣疯ぐ鎺濇晝闁靛牆娲ㄩ弳顖滅磽閸屾瑧鍔嶉柡灞诲姂椤㈡瑩骞夐崸鏄猧pants闂傚倸鍊搁崐鐑芥倿閿旈敮鍋撶粭娑樻噽閻瑩鏌熸潏楣冩闁搞倖鍔栭妵鍕冀椤愵澀绮堕梺鎼炲妼閸婂骞夐幖浣瑰亱闁割偅绻勯悷鏌ユ⒑閹惰姤鏁辨俊顐㈠暣瀵寮撮姀鐘诲敹濠电娀娼у ù鍌毼涢悙鐑樷拺缂備焦蓱閹牏绱撳鍕槮妞?
// - participant: 婵犵數濮烽弫鎼佸磻濞戙埄鏁嬫い鎾跺枑閸欏繘鏌熺紒銏犳灈缂佺姷濞€閺岀喖骞戦幇闈涙闂佸憡淇洪~澶屾崲濠靛洨绡€闁稿本纰嶉悘鎾绘⒑閸濄儱鏋庨柟铏锝夊醇閺囩偤鍞跺┑顔斤供閸樹粙鎮甸敃鍌涒拺閻犲洤寮堕崬澶愭倶韫囨梻鎳囬柛鈹惧亾濡炪倖甯婄欢锟犲疮韫囨稒鐓曢柣妯哄暱濞搭喚鈧娲橀崹鍧楃嵁濡偐纾兼俊顖滃帶瀵櫕绻濋悽闈涗沪闁搞劍鍎奸幗顐⑩攽閻橆偄浜炬繛瀵稿帶閻°劑鎮¢弴銏$厓閻犲洠鈧啿绠瑰┑鐐额嚋缁犳捇宕哄☉銏犵闁哄啫鍊婚敍婵嬫⒑閸撴彃浜栭柛銊ф嚀閺嗏晝绱撻崒娆掑厡濠殿喖纾崚鎺戔枎閹惧疇鎽?
2026-04-29 15:18:30 +08:00
await this . hangUp ( ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鍨鹃幇浣圭稁缂傚倷鐒﹁摫闁告瑥绻橀弻鐔虹磼閵忕姵鐏堥梺娲诲幗椤ㄥ﹪寮诲☉銏犵労闁告劦浜栨慨鍥⒑缂佹ê绗х紒顕呭灦楠炲牓濡搁妷搴e枛閹煎綊鎯傞崫銉ь槸濠?
2026-04-29 15:18:30 +08:00
async joinCall ( connectionId ) {
this . state . session . status = 'connecting' ;
this . notify ( { type : 'CALL_STATUS_CHANGE' , status : 'connecting' } ) ;
2026-05-24 01:46:57 +08:00
showNotification ( 'Joining call (' + connectionId + ')' ) ;
2026-04-29 15:18:30 +08:00
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹濠德板€曢崯顖氱暦閺屻儲鐓曠€光偓閳ь剟宕曢幋鐘电闁哄稁鍘介悡娆撴煟濡も偓閻楀﹦娆㈤懠顒傜<闁?
2026-04-29 15:18:30 +08:00
await this . init ( ) ;
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽弫鎼佸磿閹寸姴绶ら柦妯侯棦濞差亝鏅滈柣鎰靛墮鎼村﹪姊虹粙璺ㄧ伇闁稿鍋ゅ畷鎴﹀Χ婢跺鍘繝鐢靛Т缁绘ê顬婇娴庣懓鈹冮崹顔瑰亾濠靛钃熼柨婵嗩槸缁犳稒銇勯幘璺烘灁缂佸娲︾换婵嬪閿濆棛銆愭繛鎴炴尭閼?
2026-04-29 15:18:30 +08:00
this . connectionId = connectionId ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹濠德板€曢崯浼存儗濞嗘挻鐓欓悗鐢殿焾鍟哥紒鎯у綖缁瑩寮婚悢鐓庣鐟滃繒鏁☉銏$厽闁圭儤姊荤敮娑㈡煙娓氬灝濡奸摶锝夋煣韫囨洘顏熺紒杈╁仜閳?
2026-04-29 15:18:30 +08:00
async createCall ( ) {
this . state . session . status = 'connecting' ;
this . notify ( { type : 'CALL_STATUS_CHANGE' , status : 'connecting' } ) ;
2026-05-24 01:46:57 +08:00
showNotification ( 'Creating call...' ) ;
2026-04-29 15:18:30 +08:00
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹濠德板€曢崯顖氱暦閺屻儲鐓曠€光偓閳ь剟宕曢幋鐘电闁哄稁鍘介悡娆撴煟濡も偓閻楀﹦娆㈤懠顒傜<闁?
2026-04-29 15:18:30 +08:00
await this . init ( ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鐑芥倿閿曗偓椤啴骞愭惔锝庢锤闂佺粯鍔曢幖顐ょ玻濡ゅ懎绠规繛锝庡墮婵′粙鏌涚€e吀閭柡灞剧洴瀵挳濡搁妷銉ь啋缂備線绠栫粻鏍不閺嶎厼钃熼柣鏃囥€€閸嬫捇鎮藉▓璺ㄥ姼闁哥喐鎮傚铏规兜閸滀焦缍堝┑鐐跺皺閸犳牠鐛崘顓滀汗闁圭儤鍨归悿鈧梻浣哥枃濡椼劎绮堟笟鈧敐鐐哄炊椤掍讲鎷洪梻鍌氱墐閺呮繈宕氭导瀛樼厵缁炬澘宕禍顖炴煟韫囨搩鍎旀慨濠冩そ閺屽懘鎮欓懠璺侯伃婵犫拃灞芥珝婵?
2026-04-29 15:18:30 +08:00
async detectNetworkQuality ( ) {
if ( ! this . renderstreaming ) {
return ;
}
try {
const stats = await this . renderstreaming . getStats ( ) ;
if ( ! stats ) {
return ;
}
2026-05-24 01:01:28 +08:00
const summary = summarizeInboundStats ( stats ) ;
const quality = getNetworkQualityFromSummary ( summary ) ;
2026-04-29 15:18:30 +08:00
if ( this . state . session . remoteUser . networkQuality !== quality ) {
2026-05-24 01:29:34 +08:00
this . updateRemoteUserNetworkQuality ( quality ) ;
2026-04-29 15:18:30 +08:00
this . notify ( { type : 'NETWORK_CHANGE' , quality } ) ;
}
} catch ( error ) {
console . error ( 'Error detecting network quality:' , error ) ;
}
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸劍閺呮繈鏌曟径娑橆洭缂佺姵鍎抽埞鎴︽偐閸欏鍋嶉梺閫炲苯澧柛濠傤煼楠炴垿宕熼鍌炴闂佺锕﹂崰鎰邦敂閻樼粯鐓欏瀣閳诲牊顨ラ悙瀛樺磳妤犵偞鍨块、鏇㈡晲鎼淬垻鏆﹀┑鐑囩到濞层倝鏁冮鍫涒偓渚€寮撮姀鈥充簻闂佺偓鑹鹃崐鎼佀夊顑芥斀?
2026-04-29 15:18:30 +08:00
startActivityDetection ( stream , { isLocal = false } = { } ) {
if ( ! stream ) {
return ;
}
const audioTracks = stream . getAudioTracks ( ) ;
if ( audioTracks . length === 0 ) {
return ;
}
try {
const { threshold , debounceTime , fftSize } = VAD _CONFIG ;
2026-05-24 01:29:34 +08:00
const { analyser , dataArray } = createAudioAnalyser ( stream , fftSize ) ;
2026-04-29 15:18:30 +08:00
let isSpeaking = false ;
let lastActivityTime = 0 ;
const detectActivity = ( ) => {
if ( ! stream || ! this . renderstreaming ) {
return ;
}
2026-05-24 01:29:34 +08:00
const level = getAudioLevel ( analyser , dataArray ) ;
2026-04-29 15:18:30 +08:00
const currentTime = Date . now ( ) ;
if ( level > threshold / 100 ) {
lastActivityTime = currentTime ;
if ( ! isSpeaking ) {
isSpeaking = true ;
2026-05-24 01:29:34 +08:00
this . _setSpeakingState ( isLocal , true ) ;
2026-04-29 15:18:30 +08:00
}
} else if ( isSpeaking && currentTime - lastActivityTime > debounceTime ) {
isSpeaking = false ;
2026-05-24 01:29:34 +08:00
this . _setSpeakingState ( isLocal , false ) ;
2026-04-29 15:18:30 +08:00
}
if ( this . state . session . status === 'ongoing' ) {
requestAnimationFrame ( detectActivity ) ;
}
} ;
detectActivity ( ) ;
console . log ( ` ${ isLocal ? 'Local' : 'Remote' } activity detection started ` ) ;
} catch ( error ) {
console . error ( ` Error starting ${ isLocal ? 'local' : 'remote' } activity detection: ` , error ) ;
}
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鑼槱閻熸粎澧楃敮鎺楀垂閸岀偞鐓熸俊銈傚亾闁绘锕畷锝堢疀濞戞瑧鍘撻梺鍛婄箓鐎氼剟寮抽悢铏规/濡わ絽鍟伴悾娲煛瀹€鈧崰鎾诲焵椤掑倹鏆╂い顓炵墕閺嗏晠姊绘担渚敯妞ゎ偄顦叅闁绘梻鍘х粻鏍喐閻楀牆绗掔紒鐘崇洴閺屽秵娼幍顕呮М濡炪値鍋勫ú顓烆潖濞差亝顥堥柍鍝勫暙閸╁矂姊洪崷顓涙嫛闁稿顦甸幃妯尖偓锝庡枟閳锋垿姊洪銈呬粶闁兼椿鍨遍弲鍫曨敋閳ь剙螞?
2026-04-29 15:18:30 +08:00
startNetworkQualityDetection ( ) {
2026-05-24 01:46:57 +08:00
// 濠?缂傚倸鍊搁崐鎼佸磹妞嬪海鐭嗗〒姘e亾閽樻繈鏌熷▓鍨灍闁哄棙绮嶆穱濠囧Χ閸涱喖鍔欐繝銏e煐閸旀洖顔忓┑鍥ヤ簻闁圭儤鏌ㄧ敮鑸电節閳ь剟鏌嗗鍛姦濡炪倖甯掗敃锕€鐣濆☉銏$厸闁告侗鍠氱粻鐐淬亜閵忊剝鈷愭繛鐓庣箻婵℃瓕顦存い鏃€娲熷铏瑰寲閺囩噥娼戦梺鍛娒晶鑺ョ珶閺囩姵宕夐柕濞у拑绱抽柣搴$畭閸庨亶骞忕€n€綁鎼归崷顓狅紲闁哄鐗勯崝宀€绮幒鎾变簻妞ゆ挻绮屾慨鍌溾偓瑙勬礀瀹曨剝鐏冮梺鍛婂姦娴滄粓寮伴妷鈺傗拻?
2026-04-29 15:18:30 +08:00
this . networkQualityInterval = setInterval ( ( ) => {
this . detectNetworkQuality ( ) ;
} , 3000 ) ;
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐鐑芥嚄閸洍鈧箓宕奸妷顔芥櫈闂佺硶鍓濋悷銉╁垂濠靛牃鍋撻獮鍨姎妞わ缚绮欏顐﹀幢濡偐顔曢梺鐟邦嚟閸嬬偤鎯冮幋鐘垫/濡わ絽鍟伴悾娲煛瀹€鈧崰鎾诲焵椤掑倹鏆╂い顓炵墕閺嗏晠姊绘担渚敯妞ゎ偄顦叅闁绘梻鍘х粻鏍喐閻楀牆绗掔紒鐘崇洴閺屽秵娼幍顕呮М濡炪値鍋勫ú顓烆潖濞差亝顥堥柍鍝勫暙閸╁矂姊洪崷顓涙嫛闁稿顦甸幃妯尖偓锝庡枟閳锋垿姊洪銈呬粶闁兼椿鍨遍弲鍫曨敋閳ь剙螞?
2026-04-29 15:18:30 +08:00
stopNetworkQualityDetection ( ) {
if ( this . networkQualityInterval ) {
clearInterval ( this . networkQualityInterval ) ;
this . networkQualityInterval = null ;
}
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鍐蹭画闂佹寧娲栭崐鎼佸垂閸岀偞鐓曠憸搴ㄣ€冮崨瀛樺€块柛顭戝亖娴滄粓鏌熸潏鍓хɑ缁绢叀鍩栭妵鍕晜閼测晝鏆ら梺鍝勭焿缁蹭粙鍩ユ径濠庢僵妞ゆ帊鑳堕埀顒勭畺濮婃椽鎮烽弶鎸幮╅梺纭呮珪閿曘垽鎮伴鍢夌喖宕楅悡搴o紡闂備胶鍎甸弲婊呮暜椤忓棛涓嶉柟鎹愵嚙閽冪喖鏌i弮鍌楁嫛闁轰礁瀚伴幃瑙勭瑹椤栨粌甯ュ┑鈥虫▕閸o絽顫忛搹鐟板闁哄洨鍠愰悵鏃堟⒑濞茶骞栭柛濠冩倐椤㈡岸鏁愭径瀣垫濠电偞鍨靛畷顒勫箖閹达附鈷戦柛娑橈梗缁堕亶鏌涘▎蹇涱€楁い顓炴喘楠炲酣鎳為妷銏″?
2026-04-29 15:18:30 +08:00
emitMediaStateChange ( ) {
const payload = {
userId : this . state . session . localUser . id ,
... this . state . session . localUser . mediaState
} ;
console . log ( '[WebSocket Emit] media-state-changed:' , payload ) ;
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽弫鎼佸磻閻樿绠垫い蹇撴缁€濠囨煃瑜滈崜姘辨崲濞戞瑥绶為悗锝庡亞椤︿即鎮楀▓鍨珮闁稿锕ㄥΛ銏㈢磽娴g 绾х 紒鏃偽朤C闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁撻悩鍐蹭画闂佹寧娲栭崐鎼佸垂閸岀偞鐓曠憸搴ㄣ€冮崨瀛樺€块柛顭戝亖娴滄粓鏌熸潏鍓х ɑ 缁绢叀鍩栭妵鍕晜閼测晝鏆ら梺鍝勭焿缁蹭粙鍩ユ径濠庢僵妞ゆ帊鑳堕埀顒勭畺濮婃椽鎮烽弶鎸幮╅梺纭呮珪閿曘垽鎮伴鍢夌喖宕楅悡搴o 紡闂備胶鍎甸弲婊呮暜椤忓棛涓嶉柟鎹愵嚙閽冪喖鏌i 弮鍌楁嫛闁轰礁瀚伴幃瑙勭瑹椤栨粌甯ュ┑鈥虫▕閸o 綁骞冨畡閭︾叆闁告洦鍓涢崙鈥愁渻閵堝繒鐣垫繛浣冲洦鍋?
2026-04-29 15:18:30 +08:00
if ( this . renderstreaming ) {
this . renderstreaming . sendMessage ( {
type : 'media-state-changed' ,
data : payload
} ) ;
}
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢妶鍌氫壕婵鍘ф晶顖炴煛閸涙澘鐓愮紒鍌涘笧閳ь剨缍嗛埀顒夊弿闂勫嫰骞堥妸銉庣喐寰勭粙鎸庡創缂備胶鍋撻崕鎶藉Χ閹间礁钃熼柨婵嗩槸缁秹鏌嶈閸撴瑩鈥旈崘顔奸敜婵°倐鍋撻悷娆欑畵閺屽秷顧侀柛鎾存皑閹广垹鈽夐姀鈩冩珳闂佸憡渚楅崰姘端囬柆宥嗏拺闁告稑锕ョ亸鎵磼鐠囨彃鏆熼柍?
2026-04-29 15:18:30 +08:00
async showStatsMessage ( ) {
console . log ( 'Showing stats message' ) ;
2026-05-24 01:46:57 +08:00
// 缂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏇炲€搁拑鐔兼煏婵炵偓娅撻柡浣稿閺屾稑鈽夐崡鐐茬闂佸搫妫庨崐婵嬪蓟濞戙垹鐒洪柛鎰典簴濡插牏绱撴担鍝勑為柛搴㈠▕楠炲骞栨担鍝ヮ吅闂佹寧妫侀妴鈧柛瀣尰缁楃喖鍩€椤掑嫮宓侀悗锝庡枟閸婇鐥悧鍩虫垿鎮橀崘顔解拻濞撴埃鍋撻柍褜鍓涢崑娑㈡嚐椤栨稒娅犳い鏇楀亾闁哄苯绉堕幏鐘诲蓟閵夈儱鍙婇梻浣芥〃缁€浣虹矓閻熸壆鏆﹂柣鏃傗拡閺佸洭鏌eΟ娲荤劶闁告侗鍨抽敍婵嬫⒑缁嬫寧婀伴柣鐔濆洤绀夌€广儱娲ㄧ壕鍏笺亜閺冨洤浜圭紒鐘靛閵囧嫰寮捄銊ь唶闂佸疇顫夐崹褰掑焵椤掑﹦绉甸柛瀣瀹曘垽骞掑Δ渚囨濡炪倖鍔戦崹鐑樺緞閸曨兛绻嗘い鎰╁灮缁犵増銇勯銏㈢闁诡喗鐟╅、妤呭磼濠婂骸鏅?
2026-04-29 15:18:30 +08:00
await this . detectNetworkQuality ( ) ;
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊峰ù鍥敋瑜嶉~婵嬫晝閸岋妇绋忔繝銏f硾閼活垶寮搁崼鈶╁亾楠炲灝鍔氶柟閿嬪灴閹虫捇宕稿Δ浣哄弳闂佺粯鏌ㄩ幖顐㈢摥闂備礁鎲¢悷銉╂晝椤忓牆钃熸繛鎴欏灩缁犵粯淇婇悙闈涗壕妞わ箓顥撶槐鎺楊敊閻愵剚姣堥梺鍝勭焿缂嶄線骞冮埡鍛煑濠㈣泛琚崑鎺戔攽閻樻鏆柍褜鍓濆▍鏇烆啅濠靛鐓曢柟鐑樻尭缁椻晝绱掗悩宕囨创鐎殿喗鎸抽幃鈺冨枈婢跺苯绨ラ梻鍌氬€烽懗鍓佸垝椤栨凹娼栭柣鐔煎亰濞撳鏌涜椤ㄥ懐鎲撮敃鍌涚厓鐟滄粓宕滃杈ㄥ床婵炴垯鍨洪弲鏌ユ煕濞戝崬鏋ゆい锕備憾濮婃椽宕ㄦ繝鍐ㄩ瀺缂備浇顕ч崯浼村焵?
2026-04-29 15:18:30 +08:00
this . statsInterval = setInterval ( async ( ) => {
if ( ! this . renderstreaming ) {
return ;
}
try {
const stats = await this . renderstreaming . getStats ( ) ;
if ( ! stats ) {
return ;
}
2026-05-24 01:01:28 +08:00
const statsSummary = summarizeInboundStats ( stats ) ;
2026-05-24 01:29:34 +08:00
const statsLog = buildStatsLogPayload ( this . state . session . remoteUser . networkQuality , statsSummary ) ;
2026-04-29 15:18:30 +08:00
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊风粈渚€骞栭位鍥敍閻愭潙浜辨繝鐢靛Т濞层倗绮绘导瀛樼厵闂傚倸顕ˇ锕傛煕濮樻剚娼愰柕鍥у楠炴鎹勯惄鎺炵秮閹顫濋鐐叉懙闂佸搫琚崝鎴濐嚕椤掑嫬鍨傛い鏃囶潐鐎垫牠姊绘担瑙勩仧闁告鍘ч湁濡炲瀛╅~鏇㈡煙閹呮憼濠殿垱鎸抽弻娑樷攽閸℃浠鹃梺璇茬箰濡鈥旈崘顔嘉ч柛鈩兠喊宥夋⒑绾拋鍤嬬紒杈ㄦ礈閸掓帒顫濋懜鐢靛姸閻庡箍鍎遍幊鎰板礉閹绢喗鈷戦柛娑橈工婢瑰啴鏌涘☉鍗炵伇婵?
2026-04-29 15:18:30 +08:00
console . log ( '=== WebRTC Statistics ===' ) ;
2026-05-24 01:29:34 +08:00
console . log ( ` Network Quality: ${ statsLog . networkQuality } ` ) ;
console . log ( 'Video Stats:' , statsLog . video ) ;
console . log ( 'Audio Stats:' , statsLog . audio ) ;
2026-04-29 15:18:30 +08:00
console . log ( '========================' ) ;
} catch ( error ) {
console . error ( 'Error showing stats message:' , error ) ;
}
2026-05-24 01:46:57 +08:00
} , 5000 ) ; // 濠?缂傚倸鍊搁崐鎼佸磹妞嬪海鐭嗗〒姘e亾閽樻繈鏌熷▓鍨灍闁哄棙绮嶆穱濠囧Χ閸屾矮澹曢梺缁樻尪閸婃繈寮诲☉婊庢Ъ濡炪們鍔岄幊搴ょ亱濠碘槅鍨紞宥呪槈濞嗘瑧鐭楁繛杈剧秬濡嫰宕㈤垾鎰佹富闁靛牆鎳愮粻鍝勵渻閺夋垶鎲搁柟骞垮灩閳藉濮€閻樿尪鈧灝顪冮妶鍡樺暗濠殿喚鍏橀弫宥夋偄閾忓湱锛濇繛鎾磋壘濞层倝寮搁敂鐣岀闁告粌鍟扮粔顔筋殽閻愯尙绠抽柍褜鍓ㄧ紞鍡涘窗閺嶎厽鍊堕柟鎯板Г椤ュ﹥銇勯幇闈涗簻妤犵偞顭囩槐鎺楁偐閸愭彃鎽靛┑?
2026-04-29 15:18:30 +08:00
}
2026-05-24 01:46:57 +08:00
// 濠电姷鏁告慨鐑藉极閹间礁纾婚柣鎰惈缁犱即鏌熼梻瀵割槮缂佺姷濮垫穱濠囶敍濠靛嫧鍋撻埀顒勬煛鐎n亞效妤犵偞鐗楀蹇涘礈瑜庨崑褏绱掗悙顒€鍔ら柕鍫熸倐瀵鏁愭径濠勭潉闂侀€炲苯澧い顏勫暣瀹曠螖閳ь剛鎲撮敃鍌涚厓鐟滄粓宕滃杈ㄥ床婵炴垯鍨洪弲鏌ユ煕濞戝崬鏋ゆい锕備憾濮婃椽宕ㄦ繝鍐ㄩ瀺缂備浇顕ч崯浼村焵?
2026-04-29 15:18:30 +08:00
clearStatsMessage ( ) {
console . log ( 'Clearing stats message' ) ;
2026-05-24 01:46:57 +08:00
// 濠电姷鏁告慨鐑藉极閹间礁纾婚柣鎰惈缁犱即鏌熼梻瀵割槮缂佺姷濞€閺岀喖鎮ч崼鐔哄嚒缂備胶濮甸悧鏇㈠煘閹达附鍋愰柟缁樺俯娴尖偓缂備胶鍋撻崕鎶藉Χ閹间礁钃熼柨婵嗩槸缁秹鏌嶈閸撴瑩鈥旈崘顔奸敜婵°倐鍋撻悷娆欑畵閺屽秷顧侀柛鎾存皑閹广垹鈽夐姀鈩冩珳闂佸憡渚楅崰姘端囬柆宥嗏拺闁告稑锕ョ亸鎵磼鐠囨彃鏆熼柍褜鍓熷褔濡剁粙璺ㄦ殾闁靛ň鏅╅弫宥嗕繆閵堝倸浜炬繛瀛樼矒缁犳牠骞冨畡鎵虫瀻闊洦鎼╂导鈧俊鐐€х徊钘夘嚕閼哥數鈹嶅┑鐘叉处閸婄兘鏌熺紒妯哄潑闁稿鎹囧畷褰掝敊閻愵剚顔?
2026-04-29 15:18:30 +08:00
if ( this . statsInterval ) {
clearInterval ( this . statsInterval ) ;
this . statsInterval = null ;
}
}
// Getters
getState ( ) { return this . state ; }
getLocalUser ( ) { return this . state . session . localUser ; }
getRemoteUser ( ) { return this . state . session . remoteUser ; }
getConnectionId ( ) { return this . connectionId ; }
getRenderStreaming ( ) { return this . renderstreaming ; }
}
2026-05-24 01:46:57 +08:00
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炲鏁嶉崟顒佹濠德板€曢崯浼存儗濞嗘挻鐓欓悗鐢殿焾鍟哥紒鎯у綖缁瑩寮婚悢鐓庣闁逛即娼у▓顓犵磼缂併垹骞愰柛瀣崌濮婄粯鎷呴崨濠冨創濠电偠顕滅粻鎴︼綖濠靛惟闁挎洍鍋撴い鏇憾閺岀喖骞嶉纰辨毉闂佺锕﹂弫濠氬蓟濞戙垺鏅滈悹鍥ㄥ絻缁犺绻涚€电校閻㈩垳鍋熷Σ?
2026-04-29 15:18:30 +08:00
const store = new CallStateManager ( ) ;
2026-05-24 01:46:57 +08:00
// 婵犵數濮烽。顔炬閺囥垹纾婚柟杈剧畱绾捐淇婇妶鍛櫣闁哄绶氶幃褰掑炊瑜庨埢鏇㈡煟閹烘洘顥夐棁澶愭煕韫囨挸鎮戠紓宥嗗灩閻ヮ亪顢樺☉妯瑰婵犵數濮甸鏍窗濡ゅ啯宕查柛宀€鍋涚€氬銇勯幒鎴濃偓濠氭儗閸緷褰掓偂鎼达絾鎲奸梺缁樻尰濞茬喖寮婚悢鐓庣畾鐟滃繘鏁嶅澶婂唨闁挎稑瀚壕钘壝归敐鍫綈闁绘挶鍎查妵鍕敇閻樻彃骞嬪Δ?
2026-04-29 15:18:30 +08:00
window . addEventListener ( 'beforeunload' , async ( ) => {
if ( ! store . renderstreaming )
return ;
2026-05-24 01:46:57 +08:00
await store . renderstreaming . stop ( ) ; // 闂傚倸鍊搁崐鐑芥嚄閸洍鈧箓宕奸妷顔芥櫈闂佺硶鍓濋悷銉╁垂濠靛牃鍋撻獮鍨姎妞わ缚绮欏顐﹀幢濡偐顔曢柣蹇曞仩婵倝顢撻崫绛奣C闂傚倸鍊风粈渚€骞栭位鍥敃閿曗偓閻ょ偓绻濋棃娑卞剰缁炬儳顭烽弻锝夊箛椤掑倷绮甸梺?
2026-04-29 15:18:30 +08:00
} , true ) ;
export default store ;