【m】开始尝试接入后端
This commit is contained in:
@@ -3,9 +3,20 @@
|
||||
* 使用简单的 Observable 模式,可替换为 Redux/Vuex/Pinia
|
||||
*/
|
||||
import { mockCallSession, mockMessages } from './models.js';
|
||||
import { Signaling, WebSocketSignaling } from "../../module/signaling.js";// 信令管理
|
||||
import { RenderStreaming } from "../../module/renderstreaming.js"; // WebRTC连接管理
|
||||
import { getServerConfig, getRTCConfiguration } from "../js/config.js";//服务器配置和RTC配置
|
||||
// 默认视频流尺寸
|
||||
const defaultStreamWidth = 1280;
|
||||
const defaultStreamHeight = 720;
|
||||
|
||||
|
||||
|
||||
class CallStateManager {
|
||||
constructor() {
|
||||
let renderstreaming; // WebRTC连接管理实例
|
||||
let useWebSocket; // 是否使用WebSocket信令
|
||||
let connectionId; // 连接ID
|
||||
// 核心状态
|
||||
this.state = {
|
||||
session: { ...mockCallSession },
|
||||
@@ -20,7 +31,7 @@ class CallStateManager {
|
||||
this.listeners = [];
|
||||
|
||||
// 初始化
|
||||
this.init();
|
||||
//this.init();
|
||||
}
|
||||
|
||||
// 订阅状态变化
|
||||
@@ -43,17 +54,22 @@ class CallStateManager {
|
||||
this.state.session.duration++;
|
||||
this.notify({ type: 'DURATION_UPDATE', duration: this.state.session.duration });
|
||||
}, 1000);
|
||||
|
||||
// 初始化配置
|
||||
this.setupConfig();
|
||||
// 获取本地摄像头视频流
|
||||
this.getLocalStream();
|
||||
|
||||
|
||||
// 模拟远端音频活动 (实际应由 WebRTC VAD 检测触发)
|
||||
this.simulateRemoteActivity();
|
||||
|
||||
// 模拟网络质量变化
|
||||
this.simulateNetworkChange();
|
||||
}
|
||||
|
||||
async setupConfig() {
|
||||
const res = await getServerConfig();
|
||||
this.useWebSocket = res.useWebSocket;
|
||||
}
|
||||
// 获取本地摄像头视频流
|
||||
async getLocalStream() {
|
||||
try {
|
||||
@@ -103,6 +119,30 @@ class CallStateManager {
|
||||
|
||||
// 更新本地媒体状态
|
||||
async updateLocalMedia(mediaType, value) {
|
||||
|
||||
// 如果是开启视频,重新获取摄像头资源
|
||||
if (mediaType === 'video' && value) {
|
||||
if (this.state.localStream) {
|
||||
this.state.localStream = null;
|
||||
}
|
||||
//if(this.state.localStream.getVideoTracks().length==0){
|
||||
// 请求摄像头权限并获取媒体流
|
||||
this.state.localStream = await navigator.mediaDevices.getUserMedia({
|
||||
video: true,
|
||||
audio: true
|
||||
});
|
||||
// }
|
||||
await this.getLocalStream();
|
||||
} else {
|
||||
// 直接更新媒体状态
|
||||
this.state.session.localUser.mediaState[mediaType] = value;
|
||||
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType, value });
|
||||
|
||||
// 发送媒体状态到服务器
|
||||
this.emitMediaStateChange();
|
||||
}
|
||||
|
||||
|
||||
// 如果是关闭视频,释放摄像头资源
|
||||
if (mediaType === 'video' && !value && this.state.localStream) {
|
||||
this.state.localStream.getTracks().forEach(track => {
|
||||
@@ -122,28 +162,107 @@ class CallStateManager {
|
||||
});
|
||||
}
|
||||
|
||||
// 如果是开启视频,重新获取摄像头资源
|
||||
if (mediaType === 'video' && value ) {
|
||||
if(this.state.localStream){
|
||||
this.state.localStream=null;
|
||||
}
|
||||
//if(this.state.localStream.getVideoTracks().length==0){
|
||||
// 请求摄像头权限并获取媒体流
|
||||
this.state.localStream = await navigator.mediaDevices.getUserMedia({
|
||||
video: true,
|
||||
audio: true
|
||||
});
|
||||
// }
|
||||
await this.getLocalStream();
|
||||
} else {
|
||||
// 直接更新媒体状态
|
||||
this.state.session.localUser.mediaState[mediaType] = value;
|
||||
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType, value });
|
||||
|
||||
// 发送媒体状态到服务器
|
||||
this.emitMediaStateChange();
|
||||
}
|
||||
/**
|
||||
* 设置WebRTC连接
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async setUp(connectionId) {
|
||||
//TODO
|
||||
this.connectionId = connectionId; // 获取连接ID
|
||||
codecPreferences.disabled = true; // 禁用编解码器选择
|
||||
|
||||
// 创建信令实例
|
||||
const signaling = useWebSocket ? new WebSocketSignaling() : new Signaling();
|
||||
const config = getRTCConfiguration(); // 获取RTC配置
|
||||
this.renderstreaming = new RenderStreaming(signaling, config); // 创建WebRTC连接管理实例
|
||||
|
||||
// 连接建立回调
|
||||
this.renderstreaming.onConnect = () => {
|
||||
const tracks = this.state.localStream.getTracks(); // 获取本地媒体轨道
|
||||
for (const track of tracks) {
|
||||
this.renderstreaming.addTransceiver(track, { direction: 'sendonly' }); // 添加发送轨道
|
||||
}
|
||||
setCodecPreferences(); // 设置编解码器偏好
|
||||
showStatsMessage(); // 显示统计信息
|
||||
};
|
||||
|
||||
// 连接断开回调
|
||||
this.renderstreaming.onDisconnect = () => {
|
||||
hangUp(); // 挂断连接
|
||||
};
|
||||
|
||||
// 轨道事件回调
|
||||
this.renderstreaming.onTrackEvent = (data) => {
|
||||
const direction = data.transceiver.direction;
|
||||
if (direction == "sendrecv" || direction == "recvonly") {
|
||||
if (this.state.remoteStream == null) {
|
||||
this.state.remoteStream = new MediaStream();
|
||||
}
|
||||
this.state.remoteStream.addTrack(data.track);
|
||||
}
|
||||
};
|
||||
|
||||
// 启动WebRTC连接
|
||||
await this.renderstreaming.start();
|
||||
await this.renderstreaming.createConnection(connectionId);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂断WebRTC连接
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async hangUp() {
|
||||
clearStatsMessage(); // 清除统计信息
|
||||
messageDiv.style.display = 'block';
|
||||
messageDiv.innerText = `Disconnect peer on ${connectionId}.`;
|
||||
|
||||
// 删除连接并停止WebRTC
|
||||
await renderstreaming.deleteConnection();
|
||||
await renderstreaming.stop();
|
||||
renderstreaming = null;
|
||||
remoteVideo.srcObject = null; // 清除远程视频源
|
||||
|
||||
connectionId = null;
|
||||
|
||||
// 启用编解码器选择
|
||||
if (supportsSetCodecPreferences) {
|
||||
codecPreferences.disabled = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置编解码器偏好
|
||||
*/
|
||||
setCodecPreferences() {
|
||||
/** @type {RTCRtpCodecCapability[] | null} */
|
||||
let selectedCodecs = null;
|
||||
|
||||
if (supportsSetCodecPreferences) {
|
||||
const preferredCodec = codecPreferences.options[codecPreferences.selectedIndex];
|
||||
if (preferredCodec.value !== '') {
|
||||
const [mimeType, sdpFmtpLine] = preferredCodec.value.split(' ');
|
||||
const { codecs } = RTCRtpSender.getCapabilities('video');
|
||||
const selectedCodecIndex = codecs.findIndex(c => c.mimeType === mimeType && c.sdpFmtpLine === sdpFmtpLine);
|
||||
const selectCodec = codecs[selectedCodecIndex];
|
||||
selectedCodecs = [selectCodec];
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedCodecs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取视频收发器并设置编解码器偏好
|
||||
const transceivers = renderstreaming.getTransceivers().filter(t => t.receiver.track.kind == "video");
|
||||
if (transceivers && transceivers.length > 0) {
|
||||
transceivers.forEach(t => t.setCodecPreferences(selectedCodecs));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新远端媒体状态 (由 WebSocket 消息触发)
|
||||
updateRemoteMedia(mediaState) {
|
||||
@@ -228,4 +347,11 @@ class CallStateManager {
|
||||
// 创建单例实例
|
||||
const store = new CallStateManager();
|
||||
|
||||
// 页面卸载前清理
|
||||
window.addEventListener('beforeunload', async () => {
|
||||
if (!store.renderstreaming)
|
||||
return;
|
||||
await store.renderstreaming.stop(); // 停止WebRTC连接
|
||||
}, true);
|
||||
export default store;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user