songww
2020-05-13 6ecd801c2365feb4e65f6684065aa97f11615797
增加设备删除接口,只允许删除离线设备;增加视频停止播放接口
20个文件已修改
1个文件已添加
1 文件已重命名
332 ■■■■ 已修改文件
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
@@ -12,6 +12,8 @@
    Integer sipPort;
    @Value("${sip.domain}")
    String sipDomain;
    @Value("${sip.id}")
    String sipId;
    @Value("${sip.password}")
    String sipPassword;
    @Value("${media.ip}")
@@ -77,6 +79,12 @@
    public void setSpeed(Integer speed) {
        this.speed = speed;
    }
    public String getSipId() {
        return sipId;
    }
    public void setSipId(String sipId) {
        this.sipId = sipId;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
@@ -117,7 +117,7 @@
    @Override
    public void processRequest(RequestEvent evt) {
        ISIPRequestProcessor processor = processorFactory.createRequestProcessor(evt);
        processor.process(evt, this, getServerTransaction(evt));
        processor.process(evt, this);
    }
    @Override
@@ -200,7 +200,7 @@
    }
    private ServerTransaction getServerTransaction(RequestEvent evt) {
    public ServerTransaction getServerTransaction(RequestEvent evt) {
        Request request = evt.getRequest();
        ServerTransaction serverTransaction = evt.getServerTransaction();
        // 判断TCP还是UDP
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java
File was renamed from src/main/java/com/genersoft/iot/vmp/gb28181/utils/SsrcUtil.java
@@ -1,4 +1,4 @@
package com.genersoft.iot.vmp.gb28181.utils;
package com.genersoft.iot.vmp.gb28181.session;
import java.util.ArrayList;
import java.util.List;
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
New file
@@ -0,0 +1,41 @@
package com.genersoft.iot.vmp.gb28181.session;
import java.util.concurrent.ConcurrentHashMap;
import javax.sip.ClientTransaction;
import org.springframework.stereotype.Component;
/**
 * @Description:视频流session管理器,管理视频预览、预览回放的通信句柄
 * @author: songww
 * @date:   2020年5月13日 下午4:03:02
 */
@Component
public class VideoStreamSessionManager {
    private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
    public String createPlaySsrc(){
        String ssrc = SsrcUtil.getPlaySsrc();
        return ssrc;
    }
    public String createPlayBackSsrc(){
        String ssrc = SsrcUtil.getPlayBackSsrc();
        return ssrc;
    }
    public void put(String ssrc,ClientTransaction transaction){
        sessionMap.put(ssrc, transaction);
    }
    public ClientTransaction get(String ssrc){
        return sessionMap.get(ssrc);
    }
    public void remove(String ssrc) {
        sessionMap.remove(ssrc);
        SsrcUtil.releaseSsrc(ssrc);
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -82,6 +82,13 @@
    public String playbackStreamCmd(Device device,String channelId, String startTime, String endTime);
    
    /**
     * 视频流停止
     *
     * @param ssrc  ssrc
     */
    public void streamByeCmd(String ssrc);
    /**
     * 语音广播
     * 
     * @param device  视频设备
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
@@ -46,14 +46,15 @@
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        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(),
        SipURI fromSipURI = layer.getAddressFactory().createSipURI(sipConfig.getSipId(),
                sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
        Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI);
        FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress());
        SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain());
        Address toAddress = layer.getAddressFactory().createAddress(toSipURI);
        ToHeader toHeader = layer.getHeaderFactory().createToHeader(toAddress, toTag);
        // callid
@@ -71,6 +72,49 @@
        return request;
    }
    
//    public Request createInviteRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException {
//        Request request = null;
//        Host host = device.getHost();
//        //请求行
//        SipURI requestLine = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress());
//        //via
//        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
//        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(),sipConfig.getSipIp()+":"+sipConfig.getSipPort());
//        Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI);
//        FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
//        //to
//        SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),host.getAddress());
//        Address toAddress = layer.getAddressFactory().createAddress(toSipURI);
//        ToHeader toHeader = layer.getHeaderFactory().createToHeader(toAddress,null);
//
//        //callid
//        CallIdHeader callIdHeader = null;
//        if(device.getTransport().equals("TCP")) {
//            callIdHeader = layer.getTcpSipProvider().getNewCallId();
//        }
//        if(device.getTransport().equals("UDP")) {
//            callIdHeader = layer.getUdpSipProvider().getNewCallId();
//        }
//
//        //Forwards
//        MaxForwardsHeader maxForwards = layer.getHeaderFactory().createMaxForwardsHeader(70);
//
//        //ceq
//        CSeqHeader cSeqHeader = layer.getHeaderFactory().createCSeqHeader(1L, Request.INVITE);
//        request = layer.getMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
//
//        Address concatAddress = layer.getAddressFactory().createAddress(layer.getAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
//        request.addHeader(layer.getHeaderFactory().createContactHeader(concatAddress));
//
//        ContentTypeHeader contentTypeHeader = layer.getHeaderFactory().createContentTypeHeader("Application", "SDP");
//        request.setContent(content, contentTypeHeader);
//        return request;
//    }
    public Request createInviteRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException {
        Request request = null;
        Host host = device.getHost();
@@ -82,11 +126,11 @@
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        //from
        SipURI fromSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),sipConfig.getSipIp()+":"+sipConfig.getSipPort());
        SipURI fromSipURI = layer.getAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain());
        Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI);
        FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
        //to
        SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),host.getAddress());
        SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),sipConfig.getSipDomain());
        Address toAddress = layer.getAddressFactory().createAddress(toSipURI);
        ToHeader toHeader = layer.getHeaderFactory().createToHeader(toAddress,null);
@@ -101,9 +145,14 @@
        
        //Forwards
        MaxForwardsHeader maxForwards = layer.getHeaderFactory().createMaxForwardsHeader(70);
        //ceq
        CSeqHeader cSeqHeader = layer.getHeaderFactory().createCSeqHeader(1L, Request.INVITE);
        request = layer.getMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
        Address concatAddress = layer.getAddressFactory().createAddress(layer.getAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
        request.addHeader(layer.getHeaderFactory().createContactHeader(concatAddress));
        ContentTypeHeader contentTypeHeader = layer.getHeaderFactory().createContentTypeHeader("Application", "SDP");
        request.setContent(content, contentTypeHeader);
        return request;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -3,8 +3,11 @@
import java.text.ParseException;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import javax.sip.TransactionDoesNotExistException;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,10 +16,10 @@
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
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;
/**    
 * @Description:设备能力接口,用于定义设备的控制、查询能力   
@@ -34,6 +37,9 @@
    
    @Autowired
    private SipLayer sipLayer;
    @Autowired
    private VideoStreamSessionManager streamSession;
    
    /**
     * 云台方向放控制,使用配置文件中的默认镜头移动速度
@@ -135,11 +141,11 @@
    public String playStreamCmd(Device device, String channelId) {
        try {
            
            String ssrc = SsrcUtil.getPlaySsrc();
            String ssrc = streamSession.createPlaySsrc();
            //
            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("o="+channelId+" 0 0 IN IP4 "+sipConfig.getMediaIp()+"\r\n");
            content.append("s=Play\r\n");
            content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n");
            content.append("t=0 0\r\n");
@@ -161,7 +167,8 @@
            
            Request request = headerProvider.createInviteRequest(device, content.toString(), null, "live", null);
    
            transmitRequest(device, request);
            ClientTransaction transaction = transmitRequest(device, request);
            streamSession.put(ssrc, transaction);
            return ssrc;
        } catch ( SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
@@ -181,11 +188,11 @@
    public String playbackStreamCmd(Device device, String channelId, String startTime, String endTime) {
        try {
            
            String ssrc = SsrcUtil.getPlayBackSsrc();
            String ssrc = streamSession.createPlayBackSsrc();
            //
            StringBuffer content = new StringBuffer(200);
            content.append("v=0\r\n");
            content.append("o="+device.getDeviceId()+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n");
            content.append("o="+device.getDeviceId()+" 0 0 IN IP4 "+sipConfig.getMediaIp()+"\r\n");
            content.append("s=Playback\r\n");
            content.append("u="+channelId+":3\r\n");
            content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n");
@@ -208,11 +215,48 @@
            
            Request request = headerProvider.createInviteRequest(device, content.toString(), null, "live", null);
    
            transmitRequest(device, request);
            ClientTransaction transaction = transmitRequest(device, request);
            streamSession.put(ssrc, transaction);
            return ssrc;
        } catch ( SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 视频流停止
     *
     * @param device  视频设备
     * @param channelId  预览通道
     */
    @Override
    public void streamByeCmd(String ssrc) {
        try {
            ClientTransaction transaction = streamSession.get(ssrc);
            if (transaction == null) {
                return;
            }
            Dialog dialog = transaction.getDialog();
            if (dialog == null) {
                return;
            }
            Request byeRequest = dialog.createRequest(Request.BYE);
            ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME);
            String protocol = viaHeader.getTransport();
            ClientTransaction clientTransaction = null;
            if("TCP".equals(protocol)) {
                clientTransaction = sipLayer.getTcpSipProvider().getNewClientTransaction(byeRequest);
            } else if("UDP".equals(protocol)) {
                clientTransaction = sipLayer.getUdpSipProvider().getNewClientTransaction(byeRequest);
            }
            dialog.sendRequest(clientTransaction);
        } catch (TransactionDoesNotExistException e) {
            e.printStackTrace();
        } catch (SipException e) {
            e.printStackTrace();
        }
    }
@@ -435,16 +479,15 @@
        return false;
    }
    
    private void transmitRequest(Device device, Request request) throws SipException {
    private ClientTransaction transmitRequest(Device device, Request request) throws SipException {
        ClientTransaction clientTransaction = null;
        if(device.getTransport().equals("TCP")) {
        if("TCP".equals(device.getTransport())) {
            clientTransaction = sipLayer.getTcpSipProvider().getNewClientTransaction(request);
            //sipLayer.getTcpSipProvider().sendRequest(request);
        } else if(device.getTransport().equals("UDP")) {
        } else if("UDP".equals(device.getTransport())) {
            clientTransaction = sipLayer.getUdpSipProvider().getNewClientTransaction(request);
            //sipLayer.getUdpSipProvider().sendRequest(request);
        }
        clientTransaction.sendRequest();
        return clientTransaction;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java
@@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.gb28181.transmit.request;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import com.genersoft.iot.vmp.gb28181.SipLayer;
@@ -12,6 +11,6 @@
 */
public interface ISIPRequestProcessor {
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction);
    public void process(RequestEvent evt, SipLayer layer);
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java
@@ -31,7 +31,7 @@
     * @param config    
     */  
    @Override
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
    public void process(RequestEvent evt, SipLayer layer) {
        Request request = evt.getRequest();
        Dialog dialog = evt.getDialog();
        try {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
@@ -25,7 +25,7 @@
     * @param config    
     */  
    @Override
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
    public void process(RequestEvent evt, SipLayer layer) {
        // TODO Auto-generated method stub
        
    }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java
@@ -25,7 +25,7 @@
     * @param config    
     */  
    @Override
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
    public void process(RequestEvent evt, SipLayer layer) {
        // TODO Auto-generated method stub
        
    }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
@@ -23,7 +23,7 @@
     *            请求消息
     */ 
    @Override
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
    public void process(RequestEvent evt, SipLayer layer) {
        // TODO Auto-generated method stub
//        Request request = requestEvent.getRequest();
//
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
@@ -93,10 +93,10 @@
     * @param transaction  
     */  
    @Override
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
    public void process(RequestEvent evt, SipLayer layer) {
        
        this.layer = layer;
        this.transaction = transaction;
        this.transaction = layer.getServerTransaction(evt);
        
        Request request = evt.getRequest();
        SAXReader reader = new SAXReader();
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java
@@ -25,7 +25,7 @@
     * @param config    
     */  
    @Override
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
    public void process(RequestEvent evt, SipLayer layer) {
        System.out.println("no support the method! Method:" + evt.getRequest().getMethod());
    }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
@@ -63,7 +63,7 @@
     *            请求消息
     */ 
    @Override
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
    public void process(RequestEvent evt, SipLayer layer) {
        try {
            System.out.println("收到注册请求,开始处理");
            Request request = evt.getRequest();
@@ -141,7 +141,7 @@
                    device.setTransport(isTcp ? "TCP" : "UDP");
                }
            }
            transaction.sendResponse(response);
            layer.getServerTransaction(evt).sendResponse(response);
            // 注册成功
            // 保存到redis
            // 下发catelog查询目录
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java
@@ -32,7 +32,7 @@
     * @param config    
     */
    @Override
    public void process(RequestEvent evt, SipLayer layer, ServerTransaction transaction) {
    public void process(RequestEvent evt, SipLayer layer) {
        Request request = evt.getRequest();
        try {
@@ -43,7 +43,7 @@
                response.setExpires(expireHeader);
            }
            System.out.println("response : " + response.toString());
            ServerTransaction transaction = layer.getServerTransaction(evt);
            if (transaction != null) {
                transaction.sendResponse(response);
                transaction.terminate();
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
@@ -50,30 +50,32 @@
            //成功响应
            //下发ack
            if(statusCode == Response.OK){
                ClientTransaction clientTransaction = evt.getClientTransaction();
                if(clientTransaction == null){
                    logger.error("回复ACK时,clientTransaction为null >>> {}",response);
                    return;
                }
                Dialog clientDialog = clientTransaction.getDialog();
                CSeqHeader clientCSeqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
                long cseqId = clientCSeqHeader.getSeqNumber();
                /*
                createAck函数,创建的ackRequest,会采用Invite响应的200OK,中的contact字段中的地址,作为目标地址。
                有的终端传上来的可能还是内网地址,会造成ack发送不出去。接受不到音视频流
                所以在此处统一替换地址。和响应消息的Via头中的地址保持一致。
                 */
                Request ackRequest = clientDialog.createAck(cseqId);
                SipURI requestURI = (SipURI) ackRequest.getRequestURI();
                ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME);
                requestURI.setHost(viaHeader.getHost());
                requestURI.setPort(viaHeader.getPort());
                clientDialog.sendAck(ackRequest);
//                ClientTransaction clientTransaction = evt.getClientTransaction();
//                if(clientTransaction == null){
//                    logger.error("回复ACK时,clientTransaction为null >>> {}",response);
//                    return;
//                }
//                Dialog clientDialog = clientTransaction.getDialog();
//
//                CSeqHeader clientCSeqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
//                long cseqId = clientCSeqHeader.getSeqNumber();
//                /*
//                createAck函数,创建的ackRequest,会采用Invite响应的200OK,中的contact字段中的地址,作为目标地址。
//                有的终端传上来的可能还是内网地址,会造成ack发送不出去。接受不到音视频流
//                所以在此处统一替换地址。和响应消息的Via头中的地址保持一致。
//                 */
//                Request ackRequest = clientDialog.createAck(cseqId);
//                SipURI requestURI = (SipURI) ackRequest.getRequestURI();
//                ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME);
//                requestURI.setHost(viaHeader.getHost());
//                requestURI.setPort(viaHeader.getPort());
//                clientDialog.sendAck(ackRequest);
                Dialog dialog = evt.getDialog();
                Request reqAck =dialog.createAck(1L);
                dialog.sendAck(reqAck);
            }
        } catch (InvalidArgumentException | SipException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java
@@ -96,8 +96,7 @@
     */  
    @Override
    public boolean delete(String deviceId) {
        redis.del(VideoManagerConstants.CACHEKEY_PREFIX+deviceId);
        return true;
        return redis.del(VideoManagerConstants.CACHEKEY_PREFIX+deviceId);
    }
    /**   
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
@@ -68,14 +68,20 @@
     * @SuppressWarnings("unchecked") 忽略类型转换警告
     * @param key 键(一个或者多个)
     */
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
//                传入一个 Collection<String> 集合
                redisTemplate.delete(CollectionUtils.arrayToList(key));
    public boolean del(String... key) {
        try {
            if (key != null && key.length > 0) {
                if (key.length == 1) {
                    redisTemplate.delete(key[0]);
                } else {
//                    传入一个 Collection<String> 集合
                    redisTemplate.delete(CollectionUtils.arrayToList(key));
                }
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java
@@ -14,7 +14,9 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
@@ -33,6 +35,9 @@
    
    @Autowired
    private DeferredResultHolder resultHolder;
    @Autowired
    private DeviceOffLineDetector offLineDetector;
    
    @GetMapping("/devices/{deviceId}")
    public ResponseEntity<Device> devices(@PathVariable String deviceId){
@@ -69,4 +74,25 @@
        resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result);
        return result;
    }
    @PostMapping("/devices/{deviceId}/delete")
    public ResponseEntity<String> delete(@PathVariable String deviceId){
        if (logger.isDebugEnabled()) {
            logger.debug("设备信息删除API调用,deviceId:" + deviceId);
        }
        if (offLineDetector.isOnline(deviceId)) {
            return new ResponseEntity<String>("不允许删除在线设备!", HttpStatus.NOT_ACCEPTABLE);
        }
        boolean isSuccess = storager.delete(deviceId);
        if (isSuccess) {
            JSONObject json = new JSONObject();
            json.put("deviceId", deviceId);
            return new ResponseEntity<>(json.toString(),HttpStatus.OK);
        } else {
            logger.warn("设备预览API调用失败!");
            return new ResponseEntity<String>("设备预览API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -7,6 +7,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -47,4 +48,23 @@
            return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    @PostMapping("/play/{ssrc}/stop")
    public ResponseEntity<String> playStop(@PathVariable String ssrc){
        cmder.streamByeCmd(ssrc);
        if (logger.isDebugEnabled()) {
            logger.debug(String.format("设备预览停止API调用,ssrc:%s", ssrc));
        }
        if(ssrc!=null) {
            JSONObject json = new JSONObject();
            json.put("ssrc", ssrc);
            return new ResponseEntity<String>(json.toString(),HttpStatus.OK);
        } else {
            logger.warn("设备预览停止API调用失败!");
            return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}
src/main/resources/application.yml
@@ -26,7 +26,7 @@
server:
    port: 8080
sip:
    ip: 127.0.0.1
    ip: 10.200.64.63
    port: 5060
    # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)
    # 后两位为行业编码,定义参照附录D.3