[*] 日常优化

This commit is contained in:
acgist
2023-09-15 08:13:52 +08:00
parent 738607226f
commit f0d728cf00
2 changed files with 364 additions and 355 deletions

View File

@@ -801,30 +801,6 @@ class Taoyao {
me.push(message);
}
/**
* 重启ICE信令
*
* @param {*} message 消息
* @param {*} body 消息主体
*/
async mediaIceRestart(message, body) {
const me = this;
const {
roomId,
transportId
} = body;
const room = me.rooms.get(roomId);
const transport = room?.transports.get(transportId);
if(transport) {
console.debug("重启ICE", transportId);
const iceParameters = await transport.restartIce();
message.body.iceParameters = iceParameters;
me.push(message);
} else {
console.warn("重启ICE无效", transportId);
}
}
/**
* 消费媒体信令
*
@@ -1376,6 +1352,31 @@ class Taoyao {
}
}
/**
* 重启ICE信令
*
* @param {*} message 信令消息
* @param {*} body 消息主体
*/
async mediaIceRestart(message, body) {
const {
roomId,
transportId
} = body;
const room = this.rooms.get(roomId);
const transport = room?.transports.get(transportId);
if(!transport) {
console.warn("重启ICE无效通道", transportId);
return;
}
console.debug("重启ICE", transportId);
message.body = {
...body,
iceParameters: await transport.restartIce()
};
this.push(message);
}
/**
* 生产媒体信令
*
@@ -1594,10 +1595,11 @@ class Taoyao {
const {
roomId
} = body;
const room = this.rooms.get(roomId);
const room = this.rooms.get(roomId);
const rtpCapabilities = room?.mediasoupRouter.rtpCapabilities;
message.body = {
...message.body,
rtpCapabilities: room?.mediasoupRouter.rtpCapabilities
...body,
rtpCapabilities
};
this.push(message);
}

View File

@@ -1800,29 +1800,6 @@ class Taoyao extends RemoteClient {
}));
}
/**
* 重启ICE信令
*/
async mediaIceRestart() {
const me = this;
if (me.sendTransport) {
const response = await me.request(protocol.buildMessage("media::ice::restart", {
roomId : me.roomId,
transportId: me.sendTransport.id
}));
const { iceParameters } = response.body;
await me.sendTransport.restartIce({ iceParameters });
}
if (me.recvTransport) {
const response = await me.request(protocol.buildMessage("media::ice::restart", {
roomId : me.roomId,
transportId: me.recvTransport.id
}));
const { iceParameters } = response.body;
await me.recvTransport.restartIce({ iceParameters });
}
}
/**
* 消费媒体信令
*
@@ -2024,233 +2001,6 @@ class Taoyao extends RemoteClient {
}
}
/**
* 媒体回调
*
* @param {*} clientId 终端ID
* @param {*} track 媒体轨道
*/
callbackTrack(clientId, track) {
const me = this;
const callbackMessage = protocol.buildMessage("media::track", {
clientId,
track,
});
callbackMessage.code = SUCCESS_CODE;
callbackMessage.message = SUCCESS_MESSAGE;
me.callback(callbackMessage);
}
/**
* 生产媒体
*
* 如果需要加密producer.rtpSender
* const senderStreams = sender.createEncodedStreams();
* const readableStream = senderStreams.readable || senderStreams.readableStream;
* const writableStream = senderStreams.writable || senderStreams.writableStream;
*
* @returns 所有生产者
*/
async mediaProduce(audioTrack, videoTrack) {
const me = this;
if(!audioTrack || !videoTrack) {
await me.checkDevice();
}
await me.createSendTransport();
await me.createRecvTransport();
await me.produceAudio(audioTrack);
await me.produceVideo(videoTrack);
await me.produceData();
return {
dataProducer : me.dataProducer,
audioProducer: me.audioProducer,
videoProducer: me.videoProducer,
}
}
/**
* 生产音频
*
* @param {*} audioTrack 音频轨道(可以为空自动)
*/
async produceAudio(audioTrack) {
const me = this;
if (me.audioProducer) {
console.debug("已经存在音频生产者");
return;
}
if (
!me.audioProduce ||
!me.mediasoupDevice.canProduce("audio")
) {
console.debug("不能生产音频数媒");
return;
}
const track = audioTrack || await me.getAudioTrack();
const codecOptions = {
opusDtx : true,
opusFec : true,
opusNack : true,
opusStereo : true,
};
me.audioTrack = track;
me.audioProducer = await me.sendTransport.produce({
track,
codecOptions,
appData: {
videoSource: me.videoSource
},
});
me.callbackTrack(me.clientId, track);
if (me.proxy && me.proxy.media) {
me.proxy.media(track, me.audioProducer);
} else {
console.warn("终端没有实现服务代理");
}
me.audioProducer.on("transportclose", () => {
console.debug("关闭音频生产者(通道关闭)", me.audioProducer.id);
me.audioProducer.close();
});
me.audioProducer.on("trackended", () => {
console.debug("关闭音频生产者(媒体结束)", me.audioProducer.id);
me.audioProducer.close();
});
me.audioProducer.observer.on("close", () => {
console.debug("关闭音频生产者", me.audioProducer.id);
me.audioProducer = null;
});
}
/**
* 关闭音频生产者
*/
async closeAudioProducer() {
this.mediaProducerClose(this.audioProducer?.id);
}
/**
* 暂停音频生产者
*/
async pauseAudioProducer() {
this.mediaProducerPause(this.audioProducer?.id);
}
/**
* 恢复音频生产者
*/
async resumeAudioProducer() {
this.mediaProducerResume(this.audioProducer?.id);
}
/**
* 生产视频
*
* @param {*} videoTrack 音频轨道(可以为空自动)
*/
async produceVideo(videoTrack) {
const me = this;
if (me.videoProducer) {
console.debug("已经存在视频生产者");
return;
}
if (
!me.videoProduce ||
!me.mediasoupDevice.canProduce("video")
) {
console.debug("不能生产视频媒体");
}
const track = videoTrack || await me.getVideoTrack();
const codecOptions = {
videoGoogleStartBitrate: 400,
videoGoogleMinBitrate : 800,
videoGoogleMaxBitrate : 1600,
};
let codec;
if(me.forceVP8) {
codec = me.mediasoupDevice.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === "video/vp8");
if (codec) {
console.debug("强制使用VP8视频编码");
} else {
console.debug("不支持VP8视频编码");
}
} else if (me.forceVP9) {
codec = me.mediasoupDevice.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === "video/vp9");
if (codec) {
console.debug("强制使用VP9视频编码");
} else {
console.debug("不支持VP9视频编码");
}
} else if (me.forceH264) {
codec = me.mediasoupDevice.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === "video/h264");
if (codec) {
console.debug("强制使用H264视频编码");
} else {
console.debug("不支持H264视频编码");
}
}
let encodings;
if (me.useLayers) {
const priorityVideoCodec = me.mediasoupDevice.rtpCapabilities.codecs.find((c) => c.kind === "video");
if (
(me.forceVP9 && codec) ||
priorityVideoCodec.mimeType.toLowerCase() === "video/vp9"
) {
encodings = defaultSvcEncodings;
} else {
encodings = defaultSimulcastEncodings;
}
}
me.videoTrack = track;
me.videoProducer = await me.sendTransport.produce({
codec,
track,
encodings,
codecOptions,
appData: {
videoSource: me.videoSource
},
});
me.callbackTrack(me.clientId, track);
if (me.proxy && me.proxy.media) {
me.proxy.media(track, me.videoProducer);
} else {
console.warn("终端没有实现服务代理");
}
me.videoProducer.on("transportclose", () => {
console.debug("关闭视频生产者(通道关闭)", me.videoProducer.id);
me.videoProducer.close();
});
me.videoProducer.on("trackended", () => {
console.debug("关闭视频生产者(媒体结束)", me.videoProducer.id);
me.videoProducer.close();
});
me.videoProducer.observer.on("close", () => {
console.debug("关闭视频生产者", me.videoProducer.id);
me.videoProducer = null;
});
}
/**
* 关闭视频生产者
*/
async closeVideoProducer() {
this.mediaProducerClose(this.videoProducer?.id);
}
/**
* 暂停视频生产者
*/
async pauseVideoProducer() {
this.mediaProducerPause(this.videoProducer?.id);
}
/**
* 恢复视频生产者
*/
async resumeVideoProducer() {
this.mediaProducerResume(this.videoProducer?.id);
}
/**
* 生产数据
*/
@@ -2290,13 +2040,6 @@ class Taoyao extends RemoteClient {
// me.dataProducer.on("sctpsendbufferfull", fn());
}
/**
* 关闭数据生产者
*/
async closeDataProducer() {
this.mediaDataProducerClose(this.dataProducer?.id);
}
/**
* 通过数据生产者发送数据
*
@@ -2306,29 +2049,276 @@ class Taoyao extends RemoteClient {
this.dataProducer?.send(data);
}
/**
* 关闭数据生产者
*/
async closeDataProducer() {
this.mediaDataProducerClose(this.dataProducer?.id);
}
/**
* 重启ICE信令
*/
async mediaIceRestart() {
if (this.sendTransport) {
const response = await this.request(protocol.buildMessage("media::ice::restart", {
roomId : this.roomId,
transportId: this.sendTransport.id
}));
const {
iceParameters
} = response.body;
await this.sendTransport.restartIce({
iceParameters
});
}
if (this.recvTransport) {
const response = await this.request(protocol.buildMessage("media::ice::restart", {
roomId : this.roomId,
transportId: this.recvTransport.id
}));
const {
iceParameters
} = response.body;
await this.recvTransport.restartIce({
iceParameters
});
}
}
/**
* 生产媒体
*
* 如果需要加密producer.rtpSender
* const senderStreams = sender.createEncodedStreams();
* const readableStream = senderStreams.readable || senderStreams.readableStream;
* const writableStream = senderStreams.writable || senderStreams.writableStream;
*
* @returns 所有生产者
*/
async mediaProduce(audioTrack, videoTrack) {
if(!audioTrack || !videoTrack) {
await this.checkDevice();
}
if(!this.sendTransport) {
await this.createSendTransport();
}
if(!this.recvTransport) {
await this.createRecvTransport();
}
await this.produceAudio(audioTrack);
await this.produceVideo(videoTrack);
await this.produceData();
return {
dataProducer : this.dataProducer,
audioProducer: this.audioProducer,
videoProducer: this.videoProducer,
}
}
/**
* 生产音频
*
* @param {*} audioTrack 音频轨道(可以为空自动)
*/
async produceAudio(audioTrack) {
if (this.audioProducer) {
console.debug("已经存在音频生产者");
return;
}
if (
!this.audioProduce ||
!this.mediasoupDevice.canProduce("audio")
) {
console.debug("不能生产音频数媒");
return;
}
const codecOptions = {
opusDtx : true,
opusFec : true,
opusNack : true,
opusStereo : true,
};
const track = audioTrack || await this.getAudioTrack();
this.audioTrack = track;
this.audioProducer = await this.sendTransport.produce({
track,
codecOptions,
appData: {
videoSource: this.videoSource
},
});
this.callbackTrack(this.clientId, track);
if (this.proxy && this.proxy.media) {
this.proxy.media(track, this.audioProducer);
} else {
console.warn("终端没有实现服务代理");
}
this.audioProducer.on("transportclose", () => {
console.debug("关闭音频生产者(通道关闭)", this.audioProducer.id);
this.audioProducer.close();
});
this.audioProducer.on("trackended", () => {
console.debug("关闭音频生产者(媒体结束)", this.audioProducer.id);
this.audioProducer.close();
});
this.audioProducer.observer.on("close", () => {
console.debug("关闭音频生产者", this.audioProducer.id);
this.audioProducer = null;
});
}
/**
* 关闭音频生产者
*/
async closeAudioProducer() {
this.mediaProducerClose(this.audioProducer?.id);
}
/**
* 暂停音频生产者
*/
async pauseAudioProducer() {
this.mediaProducerPause(this.audioProducer?.id);
}
/**
* 恢复音频生产者
*/
async resumeAudioProducer() {
this.mediaProducerResume(this.audioProducer?.id);
}
/**
* 生产视频
*
* @param {*} videoTrack 音频轨道(可以为空自动)
*/
async produceVideo(videoTrack) {
if (this.videoProducer) {
console.debug("已经存在视频生产者");
return;
}
if (
!this.videoProduce ||
!this.mediasoupDevice.canProduce("video")
) {
console.debug("不能生产视频媒体");
return;
}
const codecOptions = {
videoGoogleStartBitrate: 400,
videoGoogleMinBitrate : 800,
videoGoogleMaxBitrate : 1600,
};
let codec;
if(this.forceVP8) {
codec = this.mediasoupDevice.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === "video/vp8");
if (codec) {
console.debug("强制使用VP8视频编码");
} else {
console.debug("不支持VP8视频编码");
}
}
if (this.forceVP9) {
codec = this.mediasoupDevice.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === "video/vp9");
if (codec) {
console.debug("强制使用VP9视频编码");
} else {
console.debug("不支持VP9视频编码");
}
}
if (this.forceH264) {
codec = this.mediasoupDevice.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === "video/h264");
if (codec) {
console.debug("强制使用H264视频编码");
} else {
console.debug("不支持H264视频编码");
}
}
let encodings;
if (this.useLayers) {
const priorityVideoCodec = this.mediasoupDevice.rtpCapabilities.codecs.find((c) => c.kind === "video");
if ((this.forceVP9 && codec) || priorityVideoCodec.mimeType.toLowerCase() === "video/vp9") {
encodings = defaultSvcEncodings;
} else {
encodings = defaultSimulcastEncodings;
}
}
const track = videoTrack || await this.getVideoTrack();
this.videoTrack = track;
this.videoProducer = await this.sendTransport.produce({
codec,
track,
encodings,
codecOptions,
appData: {
videoSource: this.videoSource
},
});
this.callbackTrack(this.clientId, track);
if (this.proxy && this.proxy.media) {
this.proxy.media(track, this.videoProducer);
} else {
console.warn("终端没有实现服务代理");
}
this.videoProducer.on("transportclose", () => {
console.debug("关闭视频生产者(通道关闭)", this.videoProducer.id);
this.videoProducer.close();
});
this.videoProducer.on("trackended", () => {
console.debug("关闭视频生产者(媒体结束)", this.videoProducer.id);
this.videoProducer.close();
});
this.videoProducer.observer.on("close", () => {
console.debug("关闭视频生产者", this.videoProducer.id);
this.videoProducer = null;
});
}
/**
* 关闭视频生产者
*/
async closeVideoProducer() {
this.mediaProducerClose(this.videoProducer?.id);
}
/**
* 暂停视频生产者
*/
async pauseVideoProducer() {
this.mediaProducerPause(this.videoProducer?.id);
}
/**
* 恢复视频生产者
*/
async resumeVideoProducer() {
this.mediaProducerResume(this.videoProducer?.id);
}
/**
* 切换视频来源
*
* @param {*} videoSource 视频来源(可以为空)
*/
async exchangeVideoSource(videoSource) {
const me = this;
console.debug("切换视频来源", videoSource, me.videoSource);
console.debug("切换视频来源", videoSource, this.videoSource);
const old = this.videoSource;
if(videoSource) {
me.videoSource = videoSource;
this.videoSource = videoSource;
} else {
if(me.videoSource === "file") {
me.videoSource = "camera";
} else if(me.videoSource === "camera") {
me.videoSource = "screen";
} else if(me.videoSource === "screen") {
me.videoSource = "file";
if(this.videoSource === "file") {
this.videoSource = "camera";
} else if(this.videoSource === "camera") {
this.videoSource = "screen";
} else if(this.videoSource === "screen") {
this.videoSource = "file";
} else {
me.videoSource = "camera";
this.videoSource = "camera";
}
}
await me.updateVideoProducer();
await this.updateVideoProducer();
if(old === "file") {
this.closeFileVideo();
}
@@ -2340,20 +2330,22 @@ class Taoyao extends RemoteClient {
* @param {*} 更新视频轨道
*/
async updateVideoProducer(videoTrack) {
const me = this;
const track = videoTrack || await me.getVideoTrack();
await me.videoProducer.replaceTrack({
const track = videoTrack || await this.getVideoTrack();
await this.videoProducer.replaceTrack({
track
});
me.callbackTrack(me.clientId, track);
me.proxy.media(track, me.videoProducer);
this.callbackTrack(this.clientId, track);
if (this.proxy && this.proxy.media) {
this.proxy.media(track, this.videoProducer);
} else {
console.warn("终端没有实现服务代理");
}
}
/**
* 验证设备
*/
async checkDevice() {
const me = this;
if (
navigator.mediaDevices &&
navigator.mediaDevices.getUserMedia &&
@@ -2375,18 +2367,18 @@ class Taoyao extends RemoteClient {
break;
}
});
if (!audioEnabled && me.audioProduce) {
me.platformError("没有音频媒体设备");
if (!audioEnabled && this.audioProduce) {
this.platformError("没有音频媒体设备");
// 强制修改
me.audioProduce = false;
this.audioProduce = false;
}
if (!videoEnabled && me.videoProduce) {
me.platformError("没有视频媒体设备");
if (!videoEnabled && this.videoProduce) {
this.platformError("没有视频媒体设备");
// 强制修改
me.videoProduce = false;
this.videoProduce = false;
}
} else {
me.platformError("没有媒体权限");
this.platformError("没有媒体权限");
}
}
@@ -3601,6 +3593,22 @@ class Taoyao extends RemoteClient {
await track.applyConstraints(Object.assign(track.getSettings(), setting));
}
/**
* 媒体回调
*
* @param {*} clientId 终端ID
* @param {*} track 媒体轨道
*/
callbackTrack(clientId, track) {
const callbackMessage = protocol.buildMessage("media::track", {
track,
clientId,
});
callbackMessage.code = SUCCESS_CODE;
callbackMessage.message = SUCCESS_MESSAGE;
this.callback(callbackMessage);
}
/**
* 统计设备信息
*
@@ -3609,15 +3617,14 @@ class Taoyao extends RemoteClient {
* @returns 设备信息统计
*/
async getClientStats(clientId) {
const me = this;
const stats = {};
if(clientId === me.clientId) {
stats.sendTransport = await me.sendTransport.getStats();
stats.recvTransport = await me.recvTransport.getStats();
stats.audioProducer = await me.audioProducer.getStats();
stats.videoProducer = await me.videoProducer.getStats();
if(clientId === this.clientId) {
stats.sendTransport = await this.sendTransport.getStats();
stats.recvTransport = await this.recvTransport.getStats();
stats.audioProducer = await this.audioProducer.getStats();
stats.videoProducer = await this.videoProducer.getStats();
} else {
const consumers = Array.from(me.consumers.values());
const consumers = Array.from(this.consumers.values());
for(const consumer of consumers) {
if(clientId === consumer.sourceId) {
stats[consumer.kind] = await consumer.getStats();
@@ -3663,42 +3670,44 @@ class Taoyao extends RemoteClient {
*/
async closeRoomMedia() {
console.debug("关闭视频房间媒体");
const me = this;
me.roomId = null;
await me.close();
if (me.sendTransport) {
await me.sendTransport.close();
me.sendTransport = null;
}
if (me.recvTransport) {
await me.recvTransport.close();
me.recvTransport = null;
}
if(me.dataProducer) {
await me.dataProducer.close();
me.dataProducer = null;
}
if(me.audioProducer) {
await me.audioProducer.close();
me.audioProducer = null;
}
if(me.videoProducer) {
await me.videoProducer.close();
me.videoProducer = null;
}
me.consumers.forEach(async (consumer, consumerId) => {
this.roomId = null;
await this.close();
this.consumers.forEach(async (consumer, consumerId) => {
await consumer.close();
});
me.consumers.clear();
me.dataConsumers.forEach(async (dataConsumer, consumerId) => {
this.consumers.clear();
this.dataConsumers.forEach(async (dataConsumer, consumerId) => {
await dataConsumer.close();
});
me.dataConsumers.clear();
me.remoteClients.forEach(async (client, clientId) => {
await client.close();
this.dataConsumers.clear();
this.remoteClients.forEach(async (remoteClient, clientId) => {
await remoteClient.close();
});
me.remoteClients.clear();
me.closeFileVideo();
this.remoteClients.clear();
if(this.audioProducer) {
await this.audioProducer.close();
this.audioProducer = null;
}
if(this.videoProducer) {
await this.videoProducer.close();
this.videoProducer = null;
}
if(this.dataProducer) {
await this.dataProducer.close();
this.dataProducer = null;
}
if (this.sendTransport) {
await this.sendTransport.close();
this.sendTransport = null;
}
if (this.recvTransport) {
await this.recvTransport.close();
this.recvTransport = null;
}
if(this.mediasoupDevice) {
this.mediasoupDevice = null;
}
this.closeFileVideo();
}
/**
@@ -3706,26 +3715,24 @@ class Taoyao extends RemoteClient {
*/
closeSessionMedia() {
console.debug("关闭视频会话媒体");
const me = this;
me.sessionClients.forEach((session, sessionId) => {
this.sessionClients.forEach((session, sessionId) => {
session.close();
console.debug("关闭会话", sessionId);
});
me.sessionClients.clear();
me.closeFileVideo();
this.sessionClients.clear();
this.closeFileVideo();
}
/**
* 关闭资源
*/
closeAll() {
const me = this;
if(me.closed) {
if(this.closed) {
return;
}
me.closed = true;
me.closeRoomMedia();
me.closeSessionMedia();
this.closed = true;
this.closeRoomMedia();
this.closeSessionMedia();
signalChannel.close();
}
}