【m】开始尝试接入后端
This commit is contained in:
@@ -1,76 +1,101 @@
|
||||
import { SendVideo } from "./sendvideo.js";
|
||||
import { getServerConfig, getRTCConfiguration } from "../../js/config.js";
|
||||
import { createDisplayStringArray } from "../../js/stats.js";
|
||||
import { RenderStreaming } from "../../module/renderstreaming.js";
|
||||
import { Signaling, WebSocketSignaling } from "../../module/signaling.js";
|
||||
/**
|
||||
* 双向视频通话应用主文件
|
||||
* 负责初始化视频设备、建立WebRTC连接、处理信令和显示视频流
|
||||
*/
|
||||
|
||||
// 导入必要的模块
|
||||
import { SendVideo } from "./sendvideo.js"; // 视频发送和接收处理
|
||||
import { getServerConfig, getRTCConfiguration } from "../../js/config.js"; // 服务器配置和RTC配置
|
||||
import { createDisplayStringArray } from "../../js/stats.js"; // 统计信息处理
|
||||
import { RenderStreaming } from "../../module/renderstreaming.js"; // WebRTC连接管理
|
||||
import { Signaling, WebSocketSignaling } from "../../module/signaling.js"; // 信令管理
|
||||
|
||||
// 默认视频流尺寸
|
||||
const defaultStreamWidth = 1280;
|
||||
const defaultStreamHeight = 720;
|
||||
|
||||
// 预定义的视频分辨率列表
|
||||
const streamSizeList =
|
||||
[
|
||||
{ width: 640, height: 360 },
|
||||
{ width: 1280, height: 720 },
|
||||
{ width: 1920, height: 1080 },
|
||||
{ width: 2560, height: 1440 },
|
||||
{ width: 3840, height: 2160 },
|
||||
{ width: 360, height: 640 },
|
||||
{ width: 720, height: 1280 },
|
||||
{ width: 1080, height: 1920 },
|
||||
{ width: 1440, height: 2560 },
|
||||
{ width: 2160, height: 3840 },
|
||||
{ width: 640, height: 360 }, // 标清
|
||||
{ width: 1280, height: 720 }, // 高清
|
||||
{ width: 1920, height: 1080 }, // 全高清
|
||||
{ width: 2560, height: 1440 }, // 2K
|
||||
{ width: 3840, height: 2160 }, // 4K
|
||||
{ width: 360, height: 640 }, // 竖屏标清
|
||||
{ width: 720, height: 1280 }, // 竖屏高清
|
||||
{ width: 1080, height: 1920 }, // 竖屏全高清
|
||||
{ width: 1440, height: 2560 }, // 竖屏2K
|
||||
{ width: 2160, height: 3840 }, // 竖屏4K
|
||||
];
|
||||
|
||||
const localVideo = document.getElementById('localVideo');
|
||||
const remoteVideo = document.getElementById('remoteVideo');
|
||||
const localVideoStatsDiv = document.getElementById('localVideoStats');
|
||||
const remoteVideoStatsDiv = document.getElementById('remoteVideoStats');
|
||||
const textForConnectionId = document.getElementById('textForConnectionId');
|
||||
textForConnectionId.value = getRandom();
|
||||
const videoSelect = document.querySelector('select#videoSource');
|
||||
const audioSelect = document.querySelector('select#audioSource');
|
||||
const videoResolutionSelect = document.querySelector('select#videoResolution');
|
||||
const cameraWidthInput = document.querySelector('input#cameraWidth');
|
||||
const cameraHeightInput = document.querySelector('input#cameraHeight');
|
||||
// DOM元素引用
|
||||
const localVideo = document.getElementById('localVideo'); // 本地视频元素
|
||||
const remoteVideo = document.getElementById('remoteVideo'); // 远程视频元素
|
||||
const localVideoStatsDiv = document.getElementById('localVideoStats'); // 本地视频统计信息
|
||||
const remoteVideoStatsDiv = document.getElementById('remoteVideoStats'); // 远程视频统计信息
|
||||
const textForConnectionId = document.getElementById('textForConnectionId'); // 连接ID输入框
|
||||
textForConnectionId.value = getRandom(); // 生成随机连接ID
|
||||
const videoSelect = document.querySelector('select#videoSource'); // 视频设备选择
|
||||
const audioSelect = document.querySelector('select#audioSource'); // 音频设备选择
|
||||
const videoResolutionSelect = document.querySelector('select#videoResolution'); // 视频分辨率选择
|
||||
const cameraWidthInput = document.querySelector('input#cameraWidth'); // 自定义宽度输入
|
||||
const cameraHeightInput = document.querySelector('input#cameraHeight'); // 自定义高度输入
|
||||
|
||||
// 编解码器偏好设置
|
||||
const codecPreferences = document.getElementById('codecPreferences');
|
||||
// 检查浏览器是否支持设置编解码器偏好
|
||||
const supportsSetCodecPreferences = window.RTCRtpTransceiver &&
|
||||
'setCodecPreferences' in window.RTCRtpTransceiver.prototype;
|
||||
const messageDiv = document.getElementById('message');
|
||||
messageDiv.style.display = 'none';
|
||||
const messageDiv = document.getElementById('message'); // 消息显示区域
|
||||
messageDiv.style.display = 'none'; // 初始隐藏消息区域
|
||||
|
||||
let useCustomResolution = false;
|
||||
let useCustomResolution = false; // 是否使用自定义分辨率
|
||||
|
||||
// 初始化输入选择和编解码器选择
|
||||
setUpInputSelect();
|
||||
showCodecSelect();
|
||||
|
||||
/** @type {SendVideo} */
|
||||
let sendVideo = new SendVideo(localVideo, remoteVideo);
|
||||
let sendVideo = new SendVideo(localVideo, remoteVideo); // 视频处理实例
|
||||
/** @type {RenderStreaming} */
|
||||
let renderstreaming;
|
||||
let useWebSocket;
|
||||
let connectionId;
|
||||
let renderstreaming; // WebRTC连接管理实例
|
||||
let useWebSocket; // 是否使用WebSocket信令
|
||||
let connectionId; // 连接ID
|
||||
|
||||
// 按钮事件绑定
|
||||
const startButton = document.getElementById('startVideoButton');
|
||||
startButton.addEventListener('click', startVideo);
|
||||
startButton.addEventListener('click', startVideo); // 启动视频按钮
|
||||
const setupButton = document.getElementById('setUpButton');
|
||||
setupButton.addEventListener('click', setUp);
|
||||
setupButton.addEventListener('click', setUp); // 设置连接按钮
|
||||
const hangUpButton = document.getElementById('hangUpButton');
|
||||
hangUpButton.addEventListener('click', hangUp);
|
||||
hangUpButton.addEventListener('click', hangUp); // 挂断按钮
|
||||
|
||||
// 页面卸载前清理
|
||||
window.addEventListener('beforeunload', async () => {
|
||||
if(!renderstreaming)
|
||||
return;
|
||||
await renderstreaming.stop();
|
||||
await renderstreaming.stop(); // 停止WebRTC连接
|
||||
}, true);
|
||||
|
||||
// 初始化配置
|
||||
setupConfig();
|
||||
|
||||
/**
|
||||
* 初始化服务器配置
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function setupConfig() {
|
||||
const res = await getServerConfig();
|
||||
useWebSocket = res.useWebSocket;
|
||||
showWarningIfNeeded(res.startupMode);
|
||||
const res = await getServerConfig(); // 获取服务器配置
|
||||
useWebSocket = res.useWebSocket; // 设置是否使用WebSocket
|
||||
showWarningIfNeeded(res.startupMode); // 显示启动模式警告
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据启动模式显示警告信息
|
||||
* @param {string} startupMode - 启动模式,可能的值包括"public"和"private"
|
||||
*/
|
||||
function showWarningIfNeeded(startupMode) {
|
||||
const warningDiv = document.getElementById("warning");
|
||||
if (startupMode == "public") {
|
||||
@@ -79,7 +104,13 @@ function showWarningIfNeeded(startupMode) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动本地视频
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function startVideo() {
|
||||
// 禁用相关输入控件
|
||||
videoSelect.disabled = true;
|
||||
audioSelect.disabled = true;
|
||||
videoResolutionSelect.disabled = true;
|
||||
@@ -89,6 +120,8 @@ async function startVideo() {
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
|
||||
// 根据选择的分辨率设置视频尺寸
|
||||
if (useCustomResolution) {
|
||||
width = cameraWidthInput.value ? cameraWidthInput.value : defaultStreamWidth;
|
||||
height = cameraHeightInput.value ? cameraHeightInput.value : defaultStreamHeight;
|
||||
@@ -98,48 +131,65 @@ async function startVideo() {
|
||||
height = size.height;
|
||||
}
|
||||
|
||||
// 启动本地视频
|
||||
await sendVideo.startLocalVideo(videoSelect.value, audioSelect.value, width, height);
|
||||
|
||||
// enable setup button after initializing local video.
|
||||
// 启用设置按钮
|
||||
setupButton.disabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置WebRTC连接
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function setUp() {
|
||||
setupButton.disabled = true;
|
||||
hangUpButton.disabled = false;
|
||||
connectionId = textForConnectionId.value;
|
||||
codecPreferences.disabled = true;
|
||||
setupButton.disabled = true; // 禁用设置按钮
|
||||
hangUpButton.disabled = false; // 启用挂断按钮
|
||||
connectionId = textForConnectionId.value; // 获取连接ID
|
||||
codecPreferences.disabled = true; // 禁用编解码器选择
|
||||
|
||||
// 创建信令实例
|
||||
const signaling = useWebSocket ? new WebSocketSignaling() : new Signaling();
|
||||
const config = getRTCConfiguration();
|
||||
renderstreaming = new RenderStreaming(signaling, config);
|
||||
const config = getRTCConfiguration(); // 获取RTC配置
|
||||
renderstreaming = new RenderStreaming(signaling, config); // 创建WebRTC连接管理实例
|
||||
|
||||
// 连接建立回调
|
||||
renderstreaming.onConnect = () => {
|
||||
const tracks = sendVideo.getLocalTracks();
|
||||
const tracks = sendVideo.getLocalTracks(); // 获取本地媒体轨道
|
||||
for (const track of tracks) {
|
||||
renderstreaming.addTransceiver(track, { direction: 'sendonly' });
|
||||
renderstreaming.addTransceiver(track, { direction: 'sendonly' }); // 添加发送轨道
|
||||
}
|
||||
setCodecPreferences();
|
||||
showStatsMessage();
|
||||
setCodecPreferences(); // 设置编解码器偏好
|
||||
showStatsMessage(); // 显示统计信息
|
||||
};
|
||||
|
||||
// 连接断开回调
|
||||
renderstreaming.onDisconnect = () => {
|
||||
hangUp();
|
||||
hangUp(); // 挂断连接
|
||||
};
|
||||
|
||||
// 轨道事件回调
|
||||
renderstreaming.onTrackEvent = (data) => {
|
||||
const direction = data.transceiver.direction;
|
||||
if (direction == "sendrecv" || direction == "recvonly") {
|
||||
sendVideo.addRemoteTrack(data.track);
|
||||
sendVideo.addRemoteTrack(data.track); // 添加远程轨道
|
||||
}
|
||||
};
|
||||
|
||||
// 启动WebRTC连接
|
||||
await renderstreaming.start();
|
||||
await renderstreaming.createConnection(connectionId);
|
||||
|
||||
}
|
||||
// 获取浏览器麦克风并发送到 Unity
|
||||
|
||||
/**
|
||||
* 设置编解码器偏好
|
||||
*/
|
||||
function setCodecPreferences() {
|
||||
/** @type {RTCRtpCodecCapability[] | null} */
|
||||
let selectedCodecs = null;
|
||||
|
||||
if (supportsSetCodecPreferences) {
|
||||
const preferredCodec = codecPreferences.options[codecPreferences.selectedIndex];
|
||||
if (preferredCodec.value !== '') {
|
||||
@@ -154,41 +204,63 @@ function setCodecPreferences() {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂断WebRTC连接
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function hangUp() {
|
||||
clearStatsMessage();
|
||||
clearStatsMessage(); // 清除统计信息
|
||||
messageDiv.style.display = 'block';
|
||||
messageDiv.innerText = `Disconnect peer on ${connectionId}.`;
|
||||
|
||||
hangUpButton.disabled = true;
|
||||
setupButton.disabled = false;
|
||||
hangUpButton.disabled = true; // 禁用挂断按钮
|
||||
setupButton.disabled = false; // 启用设置按钮
|
||||
|
||||
// 删除连接并停止WebRTC
|
||||
await renderstreaming.deleteConnection();
|
||||
await renderstreaming.stop();
|
||||
renderstreaming = null;
|
||||
remoteVideo.srcObject = null;
|
||||
remoteVideo.srcObject = null; // 清除远程视频源
|
||||
|
||||
textForConnectionId.value = getRandom();
|
||||
textForConnectionId.value = getRandom(); // 生成新的随机连接ID
|
||||
connectionId = null;
|
||||
|
||||
// 启用编解码器选择
|
||||
if (supportsSetCodecPreferences) {
|
||||
codecPreferences.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机连接ID
|
||||
* @returns {string} 5位随机数字字符串
|
||||
*/
|
||||
function getRandom() {
|
||||
const max = 99999;
|
||||
const length = String(max).length;
|
||||
const number = Math.floor(Math.random() * max);
|
||||
return (Array(length).join('0') + number).slice(-length);
|
||||
return (Array(length).join('0') + number).slice(-length); // 补零确保5位
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入选择控件
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function setUpInputSelect() {
|
||||
// 获取媒体设备列表
|
||||
const deviceInfos = await navigator.mediaDevices.enumerateDevices();
|
||||
|
||||
// 填充视频设备选择
|
||||
for (let i = 0; i !== deviceInfos.length; ++i) {
|
||||
const deviceInfo = deviceInfos[i];
|
||||
if (deviceInfo.kind === 'videoinput') {
|
||||
@@ -197,6 +269,7 @@ async function setUpInputSelect() {
|
||||
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
|
||||
videoSelect.appendChild(option);
|
||||
} else if (deviceInfo.kind === 'audioinput') {
|
||||
// 填充音频设备选择
|
||||
const option = document.createElement('option');
|
||||
option.value = deviceInfo.deviceId;
|
||||
option.text = deviceInfo.label || `mic ${audioSelect.length + 1}`;
|
||||
@@ -204,6 +277,7 @@ async function setUpInputSelect() {
|
||||
}
|
||||
}
|
||||
|
||||
// 填充视频分辨率选择
|
||||
for (let i = 0; i < streamSizeList.length; i++) {
|
||||
const streamSize = streamSizeList[i];
|
||||
const option = document.createElement('option');
|
||||
@@ -212,12 +286,14 @@ async function setUpInputSelect() {
|
||||
videoResolutionSelect.appendChild(option);
|
||||
}
|
||||
|
||||
// 添加自定义分辨率选项
|
||||
const option = document.createElement('option');
|
||||
option.value = streamSizeList.length;
|
||||
option.text = 'Custom';
|
||||
videoResolutionSelect.appendChild(option);
|
||||
videoResolutionSelect.value = 1; // default select index (1280 x 720)
|
||||
videoResolutionSelect.value = 1; // 默认选择1280 x 720
|
||||
|
||||
// 分辨率选择变化事件
|
||||
videoResolutionSelect.addEventListener('change', (event) => {
|
||||
const isCustom = event.target.value >= streamSizeList.length;
|
||||
cameraWidthInput.disabled = !isCustom;
|
||||
@@ -226,6 +302,9 @@ async function setUpInputSelect() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示编解码器选择
|
||||
*/
|
||||
function showCodecSelect() {
|
||||
if (!supportsSetCodecPreferences) {
|
||||
messageDiv.style.display = 'block';
|
||||
@@ -233,8 +312,10 @@ function showCodecSelect() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取视频编解码器能力
|
||||
const codecs = RTCRtpSender.getCapabilities('video').codecs;
|
||||
codecs.forEach(codec => {
|
||||
// 跳过冗余和FEC编解码器
|
||||
if (['video/red', 'video/ulpfec', 'video/rtx'].includes(codec.mimeType)) {
|
||||
return;
|
||||
}
|
||||
@@ -246,14 +327,21 @@ function showCodecSelect() {
|
||||
codecPreferences.disabled = false;
|
||||
}
|
||||
|
||||
let lastStats;
|
||||
let intervalId;
|
||||
// 统计信息相关变量
|
||||
let lastStats; // 上次统计信息
|
||||
let intervalId; // 统计信息更新间隔ID
|
||||
|
||||
/**
|
||||
* 显示统计信息
|
||||
*/
|
||||
function showStatsMessage() {
|
||||
// 每秒更新一次统计信息
|
||||
intervalId = setInterval(async () => {
|
||||
// 显示本地视频分辨率
|
||||
if (localVideo.videoWidth) {
|
||||
localVideoStatsDiv.innerHTML = `<strong>Sending resolution:</strong> ${localVideo.videoWidth} x ${localVideo.videoHeight} px`;
|
||||
}
|
||||
// 显示远程视频分辨率
|
||||
if (remoteVideo.videoWidth) {
|
||||
remoteVideoStatsDiv.innerHTML = `<strong>Receiving resolution:</strong> ${remoteVideo.videoWidth} x ${remoteVideo.videoHeight} px`;
|
||||
}
|
||||
@@ -262,11 +350,13 @@ function showStatsMessage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取WebRTC统计信息
|
||||
const stats = await renderstreaming.getStats();
|
||||
if (stats == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建统计信息显示数组
|
||||
const array = createDisplayStringArray(stats, lastStats);
|
||||
if (array.length) {
|
||||
messageDiv.style.display = 'block';
|
||||
@@ -276,9 +366,12 @@ function showStatsMessage() {
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除统计信息
|
||||
*/
|
||||
function clearStatsMessage() {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
clearInterval(intervalId); // 清除定时器
|
||||
}
|
||||
lastStats = null;
|
||||
intervalId = null;
|
||||
|
||||
Reference in New Issue
Block a user