[*] 踢人 拉人 切换视频媒体
This commit is contained in:
@@ -58,7 +58,7 @@ public class DataConsumer extends OperatorAdapter {
|
||||
@Override
|
||||
public void remove() {
|
||||
log.info("移除数据消费者:{} - {}", this.streamId, this.consumerId);
|
||||
this.room.getDataProducers().remove(this.consumerId);
|
||||
this.room.getDataConsumers().remove(this.consumerId);
|
||||
this.dataProducer.getDataConsumers().remove(this.consumerId);
|
||||
this.consumerClient.getDataConsumers().remove(this.consumerId);
|
||||
}
|
||||
|
||||
@@ -170,6 +170,18 @@ public class Room extends OperatorAdapter {
|
||||
return this.mediaClient.request(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单播消息
|
||||
*
|
||||
* @param to 接收终端
|
||||
* @param message 消息
|
||||
*/
|
||||
public void unicast(String to, Message message) {
|
||||
this.clients.keySet().stream()
|
||||
.filter(v -> Objects.equals(to, v.clientId()))
|
||||
.forEach(v -> v.push(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播消息
|
||||
*
|
||||
@@ -305,5 +317,16 @@ public class Room extends OperatorAdapter {
|
||||
);
|
||||
this.clients.values().forEach(ClientWrapper::log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理没有关联终端的资源
|
||||
*/
|
||||
public void releaseUnknowClient() {
|
||||
this.transports.values().stream().filter(v -> !this.clients.containsKey(v.getClient())).forEach(Transport::close);
|
||||
this.consumers.values().stream().filter(v -> !this.clients.containsValue(v.getConsumerClient())).forEach(Consumer::close);
|
||||
this.producers.values().stream().filter(v -> !this.clients.containsValue(v.getProducerClient())).forEach(Producer::close);
|
||||
this.dataConsumers.values().stream().filter(v -> !this.clients.containsValue(v.getConsumerClient())).forEach(DataConsumer::close);
|
||||
this.dataProducers.values().stream().filter(v -> !this.clients.containsValue(v.getProducerClient())).forEach(DataProducer::close);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import com.acgist.taoyao.boot.annotation.Manager;
|
||||
import com.acgist.taoyao.boot.config.Constant;
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
@@ -37,6 +39,11 @@ public class RoomManager {
|
||||
this.clientManager = clientManager;
|
||||
this.rooms = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
@Scheduled(cron = "${taoyao.scheduled.room:0 0/5 * * * ?}")
|
||||
public void scheduled() {
|
||||
this.releaseUnknowClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param roomId 房间标识
|
||||
@@ -161,5 +168,12 @@ public class RoomManager {
|
||||
);
|
||||
this.rooms.forEach(Room::log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理没有关联终端的资源
|
||||
*/
|
||||
private void releaseUnknowClient() {
|
||||
this.rooms.forEach(Room::releaseUnknowClient);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ public abstract class ProtocolClientAdapter extends ProtocolAdapter {
|
||||
/**
|
||||
* 处理终端信令
|
||||
*
|
||||
* @param clientId 终端标识
|
||||
* @param clientId 终端标识
|
||||
* @param clientType 终端类型
|
||||
* @param client 终端
|
||||
* @param message 消息
|
||||
* @param body 消息主体
|
||||
* @param client 终端
|
||||
* @param message 消息
|
||||
* @param body 消息主体
|
||||
*/
|
||||
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
|
||||
}
|
||||
|
||||
@@ -33,13 +33,13 @@ public abstract class ProtocolControlAdapter extends ProtocolClientAdapter {
|
||||
/**
|
||||
* 处理终端控制信令
|
||||
*
|
||||
* @param clientId 终端标识
|
||||
* @param clientType 终端类型
|
||||
* @param room 房间
|
||||
* @param client 终端
|
||||
* @param clientId 终端标识
|
||||
* @param clientType 终端类型
|
||||
* @param room 房间
|
||||
* @param client 终端
|
||||
* @param targetClient 目标
|
||||
* @param message 消息
|
||||
* @param body 消息主体
|
||||
* @param message 消息
|
||||
* @param body 消息主体
|
||||
*/
|
||||
public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map<String, Object> body) {
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public abstract class ProtocolControlAdapter extends ProtocolClientAdapter {
|
||||
* 请求终端执行控制信令
|
||||
*
|
||||
* @param clientId 终端ID
|
||||
* @param request 请求
|
||||
* @param request 请求
|
||||
*
|
||||
* @return 响应
|
||||
*/
|
||||
|
||||
@@ -31,9 +31,7 @@ public abstract class ProtocolRoomAdapter extends ProtocolClientAdapter {
|
||||
if(!this.authenticate(room, client)) {
|
||||
throw MessageCodeException.of("终端没有房间权限:" + clientId);
|
||||
}
|
||||
synchronized (room) {
|
||||
this.execute(clientId, clientType, room, client, room.getMediaClient(), message, body);
|
||||
}
|
||||
this.execute(clientId, clientType, room, client, room.getMediaClient(), message, body);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,12 @@ import lombok.extern.slf4j.Slf4j;
|
||||
终端生产媒体当前房间所有终端根据订阅类型自动消费媒体
|
||||
终端创建WebRTC消费通道根据订阅类型自动消费当前房间已有媒体
|
||||
""",
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID"
|
||||
"producerId": "生产者ID"
|
||||
}
|
||||
""",
|
||||
flow = {
|
||||
"终端-[生产媒体]>信令服务-[其他终端消费])信令服务",
|
||||
"终端-[创建WebRTC消费通道]>信令服务-[消费其他终端])信令服务",
|
||||
@@ -118,7 +124,7 @@ public class MediaConsumeProtocol extends ProtocolRoomAdapter implements Applica
|
||||
private void consume(Room room, ClientWrapper consumerClientWrapper, Producer producer, Message message) {
|
||||
final Client mediaClient = room.getMediaClient();
|
||||
final String consumerClientId = consumerClientWrapper.getClientId();
|
||||
final String streamId = producer.getStreamId() + "->" + consumerClientId;
|
||||
final String streamId = Constant.STREAM_ID_CONSUMER.apply(producer.getStreamId(), consumerClientId);
|
||||
final ClientWrapper producerClientWrapper = producer.getProducerClient();
|
||||
final String producerClientId = producerClientWrapper.getClientId();
|
||||
if(consumerClientWrapper.consumed(producer)) {
|
||||
|
||||
@@ -69,6 +69,7 @@ public class MediaConsumerCloseProtocol extends ProtocolRoomAdapter implements A
|
||||
if(clientType.mediaClient()) {
|
||||
consumer.close();
|
||||
} else if(clientType.mediaServer()) {
|
||||
// TODO:路由到真实消费者
|
||||
consumer.remove();
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
|
||||
@@ -59,6 +59,7 @@ public class MediaConsumerPauseProtocol extends ProtocolRoomAdapter implements A
|
||||
final Consumer consumer = room.consumer(consumerId);
|
||||
consumer.pause();
|
||||
} else if(clientType.mediaServer()) {
|
||||
// TODO:路由到真实消费者
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
public class MediaConsumerRequestKeyFrameProtocol {
|
||||
import java.util.Map;
|
||||
|
||||
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.boot.utils.MapUtils;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 请求关键帧信令
|
||||
* 注意:视频才有
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID",
|
||||
"consumerId": "消费者ID"
|
||||
}
|
||||
""",
|
||||
flow = "终端->信令服务->媒体服务->信令服务->终端"
|
||||
)
|
||||
public class MediaConsumerRequestKeyFrameProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::consumer::request::key::frame";
|
||||
|
||||
public MediaConsumerRequestKeyFrameProtocol() {
|
||||
super("请求关键帧信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
body.put(Constant.CLIENT_ID, clientId);
|
||||
mediaClient.push(message);
|
||||
} else if(clientType.mediaServer()) {
|
||||
final String requestClientId = MapUtils.remove(body, Constant.CLIENT_ID);
|
||||
room.unicast(requestClientId, message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
)
|
||||
public class MediaConsumerResumeProtocol extends ProtocolRoomAdapter implements ApplicationListener<MediaConsumerResumeEvent> {
|
||||
|
||||
public static final String SIGNAL = "media::consumer::resumt";
|
||||
public static final String SIGNAL = "media::consumer::resume";
|
||||
|
||||
public MediaConsumerResumeProtocol() {
|
||||
super("恢复消费者信令", SIGNAL);
|
||||
@@ -57,8 +57,9 @@ public class MediaConsumerResumeProtocol extends ProtocolRoomAdapter implements
|
||||
if(clientType.mediaClient()) {
|
||||
final String consumerId = MapUtils.get(body, Constant.CONSUMER_ID);
|
||||
final Consumer consumer = room.consumer(consumerId);
|
||||
consumer.pause();
|
||||
consumer.resume();
|
||||
} else if(clientType.mediaServer()) {
|
||||
// TODO:路由到真实消费者
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
|
||||
@@ -1,5 +1,57 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
public class MediaConsumerSetPreferredLayersProtocol {
|
||||
import java.util.Map;
|
||||
|
||||
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.boot.utils.MapUtils;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 修改最佳空间层和时间层信令
|
||||
* 空间层(spatialLayer):分辨率
|
||||
* 时间层(temporalLayer):帧率
|
||||
* 码率:数据大小和时间的比值
|
||||
* 注意:只有simulcast和SVC消费者有效
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID",
|
||||
"consumerId": "消费者ID",
|
||||
"spatialLayer": 最佳空间层,
|
||||
"temporalLayer": 最佳时间层
|
||||
}
|
||||
""",
|
||||
flow = "终端->信令服务->媒体服务->信令服务->终端"
|
||||
)
|
||||
public class MediaConsumerSetPreferredLayersProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::consumer::set::preferred::layers";
|
||||
|
||||
public MediaConsumerSetPreferredLayersProtocol() {
|
||||
super("修改最佳空间层和时间层信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
body.put(Constant.CLIENT_ID, clientId);
|
||||
mediaClient.push(message);
|
||||
} else if(clientType.mediaServer()) {
|
||||
final String requestClientId = MapUtils.remove(body, Constant.CLIENT_ID);
|
||||
room.unicast(requestClientId, message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
public class MediaConsumerSetPriorityProtocol {
|
||||
import java.util.Map;
|
||||
|
||||
import com.acgist.taoyao.boot.annotation.Description;
|
||||
import com.acgist.taoyao.boot.annotation.Protocol;
|
||||
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.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 设置消费者优先级信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID",
|
||||
"consumerId": "消费者ID",
|
||||
"priority": 优先级(1~255)
|
||||
}
|
||||
"""
|
||||
)
|
||||
public class MediaConsumerSetPriorityProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::consumer::set::priority";
|
||||
|
||||
public MediaConsumerSetPriorityProtocol() {
|
||||
super("设置消费者优先级信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
client.push(mediaClient.request(message));
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,96 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
public class MediaDataConsumeProtocol {
|
||||
import java.util.Map;
|
||||
|
||||
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.boot.model.MessageCodeException;
|
||||
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.media.ClientWrapper;
|
||||
import com.acgist.taoyao.signal.party.media.DataConsumer;
|
||||
import com.acgist.taoyao.signal.party.media.DataProducer;
|
||||
import com.acgist.taoyao.signal.party.media.Room;
|
||||
import com.acgist.taoyao.signal.party.media.Transport;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 消费数据信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
@Protocol
|
||||
@Description(
|
||||
memo = """
|
||||
数据通道消费者不会自动创建,需要用户自己订阅生产者。
|
||||
""",
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID"
|
||||
"producerId": "生产者ID",
|
||||
}
|
||||
""",
|
||||
flow = {
|
||||
"终端=>信令服务->媒体服务->信令服务->媒体服务"
|
||||
}
|
||||
)
|
||||
public class MediaDataConsumeProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::data::consume";
|
||||
|
||||
public MediaDataConsumeProtocol() {
|
||||
super("消费数据信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
final String producerId = MapUtils.get(body, Constant.PRODUCER_ID);
|
||||
final DataProducer dataProducer = room.dataProducer(producerId);
|
||||
if(dataProducer == null) {
|
||||
throw MessageCodeException.of("没有提供数据生产:" + producerId);
|
||||
}
|
||||
if(clientType.mediaClient()) {
|
||||
final ClientWrapper dataConsumerClientWrapper = room.clientWrapper(client);
|
||||
final String dataConsumerClientId = dataConsumerClientWrapper.getClientId();
|
||||
final ClientWrapper dataProducerClientWrapper = dataProducer.getProducerClient();
|
||||
final String dataProducerClientId = dataProducerClientWrapper.getClientId();
|
||||
final Transport recvTransport = dataConsumerClientWrapper.getRecvTransport();
|
||||
final String streamId = Constant.STREAM_ID_CONSUMER.apply(dataProducer.getStreamId(), dataConsumerClientId);
|
||||
body.put(Constant.ROOM_ID, room.getRoomId());
|
||||
body.put(Constant.CLIENT_ID, dataConsumerClientId);
|
||||
body.put(Constant.SOURCE_ID, dataProducerClientId);
|
||||
body.put(Constant.STREAM_ID, streamId);
|
||||
body.put(Constant.PRODUCER_ID, dataProducer.getProducerId());
|
||||
body.put(Constant.TRANSPORT_ID, recvTransport.getTransportId());
|
||||
body.put(Constant.RTP_CAPABILITIES, dataConsumerClientWrapper.getRtpCapabilities());
|
||||
body.put(Constant.SCTP_CAPABILITIES, dataConsumerClientWrapper.getSctpCapabilities());
|
||||
mediaClient.push(message);
|
||||
} else if(clientType.mediaServer()) {
|
||||
final String streamId = MapUtils.get(body, Constant.STREAM_ID);
|
||||
final String consumerId = MapUtils.get(body, Constant.CONSUMER_ID);
|
||||
final String dataConsumerClientId = MapUtils.get(body, Constant.CLIENT_ID);
|
||||
final ClientWrapper dataConsumerClientWrapper = room.clientWrapper(dataConsumerClientId);
|
||||
final Map<String, DataConsumer> roomDataConsumers = room.getDataConsumers();
|
||||
final Map<String, DataConsumer> clientDataConsumers = dataConsumerClientWrapper.getDataConsumers();
|
||||
final Map<String, DataConsumer> producerDataConsumers = dataProducer.getDataConsumers();
|
||||
final DataConsumer dataConsumer = new DataConsumer(streamId, consumerId, room, dataProducer, dataConsumerClientWrapper);
|
||||
final DataConsumer oldDataRoomConsumer = roomDataConsumers.put(consumerId, dataConsumer);
|
||||
final DataConsumer oldDataClientConsumer = clientDataConsumers.put(consumerId, dataConsumer);
|
||||
final DataConsumer oldDataProducerConsumer = producerDataConsumers.put(consumerId, dataConsumer);
|
||||
if(oldDataRoomConsumer != null || oldDataClientConsumer != null || oldDataProducerConsumer != null) {
|
||||
log.warn("消费者已经存在:{}", consumerId);
|
||||
}
|
||||
final Client consumeClient = dataConsumerClientWrapper.getClient();
|
||||
consumeClient.push(message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public class MediaDataConsumerCloseProtocol extends ProtocolRoomAdapter implemen
|
||||
if(clientType.mediaClient()) {
|
||||
dataConsumer.close();
|
||||
} else if(clientType.mediaServer()) {
|
||||
// TODO:路由到真实消费者
|
||||
dataConsumer.remove();
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
public class MediaDataConsumerStatusProtocol {
|
||||
import java.util.Map;
|
||||
|
||||
import com.acgist.taoyao.boot.annotation.Description;
|
||||
import com.acgist.taoyao.boot.annotation.Protocol;
|
||||
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.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 查询数据消费者状态信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID",
|
||||
"consumerId": "数据消费者ID"
|
||||
}
|
||||
""",
|
||||
flow = "终端=>信令服务->媒体服务->信令服务->终端"
|
||||
)
|
||||
public class MediaDataConsumerStatusProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::data::consumer::status";
|
||||
|
||||
public MediaDataConsumerStatusProtocol() {
|
||||
super("查询数据消费者状态信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
client.push(mediaClient.request(message));
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,75 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
public class MediaDataProduceProtocol {
|
||||
import java.util.Map;
|
||||
|
||||
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.boot.utils.MapUtils;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.party.media.ClientWrapper;
|
||||
import com.acgist.taoyao.signal.party.media.DataProducer;
|
||||
import com.acgist.taoyao.signal.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 生产数据信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
@Protocol
|
||||
@Description(
|
||||
body = {
|
||||
"""
|
||||
{
|
||||
"roomId": "房间标识",
|
||||
"transportId": "通道标识"
|
||||
}
|
||||
"""
|
||||
},
|
||||
flow = "终端->信令服务->媒体服务->信令服务->终端"
|
||||
)
|
||||
public class MediaDataProduceProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::data::produce";
|
||||
|
||||
public MediaDataProduceProtocol() {
|
||||
super("生产数据信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
final String streamId = Constant.STREAM_ID_PRODUCER.apply(Constant.DATA, clientId);
|
||||
body.put(Constant.CLIENT_ID, clientId);
|
||||
body.put(Constant.STREAM_ID, streamId);
|
||||
final Message response = room.request(message);
|
||||
final Map<String, Object> responseBody = response.body();
|
||||
final String producerId = MapUtils.get(responseBody, Constant.PRODUCER_ID);
|
||||
final ClientWrapper producerClientWrapper = room.clientWrapper(client);
|
||||
final Map<String, DataProducer> roomDataProducers = room.getDataProducers();
|
||||
final Map<String, DataProducer> clientDataProducers = producerClientWrapper.getDataProducers();
|
||||
final DataProducer dataProducer = new DataProducer(streamId, producerId, room, producerClientWrapper);
|
||||
final DataProducer oldRoomDataProducer = roomDataProducers.put(producerId, dataProducer);
|
||||
final DataProducer oldClientDataProducer = clientDataProducers.put(producerId, dataProducer);
|
||||
if(oldRoomDataProducer != null || oldClientDataProducer != null) {
|
||||
log.warn("数据生产者已经存在:{}", producerId);
|
||||
}
|
||||
final Message responseMessage = response.cloneWithoutBody();
|
||||
responseMessage.setBody(Map.of(
|
||||
Constant.STREAM_ID, streamId,
|
||||
Constant.PRODUCER_ID, producerId
|
||||
));
|
||||
room.broadcast(responseMessage);
|
||||
log.info("{}生产数据:{} - {}", clientId, streamId, producerId);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ public class MediaDataProducerCloseProtocol extends ProtocolRoomAdapter implemen
|
||||
if(clientType.mediaClient()) {
|
||||
dataProducer.close();
|
||||
} else if(clientType.mediaServer()) {
|
||||
// TODO:路由到真实消费者
|
||||
dataProducer.remove();
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
public class MediaDataProducerStatusProtocol {
|
||||
import java.util.Map;
|
||||
|
||||
import com.acgist.taoyao.boot.annotation.Description;
|
||||
import com.acgist.taoyao.boot.annotation.Protocol;
|
||||
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.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 查询数据生产者状态信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID",
|
||||
"producerId": "数据生产者ID"
|
||||
}
|
||||
""",
|
||||
flow = "终端=>信令服务->媒体服务->信令服务->终端"
|
||||
)
|
||||
public class MediaDataProducerStatusProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::data::producer::status";
|
||||
|
||||
public MediaDataProducerStatusProtocol() {
|
||||
super("查询数据生产者状态信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
client.push(mediaClient.request(message));
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.acgist.taoyao.boot.annotation.Description;
|
||||
import com.acgist.taoyao.boot.annotation.Protocol;
|
||||
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.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 媒体重启ICE信令
|
||||
* 重启ICE信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@@ -25,10 +32,23 @@ import com.acgist.taoyao.boot.annotation.Protocol;
|
||||
}
|
||||
"""
|
||||
},
|
||||
flow = "终端->信令服务->媒体服务->信令服务->终端"
|
||||
flow = "终端=>信令服务->媒体服务->信令服务->终端"
|
||||
)
|
||||
public class MediaIceRestartProtocol {
|
||||
public class MediaIceRestartProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::ice::restart";
|
||||
|
||||
public MediaIceRestartProtocol() {
|
||||
super("重启ICE信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
client.push(mediaClient.request(message));
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class MediaProduceProtocol extends ProtocolRoomAdapter {
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
final String kind = MapUtils.get(body, Constant.KIND);
|
||||
final String streamId = kind + "::" + clientId;
|
||||
final String streamId = Constant.STREAM_ID_PRODUCER.apply(kind, clientId);
|
||||
body.put(Constant.CLIENT_ID, clientId);
|
||||
body.put(Constant.STREAM_ID, streamId);
|
||||
final Message response = room.request(message);
|
||||
@@ -69,7 +69,7 @@ public class MediaProduceProtocol extends ProtocolRoomAdapter {
|
||||
Constant.KIND, kind,
|
||||
Constant.STREAM_ID, streamId,
|
||||
Constant.PRODUCER_ID, producerId
|
||||
));
|
||||
));
|
||||
room.broadcast(responseMessage);
|
||||
log.info("{}生产媒体:{} - {}", clientId, streamId, producerId);
|
||||
this.publishEvent(new MediaConsumeEvent(room, producer));
|
||||
|
||||
@@ -66,6 +66,7 @@ public class MediaProducerCloseProtocol extends ProtocolRoomAdapter implements A
|
||||
if(clientType.mediaClient()) {
|
||||
producer.close();
|
||||
} else if(clientType.mediaServer()) {
|
||||
// TODO:路由到真实消费者
|
||||
producer.remove();
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
|
||||
@@ -57,6 +57,7 @@ public class MediaProducerPauseProtocol extends ProtocolRoomAdapter implements A
|
||||
final Producer producer = room.producer(producerId);
|
||||
producer.pause();
|
||||
} else if(clientType.mediaServer()) {
|
||||
// TODO:路由到真实消费者
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
|
||||
@@ -57,6 +57,7 @@ public class MediaProducerResumeProtocol extends ProtocolRoomAdapter implements
|
||||
final Producer producer = room.producer(producerId);
|
||||
producer.resume();
|
||||
} else if(clientType.mediaServer()) {
|
||||
// TODO:路由到真实消费者
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
|
||||
@@ -80,6 +80,7 @@ public class MediaTransportWebRtcCreateProtocol extends ProtocolRoomAdapter {
|
||||
clientWrapper.setRecvTransport(recvTransport);
|
||||
// 拷贝属性
|
||||
recvTransport.copy(responseBody);
|
||||
// 消费媒体:不能在连接时调用
|
||||
this.publishEvent(new MediaConsumeEvent(room, clientWrapper));
|
||||
}
|
||||
// 生产者
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
package com.acgist.taoyao.signal.protocol.media;
|
||||
|
||||
public class MediaVideoOrientationChangeProtocol {
|
||||
import java.util.Map;
|
||||
|
||||
import com.acgist.taoyao.boot.annotation.Description;
|
||||
import com.acgist.taoyao.boot.annotation.Protocol;
|
||||
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.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 视频方向变化信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
""",
|
||||
flow = "媒体服务->信令服务->终端"
|
||||
)
|
||||
public class MediaVideoOrientationChangeProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "media::video::orientation::change";
|
||||
|
||||
public MediaVideoOrientationChangeProtocol() {
|
||||
super("视频方向变化信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaServer()) {
|
||||
room.broadcast(message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,48 @@
|
||||
package com.acgist.taoyao.signal.protocol.room;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.boot.utils.MapUtils;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 踢出房间信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class RoomExpelProtocol {
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID",
|
||||
"clientId": "终端ID"
|
||||
}
|
||||
""",
|
||||
flow = "终端->信令服务->终端"
|
||||
)
|
||||
public class RoomExpelProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "room::expel";
|
||||
|
||||
public RoomExpelProtocol() {
|
||||
super("踢出房间信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
final String expelClientId = MapUtils.get(body, Constant.CLIENT_ID);
|
||||
room.unicast(expelClientId, message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,50 @@
|
||||
package com.acgist.taoyao.signal.protocol.room;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.boot.utils.MapUtils;
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 邀请房间信令
|
||||
* 邀请终端信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class RoomInviteProtocol {
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
"roomId": "房间ID",
|
||||
"clientId": "终端ID",
|
||||
"password": "密码(选填)"
|
||||
}
|
||||
""",
|
||||
flow = "终端->信令服务->终端"
|
||||
)
|
||||
public class RoomInviteProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "room::invite";
|
||||
|
||||
public RoomInviteProtocol() {
|
||||
super("邀请终端信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
if(clientType.mediaClient()) {
|
||||
final String inviteClientId = MapUtils.get(body, Constant.CLIENT_ID);
|
||||
body.put(Constant.PASSWORD, room.getPassword());
|
||||
this.clientManager.unicast(inviteClientId, message);
|
||||
} else {
|
||||
this.logNoAdapter(clientType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user