[*] 视频会话管理逻辑
This commit is contained in:
@@ -244,8 +244,8 @@ public class MainActivity extends AppCompatActivity implements Serializable {
|
||||
private void previewVideo(Message message) {
|
||||
final GridLayout video = this.binding.video;
|
||||
final int count = video.getChildCount();
|
||||
final GridLayout.Spec rowSpec = GridLayout.spec(count / 2);
|
||||
final GridLayout.Spec columnSpec = GridLayout.spec(count % 2);
|
||||
final GridLayout.Spec rowSpec = GridLayout.spec(count / 2, 1, 0);
|
||||
final GridLayout.Spec columnSpec = GridLayout.spec(count % 2, 1, 0);
|
||||
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(rowSpec, columnSpec);
|
||||
layoutParams.width = 0;
|
||||
layoutParams.height = 0;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.acgist.taoyao.media;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecList;
|
||||
import android.media.projection.MediaProjection;
|
||||
@@ -41,6 +42,9 @@ import org.webrtc.VideoFrame;
|
||||
import org.webrtc.VideoSource;
|
||||
import org.webrtc.VideoTrack;
|
||||
import org.webrtc.audio.JavaAudioDeviceModule;
|
||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||
import org.webrtc.voiceengine.WebRtcAudioRecord;
|
||||
import org.webrtc.voiceengine.WebRtcAudioUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -361,6 +365,11 @@ public final class MediaManager {
|
||||
// .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
||||
// .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
// .build();
|
||||
// WebRtcAudioRecord.setOnAudioSamplesReady(audioSamples -> {
|
||||
// if(this.recordClient != null) {
|
||||
// this.recordClient.onWebRtcAudioRecordSamplesReady(audioSamples);
|
||||
// }
|
||||
// });
|
||||
final JavaAudioDeviceModule javaAudioDeviceModule = JavaAudioDeviceModule.builder(this.context)
|
||||
// .setSampleRate()
|
||||
// .setAudioSource(MediaRecorder.AudioSource.MIC)
|
||||
|
||||
@@ -70,7 +70,6 @@ export default {
|
||||
this.taoyao.sessionClose(this.client.id);
|
||||
},
|
||||
media(track) {
|
||||
console.log(track);
|
||||
if(track.kind === 'audio') {
|
||||
if (this.audioStream) {
|
||||
// TODO:资源释放
|
||||
|
||||
@@ -12,3 +12,7 @@
|
||||
## 信令格式
|
||||
|
||||
[信令格式](https://localhost:8888/protocol/list)
|
||||
|
||||
## STUN/TURN
|
||||
|
||||
视频房间不用`STUN/TURN`服务,视频会话需要自己搭建`coturn`服务。
|
||||
|
||||
@@ -13,7 +13,6 @@ import lombok.Setter;
|
||||
|
||||
/**
|
||||
* WebRTC配置
|
||||
* P2P视频监控会用,正常会议不会使用,需要自己搭建`coturn`服务。
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.acgist.taoyao.signal.event;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.signal.party.session.Session;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 视频会话事件适配器
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
public abstract class SessionEventAdapter extends ApplicationEventAdapter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 视频会话
|
||||
*/
|
||||
private final Session session;
|
||||
/**
|
||||
* 视频会话ID
|
||||
*/
|
||||
private final String sessionId;
|
||||
|
||||
public SessionEventAdapter(Session session) {
|
||||
this(session, null, null);
|
||||
}
|
||||
|
||||
public SessionEventAdapter(Session session, Message message) {
|
||||
this(session, message, null);
|
||||
}
|
||||
|
||||
public SessionEventAdapter(Session session, Message message, Map<String, Object> body) {
|
||||
super(session, message, body);
|
||||
this.session = session;
|
||||
this.sessionId = session.getId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.acgist.taoyao.signal.event.session;
|
||||
|
||||
import com.acgist.taoyao.signal.event.SessionEventAdapter;
|
||||
import com.acgist.taoyao.signal.party.session.Session;
|
||||
|
||||
/**
|
||||
* 关闭视频会话事件
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class SessionCloseEvent extends SessionEventAdapter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SessionCloseEvent(Session session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -139,6 +139,7 @@ public class Room extends OperatorAdapter {
|
||||
synchronized (this.clients) {
|
||||
final ClientWrapper wrapper = this.clients.remove(client);
|
||||
if(wrapper != null) {
|
||||
log.info("终端离开房间:{} - {}", this.roomId, client.clientId());
|
||||
try {
|
||||
wrapper.close();
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -4,11 +4,13 @@ import java.io.Closeable;
|
||||
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.event.EventPublisher;
|
||||
import com.acgist.taoyao.signal.event.session.SessionCloseEvent;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* P2P会话
|
||||
* 视频会话
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@@ -27,16 +29,11 @@ public class Session implements Closeable {
|
||||
* 接收者
|
||||
*/
|
||||
private final Client target;
|
||||
/**
|
||||
* P2P会话管理器
|
||||
*/
|
||||
private final SessionManager sessionManager;
|
||||
|
||||
public Session(String id, Client source, Client target, SessionManager sessionManager) {
|
||||
public Session(String id, Client source, Client target) {
|
||||
this.id = id;
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,10 +60,18 @@ public class Session implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.sessionManager.remove(this.id);
|
||||
/**
|
||||
* @param client 终端
|
||||
*
|
||||
* @return 是否含有终端
|
||||
*/
|
||||
public boolean hasClient(Client client) {
|
||||
return this.source == client || this.target == client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
EventPublisher.publishEvent(new SessionCloseEvent(this));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,11 +7,14 @@ import com.acgist.taoyao.boot.annotation.Manager;
|
||||
import com.acgist.taoyao.boot.service.IdService;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* P2P会话管理器
|
||||
* 视频会话管理器
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
@Manager
|
||||
public class SessionManager {
|
||||
|
||||
@@ -30,8 +33,9 @@ public class SessionManager {
|
||||
* @return 会话
|
||||
*/
|
||||
public Session call(Client source, Client target) {
|
||||
final Session session = new Session(this.idService.buildUuid(), source, target, this);
|
||||
final Session session = new Session(this.idService.buildUuid(), source, target);
|
||||
this.sessions.put(session.getId(), session);
|
||||
log.info("创建视频会话:{} - {} - {}", session.getId(), session.getSource().clientId(), session.getTarget().clientId());
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -50,7 +54,22 @@ public class SessionManager {
|
||||
* @return 会话
|
||||
*/
|
||||
public Session remove(String sessionId) {
|
||||
return this.sessions.remove(sessionId);
|
||||
final Session session = this.sessions.remove(sessionId);
|
||||
if(session != null) {
|
||||
log.info("视频会话关闭:{} - {} - {}", sessionId, session.getSource().clientId(), session.getTarget().clientId());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有资源
|
||||
*
|
||||
* @param client 终端
|
||||
*/
|
||||
public void close(Client client) {
|
||||
this.sessions.values().stream()
|
||||
.filter(v -> v.hasClient(client))
|
||||
.forEach(Session::close);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.acgist.taoyao.boot.service.IdService;
|
||||
import com.acgist.taoyao.signal.client.ClientManager;
|
||||
import com.acgist.taoyao.signal.event.ApplicationEventAdapter;
|
||||
import com.acgist.taoyao.signal.party.media.RoomManager;
|
||||
import com.acgist.taoyao.signal.party.session.SessionManager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -32,6 +33,8 @@ public abstract class ProtocolAdapter implements Protocol {
|
||||
@Autowired
|
||||
protected ClientManager clientManager;
|
||||
@Autowired
|
||||
protected SessionManager sessionManager;
|
||||
@Autowired
|
||||
protected TaoyaoProperties taoyaoProperties;
|
||||
@Autowired
|
||||
protected ApplicationContext applicationContext;
|
||||
|
||||
@@ -2,8 +2,6 @@ package com.acgist.taoyao.signal.protocol;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.acgist.taoyao.boot.config.Constant;
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.boot.model.MessageCodeException;
|
||||
@@ -11,7 +9,6 @@ import com.acgist.taoyao.boot.utils.MapUtils;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.party.session.Session;
|
||||
import com.acgist.taoyao.signal.party.session.SessionManager;
|
||||
|
||||
/**
|
||||
* 会话信令适配器
|
||||
@@ -20,9 +17,6 @@ import com.acgist.taoyao.signal.party.session.SessionManager;
|
||||
*/
|
||||
public abstract class ProtocolSessionAdapter extends ProtocolClientAdapter {
|
||||
|
||||
@Autowired
|
||||
protected SessionManager sessionManager;
|
||||
|
||||
protected ProtocolSessionAdapter(String name, String signal) {
|
||||
super(name, signal);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ public class ClientCloseProtocol extends ProtocolClientAdapter implements Applic
|
||||
log.info("关闭终端:{}", clientId);
|
||||
// 释放房间终端
|
||||
this.roomManager.leave(client);
|
||||
// 释放会话终端
|
||||
this.sessionManager.close(client);
|
||||
// 终端下线事件
|
||||
this.publishEvent(new ClientOfflineEvent(client));
|
||||
}
|
||||
|
||||
@@ -12,11 +12,14 @@ import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.party.session.Session;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolSessionAdapter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 发起会话信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
@@ -43,6 +46,10 @@ public class SessionCallProtocol extends ProtocolSessionAdapter {
|
||||
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
|
||||
final String targetId = MapUtils.get(body, Constant.CLIENT_ID);
|
||||
final Client target = this.clientManager.clients(targetId);
|
||||
if(target == null) {
|
||||
log.warn("邀请对象无效:{}", clientId);
|
||||
return;
|
||||
}
|
||||
final Session session = this.sessionManager.call(client, target);
|
||||
message.setBody(Map.of(
|
||||
Constant.NAME, target.status().getName(),
|
||||
|
||||
@@ -2,11 +2,15 @@ package com.acgist.taoyao.signal.protocol.session;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
||||
import com.acgist.taoyao.boot.annotation.Description;
|
||||
import com.acgist.taoyao.boot.annotation.Protocol;
|
||||
import com.acgist.taoyao.boot.config.Constant;
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.event.session.SessionCloseEvent;
|
||||
import com.acgist.taoyao.signal.party.session.Session;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolSessionAdapter;
|
||||
|
||||
@@ -23,7 +27,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolSessionAdapter;
|
||||
""",
|
||||
flow = "终端->信令服务+)终端"
|
||||
)
|
||||
public class SessionCloseProtocol extends ProtocolSessionAdapter {
|
||||
public class SessionCloseProtocol extends ProtocolSessionAdapter implements ApplicationListener<SessionCloseEvent> {
|
||||
|
||||
public static final String SIGNAL = "session::close";
|
||||
|
||||
@@ -31,6 +35,14 @@ public class SessionCloseProtocol extends ProtocolSessionAdapter {
|
||||
super("关闭媒体信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(SessionCloseEvent event) {
|
||||
final Session session = event.getSession();
|
||||
final Map<String, String> body = Map.of(Constant.SESSION_ID, event.getSessionId());
|
||||
session.push(this.build(body));
|
||||
this.sessionManager.remove(session.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Session session, Client client, Message message, Map<String, Object> body) {
|
||||
session.push(message);
|
||||
|
||||
Reference in New Issue
Block a user