有关wvp-pro,不了解的可以网上查找资料,如新手使用wvp-pro和ZLMediaKit的菜鸟说明(手把手教)是个开源的项目,处理流媒体和实现国标28181信令。这里只谈源码的关键部分、设计模式和思想。其项目主要设计模式是事件监听和观察者模式,在异步非阻塞编程中,这两种模式是最常用有效的。事件监听和观察者模式就是为了实现程序的异步运行。实现程序异步运行的方法有很多,例如利用消息中间件,主流的消息中间件有kafka、rocketmq,mqtt等等。
redis中也有类似消息中间件的功能,消息中间件,无非就是实现消息的发布者和订阅者,redisTemplate.convertAndSend()主题消息生产者,消费者需要实现MessageListener接口,然后用下面方法初始化配置消费者订阅的主题消息(springboot已有集成)。
RedisMessageListenerContainer.addMessageListener(MessageListener接口实现类, new PatternTopic(“your topic”));
详细的例子,可查找相关资料,这里不做过多的描述,这里是为了说明,生产者消费者模式无处不在。回到wvp-pro,信令交互就是用到生产者消费者模式,观察者模式。部分关键源码如下图。
javax.sip.SipListener (是第三方包):
其实底下是 java.util.EventListener
SIPProcessorObserver 的观察者模式:
事件监听模式例子:
/**
* 模拟业务数据
**/
public class DataBean {
public DataBean(String id,String info){
this.id=id;
this.info=info;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
private String id;
private String info;
}
import java.util.EventObject;
/**
* 事件类,用于封装事件源及一些与事件相关的参数.
* 可以封装更多要处理的参数,DataBean模拟业务数据
*/
public class MyEvent extends EventObject {
public DataBean getMeg() {
return meg;
}
public void setMeg(DataBean meg) {
this.meg = meg;
}
private DataBean meg;
public MyEvent(Object source ,DataBean meg) {
super(source);
this.meg = meg;
}
}
import java.util.ArrayList;
import java.util.List;
/**
* 事件发布者,事件来源,也就是消息生产者,生成Event,并通知监听程序处理。Event里面携带业务数据。
**/
public class EventPublisher {
private final List<MyEventListener> listeners;
public EventPublisher(){
listeners = new ArrayList<>();
}
// 给事件源注册监听器
public void addListener(MyEventListener listener) {
this.listeners.add(listener);
}
// 当事件发生时,通知注册在该事件源上的所有监听器做出相应的反应(调用回调方法)
public void publishAndnotifies(String id,String info) {
MyEvent event= new MyEvent(this,new DataBean(id,info));
for (MyEventListener listener : this.listeners) {
listener.handleEvent(event);
}
}
}
import java.util.EventListener;
/**
* 事件监听者,当事件发生时,处理事件里的消息。
**/
public interface MyEventListener extends EventListener {
void handleEvent(MyEvent event);
}
public class MyEventMain {
public static void main(String args[]){
EventPublisher publisher = new EventPublisher();
// 注册监听器
publisher.addListener(new MyEventListener() {
@Override
public void handleEvent(MyEvent e) {
System.out.println(e.getSource().toString());
System.out.println(e.getMeg().getId()+"::"+e.getMeg().getInfo());
}
});
//发布事件
publisher.publishAndnotifies("id1","message1");
publisher.publishAndnotifies("id2","message2");
}
}
附:wvp-pro几处优化的地方 ( 版本2.7.1 ):
com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.InviteRequestProcessor
public SIPResponse sendStreamAck(SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform)
/*content.append("m=video " + localPort + " RTP/AVP 96\r\n");
content.append("a=sendonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
if (sendRtpItem.isTcp()) {
content.append("a=connection:new\r\n");
if (!sendRtpItem.isTcpActive()) {
content.append("a=setup:active\r\n");
} else {
content.append("a=setup:passive\r\n");
}
}*/
if (sendRtpItem.isTcp()) {
content.append("m=video " + localPort + " TCP/RTP/AVP 96\r\n");
content.append("a=sendonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
content.append("a=connection:new\r\n");
if (!sendRtpItem.isTcpActive()) {
content.append("a=setup:active\r\n");
} else {
content.append("a=setup:passive\r\n");
}
} else {
content.append("m=video " + localPort + " RTP/AVP 96\r\n");
content.append("a=sendonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
}
// 替换上面注释
com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.NotifyRequestForCatalogProcessor
com.genersoft.iot.vmp.storager.impl.VideoManagerStorageImpl
public boolean resetChannels(String deviceId, List deviceChannelList) {}