From 0908996fb08681819782eb8f82e43c52fd138445 Mon Sep 17 00:00:00 2001 From: acgist <289547414@qq.com> Date: Sun, 11 Jun 2023 08:12:29 +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 --- .../acgist/taoyao/client/signal/Taoyao.java | 3 +- .../client/src/main/res/values/settings.xml | 2 + .../com/acgist/taoyao/media/client/Room.java | 431 +++++++++++++++--- taoyao-client-media/src/Config.js | 7 + taoyao-client-media/src/Server.js | 2 +- taoyao-client-media/src/Taoyao.js | 16 +- 6 files changed, 390 insertions(+), 71 deletions(-) diff --git a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/signal/Taoyao.java b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/signal/Taoyao.java index 035d9fa..a9b0fa6 100644 --- a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/signal/Taoyao.java +++ b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/signal/Taoyao.java @@ -1032,7 +1032,7 @@ public final class Taoyao implements ITaoyao { roomId, key -> new Room( roomId, this.name, - this.clientId, password, + password, this.clientId, this, this.mainHandler, resources.getBoolean(R.bool.preview), resources.getBoolean(R.bool.playAudio), @@ -1043,6 +1043,7 @@ public final class Taoyao implements ITaoyao { resources.getBoolean(R.bool.dataProduce), resources.getBoolean(R.bool.audioProduce), resources.getBoolean(R.bool.videoProduce), + resources.getBoolean(R.bool.roomUseIceServer), this.mediaManager.getMediaProperties(), this.mediaManager.getWebrtcProperties() ) diff --git a/taoyao-client-android/taoyao/client/src/main/res/values/settings.xml b/taoyao-client-android/taoyao/client/src/main/res/values/settings.xml index ce3ea1a..0eeab60 100644 --- a/taoyao-client-android/taoyao/client/src/main/res/values/settings.xml +++ b/taoyao-client-android/taoyao/client/src/main/res/values/settings.xml @@ -40,6 +40,8 @@ 1 1 + + false /taoyao diff --git a/taoyao-client-android/taoyao/media/src/main/java/com/acgist/taoyao/media/client/Room.java b/taoyao-client-android/taoyao/media/src/main/java/com/acgist/taoyao/media/client/Room.java index fbfb99b..36a3bf6 100644 --- a/taoyao-client-android/taoyao/media/src/main/java/com/acgist/taoyao/media/client/Room.java +++ b/taoyao-client-android/taoyao/media/src/main/java/com/acgist/taoyao/media/client/Room.java @@ -33,64 +33,139 @@ import java.util.concurrent.ConcurrentHashMap; */ public class Room extends CloseableClient implements RouterCallback { - private final String name; + /** + * 房间ID + */ private final String roomId; - private final String clientId; + /** + * 房间名称 + */ + private final String name; + /** + * 房间密码 + */ private final String password; + /** + * 当前终端ID + */ + private final String clientId; + /** + * 是否预览 + */ private final boolean preview; + /** + * 是否播放音频 + */ private final boolean playAudio; + /** + * 是否播放视频 + */ private final boolean playVideo; + /** + * 是否消费数据 + */ private final boolean dataConsume; + /** + * 是否消费音频 + */ private final boolean audioConsume; + /** + * 是否消费视频 + */ private final boolean videoConsume; + /** + * 是否生产数据 + */ private final boolean dataProduce; + /** + * 是否生产音频 + */ private final boolean audioProduce; + /** + * 是否生产视频 + */ private final boolean videoProduce; + /** + * 是否使用IceServer + */ + private final boolean useIceServer; + /** + * 是否已经开始生产 + */ private boolean produce; + /** + * 媒体配置 + */ private final MediaProperties mediaProperties; + /** + * WebRTC配置 + */ private final WebrtcProperties webrtcProperties; - private final Map remoteClients; + /** + * 房间指针 + */ private final long nativeRoomPointer; + /** + * 本地终端 + */ private LocalClient localClient; - private PeerConnection.RTCConfiguration rtcConfiguration; - private PeerConnectionFactory peerConnectionFactory; + /** + * 远程终端 + */ + private final Map remoteClients; + /** + * RTC能力 + */ private Object rtpCapabilities; + /** + * SCTP能力 + */ private Object sctpCapabilities; + /** + * RTC配置 + */ + private PeerConnection.RTCConfiguration rtcConfiguration; + /** + * PeerConnectionFactory + */ + private PeerConnectionFactory peerConnectionFactory; /** - * @param roomId 房间ID - * @param name 终端名称 - * @param clientId 当前终端ID - * @param password 房间密码 - * @param taoyao 信令 - * @param mainHandler MainHandler - * @param preview 是否预览视频 - * @param playAudio 是否播放音频 - * @param playVideo 是否播放视频 - * @param dataConsume 是否消费数据 - * @param audioConsume 是否消费音频 - * @param videoConsume 是否消费视频 - * @param dataProduce 是否生产数据 - * @param audioProduce 是否生产音频 - * @param videoProduce 是否生产视频 + * @param roomId 房间ID + * @param name 终端名称 + * @param clientId 当前终端ID + * @param password 房间密码 + * @param taoyao 信令 + * @param mainHandler MainHandler + * @param preview 是否预览视频 + * @param playAudio 是否播放音频 + * @param playVideo 是否播放视频 + * @param dataConsume 是否消费数据 + * @param audioConsume 是否消费音频 + * @param videoConsume 是否消费视频 + * @param dataProduce 是否生产数据 + * @param audioProduce 是否生产音频 + * @param videoProduce 是否生产视频 + * @param useIceServer 是否使用IceServer * @param mediaProperties 媒体配置 * @param webrtcProperties WebRTC配置 */ public Room( - String roomId, String name, - String clientId, String password, - ITaoyao taoyao, Handler mainHandler, + String roomId, String name, + String password, String clientId, + ITaoyao taoyao, Handler mainHandler, boolean preview, boolean playAudio, boolean playVideo, boolean dataConsume, boolean audioConsume, boolean videoConsume, boolean dataProduce, boolean audioProduce, boolean videoProduce, + boolean useIceServer, MediaProperties mediaProperties, WebrtcProperties webrtcProperties ) { super(taoyao, mainHandler); - this.roomId = roomId; - this.name = name; - this.clientId = clientId; - this.password = password; - this.preview = preview; + this.roomId = roomId; + this.name = name; + this.password = password; + this.clientId = clientId; + this.preview = preview; this.playAudio = playAudio; this.playVideo = playVideo; this.dataConsume = dataConsume; @@ -99,13 +174,17 @@ public class Room extends CloseableClient implements RouterCallback { this.dataProduce = dataProduce; this.audioProduce = audioProduce; this.videoProduce = videoProduce; + this.useIceServer = useIceServer; this.produce = false; - this.mediaProperties = mediaProperties; - this.webrtcProperties = webrtcProperties; + this.mediaProperties = mediaProperties; + this.webrtcProperties = webrtcProperties; this.remoteClients = new ConcurrentHashMap<>(); this.nativeRoomPointer = this.nativeNewRoom(roomId, this); } + /** + * @return 是否成功进入房间 + */ public boolean enter() { synchronized (this) { if (this.init) { @@ -120,12 +199,18 @@ public class Room extends CloseableClient implements RouterCallback { this.localClient.playVideo(); } // STUN | TURN - final List iceServers = new ArrayList<>(); - // 不用配置 -// final List iceServers = this.webrtcProperties.getIceServers(); + final List iceServers; + if(this.useIceServer) { + // 不用配置:正常情况都是能够直接访问媒体服务 + iceServers = this.webrtcProperties.getIceServers(); + } else { + iceServers = new ArrayList<>(); + } this.rtcConfiguration = new PeerConnection.RTCConfiguration(iceServers); + // 开始协商 return this.taoyao.requestFuture( this.taoyao.buildMessage("media::router::rtp::capabilities", "roomId", this.roomId), + // 成功加载Mediasoup房间 response -> { this.nativeEnterRoom( this.nativeRoomPointer, @@ -135,6 +220,7 @@ public class Room extends CloseableClient implements RouterCallback { ); return true; }, + // 失败关闭资源 response -> { this.close(); return false; @@ -143,11 +229,16 @@ public class Room extends CloseableClient implements RouterCallback { } } + /** + * 生产媒体 + */ public void mediaProduce() { - if(this.produce) { - return; + synchronized(this) { + if(this.produce) { + return; + } + this.produce = true; } - this.produce = true; if (this.audioProduce || this.videoProduce) { this.createSendTransport(); } @@ -164,6 +255,9 @@ public class Room extends CloseableClient implements RouterCallback { } } + /** + * 创建发送媒体通道 + */ private void createSendTransport() { this.taoyao.requestFuture( this.taoyao.buildMessage( @@ -183,6 +277,9 @@ public class Room extends CloseableClient implements RouterCallback { ); } + /** + * 创建接收媒体通道 + */ private void createRecvTransport() { this.taoyao.requestFuture( this.taoyao.buildMessage( @@ -202,6 +299,12 @@ public class Room extends CloseableClient implements RouterCallback { ); } + /** + * 媒体消费 + * + * @param message 信令消息 + * @param body 消息主体 + */ public void mediaConsume(Message message, Map body) { this.nativeMediaConsume(this.nativeRoomPointer, JSONUtils.toJSON(message)); } @@ -213,41 +316,63 @@ public class Room extends CloseableClient implements RouterCallback { */ public void newRemoteClientFromRoomEnter(Map body) { final String clientId = MapUtils.get(body, "clientId"); + // 忽略自己 if(this.clientId.equals(clientId)) { return; } - final Map status = MapUtils.get(body, "status"); - final String name = MapUtils.get(status, "name"); - final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler); - final RemoteClient old = this.remoteClients.put(clientId, remoteClient); - if(old != null) { + final Map status = MapUtils.get(body, "status"); + final String name = MapUtils.get(status, "name"); + final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler); + final RemoteClient oldRemoteClient = this.remoteClients.put(clientId, remoteClient); + if(oldRemoteClient != null) { // 关闭旧的资源 - old.close(); + this.closeRemoteClient(oldRemoteClient); } } + /** + * 新增远程终端 + * + * @param body 消息主体 + */ public void newRemoteClientFromRoomClientList(Map body) { final List> clients = MapUtils.get(body, "clients"); if(CollectionUtils.isEmpty(clients)) { return; } clients.forEach(map -> { - final String name = MapUtils.get(map, "name"); + final String name = MapUtils.get(map, "name"); final String clientId = MapUtils.get(map, "clientId"); + // 忽略自己 if(this.clientId.equals(clientId)) { return; } - final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler); - final RemoteClient old = this.remoteClients.put(clientId, remoteClient); - if(old != null) { + final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler); + final RemoteClient oldRemoteClient = this.remoteClients.put(clientId, remoteClient); + if(oldRemoteClient != null) { // 关闭旧的资源 - old.close(); + this.closeRemoteClient(oldRemoteClient); } }); } + /** + * 关闭远程终端 + * + * @param clientId 远程终端ID + */ public void closeRemoteClient(String clientId) { final RemoteClient remoteClient = this.remoteClients.remove(clientId); + this.closeRemoteClient(remoteClient); + } + + /** + * 关闭远程终端 + * 注意:需要自己从列表中删除 + * + * @param remoteClient 远程终端 + */ + private void closeRemoteClient(RemoteClient remoteClient) { if(remoteClient == null) { return; } @@ -265,88 +390,150 @@ public class Room extends CloseableClient implements RouterCallback { } Log.i(Room.class.getSimpleName(), "关闭房间:" + this.roomId); super.close(); + // 关闭Mediasoup房间 this.nativeCloseRoom(this.nativeRoomPointer); // 关闭远程媒体 - this.remoteClients.values().forEach(v -> this.closeRemoteClient(v.clientId)); + this.remoteClients.values().forEach(this::closeRemoteClient); this.remoteClients.clear(); // 关闭本地媒体 this.localClient.close(); + // 释放终端 this.mediaManager.closeClient(); } } + /** + * 主动关闭消费者 + * + * @param consumerId 消费者ID + */ public void mediaConsumerClose(String consumerId) { this.taoyao.push(this.taoyao.buildMessage( "media::consumer::close", - "roomId", this.roomId, + "roomId", this.roomId, "consumerId", consumerId )); } + /** + * 关闭消费者回调(信令) + * + * @param body 消息主体 + */ public void mediaConsumerClose(Map body) { final String consumerId = MapUtils.get(body, "consumerId"); this.nativeMediaConsumerClose(this.nativeRoomPointer, consumerId); this.remoteClients.values().forEach(v -> v.close(consumerId)); } + /** + * 主动暂停消费者 + * + * @param consumerId 消费者ID + */ public void mediaConsumerPause(String consumerId) { this.taoyao.push(this.taoyao.buildMessage( "media::consumer::pause", - "roomId", this.roomId, + "roomId", this.roomId, "consumerId", consumerId )); } + /** + * 暂停消费者回调(信令) + * + * @param body 消息主体 + */ public void mediaConsumerPause(Map body) { this.nativeMediaConsumerPause(this.nativeRoomPointer, MapUtils.get(body, "consumerId")); } + /** + * 主动恢复消费者 + * + * @param consumerId 消费者ID + */ public void mediaConsumerResume(String consumerId) { this.taoyao.push(this.taoyao.buildMessage( "media::consumer::resume", - "roomId", this.roomId, + "roomId", this.roomId, "consumerId", consumerId )); } + /** + * 恢复消费者回调(信令) + * + * @param body 消息主体 + */ public void mediaConsumerResume(Map body) { this.nativeMediaConsumerResume(this.nativeRoomPointer, MapUtils.get(body, "consumerId")); } + /** + * 主动关闭生产者 + * + * @param producerId 生产者ID + */ public void mediaProducerClose(String producerId) { this.taoyao.push(this.taoyao.buildMessage( "media::producer::close", - "roomId", this.roomId, + "roomId", this.roomId, "producerId", producerId )); } + /** + * 关闭生产者回调(信令) + * + * @param body 消息主体 + */ public void mediaProducerClose(Map body) { final String producerId = MapUtils.get(body, "producerId"); this.nativeMediaProducerClose(this.nativeRoomPointer, producerId); this.localClient.close(producerId); } + /** + * 主动暂停生产者 + * + * @param producerId 生产者ID + */ public void mediaProducerPause(String producerId) { this.taoyao.push(this.taoyao.buildMessage( "media::producer::pause", - "roomId", this.roomId, + "roomId", this.roomId, "producerId", producerId )); } + /** + * 暂停生产者回调(信令) + * + * @param body 消息主体 + */ public void mediaProducerPause(Map body) { this.nativeMediaProducerPause(this.nativeRoomPointer, MapUtils.get(body, "producerId")); } + /** + * 主动恢复生产者 + * + * @param producerId 生产者ID + */ public void mediaProducerResume(String producerId) { this.taoyao.push(this.taoyao.buildMessage( "media::producer::resume", - "roomId", this.roomId, + "roomId", this.roomId, "producerId", producerId )); } + /** + * 恢复生产者回调(信令) + * + * @param body 消息主体 + */ public void mediaProducerResume(Map body) { this.nativeMediaProducerResume(this.nativeRoomPointer, MapUtils.get(body, "producerId")); } @@ -416,6 +603,18 @@ public class Room extends CloseableClient implements RouterCallback { } } + @Override + public void producerCloseCallback(String producerId) { + } + + @Override + public void producerPauseCallback(String producerId) { + } + + @Override + public void producerResumeCallback(String producerId) { + } + @Override public void consumerNewCallback(String message, long consumerPointer, long consumerMediaTrackPointer) { final Message response = JSONUtils.toJava(message, Message.class); @@ -423,13 +622,12 @@ public class Room extends CloseableClient implements RouterCallback { final String kind = MapUtils.get(body, "kind"); final String sourceId = MapUtils.get(body, "sourceId"); final String consumerId = MapUtils.get(body, "consumerId"); - final RemoteClient remoteClient = this.remoteClients.get(sourceId); - if(remoteClient == null) { - // TODO:资源释放 - return; - } + final RemoteClient remoteClient = this.remoteClients.computeIfAbsent(sourceId, key -> { + // 假如媒体上来时间比进入房间消息快:基本上不可能出现这种情况 + Log.w(Room.class.getSimpleName(), "未知媒体来源:" + sourceId); + return new RemoteClient(sourceId, sourceId, this.taoyao, this.mainHandler); + }); if(MediaStreamTrack.AUDIO_TRACK_KIND.equals(kind)) { -// WebRtcAudioTrack final AudioTrack audioTrack = new AudioTrack(consumerMediaTrackPointer); audioTrack.setVolume(Config.DEFAULT_VOLUME); remoteClient.tracks.put(consumerId, audioTrack); @@ -442,25 +640,136 @@ public class Room extends CloseableClient implements RouterCallback { remoteClient.playVideo(); } else { Log.w(Room.class.getSimpleName(), "未知媒体类型:" + kind); - // TODO:资源释放 return; } this.taoyao.push(response); } + @Override + public void consumerCloseCallback(String consumerId) { + } + + @Override + public void consumerPauseCallback(String consumerId) { + } + + @Override + public void consumerResumeCallback(String consumerId) { + } + + /** + * Mediasoup创建房间 + * + * @param roomId 房间ID + * @param routerCallback 路由回调 + * + * @return Mediasoup房间指针 + */ private native long nativeNewRoom(String roomId, RouterCallback routerCallback); + + /** + * Mediasou进入房间 + * + * @param nativePointer 房间指针 + * @param rtpCapabilities RTP能力 + * @param peerConnectionFactoryPointer PeerConnectionFactory指针 + * @param rtcConfiguration RTC配置 + */ private native void nativeEnterRoom(long nativePointer, String rtpCapabilities, long peerConnectionFactoryPointer, PeerConnection.RTCConfiguration rtcConfiguration); + + /** + * Mediasoup关闭房间 + * + * @param nativePointer 房间指针 + */ private native void nativeCloseRoom(long nativePointer); + + /** + * Mediasoup创建发送通道 + * + * @param nativeRoomPointer 房间指针 + * @param body 消息主体 + */ private native void nativeCreateSendTransport(long nativeRoomPointer, String body); + + /** + * Mediasoup创建接收通道 + * + * @param nativeRoomPointer 房间指针 + * @param body 消息主体 + */ private native void nativeCreateRecvTransport(long nativeRoomPointer, String body); + + /** + * Mediasoup生产音频 + * + * @param nativeRoomPointer 房间指针 + * @param mediaStreamPointer 媒体指针 + */ private native void nativeMediaProduceAudio(long nativeRoomPointer, long mediaStreamPointer); + + /** + * Mediasoup生产视频 + * + * @param nativeRoomPointer 房间指针 + * @param mediaStreamPointer 媒体指针 + */ private native void nativeMediaProduceVideo(long nativeRoomPointer, long mediaStreamPointer); + + /** + * Mediasoup消费 + * + * @param nativeRoomPointer 房间指针 + * @param message 信令消息 + */ private native void nativeMediaConsume(long nativeRoomPointer, String message); + + /** + * Mediasoup暂停生产者 + * + * @param nativeRoomPointer 房间指针 + * @param producerId 生产者ID + */ private native void nativeMediaProducerPause(long nativeRoomPointer, String producerId); + + /** + * Mediasoup恢复生产者 + * + * @param nativeRoomPointer 房间指针 + * @param producerId 生产者ID + */ private native void nativeMediaProducerResume(long nativeRoomPointer, String producerId); + + /** + * Mediasoup关闭生产者 + * + * @param nativeRoomPointer 房间指针 + * @param producerId 生产者ID + */ private native void nativeMediaProducerClose(long nativeRoomPointer, String producerId); + + /** + * Mediasoup暂停消费者 + * + * @param nativeRoomPointer 房间指针 + * @param consumerId 消费者ID + */ private native void nativeMediaConsumerPause(long nativeRoomPointer, String consumerId); + + /** + * Mediasoup恢复消费者 + * + * @param nativeRoomPointer 房间指针 + * @param consumerId 消费者ID + */ private native void nativeMediaConsumerResume(long nativeRoomPointer, String consumerId); + + /** + * Mediasoup关闭消费者 + * + * @param nativeRoomPointer 房间指针 + * @param consumerId 消费者ID + */ private native void nativeMediaConsumerClose(long nativeRoomPointer, String consumerId); } diff --git a/taoyao-client-media/src/Config.js b/taoyao-client-media/src/Config.js index a431449..8216019 100644 --- a/taoyao-client-media/src/Config.js +++ b/taoyao-client-media/src/Config.js @@ -31,6 +31,13 @@ module.exports = { // 信令密码 password: "taoyao", }, + // 录像配置 + record: { + // 请求关键帧的最大次数 + requestKeyFrameMaxIndex: 16, + // 请求关键帧的文件大小 + requestKeyFrameFileSize: 32 * 1024, + }, // Mediasoup mediasoup: { // 配置Worker进程数量 diff --git a/taoyao-client-media/src/Server.js b/taoyao-client-media/src/Server.js index a7ffc0d..bac385c 100644 --- a/taoyao-client-media/src/Server.js +++ b/taoyao-client-media/src/Server.js @@ -79,7 +79,7 @@ async function connectSignalServer() { * 启动方法 */ async function main() { - console.log(` + console.debug(` 桃之夭夭,灼灼其华。 之子于归,宜其室家。 diff --git a/taoyao-client-media/src/Taoyao.js b/taoyao-client-media/src/Taoyao.js index dd79deb..25b0db7 100644 --- a/taoyao-client-media/src/Taoyao.js +++ b/taoyao-client-media/src/Taoyao.js @@ -814,7 +814,7 @@ class Taoyao { audioTransport.clientId = clientId; room.transports.set(audioTransport.id, audioTransport); audioTransport.observer.on("close", () => { - console.log("controlServerRecord audioTransport close:", audioTransport.id); + console.debug("controlServerRecord audioTransport close:", audioTransport.id); room.transports.delete(audioTransport.id) }); await audioTransport.connect({ @@ -833,10 +833,10 @@ class Taoyao { audioConsumer.streamId = audioStreamId; room.consumers.set(audioConsumer.id, audioConsumer); audioConsumer.observer.on("close", () => { - console.log("controlServerRecord audioConsumer close:", audioConsumer.id); + console.debug("controlServerRecord audioConsumer close:", audioConsumer.id); room.consumers.delete(audioConsumer.id); }); - console.log("controlServerRecord audio", audioTransportId, audioConsumerId, audioTransport.tuple, audioRtpParameters); + console.debug("controlServerRecord audio", audioTransportId, audioConsumerId, audioTransport.tuple, audioRtpParameters); } if(videoProducerId) { const videoTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions); @@ -845,7 +845,7 @@ class Taoyao { videoTransport.clientId = clientId; room.transports.set(videoTransport.id, videoTransport); videoTransport.observer.on("close", () => { - console.log("controlServerRecord videoTransport close:", videoTransport.id); + console.debug("controlServerRecord videoTransport close:", videoTransport.id); room.transports.delete(videoTransport.id) }); await videoTransport.connect({ @@ -864,10 +864,10 @@ class Taoyao { videoConsumer.streamId = videoStreamId; room.consumers.set(videoConsumer.id, videoConsumer); videoConsumer.observer.on("close", () => { - console.log("controlServerRecord videoConsumer close:", videoConsumer.id); + console.debug("controlServerRecord videoConsumer close:", videoConsumer.id); room.consumers.delete(videoConsumer.id); }); - console.log("controlServerRecord video:", videoTransportId, videoConsumerId, videoTransport.tuple, videoRtpParameters); + console.debug("controlServerRecord video:", videoTransportId, videoConsumerId, videoTransport.tuple, videoRtpParameters); } if(audioConsumer) { await audioConsumer.resume(); @@ -900,7 +900,7 @@ class Taoyao { if(!filepath || !videoConsumer) { return; } - if(++index > 10) { + if(++index > config.record.requestKeyFrameMaxIndex) { console.warn("请求录像关键帧次数超限", filepath, index); return; } @@ -909,7 +909,7 @@ class Taoyao { return; } // 判断文件大小验证是否已经开始录像:创建文件 -> 视频信息 -> 视频数据 -> 封装视频 - if(fs.existsSync(filepath) && fs.statSync(filepath).size >= 128 * 1024) { + if(fs.existsSync(filepath) && fs.statSync(filepath).size >= config.record.requestKeyFrameFileSize) { console.debug("请求录像关键帧已经开始录像", filepath); return; }