完善ssrc符合国标,并完善很多小问题
This commit is contained in:
@@ -71,6 +71,16 @@ public interface ISIPCommander {
|
||||
*/
|
||||
public String playStreamCmd(Device device,String channelId);
|
||||
|
||||
/**
|
||||
* 请求回放视频流
|
||||
*
|
||||
* @param device 视频设备
|
||||
* @param channelId 预览通道
|
||||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
public String playbackStreamCmd(Device device,String channelId, String recordId, String startTime, String endTime);
|
||||
|
||||
/**
|
||||
* 语音广播
|
||||
*
|
||||
@@ -153,7 +163,7 @@ public interface ISIPCommander {
|
||||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
public boolean recordInfoQuery(Device device, String startTime, String endTime);
|
||||
public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
|
||||
|
||||
/**
|
||||
* 查询报警信息
|
||||
|
||||
@@ -35,7 +35,7 @@ public class SIPRequestHeaderProvider {
|
||||
private SipLayer layer;
|
||||
|
||||
@Autowired
|
||||
private SipConfig config;
|
||||
private SipConfig sipConfig;
|
||||
|
||||
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException {
|
||||
Request request = null;
|
||||
@@ -44,12 +44,12 @@ public class SIPRequestHeaderProvider {
|
||||
SipURI requestURI = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress());
|
||||
// via
|
||||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
|
||||
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(config.getSipIp(), config.getSipPort(),
|
||||
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(),
|
||||
device.getTransport(), viaTag);
|
||||
viaHeaders.add(viaHeader);
|
||||
// from
|
||||
SipURI fromSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),
|
||||
config.getSipIp() + ":" + config.getSipPort());
|
||||
sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
|
||||
Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI);
|
||||
FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag);
|
||||
// to
|
||||
@@ -78,11 +78,11 @@ public class SIPRequestHeaderProvider {
|
||||
SipURI requestLine = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress());
|
||||
//via
|
||||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
|
||||
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(config.getSipIp(), config.getSipPort(), device.getTransport(), viaTag);
|
||||
ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag);
|
||||
viaHeader.setRPort();
|
||||
viaHeaders.add(viaHeader);
|
||||
//from
|
||||
SipURI fromSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),config.getSipIp()+":"+config.getSipPort());
|
||||
SipURI fromSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),sipConfig.getSipIp()+":"+sipConfig.getSipPort());
|
||||
Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI);
|
||||
FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
|
||||
//to
|
||||
|
||||
@@ -17,6 +17,9 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.SsrcUtil;
|
||||
|
||||
import tk.mybatis.mapper.util.StringUtil;
|
||||
|
||||
/**
|
||||
* @Description:设备能力接口,用于定义设备的控制、查询能力
|
||||
@@ -27,7 +30,7 @@ import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
|
||||
public class SIPCommander implements ISIPCommander {
|
||||
|
||||
@Autowired
|
||||
private SipConfig config;
|
||||
private SipConfig sipConfig;
|
||||
|
||||
@Autowired
|
||||
private SIPRequestHeaderProvider headerProvider;
|
||||
@@ -46,7 +49,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
*/
|
||||
@Override
|
||||
public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) {
|
||||
return ptzCmd(device, channelId, leftRight, upDown, 0, config.getSpeed(), 0);
|
||||
return ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getSpeed(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +75,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
*/
|
||||
@Override
|
||||
public boolean ptzZoomCmd(Device device, String channelId, int inOut) {
|
||||
return ptzCmd(device, channelId, 0, 0, inOut, 0, config.getSpeed());
|
||||
return ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getSpeed());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,23 +138,19 @@ public class SIPCommander implements ISIPCommander {
|
||||
public String playStreamCmd(Device device, String channelId) {
|
||||
try {
|
||||
|
||||
//生成ssrc标识数据流 10位数字
|
||||
String ssrc = "";
|
||||
Random random = new Random();
|
||||
// ZLMediaServer最大识别7FFFFFFF即2147483647,所以随机数不能超过这个数
|
||||
ssrc = String.valueOf(random.nextInt(2147483647));
|
||||
String ssrc = SsrcUtil.getPlaySsrc();
|
||||
//
|
||||
StringBuffer content = new StringBuffer(200);
|
||||
content.append("v=0\r\n");
|
||||
content.append("o="+channelId+" 0 0 IN IP4 "+config.getSipIp()+"\r\n");
|
||||
content.append("o="+channelId+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n");
|
||||
content.append("s=Play\r\n");
|
||||
content.append("c=IN IP4 "+config.getMediaIp()+"\r\n");
|
||||
content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n");
|
||||
content.append("t=0 0\r\n");
|
||||
if(device.getTransport().equals("TCP")) {
|
||||
content.append("m=video "+config.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+sipConfig.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n");
|
||||
}
|
||||
if(device.getTransport().equals("UDP")) {
|
||||
content.append("m=video "+config.getMediaPort()+" RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+sipConfig.getMediaPort()+" RTP/AVP 96 98 97\r\n");
|
||||
}
|
||||
content.append("a=sendrecv\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
@@ -172,6 +171,53 @@ public class SIPCommander implements ISIPCommander {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求回放视频流
|
||||
*
|
||||
* @param device 视频设备
|
||||
* @param channelId 预览通道
|
||||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
@Override
|
||||
public String playbackStreamCmd(Device device, String channelId, String recordId, String startTime, String endTime) {
|
||||
try {
|
||||
|
||||
String ssrc = SsrcUtil.getPlayBackSsrc();
|
||||
//
|
||||
StringBuffer content = new StringBuffer(200);
|
||||
content.append("v=0\r\n");
|
||||
content.append("o="+channelId+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n");
|
||||
content.append("s=Playback\r\n");
|
||||
content.append("u="+recordId+":3\r\n");
|
||||
content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n");
|
||||
content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");
|
||||
if(device.getTransport().equals("TCP")) {
|
||||
content.append("m=video "+sipConfig.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n");
|
||||
}
|
||||
if(device.getTransport().equals("UDP")) {
|
||||
content.append("m=video "+sipConfig.getMediaPort()+" RTP/AVP 96 98 97\r\n");
|
||||
}
|
||||
content.append("a=recvonly\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
content.append("a=rtpmap:98 H264/90000\r\n");
|
||||
content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
||||
if(device.getTransport().equals("TCP")){
|
||||
content.append("a=setup:passive\r\n");
|
||||
content.append("a=connection:new\r\n");
|
||||
}
|
||||
content.append("y="+ssrc+"\r\n");//ssrc
|
||||
|
||||
Request request = headerProvider.createInviteRequest(device, content.toString(), null, "live", null);
|
||||
|
||||
transmitRequest(device, request);
|
||||
return ssrc;
|
||||
} catch ( SipException | ParseException | InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 语音广播
|
||||
@@ -323,22 +369,23 @@ public class SIPCommander implements ISIPCommander {
|
||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
@Override
|
||||
public boolean recordInfoQuery(Device device, String startTime, String endTime) {
|
||||
public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime) {
|
||||
|
||||
try {
|
||||
StringBuffer catalogXml = new StringBuffer(200);
|
||||
catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>");
|
||||
catalogXml.append("<Query>");
|
||||
catalogXml.append("<CmdType>RecordInfo</CmdType>");
|
||||
catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>");
|
||||
catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>");
|
||||
catalogXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>");
|
||||
catalogXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>");
|
||||
StringBuffer recordInfoXml = new StringBuffer(200);
|
||||
recordInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>");
|
||||
recordInfoXml.append("<Query>");
|
||||
recordInfoXml.append("<CmdType>RecordInfo</CmdType>");
|
||||
recordInfoXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>");
|
||||
recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>");
|
||||
recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>");
|
||||
recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>");
|
||||
recordInfoXml.append("<Secrecy>0</Secrecy>");
|
||||
// 大华NVR要求必须增加一个值为all的文本元素节点Type
|
||||
catalogXml.append("<Type>all</Type>");
|
||||
catalogXml.append("</Query>");
|
||||
recordInfoXml.append("<Type>all</Type>");
|
||||
recordInfoXml.append("</Query>");
|
||||
|
||||
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaRecordInfoBranch", "FromRecordInfoTag", "ToRecordInfoTag");
|
||||
Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "FromRecordInfoTag", "ToRecordInfoTag");
|
||||
transmitRequest(device, request);
|
||||
} catch (SipException | ParseException | InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
@@ -398,4 +445,5 @@ public class SIPCommander implements ISIPCommander {
|
||||
sipLayer.getUdpSipProvider().sendRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.SAXReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -36,6 +38,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||
|
||||
/**
|
||||
* @Description:MESSAGE请求处理器
|
||||
@@ -44,7 +47,9 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||
*/
|
||||
@Component
|
||||
public class MessageRequestProcessor implements ISIPRequestProcessor {
|
||||
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
|
||||
|
||||
private ServerTransaction transaction;
|
||||
|
||||
private SipLayer layer;
|
||||
@@ -58,9 +63,14 @@ public class MessageRequestProcessor implements ISIPRequestProcessor {
|
||||
@Autowired
|
||||
private EventPublisher publisher;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redis;
|
||||
|
||||
@Autowired
|
||||
private DeferredResultHolder deferredResultHolder;
|
||||
|
||||
private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_";
|
||||
|
||||
/**
|
||||
* 处理MESSAGE请求
|
||||
*
|
||||
@@ -77,14 +87,19 @@ public class MessageRequestProcessor implements ISIPRequestProcessor {
|
||||
Request request = evt.getRequest();
|
||||
|
||||
if (new String(request.getRawContent()).contains("<CmdType>Keepalive</CmdType>")) {
|
||||
logger.info("接收到KeepAlive消息");
|
||||
processMessageKeepAlive(evt);
|
||||
} else if (new String(request.getRawContent()).contains("<CmdType>Catalog</CmdType>")) {
|
||||
logger.info("接收到Catalog消息");
|
||||
processMessageCatalogList(evt);
|
||||
} else if (new String(request.getRawContent()).contains("<CmdType>DeviceInfo</CmdType>")) {
|
||||
logger.info("接收到DeviceInfo消息");
|
||||
processMessageDeviceInfo(evt);
|
||||
} else if (new String(request.getRawContent()).contains("<CmdType>Alarm</CmdType>")) {
|
||||
logger.info("接收到Alarm消息");
|
||||
processMessageAlarm(evt);
|
||||
} else if (new String(request.getRawContent()).contains("<CmdType>recordInfo</CmdType>")) {
|
||||
} else if (new String(request.getRawContent()).contains("<CmdType>RecordInfo</CmdType>")) {
|
||||
logger.info("接收到RecordInfo消息");
|
||||
processMessageRecordInfo(evt);
|
||||
}
|
||||
|
||||
@@ -245,6 +260,7 @@ public class MessageRequestProcessor implements ISIPRequestProcessor {
|
||||
|
||||
/***
|
||||
* 收到catalog设备目录列表请求 处理
|
||||
* TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致
|
||||
* @param evt
|
||||
*/
|
||||
private void processMessageRecordInfo(RequestEvent evt) {
|
||||
@@ -256,15 +272,15 @@ public class MessageRequestProcessor implements ISIPRequestProcessor {
|
||||
recordInfo.setDeviceId(deviceId);
|
||||
recordInfo.setName(XmlUtil.getText(rootElement,"Name"));
|
||||
recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement,"SumNum")));
|
||||
String sn = XmlUtil.getText(rootElement,"SN");
|
||||
Element recordListElement = rootElement.element("RecordList");
|
||||
if (recordListElement == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<Element> recordListIterator = recordListElement.elementIterator();
|
||||
List<RecordItem> recordList = new ArrayList<RecordItem>();
|
||||
if (recordListIterator != null) {
|
||||
|
||||
List<RecordItem> recordList = new ArrayList<RecordItem>();
|
||||
RecordItem record = new RecordItem();
|
||||
// 遍历DeviceList
|
||||
while (recordListIterator.hasNext()) {
|
||||
@@ -273,6 +289,7 @@ public class MessageRequestProcessor implements ISIPRequestProcessor {
|
||||
if (recordElement == null) {
|
||||
continue;
|
||||
}
|
||||
record = new RecordItem();
|
||||
record.setDeviceId(XmlUtil.getText(itemRecord,"DeviceID"));
|
||||
record.setName(XmlUtil.getText(itemRecord,"Name"));
|
||||
record.setFilePath(XmlUtil.getText(itemRecord,"FilePath"));
|
||||
@@ -281,13 +298,42 @@ public class MessageRequestProcessor implements ISIPRequestProcessor {
|
||||
record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord,"EndTime")));
|
||||
record.setSecrecy(itemRecord.element("Secrecy") == null? 0:Integer.parseInt(XmlUtil.getText(itemRecord,"Secrecy")));
|
||||
record.setType(XmlUtil.getText(itemRecord,"Type"));
|
||||
record.setRecordId(XmlUtil.getText(itemRecord,"RecordID"));
|
||||
record.setRecordId(XmlUtil.getText(itemRecord,"RecorderID"));
|
||||
recordList.add(record);
|
||||
}
|
||||
recordInfo.setRecordList(recordList);
|
||||
}
|
||||
|
||||
|
||||
// 存在录像且如果当前录像明细个数小于总条数,说明拆包返回,需要组装,暂不返回
|
||||
if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) {
|
||||
// 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分
|
||||
String cacheKey = CACHE_RECORDINFO_KEY+deviceId+sn;
|
||||
// TODO 暂时直接操作redis存储,后续封装专用缓存接口,改为本地内存缓存
|
||||
if (redis.hasKey(cacheKey)) {
|
||||
List<RecordItem> previousList = (List<RecordItem>) redis.get(cacheKey);
|
||||
if (previousList != null && previousList.size() > 0) {
|
||||
recordList.addAll(previousList);
|
||||
}
|
||||
// 本分支表示录像列表被拆包,且加上之前的数据还是不够,保存缓存返回,等待下个包再处理
|
||||
if (recordList.size() < recordInfo.getSumNum()) {
|
||||
redis.set(cacheKey, recordList, 180);
|
||||
return;
|
||||
} else {
|
||||
// 本分支表示录像被拆包,但加上之前的数据够足够,返回响应
|
||||
// 因设备心跳有监听redis过期机制,为提高性能,此处手动删除
|
||||
redis.del(cacheKey);
|
||||
}
|
||||
} else {
|
||||
// 本分支有两种可能:1、录像列表被拆包,且是第一个包,直接保存缓存返回,等待下个包再处理
|
||||
// 2、之前有包,但超时清空了,那么这次sn批次的响应数据已经不完整,等待过期时间后redis自动清空数据
|
||||
redis.set(cacheKey, recordList, 180);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
// 走到这里,有以下可能:1、没有录像信息,第一次收到recordinfo的消息即返回响应数据,无redis操作
|
||||
// 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作
|
||||
// 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据
|
||||
RequestMessage msg = new RequestMessage();
|
||||
msg.setDeviceId(deviceId);
|
||||
msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
|
||||
|
||||
@@ -45,7 +45,7 @@ import gov.nist.javax.sip.header.Expires;
|
||||
public class RegisterRequestProcessor implements ISIPRequestProcessor {
|
||||
|
||||
@Autowired
|
||||
private SipConfig config;
|
||||
private SipConfig sipConfig;
|
||||
|
||||
@Autowired
|
||||
private RegisterLogicHandler handler;
|
||||
@@ -77,7 +77,7 @@ public class RegisterRequestProcessor implements ISIPRequestProcessor {
|
||||
// 校验密码是否正确
|
||||
if (authorhead != null) {
|
||||
passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request,
|
||||
config.getSipPassword());
|
||||
sipConfig.getSipPassword());
|
||||
}
|
||||
|
||||
// 未携带授权头或者密码错误 回复401
|
||||
@@ -89,7 +89,7 @@ public class RegisterRequestProcessor implements ISIPRequestProcessor {
|
||||
System.out.println("密码错误 回复401");
|
||||
}
|
||||
response = layer.getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
|
||||
new DigestServerAuthenticationHelper().generateChallenge(layer.getHeaderFactory(), response, config.getSipDomain());
|
||||
new DigestServerAuthenticationHelper().generateChallenge(layer.getHeaderFactory(), response, sipConfig.getSipDomain());
|
||||
}
|
||||
// 携带授权头并且密码正确
|
||||
else if (passwordCorrect) {
|
||||
|
||||
Reference in New Issue
Block a user