[*] 日常优化

This commit is contained in:
acgist
2023-06-11 08:12:29 +08:00
parent 6749339162
commit 0908996fb0
6 changed files with 390 additions and 71 deletions

View File

@@ -1032,7 +1032,7 @@ public final class Taoyao implements ITaoyao {
roomId, roomId,
key -> new Room( key -> new Room(
roomId, this.name, roomId, this.name,
this.clientId, password, password, this.clientId,
this, this.mainHandler, this, this.mainHandler,
resources.getBoolean(R.bool.preview), resources.getBoolean(R.bool.preview),
resources.getBoolean(R.bool.playAudio), resources.getBoolean(R.bool.playAudio),
@@ -1043,6 +1043,7 @@ public final class Taoyao implements ITaoyao {
resources.getBoolean(R.bool.dataProduce), resources.getBoolean(R.bool.dataProduce),
resources.getBoolean(R.bool.audioProduce), resources.getBoolean(R.bool.audioProduce),
resources.getBoolean(R.bool.videoProduce), resources.getBoolean(R.bool.videoProduce),
resources.getBoolean(R.bool.roomUseIceServer),
this.mediaManager.getMediaProperties(), this.mediaManager.getMediaProperties(),
this.mediaManager.getWebrtcProperties() this.mediaManager.getWebrtcProperties()
) )

View File

@@ -40,6 +40,8 @@
<integer name="channelCount">1</integer> <integer name="channelCount">1</integer>
<!-- 视频关键帧频率 --> <!-- 视频关键帧频率 -->
<integer name="iFrameInterval">1</integer> <integer name="iFrameInterval">1</integer>
<!-- 视频房间是否使用iceServer -->
<bool name="roomUseIceServer">false</bool>
<!-- 图片存储目录 --> <!-- 图片存储目录 -->
<string name="imagePath">/taoyao</string> <string name="imagePath">/taoyao</string>
<!-- 视频存储目录 --> <!-- 视频存储目录 -->

View File

@@ -33,64 +33,139 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class Room extends CloseableClient implements RouterCallback { public class Room extends CloseableClient implements RouterCallback {
private final String name; /**
* 房间ID
*/
private final String roomId; private final String roomId;
private final String clientId; /**
* 房间名称
*/
private final String name;
/**
* 房间密码
*/
private final String password; private final String password;
/**
* 当前终端ID
*/
private final String clientId;
/**
* 是否预览
*/
private final boolean preview; private final boolean preview;
/**
* 是否播放音频
*/
private final boolean playAudio; private final boolean playAudio;
/**
* 是否播放视频
*/
private final boolean playVideo; private final boolean playVideo;
/**
* 是否消费数据
*/
private final boolean dataConsume; private final boolean dataConsume;
/**
* 是否消费音频
*/
private final boolean audioConsume; private final boolean audioConsume;
/**
* 是否消费视频
*/
private final boolean videoConsume; private final boolean videoConsume;
/**
* 是否生产数据
*/
private final boolean dataProduce; private final boolean dataProduce;
/**
* 是否生产音频
*/
private final boolean audioProduce; private final boolean audioProduce;
/**
* 是否生产视频
*/
private final boolean videoProduce; private final boolean videoProduce;
/**
* 是否使用IceServer
*/
private final boolean useIceServer;
/**
* 是否已经开始生产
*/
private boolean produce; private boolean produce;
/**
* 媒体配置
*/
private final MediaProperties mediaProperties; private final MediaProperties mediaProperties;
/**
* WebRTC配置
*/
private final WebrtcProperties webrtcProperties; private final WebrtcProperties webrtcProperties;
private final Map<String, RemoteClient> remoteClients; /**
* 房间指针
*/
private final long nativeRoomPointer; private final long nativeRoomPointer;
/**
* 本地终端
*/
private LocalClient localClient; private LocalClient localClient;
private PeerConnection.RTCConfiguration rtcConfiguration; /**
private PeerConnectionFactory peerConnectionFactory; * 远程终端
*/
private final Map<String, RemoteClient> remoteClients;
/**
* RTC能力
*/
private Object rtpCapabilities; private Object rtpCapabilities;
/**
* SCTP能力
*/
private Object sctpCapabilities; private Object sctpCapabilities;
/**
* RTC配置
*/
private PeerConnection.RTCConfiguration rtcConfiguration;
/**
* PeerConnectionFactory
*/
private PeerConnectionFactory peerConnectionFactory;
/** /**
* @param roomId 房间ID * @param roomId 房间ID
* @param name 终端名称 * @param name 终端名称
* @param clientId 当前终端ID * @param clientId 当前终端ID
* @param password 房间密码 * @param password 房间密码
* @param taoyao 信令 * @param taoyao 信令
* @param mainHandler MainHandler * @param mainHandler MainHandler
* @param preview 是否预览视频 * @param preview 是否预览视频
* @param playAudio 是否播放音频 * @param playAudio 是否播放音频
* @param playVideo 是否播放视频 * @param playVideo 是否播放视频
* @param dataConsume 是否消费数据 * @param dataConsume 是否消费数据
* @param audioConsume 是否消费音频 * @param audioConsume 是否消费音频
* @param videoConsume 是否消费视频 * @param videoConsume 是否消费视频
* @param dataProduce 是否生产数据 * @param dataProduce 是否生产数据
* @param audioProduce 是否生产音频 * @param audioProduce 是否生产音频
* @param videoProduce 是否生产视频 * @param videoProduce 是否生产视频
* @param useIceServer 是否使用IceServer
* @param mediaProperties 媒体配置 * @param mediaProperties 媒体配置
* @param webrtcProperties WebRTC配置 * @param webrtcProperties WebRTC配置
*/ */
public Room( public Room(
String roomId, String name, String roomId, String name,
String clientId, String password, String password, String clientId,
ITaoyao taoyao, Handler mainHandler, ITaoyao taoyao, Handler mainHandler,
boolean preview, boolean playAudio, boolean playVideo, boolean preview, boolean playAudio, boolean playVideo,
boolean dataConsume, boolean audioConsume, boolean videoConsume, boolean dataConsume, boolean audioConsume, boolean videoConsume,
boolean dataProduce, boolean audioProduce, boolean videoProduce, boolean dataProduce, boolean audioProduce, boolean videoProduce,
boolean useIceServer,
MediaProperties mediaProperties, WebrtcProperties webrtcProperties MediaProperties mediaProperties, WebrtcProperties webrtcProperties
) { ) {
super(taoyao, mainHandler); super(taoyao, mainHandler);
this.roomId = roomId; this.roomId = roomId;
this.name = name; this.name = name;
this.clientId = clientId; this.password = password;
this.password = password; this.clientId = clientId;
this.preview = preview; this.preview = preview;
this.playAudio = playAudio; this.playAudio = playAudio;
this.playVideo = playVideo; this.playVideo = playVideo;
this.dataConsume = dataConsume; this.dataConsume = dataConsume;
@@ -99,13 +174,17 @@ public class Room extends CloseableClient implements RouterCallback {
this.dataProduce = dataProduce; this.dataProduce = dataProduce;
this.audioProduce = audioProduce; this.audioProduce = audioProduce;
this.videoProduce = videoProduce; this.videoProduce = videoProduce;
this.useIceServer = useIceServer;
this.produce = false; this.produce = false;
this.mediaProperties = mediaProperties; this.mediaProperties = mediaProperties;
this.webrtcProperties = webrtcProperties; this.webrtcProperties = webrtcProperties;
this.remoteClients = new ConcurrentHashMap<>(); this.remoteClients = new ConcurrentHashMap<>();
this.nativeRoomPointer = this.nativeNewRoom(roomId, this); this.nativeRoomPointer = this.nativeNewRoom(roomId, this);
} }
/**
* @return 是否成功进入房间
*/
public boolean enter() { public boolean enter() {
synchronized (this) { synchronized (this) {
if (this.init) { if (this.init) {
@@ -120,12 +199,18 @@ public class Room extends CloseableClient implements RouterCallback {
this.localClient.playVideo(); this.localClient.playVideo();
} }
// STUN | TURN // STUN | TURN
final List<PeerConnection.IceServer> iceServers = new ArrayList<>(); final List<PeerConnection.IceServer> iceServers;
// 不用配置 if(this.useIceServer) {
// final List<PeerConnection.IceServer> iceServers = this.webrtcProperties.getIceServers(); // 不用配置:正常情况都是能够直接访问媒体服务
iceServers = this.webrtcProperties.getIceServers();
} else {
iceServers = new ArrayList<>();
}
this.rtcConfiguration = new PeerConnection.RTCConfiguration(iceServers); this.rtcConfiguration = new PeerConnection.RTCConfiguration(iceServers);
// 开始协商
return this.taoyao.requestFuture( return this.taoyao.requestFuture(
this.taoyao.buildMessage("media::router::rtp::capabilities", "roomId", this.roomId), this.taoyao.buildMessage("media::router::rtp::capabilities", "roomId", this.roomId),
// 成功加载Mediasoup房间
response -> { response -> {
this.nativeEnterRoom( this.nativeEnterRoom(
this.nativeRoomPointer, this.nativeRoomPointer,
@@ -135,6 +220,7 @@ public class Room extends CloseableClient implements RouterCallback {
); );
return true; return true;
}, },
// 失败关闭资源
response -> { response -> {
this.close(); this.close();
return false; return false;
@@ -143,11 +229,16 @@ public class Room extends CloseableClient implements RouterCallback {
} }
} }
/**
* 生产媒体
*/
public void mediaProduce() { public void mediaProduce() {
if(this.produce) { synchronized(this) {
return; if(this.produce) {
return;
}
this.produce = true;
} }
this.produce = true;
if (this.audioProduce || this.videoProduce) { if (this.audioProduce || this.videoProduce) {
this.createSendTransport(); this.createSendTransport();
} }
@@ -164,6 +255,9 @@ public class Room extends CloseableClient implements RouterCallback {
} }
} }
/**
* 创建发送媒体通道
*/
private void createSendTransport() { private void createSendTransport() {
this.taoyao.requestFuture( this.taoyao.requestFuture(
this.taoyao.buildMessage( this.taoyao.buildMessage(
@@ -183,6 +277,9 @@ public class Room extends CloseableClient implements RouterCallback {
); );
} }
/**
* 创建接收媒体通道
*/
private void createRecvTransport() { private void createRecvTransport() {
this.taoyao.requestFuture( this.taoyao.requestFuture(
this.taoyao.buildMessage( this.taoyao.buildMessage(
@@ -202,6 +299,12 @@ public class Room extends CloseableClient implements RouterCallback {
); );
} }
/**
* 媒体消费
*
* @param message 信令消息
* @param body 消息主体
*/
public void mediaConsume(Message message, Map<String, Object> body) { public void mediaConsume(Message message, Map<String, Object> body) {
this.nativeMediaConsume(this.nativeRoomPointer, JSONUtils.toJSON(message)); this.nativeMediaConsume(this.nativeRoomPointer, JSONUtils.toJSON(message));
} }
@@ -213,41 +316,63 @@ public class Room extends CloseableClient implements RouterCallback {
*/ */
public void newRemoteClientFromRoomEnter(Map<String, Object> body) { public void newRemoteClientFromRoomEnter(Map<String, Object> body) {
final String clientId = MapUtils.get(body, "clientId"); final String clientId = MapUtils.get(body, "clientId");
// 忽略自己
if(this.clientId.equals(clientId)) { if(this.clientId.equals(clientId)) {
return; return;
} }
final Map<String, Object> status = MapUtils.get(body, "status"); final Map<String, Object> status = MapUtils.get(body, "status");
final String name = MapUtils.get(status, "name"); final String name = MapUtils.get(status, "name");
final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler); final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler);
final RemoteClient old = this.remoteClients.put(clientId, remoteClient); final RemoteClient oldRemoteClient = this.remoteClients.put(clientId, remoteClient);
if(old != null) { if(oldRemoteClient != null) {
// 关闭旧的资源 // 关闭旧的资源
old.close(); this.closeRemoteClient(oldRemoteClient);
} }
} }
/**
* 新增远程终端
*
* @param body 消息主体
*/
public void newRemoteClientFromRoomClientList(Map<String, Object> body) { public void newRemoteClientFromRoomClientList(Map<String, Object> body) {
final List<Map<String, Object>> clients = MapUtils.get(body, "clients"); final List<Map<String, Object>> clients = MapUtils.get(body, "clients");
if(CollectionUtils.isEmpty(clients)) { if(CollectionUtils.isEmpty(clients)) {
return; return;
} }
clients.forEach(map -> { clients.forEach(map -> {
final String name = MapUtils.get(map, "name"); final String name = MapUtils.get(map, "name");
final String clientId = MapUtils.get(map, "clientId"); final String clientId = MapUtils.get(map, "clientId");
// 忽略自己
if(this.clientId.equals(clientId)) { if(this.clientId.equals(clientId)) {
return; return;
} }
final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler); final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler);
final RemoteClient old = this.remoteClients.put(clientId, remoteClient); final RemoteClient oldRemoteClient = this.remoteClients.put(clientId, remoteClient);
if(old != null) { if(oldRemoteClient != null) {
// 关闭旧的资源 // 关闭旧的资源
old.close(); this.closeRemoteClient(oldRemoteClient);
} }
}); });
} }
/**
* 关闭远程终端
*
* @param clientId 远程终端ID
*/
public void closeRemoteClient(String clientId) { public void closeRemoteClient(String clientId) {
final RemoteClient remoteClient = this.remoteClients.remove(clientId); final RemoteClient remoteClient = this.remoteClients.remove(clientId);
this.closeRemoteClient(remoteClient);
}
/**
* 关闭远程终端
* 注意:需要自己从列表中删除
*
* @param remoteClient 远程终端
*/
private void closeRemoteClient(RemoteClient remoteClient) {
if(remoteClient == null) { if(remoteClient == null) {
return; return;
} }
@@ -265,88 +390,150 @@ public class Room extends CloseableClient implements RouterCallback {
} }
Log.i(Room.class.getSimpleName(), "关闭房间:" + this.roomId); Log.i(Room.class.getSimpleName(), "关闭房间:" + this.roomId);
super.close(); super.close();
// 关闭Mediasoup房间
this.nativeCloseRoom(this.nativeRoomPointer); this.nativeCloseRoom(this.nativeRoomPointer);
// 关闭远程媒体 // 关闭远程媒体
this.remoteClients.values().forEach(v -> this.closeRemoteClient(v.clientId)); this.remoteClients.values().forEach(this::closeRemoteClient);
this.remoteClients.clear(); this.remoteClients.clear();
// 关闭本地媒体 // 关闭本地媒体
this.localClient.close(); this.localClient.close();
// 释放终端
this.mediaManager.closeClient(); this.mediaManager.closeClient();
} }
} }
/**
* 主动关闭消费者
*
* @param consumerId 消费者ID
*/
public void mediaConsumerClose(String consumerId) { public void mediaConsumerClose(String consumerId) {
this.taoyao.push(this.taoyao.buildMessage( this.taoyao.push(this.taoyao.buildMessage(
"media::consumer::close", "media::consumer::close",
"roomId", this.roomId, "roomId", this.roomId,
"consumerId", consumerId "consumerId", consumerId
)); ));
} }
/**
* 关闭消费者回调(信令)
*
* @param body 消息主体
*/
public void mediaConsumerClose(Map<String, Object> body) { public void mediaConsumerClose(Map<String, Object> body) {
final String consumerId = MapUtils.get(body, "consumerId"); final String consumerId = MapUtils.get(body, "consumerId");
this.nativeMediaConsumerClose(this.nativeRoomPointer, consumerId); this.nativeMediaConsumerClose(this.nativeRoomPointer, consumerId);
this.remoteClients.values().forEach(v -> v.close(consumerId)); this.remoteClients.values().forEach(v -> v.close(consumerId));
} }
/**
* 主动暂停消费者
*
* @param consumerId 消费者ID
*/
public void mediaConsumerPause(String consumerId) { public void mediaConsumerPause(String consumerId) {
this.taoyao.push(this.taoyao.buildMessage( this.taoyao.push(this.taoyao.buildMessage(
"media::consumer::pause", "media::consumer::pause",
"roomId", this.roomId, "roomId", this.roomId,
"consumerId", consumerId "consumerId", consumerId
)); ));
} }
/**
* 暂停消费者回调(信令)
*
* @param body 消息主体
*/
public void mediaConsumerPause(Map<String, Object> body) { public void mediaConsumerPause(Map<String, Object> body) {
this.nativeMediaConsumerPause(this.nativeRoomPointer, MapUtils.get(body, "consumerId")); this.nativeMediaConsumerPause(this.nativeRoomPointer, MapUtils.get(body, "consumerId"));
} }
/**
* 主动恢复消费者
*
* @param consumerId 消费者ID
*/
public void mediaConsumerResume(String consumerId) { public void mediaConsumerResume(String consumerId) {
this.taoyao.push(this.taoyao.buildMessage( this.taoyao.push(this.taoyao.buildMessage(
"media::consumer::resume", "media::consumer::resume",
"roomId", this.roomId, "roomId", this.roomId,
"consumerId", consumerId "consumerId", consumerId
)); ));
} }
/**
* 恢复消费者回调(信令)
*
* @param body 消息主体
*/
public void mediaConsumerResume(Map<String, Object> body) { public void mediaConsumerResume(Map<String, Object> body) {
this.nativeMediaConsumerResume(this.nativeRoomPointer, MapUtils.get(body, "consumerId")); this.nativeMediaConsumerResume(this.nativeRoomPointer, MapUtils.get(body, "consumerId"));
} }
/**
* 主动关闭生产者
*
* @param producerId 生产者ID
*/
public void mediaProducerClose(String producerId) { public void mediaProducerClose(String producerId) {
this.taoyao.push(this.taoyao.buildMessage( this.taoyao.push(this.taoyao.buildMessage(
"media::producer::close", "media::producer::close",
"roomId", this.roomId, "roomId", this.roomId,
"producerId", producerId "producerId", producerId
)); ));
} }
/**
* 关闭生产者回调(信令)
*
* @param body 消息主体
*/
public void mediaProducerClose(Map<String, Object> body) { public void mediaProducerClose(Map<String, Object> body) {
final String producerId = MapUtils.get(body, "producerId"); final String producerId = MapUtils.get(body, "producerId");
this.nativeMediaProducerClose(this.nativeRoomPointer, producerId); this.nativeMediaProducerClose(this.nativeRoomPointer, producerId);
this.localClient.close(producerId); this.localClient.close(producerId);
} }
/**
* 主动暂停生产者
*
* @param producerId 生产者ID
*/
public void mediaProducerPause(String producerId) { public void mediaProducerPause(String producerId) {
this.taoyao.push(this.taoyao.buildMessage( this.taoyao.push(this.taoyao.buildMessage(
"media::producer::pause", "media::producer::pause",
"roomId", this.roomId, "roomId", this.roomId,
"producerId", producerId "producerId", producerId
)); ));
} }
/**
* 暂停生产者回调(信令)
*
* @param body 消息主体
*/
public void mediaProducerPause(Map<String, Object> body) { public void mediaProducerPause(Map<String, Object> body) {
this.nativeMediaProducerPause(this.nativeRoomPointer, MapUtils.get(body, "producerId")); this.nativeMediaProducerPause(this.nativeRoomPointer, MapUtils.get(body, "producerId"));
} }
/**
* 主动恢复生产者
*
* @param producerId 生产者ID
*/
public void mediaProducerResume(String producerId) { public void mediaProducerResume(String producerId) {
this.taoyao.push(this.taoyao.buildMessage( this.taoyao.push(this.taoyao.buildMessage(
"media::producer::resume", "media::producer::resume",
"roomId", this.roomId, "roomId", this.roomId,
"producerId", producerId "producerId", producerId
)); ));
} }
/**
* 恢复生产者回调(信令)
*
* @param body 消息主体
*/
public void mediaProducerResume(Map<String, Object> body) { public void mediaProducerResume(Map<String, Object> body) {
this.nativeMediaProducerResume(this.nativeRoomPointer, MapUtils.get(body, "producerId")); 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 @Override
public void consumerNewCallback(String message, long consumerPointer, long consumerMediaTrackPointer) { public void consumerNewCallback(String message, long consumerPointer, long consumerMediaTrackPointer) {
final Message response = JSONUtils.toJava(message, Message.class); 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 kind = MapUtils.get(body, "kind");
final String sourceId = MapUtils.get(body, "sourceId"); final String sourceId = MapUtils.get(body, "sourceId");
final String consumerId = MapUtils.get(body, "consumerId"); final String consumerId = MapUtils.get(body, "consumerId");
final RemoteClient remoteClient = this.remoteClients.get(sourceId); final RemoteClient remoteClient = this.remoteClients.computeIfAbsent(sourceId, key -> {
if(remoteClient == null) { // 假如媒体上来时间比进入房间消息快:基本上不可能出现这种情况
// TODO资源释放 Log.w(Room.class.getSimpleName(), "未知媒体来源:" + sourceId);
return; return new RemoteClient(sourceId, sourceId, this.taoyao, this.mainHandler);
} });
if(MediaStreamTrack.AUDIO_TRACK_KIND.equals(kind)) { if(MediaStreamTrack.AUDIO_TRACK_KIND.equals(kind)) {
// WebRtcAudioTrack
final AudioTrack audioTrack = new AudioTrack(consumerMediaTrackPointer); final AudioTrack audioTrack = new AudioTrack(consumerMediaTrackPointer);
audioTrack.setVolume(Config.DEFAULT_VOLUME); audioTrack.setVolume(Config.DEFAULT_VOLUME);
remoteClient.tracks.put(consumerId, audioTrack); remoteClient.tracks.put(consumerId, audioTrack);
@@ -442,25 +640,136 @@ public class Room extends CloseableClient implements RouterCallback {
remoteClient.playVideo(); remoteClient.playVideo();
} else { } else {
Log.w(Room.class.getSimpleName(), "未知媒体类型:" + kind); Log.w(Room.class.getSimpleName(), "未知媒体类型:" + kind);
// TODO资源释放
return; return;
} }
this.taoyao.push(response); 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); 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); private native void nativeEnterRoom(long nativePointer, String rtpCapabilities, long peerConnectionFactoryPointer, PeerConnection.RTCConfiguration rtcConfiguration);
/**
* Mediasoup关闭房间
*
* @param nativePointer 房间指针
*/
private native void nativeCloseRoom(long nativePointer); private native void nativeCloseRoom(long nativePointer);
/**
* Mediasoup创建发送通道
*
* @param nativeRoomPointer 房间指针
* @param body 消息主体
*/
private native void nativeCreateSendTransport(long nativeRoomPointer, String body); private native void nativeCreateSendTransport(long nativeRoomPointer, String body);
/**
* Mediasoup创建接收通道
*
* @param nativeRoomPointer 房间指针
* @param body 消息主体
*/
private native void nativeCreateRecvTransport(long nativeRoomPointer, String body); private native void nativeCreateRecvTransport(long nativeRoomPointer, String body);
/**
* Mediasoup生产音频
*
* @param nativeRoomPointer 房间指针
* @param mediaStreamPointer 媒体指针
*/
private native void nativeMediaProduceAudio(long nativeRoomPointer, long mediaStreamPointer); private native void nativeMediaProduceAudio(long nativeRoomPointer, long mediaStreamPointer);
/**
* Mediasoup生产视频
*
* @param nativeRoomPointer 房间指针
* @param mediaStreamPointer 媒体指针
*/
private native void nativeMediaProduceVideo(long nativeRoomPointer, long mediaStreamPointer); private native void nativeMediaProduceVideo(long nativeRoomPointer, long mediaStreamPointer);
/**
* Mediasoup消费
*
* @param nativeRoomPointer 房间指针
* @param message 信令消息
*/
private native void nativeMediaConsume(long nativeRoomPointer, String message); private native void nativeMediaConsume(long nativeRoomPointer, String message);
/**
* Mediasoup暂停生产者
*
* @param nativeRoomPointer 房间指针
* @param producerId 生产者ID
*/
private native void nativeMediaProducerPause(long nativeRoomPointer, String producerId); private native void nativeMediaProducerPause(long nativeRoomPointer, String producerId);
/**
* Mediasoup恢复生产者
*
* @param nativeRoomPointer 房间指针
* @param producerId 生产者ID
*/
private native void nativeMediaProducerResume(long nativeRoomPointer, String producerId); private native void nativeMediaProducerResume(long nativeRoomPointer, String producerId);
/**
* Mediasoup关闭生产者
*
* @param nativeRoomPointer 房间指针
* @param producerId 生产者ID
*/
private native void nativeMediaProducerClose(long nativeRoomPointer, String producerId); private native void nativeMediaProducerClose(long nativeRoomPointer, String producerId);
/**
* Mediasoup暂停消费者
*
* @param nativeRoomPointer 房间指针
* @param consumerId 消费者ID
*/
private native void nativeMediaConsumerPause(long nativeRoomPointer, String consumerId); private native void nativeMediaConsumerPause(long nativeRoomPointer, String consumerId);
/**
* Mediasoup恢复消费者
*
* @param nativeRoomPointer 房间指针
* @param consumerId 消费者ID
*/
private native void nativeMediaConsumerResume(long nativeRoomPointer, String consumerId); private native void nativeMediaConsumerResume(long nativeRoomPointer, String consumerId);
/**
* Mediasoup关闭消费者
*
* @param nativeRoomPointer 房间指针
* @param consumerId 消费者ID
*/
private native void nativeMediaConsumerClose(long nativeRoomPointer, String consumerId); private native void nativeMediaConsumerClose(long nativeRoomPointer, String consumerId);
} }

View File

@@ -31,6 +31,13 @@ module.exports = {
// 信令密码 // 信令密码
password: "taoyao", password: "taoyao",
}, },
// 录像配置
record: {
// 请求关键帧的最大次数
requestKeyFrameMaxIndex: 16,
// 请求关键帧的文件大小
requestKeyFrameFileSize: 32 * 1024,
},
// Mediasoup // Mediasoup
mediasoup: { mediasoup: {
// 配置Worker进程数量 // 配置Worker进程数量

View File

@@ -79,7 +79,7 @@ async function connectSignalServer() {
* 启动方法 * 启动方法
*/ */
async function main() { async function main() {
console.log(` console.debug(`
桃之夭夭,灼灼其华。 桃之夭夭,灼灼其华。
之子于归,宜其室家。 之子于归,宜其室家。

View File

@@ -814,7 +814,7 @@ class Taoyao {
audioTransport.clientId = clientId; audioTransport.clientId = clientId;
room.transports.set(audioTransport.id, audioTransport); room.transports.set(audioTransport.id, audioTransport);
audioTransport.observer.on("close", () => { audioTransport.observer.on("close", () => {
console.log("controlServerRecord audioTransport close", audioTransport.id); console.debug("controlServerRecord audioTransport close", audioTransport.id);
room.transports.delete(audioTransport.id) room.transports.delete(audioTransport.id)
}); });
await audioTransport.connect({ await audioTransport.connect({
@@ -833,10 +833,10 @@ class Taoyao {
audioConsumer.streamId = audioStreamId; audioConsumer.streamId = audioStreamId;
room.consumers.set(audioConsumer.id, audioConsumer); room.consumers.set(audioConsumer.id, audioConsumer);
audioConsumer.observer.on("close", () => { audioConsumer.observer.on("close", () => {
console.log("controlServerRecord audioConsumer close", audioConsumer.id); console.debug("controlServerRecord audioConsumer close", audioConsumer.id);
room.consumers.delete(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) { if(videoProducerId) {
const videoTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions); const videoTransport = await room.mediasoupRouter.createPlainTransport(plainTransportOptions);
@@ -845,7 +845,7 @@ class Taoyao {
videoTransport.clientId = clientId; videoTransport.clientId = clientId;
room.transports.set(videoTransport.id, videoTransport); room.transports.set(videoTransport.id, videoTransport);
videoTransport.observer.on("close", () => { videoTransport.observer.on("close", () => {
console.log("controlServerRecord videoTransport close", videoTransport.id); console.debug("controlServerRecord videoTransport close", videoTransport.id);
room.transports.delete(videoTransport.id) room.transports.delete(videoTransport.id)
}); });
await videoTransport.connect({ await videoTransport.connect({
@@ -864,10 +864,10 @@ class Taoyao {
videoConsumer.streamId = videoStreamId; videoConsumer.streamId = videoStreamId;
room.consumers.set(videoConsumer.id, videoConsumer); room.consumers.set(videoConsumer.id, videoConsumer);
videoConsumer.observer.on("close", () => { videoConsumer.observer.on("close", () => {
console.log("controlServerRecord videoConsumer close", videoConsumer.id); console.debug("controlServerRecord videoConsumer close", videoConsumer.id);
room.consumers.delete(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) { if(audioConsumer) {
await audioConsumer.resume(); await audioConsumer.resume();
@@ -900,7 +900,7 @@ class Taoyao {
if(!filepath || !videoConsumer) { if(!filepath || !videoConsumer) {
return; return;
} }
if(++index > 10) { if(++index > config.record.requestKeyFrameMaxIndex) {
console.warn("请求录像关键帧次数超限", filepath, index); console.warn("请求录像关键帧次数超限", filepath, index);
return; return;
} }
@@ -909,7 +909,7 @@ class Taoyao {
return; 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); console.debug("请求录像关键帧已经开始录像", filepath);
return; return;
} }