[*] 日常优化

This commit is contained in:
acgist
2023-10-11 08:00:08 +08:00
parent f86faee977
commit 69be5ada9b
3 changed files with 71 additions and 70 deletions

View File

@@ -606,34 +606,35 @@ class Taoyao {
/** /**
* 服务端录像信令 * 服务端录像信令
* *
* @param {*} message 消息 * @param {*} message 信令消息
* @param {*} body 消息主体 * @param {*} body 消息主体
*/ */
async controlServerRecord(message, body) { async controlServerRecord(message, body) {
const me = this; const {
const { enabled, roomId } = body; roomId,
const room = me.rooms.get(roomId); enabled,
} = body;
const room = this.rooms.get(roomId);
if(!room) { if(!room) {
// 直接关闭房间时,房间关闭可能早于结束录像。 // 直接关闭房间时,房间关闭可能早于结束录像。
console.info("服务端录像房间无效", roomId); console.debug("服务端录像房间无效", roomId);
return; return;
} }
if(enabled) { if(enabled) {
await me.controlServerRecordStart(message, body, room); await this.controlServerRecordStart(message, body, room);
} else { } else {
await me.controlServerRecordStop(message, body, room); await this.controlServerRecordStop(message, body, room);
} }
} }
/** /**
* 开始服务端录像 * 开始服务端录像
* *
* @param {*} message 消息 * @param {*} message 信令消息
* @param {*} body 消息主体 * @param {*} body 消息主体
* @param {*} room 房间 * @param {*} room 房间
*/ */
async controlServerRecordStart(message, body, room) { async controlServerRecordStart(message, body, room) {
const me = this;
const { const {
host, host,
roomId, roomId,
@@ -665,7 +666,7 @@ class Taoyao {
const audioTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions); const audioTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions);
audioTransportId = audioTransport.id; audioTransportId = audioTransport.id;
room.transports.set(audioTransportId, audioTransport); room.transports.set(audioTransportId, audioTransport);
me.transportEvent("plain", roomId, audioTransport); this.transportEvent("plain", roomId, audioTransport);
audioTransport.clientId = clientId; audioTransport.clientId = clientId;
await audioTransport.connect({ await audioTransport.connect({
ip : host, ip : host,
@@ -683,16 +684,16 @@ class Taoyao {
audioConsumer.streamId = audioStreamId; audioConsumer.streamId = audioStreamId;
room.consumers.set(audioConsumerId, audioConsumer); room.consumers.set(audioConsumerId, audioConsumer);
audioConsumer.observer.on("close", () => { audioConsumer.observer.on("close", () => {
console.info("关闭服务端录像音频消费者", audioConsumerId); console.debug("关闭服务端录像音频消费者", audioConsumerId);
room.consumers.delete(audioConsumerId); room.consumers.delete(audioConsumerId);
}); });
console.debug("创建服务器录像音频消费者", audioTransportId, audioConsumerId, audioTransport.tuple, audioRtpParameters); console.debug("创建服务器录像音频消费者", audioTransportId, audioConsumerId, audioTransport.tuple, audioRtpParameters.codecs);
} }
if(videoProducerId) { if(videoProducerId) {
const videoTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions); const videoTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions);
videoTransportId = videoTransport.id; videoTransportId = videoTransport.id;
room.transports.set(videoTransportId, videoTransport); room.transports.set(videoTransportId, videoTransport);
me.transportEvent("plain", roomId, videoTransport); this.transportEvent("plain", roomId, videoTransport);
videoTransport.clientId = clientId; videoTransport.clientId = clientId;
await videoTransport.connect({ await videoTransport.connect({
ip : host, ip : host,
@@ -710,18 +711,22 @@ class Taoyao {
videoConsumer.streamId = videoStreamId; videoConsumer.streamId = videoStreamId;
room.consumers.set(videoConsumerId, videoConsumer); room.consumers.set(videoConsumerId, videoConsumer);
videoConsumer.observer.on("close", () => { videoConsumer.observer.on("close", () => {
console.info("关闭服务器录像视频消费者", videoConsumerId); console.debug("关闭服务器录像视频消费者", videoConsumerId);
room.consumers.delete(videoConsumerId); room.consumers.delete(videoConsumerId);
}); });
console.debug("创建服务器录像视频消费者", videoTransportId, videoConsumerId, videoTransport.tuple, videoRtpParameters); console.debug("创建服务器录像视频消费者", videoTransportId, videoConsumerId, videoTransport.tuple, videoRtpParameters.codecs);
} }
if(audioConsumer) { if(audioConsumer) {
await audioConsumer.resume(); await audioConsumer.resume();
} }
if(videoConsumer) { if(videoConsumer) {
await videoConsumer.resume(); await videoConsumer.resume();
}
try {
// 请求录像关键帧 // 请求录像关键帧
me.requestKeyFrameForRecord(0, filepath, videoConsumer); this.requestKeyFrameForRecord(0, filepath, videoConsumer);
} catch (error) {
console.error("请求录像关键帧异常", error);
} }
message.body = { message.body = {
roomId : roomId, roomId : roomId,
@@ -732,7 +737,7 @@ class Taoyao {
audioRtpParameters: audioRtpParameters, audioRtpParameters: audioRtpParameters,
videoRtpParameters: videoRtpParameters, videoRtpParameters: videoRtpParameters,
}; };
me.push(message); this.push(message);
} }
/** /**
@@ -748,35 +753,34 @@ class Taoyao {
requestKeyFrameMaxIndex, requestKeyFrameMaxIndex,
requestKeyFrameFileSize requestKeyFrameFileSize
} = config.record; } = config.record;
if(++index > requestKeyFrameMaxIndex) { if(index >= requestKeyFrameMaxIndex) {
console.warn("请求录像关键帧次数超限", filepath, index); console.warn("请求录像关键帧次数超限", filepath, index);
return; return;
} }
if(videoConsumer.closed) { if(videoConsumer.closed) {
console.warn("请求录像关键帧视频关闭", filepath); console.warn("请求录像关键帧视频关闭", filepath, index);
return; return;
} }
// 判断文件大小验证是否已经开始录像:创建文件 -> 视频信息 -> 视频数据 -> 封装视频 // 判断文件大小验证是否已经开始录像:创建文件 -> 视频信息 -> 视频数据 -> 封装视频
if(fs.existsSync(filepath) && fs.statSync(filepath).size >= requestKeyFrameFileSize) { if(fs.existsSync(filepath) && fs.statSync(filepath).size >= requestKeyFrameFileSize) {
console.info("请求录像关键帧已经开始录像", filepath); console.info("请求录像关键帧已经开始录像", filepath, index);
return; return;
} }
console.debug("请求录像关键帧", filepath, index); console.debug("请求录像关键帧", filepath, index);
videoConsumer.requestKeyFrame(); videoConsumer.requestKeyFrame();
setTimeout(() => { setTimeout(() => {
this.requestKeyFrameForRecord(index, filepath, videoConsumer); this.requestKeyFrameForRecord(++index, filepath, videoConsumer);
}, 1000); }, 1000);
} }
/** /**
* 结束服务端录像 * 结束服务端录像
* *
* @param {*} message 消息 * @param {*} message 信令消息
* @param {*} body 消息主体 * @param {*} body 消息主体
* @param {*} room 房间 * @param {*} room 房间
*/ */
async controlServerRecordStop(message, body, room) { async controlServerRecordStop(message, body, room) {
const me = this;
const { const {
audioStreamId, videoStreamId, audioStreamId, videoStreamId,
audioConsumerId, videoConsumerId, audioConsumerId, videoConsumerId,
@@ -785,21 +789,21 @@ class Taoyao {
console.info("结束服务端录像", audioStreamId, videoStreamId); console.info("结束服务端录像", audioStreamId, videoStreamId);
const audioConsumer = room.consumers.get(audioConsumerId); const audioConsumer = room.consumers.get(audioConsumerId);
if(audioConsumer) { if(audioConsumer) {
audioConsumer.close(); await audioConsumer.close();
} }
const videoConsumer = room.consumers.get(videoConsumerId); const videoConsumer = room.consumers.get(videoConsumerId);
if(videoConsumer) { if(videoConsumer) {
videoConsumer.close(); await videoConsumer.close();
} }
const audioTransport = room.transports.get(audioTransportId); const audioTransport = room.transports.get(audioTransportId);
if(audioTransport) { if(audioTransport) {
audioTransport.close(); await audioTransport.close();
} }
const videoTransport = room.transports.get(videoTransportId); const videoTransport = room.transports.get(videoTransportId);
if(videoTransport) { if(videoTransport) {
videoTransport.close(); await videoTransport.close();
} }
me.push(message); this.push(message);
} }
/** /**

View File

@@ -1460,12 +1460,11 @@ class Taoyao extends RemoteClient {
* @param {*} clientId 终端ID * @param {*} clientId 终端ID
* @param {*} enabled 录制状态 * @param {*} enabled 录制状态
*/ */
controlServerRecord(clientId, enabled) { async controlServerRecord(clientId, enabled) {
const me = this; return await this.request(protocol.buildMessage("control::server::record", {
me.request(protocol.buildMessage("control::server::record", { enabled,
to : clientId, to : clientId,
roomId : me.roomId, roomId: this.roomId,
enabled: enabled
})); }));
} }

View File

@@ -2,7 +2,6 @@ package com.acgist.taoyao.signal.protocol.control;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
@@ -70,15 +69,15 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen
@Override @Override
public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map<String, Object> body) { public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map<String, Object> body) {
String filepath; String filepath;
final String roomId = MapUtils.get(body, Constant.ROOM_ID); final String roomId = MapUtils.get(body, Constant.ROOM_ID);
final Boolean enabled = MapUtils.get(body, Constant.ENABLED, Boolean.TRUE); final Boolean enabled = MapUtils.get(body, Constant.ENABLED, Boolean.TRUE);
final Room room = this.roomManager.getRoom(roomId); final Room room = this.roomManager.getRoom(roomId);
if(enabled) { if(enabled) {
filepath = this.start(room, room.clientWrapper(targetClient)); filepath = this.start(room, room.clientWrapper(targetClient));
} else { } else {
filepath = this.stop(room, room.clientWrapper(targetClient)); filepath = this.stop(room, room.clientWrapper(targetClient));
} }
body.put(Constant.FILEPATH, filepath); body.put(Constant.FILEPATH, filepath);
body.put(Constant.CLIENT_ID, clientId); body.put(Constant.CLIENT_ID, clientId);
client.push(message); client.push(message);
} }
@@ -86,7 +85,7 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen
@Override @Override
public Message execute(String roomId, String clientId, Boolean enabled) { public Message execute(String roomId, String clientId, Boolean enabled) {
String filepath; String filepath;
final Room room = this.roomManager.getRoom(roomId); final Room room = this.roomManager.getRoom(roomId);
final Client client = this.clientManager.getClients(clientId); final Client client = this.clientManager.getClients(clientId);
if(enabled) { if(enabled) {
filepath = this.start(room, room.clientWrapper(client)); filepath = this.start(room, room.clientWrapper(client));
@@ -106,7 +105,6 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen
* *
* @param room 房间 * @param room 房间
* @param clientWrapper 终端 * @param clientWrapper 终端
* @param mediaClient 媒体终端
* *
* @return 文件地址 * @return 文件地址
*/ */
@@ -114,38 +112,38 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen
if(clientWrapper == null) { if(clientWrapper == null) {
throw MessageCodeException.of("终端没有进入房间"); throw MessageCodeException.of("终端没有进入房间");
} }
final Recorder recorder;
synchronized (clientWrapper) { synchronized (clientWrapper) {
final Recorder recorder = clientWrapper.getRecorder(); final Recorder oldRecorder = clientWrapper.getRecorder();
if(recorder != null) { if(oldRecorder != null) {
return recorder.getFilepath(); return oldRecorder.getFilepath();
} }
// 打开录像线程
recorder = new Recorder(this.idService.buildUuid(), room, clientWrapper, this.ffmpegProperties);
recorder.start();
clientWrapper.setRecorder(recorder);
} }
final String name = UUID.randomUUID().toString();
// 打开录像线程
final Recorder recorder = new Recorder(name, room, clientWrapper, this.ffmpegProperties);
recorder.start();
clientWrapper.setRecorder(recorder);
// 打开媒体录像 // 打开媒体录像
final Message message = this.build(); final Message message = this.build();
final Map<String, Object> body = new HashMap<>(); final Map<String, Object> body = new HashMap<>();
body.put(Constant.HOST, this.ffmpegProperties.getHost()); body.put(Constant.HOST, this.ffmpegProperties.getHost());
body.put(Constant.ROOM_ID, room.getRoomId()); body.put(Constant.ROOM_ID, room.getRoomId());
body.put(Constant.ENABLED, true); body.put(Constant.ENABLED, true);
body.put(Constant.FILEPATH, recorder.getFilepath()); body.put(Constant.FILEPATH, recorder.getFilepath());
body.put(Constant.CLIENT_ID, clientWrapper.getClientId()); body.put(Constant.CLIENT_ID, clientWrapper.getClientId());
body.put(Constant.AUDIO_PORT, recorder.getAudioPort()); body.put(Constant.AUDIO_PORT, recorder.getAudioPort());
body.put(Constant.VIDEO_PORT, recorder.getVideoPort()); body.put(Constant.VIDEO_PORT, recorder.getVideoPort());
body.put(Constant.AUDIO_RTCP_PORT, recorder.getAudioRtcpPort()); body.put(Constant.AUDIO_RTCP_PORT, recorder.getAudioRtcpPort());
body.put(Constant.VIDEO_RTCP_PORT, recorder.getVideoRtcpPort()); body.put(Constant.VIDEO_RTCP_PORT, recorder.getVideoRtcpPort());
body.put(Constant.RTP_CAPABILITIES, clientWrapper.getRtpCapabilities()); body.put(Constant.RTP_CAPABILITIES, clientWrapper.getRtpCapabilities());
clientWrapper.getProducers().values().forEach(producer -> { clientWrapper.getProducers().values().forEach(producer -> {
if(producer.getKind() == Kind.AUDIO) { if(producer.getKind() == Kind.AUDIO) {
recorder.setAudioStreamId(Constant.STREAM_ID_CONSUMER.apply(producer.getStreamId(), clientWrapper.getClientId())); recorder.setAudioStreamId(Constant.STREAM_ID_CONSUMER.apply(producer.getStreamId(), clientWrapper.getClientId()));
body.put(Constant.AUDIO_STREAM_ID, recorder.getAudioStreamId()); body.put(Constant.AUDIO_STREAM_ID, recorder.getAudioStreamId());
body.put(Constant.AUDIO_PRODUCER_ID, producer.getProducerId()); body.put(Constant.AUDIO_PRODUCER_ID, producer.getProducerId());
} else if(producer.getKind() == Kind.VIDEO) { } else if(producer.getKind() == Kind.VIDEO) {
recorder.setAudioStreamId(Constant.STREAM_ID_CONSUMER.apply(producer.getStreamId(), clientWrapper.getClientId())); recorder.setVideoStreamId(Constant.STREAM_ID_CONSUMER.apply(producer.getStreamId(), clientWrapper.getClientId()));
body.put(Constant.VIDEO_STREAM_ID, recorder.getVideoStreamId()); body.put(Constant.VIDEO_STREAM_ID, recorder.getVideoStreamId());
body.put(Constant.VIDEO_PRODUCER_ID, producer.getProducerId()); body.put(Constant.VIDEO_PRODUCER_ID, producer.getProducerId());
} else { } else {
// 忽略 // 忽略
@@ -153,7 +151,7 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen
}); });
message.setBody(body); message.setBody(body);
final Client mediaClient = room.getMediaClient(); final Client mediaClient = room.getMediaClient();
final Message response = mediaClient.request(message); final Message response = mediaClient.request(message);
final Map<String, String> responseBody = response.body(); final Map<String, String> responseBody = response.body();
recorder.setAudioConsumerId(responseBody.get(Constant.AUDIO_CONSUMER_ID)); recorder.setAudioConsumerId(responseBody.get(Constant.AUDIO_CONSUMER_ID));
recorder.setVideoConsumerId(responseBody.get(Constant.VIDEO_CONSUMER_ID)); recorder.setVideoConsumerId(responseBody.get(Constant.VIDEO_CONSUMER_ID));
@@ -177,19 +175,19 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen
if(recorder == null) { if(recorder == null) {
return null; return null;
} }
// 关闭录像线程
recorder.stop();
clientWrapper.setRecorder(null);
} }
// 关闭录像线程
recorder.stop();
clientWrapper.setRecorder(null);
// 关闭媒体录像 // 关闭媒体录像
final Message message = this.build(); final Message message = this.build();
final Map<String, Object> body = new HashMap<>(); final Map<String, Object> body = new HashMap<>();
body.put(Constant.ROOM_ID, room.getRoomId()); body.put(Constant.ROOM_ID, room.getRoomId());
body.put(Constant.ENABLED, false); body.put(Constant.ENABLED, false);
body.put(Constant.AUDIO_STREAM_ID, recorder.getAudioStreamId()); body.put(Constant.AUDIO_STREAM_ID, recorder.getAudioStreamId());
body.put(Constant.VIDEO_STREAM_ID, recorder.getVideoStreamId()); body.put(Constant.VIDEO_STREAM_ID, recorder.getVideoStreamId());
body.put(Constant.AUDIO_CONSUMER_ID, recorder.getAudioConsumerId()); body.put(Constant.AUDIO_CONSUMER_ID, recorder.getAudioConsumerId());
body.put(Constant.VIDEO_CONSUMER_ID, recorder.getVideoConsumerId()); body.put(Constant.VIDEO_CONSUMER_ID, recorder.getVideoConsumerId());
body.put(Constant.AUDIO_TRANSPORT_ID, recorder.getAudioTransportId()); body.put(Constant.AUDIO_TRANSPORT_ID, recorder.getAudioTransportId());
body.put(Constant.VIDEO_TRANSPORT_ID, recorder.getVideoTransportId()); body.put(Constant.VIDEO_TRANSPORT_ID, recorder.getVideoTransportId());
message.setBody(body); message.setBody(body);