[*] 踢人 拉人 切换视频媒体

This commit is contained in:
acgist
2023-03-12 12:29:53 +08:00
parent 1bf8fbe415
commit ab8646a21f
39 changed files with 1779 additions and 352 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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) {
}

View File

@@ -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 响应
*/

View File

@@ -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);
}
/**

View File

@@ -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)) {

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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 {

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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 {

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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);

View File

@@ -80,6 +80,7 @@ public class MediaTransportWebRtcCreateProtocol extends ProtocolRoomAdapter {
clientWrapper.setRecvTransport(recvTransport);
// 拷贝属性
recvTransport.copy(responseBody);
// 消费媒体:不能在连接时调用
this.publishEvent(new MediaConsumeEvent(room, clientWrapper));
}
// 生产者

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}