From 69be5ada9bc585ee70d6f6f9140c18ebe1f1c200 Mon Sep 17 00:00:00 2001 From: acgist <289547414@qq.com> Date: Wed, 11 Oct 2023 08:00:08 +0800 Subject: [PATCH] =?UTF-8?q?[*]=20=E6=97=A5=E5=B8=B8=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- taoyao-client-media/src/Taoyao.js | 60 ++++++++-------- taoyao-client-web/src/components/Taoyao.js | 11 ++- .../control/ControlServerRecordProtocol.java | 70 +++++++++---------- 3 files changed, 71 insertions(+), 70 deletions(-) diff --git a/taoyao-client-media/src/Taoyao.js b/taoyao-client-media/src/Taoyao.js index c5d2c2d..acab645 100644 --- a/taoyao-client-media/src/Taoyao.js +++ b/taoyao-client-media/src/Taoyao.js @@ -606,34 +606,35 @@ class Taoyao { /** * 服务端录像信令 * - * @param {*} message 消息 + * @param {*} message 信令消息 * @param {*} body 消息主体 */ async controlServerRecord(message, body) { - const me = this; - const { enabled, roomId } = body; - const room = me.rooms.get(roomId); + const { + roomId, + enabled, + } = body; + const room = this.rooms.get(roomId); if(!room) { // 直接关闭房间时,房间关闭可能早于结束录像。 - console.info("服务端录像房间无效", roomId); + console.debug("服务端录像房间无效", roomId); return; } if(enabled) { - await me.controlServerRecordStart(message, body, room); + await this.controlServerRecordStart(message, body, room); } else { - await me.controlServerRecordStop(message, body, room); + await this.controlServerRecordStop(message, body, room); } } /** * 开始服务端录像 * - * @param {*} message 消息 + * @param {*} message 信令消息 * @param {*} body 消息主体 * @param {*} room 房间 */ async controlServerRecordStart(message, body, room) { - const me = this; const { host, roomId, @@ -665,7 +666,7 @@ class Taoyao { const audioTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions); audioTransportId = audioTransport.id; room.transports.set(audioTransportId, audioTransport); - me.transportEvent("plain", roomId, audioTransport); + this.transportEvent("plain", roomId, audioTransport); audioTransport.clientId = clientId; await audioTransport.connect({ ip : host, @@ -683,16 +684,16 @@ class Taoyao { audioConsumer.streamId = audioStreamId; room.consumers.set(audioConsumerId, audioConsumer); audioConsumer.observer.on("close", () => { - console.info("关闭服务端录像音频消费者", audioConsumerId); + console.debug("关闭服务端录像音频消费者", audioConsumerId); room.consumers.delete(audioConsumerId); }); - console.debug("创建服务器录像音频消费者", audioTransportId, audioConsumerId, audioTransport.tuple, audioRtpParameters); + console.debug("创建服务器录像音频消费者", audioTransportId, audioConsumerId, audioTransport.tuple, audioRtpParameters.codecs); } if(videoProducerId) { const videoTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions); videoTransportId = videoTransport.id; room.transports.set(videoTransportId, videoTransport); - me.transportEvent("plain", roomId, videoTransport); + this.transportEvent("plain", roomId, videoTransport); videoTransport.clientId = clientId; await videoTransport.connect({ ip : host, @@ -710,18 +711,22 @@ class Taoyao { videoConsumer.streamId = videoStreamId; room.consumers.set(videoConsumerId, videoConsumer); videoConsumer.observer.on("close", () => { - console.info("关闭服务器录像视频消费者", videoConsumerId); + console.debug("关闭服务器录像视频消费者", videoConsumerId); room.consumers.delete(videoConsumerId); }); - console.debug("创建服务器录像视频消费者", videoTransportId, videoConsumerId, videoTransport.tuple, videoRtpParameters); + console.debug("创建服务器录像视频消费者", videoTransportId, videoConsumerId, videoTransport.tuple, videoRtpParameters.codecs); } if(audioConsumer) { await audioConsumer.resume(); } if(videoConsumer) { await videoConsumer.resume(); + } + try { // 请求录像关键帧 - me.requestKeyFrameForRecord(0, filepath, videoConsumer); + this.requestKeyFrameForRecord(0, filepath, videoConsumer); + } catch (error) { + console.error("请求录像关键帧异常", error); } message.body = { roomId : roomId, @@ -732,7 +737,7 @@ class Taoyao { audioRtpParameters: audioRtpParameters, videoRtpParameters: videoRtpParameters, }; - me.push(message); + this.push(message); } /** @@ -748,35 +753,34 @@ class Taoyao { requestKeyFrameMaxIndex, requestKeyFrameFileSize } = config.record; - if(++index > requestKeyFrameMaxIndex) { + if(index >= requestKeyFrameMaxIndex) { console.warn("请求录像关键帧次数超限", filepath, index); return; } if(videoConsumer.closed) { - console.warn("请求录像关键帧视频关闭", filepath); + console.warn("请求录像关键帧视频关闭", filepath, index); return; } // 判断文件大小验证是否已经开始录像:创建文件 -> 视频信息 -> 视频数据 -> 封装视频 if(fs.existsSync(filepath) && fs.statSync(filepath).size >= requestKeyFrameFileSize) { - console.info("请求录像关键帧已经开始录像", filepath); + console.info("请求录像关键帧已经开始录像", filepath, index); return; } console.debug("请求录像关键帧", filepath, index); videoConsumer.requestKeyFrame(); setTimeout(() => { - this.requestKeyFrameForRecord(index, filepath, videoConsumer); + this.requestKeyFrameForRecord(++index, filepath, videoConsumer); }, 1000); } /** * 结束服务端录像 * - * @param {*} message 消息 + * @param {*} message 信令消息 * @param {*} body 消息主体 * @param {*} room 房间 */ async controlServerRecordStop(message, body, room) { - const me = this; const { audioStreamId, videoStreamId, audioConsumerId, videoConsumerId, @@ -785,21 +789,21 @@ class Taoyao { console.info("结束服务端录像", audioStreamId, videoStreamId); const audioConsumer = room.consumers.get(audioConsumerId); if(audioConsumer) { - audioConsumer.close(); + await audioConsumer.close(); } const videoConsumer = room.consumers.get(videoConsumerId); if(videoConsumer) { - videoConsumer.close(); + await videoConsumer.close(); } const audioTransport = room.transports.get(audioTransportId); if(audioTransport) { - audioTransport.close(); + await audioTransport.close(); } const videoTransport = room.transports.get(videoTransportId); if(videoTransport) { - videoTransport.close(); + await videoTransport.close(); } - me.push(message); + this.push(message); } /** diff --git a/taoyao-client-web/src/components/Taoyao.js b/taoyao-client-web/src/components/Taoyao.js index 839d8d8..d2a7e49 100644 --- a/taoyao-client-web/src/components/Taoyao.js +++ b/taoyao-client-web/src/components/Taoyao.js @@ -1460,12 +1460,11 @@ class Taoyao extends RemoteClient { * @param {*} clientId 终端ID * @param {*} enabled 录制状态 */ - controlServerRecord(clientId, enabled) { - const me = this; - me.request(protocol.buildMessage("control::server::record", { - to : clientId, - roomId : me.roomId, - enabled: enabled + async controlServerRecord(clientId, enabled) { + return await this.request(protocol.buildMessage("control::server::record", { + enabled, + to : clientId, + roomId: this.roomId, })); } diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlServerRecordProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlServerRecordProtocol.java index 96adce9..6a073a8 100644 --- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlServerRecordProtocol.java +++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlServerRecordProtocol.java @@ -2,7 +2,6 @@ package com.acgist.taoyao.signal.protocol.control; import java.util.HashMap; import java.util.Map; -import java.util.UUID; import org.springframework.context.ApplicationListener; import org.springframework.scheduling.annotation.Async; @@ -70,15 +69,15 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen @Override public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map body) { 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 Room room = this.roomManager.getRoom(roomId); + final Room room = this.roomManager.getRoom(roomId); if(enabled) { filepath = this.start(room, room.clientWrapper(targetClient)); } else { filepath = this.stop(room, room.clientWrapper(targetClient)); } - body.put(Constant.FILEPATH, filepath); + body.put(Constant.FILEPATH, filepath); body.put(Constant.CLIENT_ID, clientId); client.push(message); } @@ -86,7 +85,7 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen @Override public Message execute(String roomId, String clientId, Boolean enabled) { String filepath; - final Room room = this.roomManager.getRoom(roomId); + final Room room = this.roomManager.getRoom(roomId); final Client client = this.clientManager.getClients(clientId); if(enabled) { filepath = this.start(room, room.clientWrapper(client)); @@ -106,7 +105,6 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen * * @param room 房间 * @param clientWrapper 终端 - * @param mediaClient 媒体终端 * * @return 文件地址 */ @@ -114,38 +112,38 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen if(clientWrapper == null) { throw MessageCodeException.of("终端没有进入房间"); } + final Recorder recorder; synchronized (clientWrapper) { - final Recorder recorder = clientWrapper.getRecorder(); - if(recorder != null) { - return recorder.getFilepath(); + final Recorder oldRecorder = clientWrapper.getRecorder(); + if(oldRecorder != null) { + 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 Map body = new HashMap<>(); - body.put(Constant.HOST, this.ffmpegProperties.getHost()); - body.put(Constant.ROOM_ID, room.getRoomId()); - body.put(Constant.ENABLED, true); - body.put(Constant.FILEPATH, recorder.getFilepath()); - body.put(Constant.CLIENT_ID, clientWrapper.getClientId()); - body.put(Constant.AUDIO_PORT, recorder.getAudioPort()); - body.put(Constant.VIDEO_PORT, recorder.getVideoPort()); - body.put(Constant.AUDIO_RTCP_PORT, recorder.getAudioRtcpPort()); - body.put(Constant.VIDEO_RTCP_PORT, recorder.getVideoRtcpPort()); + body.put(Constant.HOST, this.ffmpegProperties.getHost()); + body.put(Constant.ROOM_ID, room.getRoomId()); + body.put(Constant.ENABLED, true); + body.put(Constant.FILEPATH, recorder.getFilepath()); + body.put(Constant.CLIENT_ID, clientWrapper.getClientId()); + body.put(Constant.AUDIO_PORT, recorder.getAudioPort()); + body.put(Constant.VIDEO_PORT, recorder.getVideoPort()); + body.put(Constant.AUDIO_RTCP_PORT, recorder.getAudioRtcpPort()); + body.put(Constant.VIDEO_RTCP_PORT, recorder.getVideoRtcpPort()); body.put(Constant.RTP_CAPABILITIES, clientWrapper.getRtpCapabilities()); clientWrapper.getProducers().values().forEach(producer -> { if(producer.getKind() == Kind.AUDIO) { 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()); } else if(producer.getKind() == Kind.VIDEO) { - recorder.setAudioStreamId(Constant.STREAM_ID_CONSUMER.apply(producer.getStreamId(), clientWrapper.getClientId())); - body.put(Constant.VIDEO_STREAM_ID, recorder.getVideoStreamId()); + recorder.setVideoStreamId(Constant.STREAM_ID_CONSUMER.apply(producer.getStreamId(), clientWrapper.getClientId())); + body.put(Constant.VIDEO_STREAM_ID, recorder.getVideoStreamId()); body.put(Constant.VIDEO_PRODUCER_ID, producer.getProducerId()); } else { // 忽略 @@ -153,7 +151,7 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen }); message.setBody(body); final Client mediaClient = room.getMediaClient(); - final Message response = mediaClient.request(message); + final Message response = mediaClient.request(message); final Map responseBody = response.body(); recorder.setAudioConsumerId(responseBody.get(Constant.AUDIO_CONSUMER_ID)); recorder.setVideoConsumerId(responseBody.get(Constant.VIDEO_CONSUMER_ID)); @@ -177,19 +175,19 @@ public class ControlServerRecordProtocol extends ProtocolControlAdapter implemen if(recorder == null) { return null; } + // 关闭录像线程 + recorder.stop(); + clientWrapper.setRecorder(null); } - // 关闭录像线程 - recorder.stop(); - clientWrapper.setRecorder(null); // 关闭媒体录像 final Message message = this.build(); final Map body = new HashMap<>(); - body.put(Constant.ROOM_ID, room.getRoomId()); - body.put(Constant.ENABLED, false); - body.put(Constant.AUDIO_STREAM_ID, recorder.getAudioStreamId()); - body.put(Constant.VIDEO_STREAM_ID, recorder.getVideoStreamId()); - body.put(Constant.AUDIO_CONSUMER_ID, recorder.getAudioConsumerId()); - body.put(Constant.VIDEO_CONSUMER_ID, recorder.getVideoConsumerId()); + body.put(Constant.ROOM_ID, room.getRoomId()); + body.put(Constant.ENABLED, false); + body.put(Constant.AUDIO_STREAM_ID, recorder.getAudioStreamId()); + body.put(Constant.VIDEO_STREAM_ID, recorder.getVideoStreamId()); + body.put(Constant.AUDIO_CONSUMER_ID, recorder.getAudioConsumerId()); + body.put(Constant.VIDEO_CONSUMER_ID, recorder.getVideoConsumerId()); body.put(Constant.AUDIO_TRANSPORT_ID, recorder.getAudioTransportId()); body.put(Constant.VIDEO_TRANSPORT_ID, recorder.getVideoTransportId()); message.setBody(body);