[*] 日常优化
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user