与master分支同步
parent
ecaf8750dd
commit
44d216100b
|
@ -5,12 +5,18 @@ import com.alibaba.fastjson.JSONArray;
|
||||||
public class StreamInfo {
|
public class StreamInfo {
|
||||||
|
|
||||||
private String ssrc;
|
private String ssrc;
|
||||||
|
private String streamId;
|
||||||
private String deviceID;
|
private String deviceID;
|
||||||
private String cahnnelId;
|
private String cahnnelId;
|
||||||
private String flv;
|
private String flv;
|
||||||
private String ws_flv;
|
private String ws_flv;
|
||||||
private String rtmp;
|
private String fmp4;
|
||||||
|
private String ws_fmp4;
|
||||||
private String hls;
|
private String hls;
|
||||||
|
private String ws_hls;
|
||||||
|
private String ts;
|
||||||
|
private String ws_ts;
|
||||||
|
private String rtmp;
|
||||||
private String rtsp;
|
private String rtsp;
|
||||||
private JSONArray tracks;
|
private JSONArray tracks;
|
||||||
|
|
||||||
|
@ -85,4 +91,52 @@ public class StreamInfo {
|
||||||
public void setTracks(JSONArray tracks) {
|
public void setTracks(JSONArray tracks) {
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFmp4() {
|
||||||
|
return fmp4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFmp4(String fmp4) {
|
||||||
|
this.fmp4 = fmp4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWs_fmp4() {
|
||||||
|
return ws_fmp4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWs_fmp4(String ws_fmp4) {
|
||||||
|
this.ws_fmp4 = ws_fmp4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWs_hls() {
|
||||||
|
return ws_hls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWs_hls(String ws_hls) {
|
||||||
|
this.ws_hls = ws_hls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTs() {
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTs(String ts) {
|
||||||
|
this.ts = ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWs_ts() {
|
||||||
|
return ws_ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWs_ts(String ws_ts) {
|
||||||
|
this.ws_ts = ws_ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStreamId() {
|
||||||
|
return streamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStreamId(String streamId) {
|
||||||
|
this.streamId = streamId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,7 @@ public class SipLayer implements SipListener {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void processRequest(RequestEvent evt) {
|
public void processRequest(RequestEvent evt) {
|
||||||
|
logger.debug(evt.getRequest().toString());
|
||||||
// 由于jainsip是单线程程序,为提高性能并发处理
|
// 由于jainsip是单线程程序,为提高性能并发处理
|
||||||
processThreadPool.execute(() -> {
|
processThreadPool.execute(() -> {
|
||||||
processorFactory.createRequestProcessor(evt).process();
|
processorFactory.createRequestProcessor(evt).process();
|
||||||
|
@ -122,6 +123,7 @@ public class SipLayer implements SipListener {
|
||||||
@Override
|
@Override
|
||||||
public void processResponse(ResponseEvent evt) {
|
public void processResponse(ResponseEvent evt) {
|
||||||
Response response = evt.getResponse();
|
Response response = evt.getResponse();
|
||||||
|
logger.debug(evt.getResponse().toString());
|
||||||
int status = response.getStatusCode();
|
int status = response.getStatusCode();
|
||||||
if (((status >= 200) && (status < 300)) || status == 401) { // Success!
|
if (((status >= 200) && (status < 300)) || status == 401) { // Success!
|
||||||
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
|
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> {
|
||||||
public void onApplicationEvent(OnlineEvent event) {
|
public void onApplicationEvent(OnlineEvent event) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("设备离线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom());
|
logger.debug("设备上线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom());
|
||||||
}
|
}
|
||||||
|
|
||||||
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId();
|
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId();
|
||||||
|
|
|
@ -17,13 +17,11 @@ public class VideoStreamSessionManager {
|
||||||
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
|
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public String createPlaySsrc(){
|
public String createPlaySsrc(){
|
||||||
String ssrc = SsrcUtil.getPlaySsrc();
|
return SsrcUtil.getPlaySsrc();
|
||||||
return ssrc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String createPlayBackSsrc(){
|
public String createPlayBackSsrc(){
|
||||||
String ssrc = SsrcUtil.getPlayBackSsrc();
|
return SsrcUtil.getPlayBackSsrc();
|
||||||
return ssrc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void put(String ssrc,ClientTransaction transaction){
|
public void put(String ssrc,ClientTransaction transaction){
|
||||||
|
|
|
@ -22,6 +22,8 @@ public class DeferredResultHolder {
|
||||||
|
|
||||||
public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
|
public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
|
||||||
|
|
||||||
|
public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY";
|
||||||
|
|
||||||
private Map<String, DeferredResult> map = new HashMap<String, DeferredResult>();
|
private Map<String, DeferredResult> map = new HashMap<String, DeferredResult>();
|
||||||
|
|
||||||
public void put(String key, DeferredResult result) {
|
public void put(String key, DeferredResult result) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
|
||||||
|
|
||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,7 +21,7 @@ public interface ISIPCommander {
|
||||||
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
|
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
|
||||||
* @param moveSpeed 镜头移动速度
|
* @param moveSpeed 镜头移动速度
|
||||||
*/
|
*/
|
||||||
public boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown);
|
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 云台方向放控制
|
* 云台方向放控制
|
||||||
|
@ -31,7 +32,7 @@ public interface ISIPCommander {
|
||||||
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
|
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
|
||||||
* @param moveSpeed 镜头移动速度
|
* @param moveSpeed 镜头移动速度
|
||||||
*/
|
*/
|
||||||
public boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed);
|
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 云台缩放控制,使用配置文件中的默认镜头缩放速度
|
* 云台缩放控制,使用配置文件中的默认镜头缩放速度
|
||||||
|
@ -40,7 +41,7 @@ public interface ISIPCommander {
|
||||||
* @param channelId 预览通道
|
* @param channelId 预览通道
|
||||||
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
|
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
|
||||||
*/
|
*/
|
||||||
public boolean ptzZoomCmd(Device device,String channelId,int inOut);
|
boolean ptzZoomCmd(Device device,String channelId,int inOut);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 云台缩放控制
|
* 云台缩放控制
|
||||||
|
@ -50,7 +51,7 @@ public interface ISIPCommander {
|
||||||
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
|
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
|
||||||
* @param zoomSpeed 镜头缩放速度
|
* @param zoomSpeed 镜头缩放速度
|
||||||
*/
|
*/
|
||||||
public boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed);
|
boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 云台控制,支持方向与缩放控制
|
* 云台控制,支持方向与缩放控制
|
||||||
|
@ -63,7 +64,7 @@ public interface ISIPCommander {
|
||||||
* @param moveSpeed 镜头移动速度
|
* @param moveSpeed 镜头移动速度
|
||||||
* @param zoomSpeed 镜头缩放速度
|
* @param zoomSpeed 镜头缩放速度
|
||||||
*/
|
*/
|
||||||
public boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
|
boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
|
* 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
|
||||||
|
@ -75,7 +76,7 @@ public interface ISIPCommander {
|
||||||
* @param parameter2 数据2
|
* @param parameter2 数据2
|
||||||
* @param combineCode2 组合码2
|
* @param combineCode2 组合码2
|
||||||
*/
|
*/
|
||||||
public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);
|
boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求预览视频流
|
* 请求预览视频流
|
||||||
|
@ -83,7 +84,7 @@ public interface ISIPCommander {
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
* @param channelId 预览通道
|
* @param channelId 预览通道
|
||||||
*/
|
*/
|
||||||
public StreamInfo playStreamCmd(Device device, String channelId);
|
void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求回放视频流
|
* 请求回放视频流
|
||||||
|
@ -93,14 +94,14 @@ public interface ISIPCommander {
|
||||||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||||
*/
|
*/
|
||||||
public StreamInfo playbackStreamCmd(Device device,String channelId, String startTime, String endTime);
|
void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 视频流停止
|
* 视频流停止
|
||||||
*
|
*
|
||||||
* @param ssrc ssrc
|
* @param ssrc ssrc
|
||||||
*/
|
*/
|
||||||
public void streamByeCmd(String ssrc);
|
void streamByeCmd(String ssrc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 语音广播
|
* 语音广播
|
||||||
|
@ -108,7 +109,7 @@ public interface ISIPCommander {
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
* @param channelId 预览通道
|
* @param channelId 预览通道
|
||||||
*/
|
*/
|
||||||
public boolean audioBroadcastCmd(Device device,String channelId);
|
boolean audioBroadcastCmd(Device device,String channelId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 音视频录像控制
|
* 音视频录像控制
|
||||||
|
@ -116,21 +117,21 @@ public interface ISIPCommander {
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
* @param channelId 预览通道
|
* @param channelId 预览通道
|
||||||
*/
|
*/
|
||||||
public boolean recordCmd(Device device,String channelId);
|
boolean recordCmd(Device device,String channelId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 报警布防/撤防命令
|
* 报警布防/撤防命令
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean guardCmd(Device device);
|
boolean guardCmd(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 报警复位命令
|
* 报警复位命令
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean alarmCmd(Device device);
|
boolean alarmCmd(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
|
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
|
||||||
|
@ -138,21 +139,21 @@ public interface ISIPCommander {
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
* @param channelId 预览通道
|
* @param channelId 预览通道
|
||||||
*/
|
*/
|
||||||
public boolean iFameCmd(Device device,String channelId);
|
boolean iFameCmd(Device device,String channelId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 看守位控制命令
|
* 看守位控制命令
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean homePositionCmd(Device device);
|
boolean homePositionCmd(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备配置命令
|
* 设备配置命令
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean deviceConfigCmd(Device device);
|
boolean deviceConfigCmd(Device device);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +161,7 @@ public interface ISIPCommander {
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean deviceStatusQuery(Device device);
|
boolean deviceStatusQuery(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询设备信息
|
* 查询设备信息
|
||||||
|
@ -168,14 +169,14 @@ public interface ISIPCommander {
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean deviceInfoQuery(Device device);
|
boolean deviceInfoQuery(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询目录列表
|
* 查询目录列表
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean catalogQuery(Device device);
|
boolean catalogQuery(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询录像信息
|
* 查询录像信息
|
||||||
|
@ -184,35 +185,33 @@ public interface ISIPCommander {
|
||||||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||||
*/
|
*/
|
||||||
public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
|
boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询报警信息
|
* 查询报警信息
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean alarmInfoQuery(Device device);
|
boolean alarmInfoQuery(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询设备配置
|
* 查询设备配置
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean configQuery(Device device);
|
boolean configQuery(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询设备预置位置
|
* 查询设备预置位置
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean presetQuery(Device device);
|
boolean presetQuery(Device device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询移动设备位置数据
|
* 查询移动设备位置数据
|
||||||
*
|
*
|
||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
*/
|
*/
|
||||||
public boolean mobilePostitionQuery(Device device);
|
boolean mobilePostitionQuery(Device device);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.conf.MediaServerConfig;
|
import com.genersoft.iot.vmp.conf.MediaServerConfig;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||||
import com.genersoft.iot.vmp.media.zlm.ZLMUtils;
|
import com.genersoft.iot.vmp.media.zlm.ZLMUtils;
|
||||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -67,6 +68,9 @@ public class SIPCommander implements ISIPCommander {
|
||||||
@Value("${media.rtp.enable}")
|
@Value("${media.rtp.enable}")
|
||||||
private boolean rtpEnable;
|
private boolean rtpEnable;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ZLMHttpHookSubscribe subscribe;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,7 +273,7 @@ public class SIPCommander implements ISIPCommander {
|
||||||
* @param channelId 预览通道
|
* @param channelId 预览通道
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public StreamInfo playStreamCmd(Device device, String channelId) {
|
public void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
String ssrc = streamSession.createPlaySsrc();
|
String ssrc = streamSession.createPlaySsrc();
|
||||||
|
@ -282,52 +286,62 @@ public class SIPCommander implements ISIPCommander {
|
||||||
}else {
|
}else {
|
||||||
mediaPort = mediaInfo.getRtpProxyPort();
|
mediaPort = mediaInfo.getRtpProxyPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
|
||||||
|
// 添加订阅
|
||||||
|
JSONObject subscribeKey = new JSONObject();
|
||||||
|
subscribeKey.put("app", "rtp");
|
||||||
|
subscribeKey.put("id", streamId);
|
||||||
|
|
||||||
|
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event);
|
||||||
//
|
//
|
||||||
StringBuffer content = new StringBuffer(200);
|
StringBuffer content = new StringBuffer(200);
|
||||||
content.append("v=0\r\n");
|
content.append("v=0\r\n");
|
||||||
content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n");
|
content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n");
|
||||||
content.append("s=Play\r\n");
|
content.append("s=Play\r\n");
|
||||||
content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n");
|
content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n");
|
||||||
content.append("t=0 0\r\n");
|
content.append("t=0 0\r\n");
|
||||||
if("TCP-PASSIVE".equals(streamMode)) {
|
if("TCP-PASSIVE".equals(streamMode)) {
|
||||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n");
|
||||||
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
||||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n");
|
||||||
}else if("UDP".equals(streamMode)) {
|
}else if("UDP".equals(streamMode)) {
|
||||||
content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
|
content.append("m=video "+ mediaPort +" RTP/AVP 126 125 99 34 98 97 96\r\n");
|
||||||
}
|
}
|
||||||
content.append("a=recvonly\r\n");
|
content.append("a=recvonly\r\n");
|
||||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
|
||||||
content.append("a=rtpmap:98 H264/90000\r\n");
|
content.append("a=rtpmap:126 H264/90000\r\n");
|
||||||
content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
content.append("a=rtpmap:125 H264S/90000\r\n");
|
||||||
if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
|
||||||
content.append("a=setup:passive\r\n");
|
content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
|
||||||
|
content.append("a=fmtp:99 profile-level-id=3\r\n");
|
||||||
|
content.append("a=rtpmap:98 H264/90000\r\n");
|
||||||
|
content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
||||||
|
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||||
|
if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
||||||
|
content.append("a=setup:passive\r\n");
|
||||||
content.append("a=connection:new\r\n");
|
content.append("a=connection:new\r\n");
|
||||||
}else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
|
}else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
|
||||||
content.append("a=setup:active\r\n");
|
content.append("a=setup:active\r\n");
|
||||||
content.append("a=connection:new\r\n");
|
content.append("a=connection:new\r\n");
|
||||||
}
|
}
|
||||||
content.append("y="+ssrc+"\r\n");//ssrc
|
content.append("y="+ssrc+"\r\n");//ssrc
|
||||||
|
|
||||||
Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null, ssrc);
|
Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null, ssrc);
|
||||||
|
|
||||||
ClientTransaction transaction = transmitRequest(device, request);
|
ClientTransaction transaction = transmitRequest(device, request);
|
||||||
streamSession.put(ssrc, transaction);
|
streamSession.put(ssrc, transaction);
|
||||||
DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId);
|
DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId);
|
||||||
if (deviceChannel != null) {
|
if (deviceChannel != null) {
|
||||||
deviceChannel.setSsrc(ssrc);
|
deviceChannel.setSsrc(ssrc);
|
||||||
storager.updateChannel(device.getDeviceId(), deviceChannel);
|
storager.updateChannel(device.getDeviceId(), deviceChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamInfo streamInfo = new StreamInfo();
|
// TODO 订阅SIP response,处理对方的错误返回
|
||||||
streamInfo.setSsrc(ssrc);
|
|
||||||
streamInfo.setCahnnelId(channelId);
|
|
||||||
streamInfo.setDeviceID(device.getDeviceId());
|
|
||||||
storager.startPlay(streamInfo);
|
|
||||||
return streamInfo;
|
|
||||||
} catch ( SipException | ParseException | InvalidArgumentException e) {
|
} catch ( SipException | ParseException | InvalidArgumentException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,10 +354,18 @@ public class SIPCommander implements ISIPCommander {
|
||||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public StreamInfo playbackStreamCmd(Device device, String channelId, String startTime, String endTime) {
|
public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event) {
|
||||||
try {
|
try {
|
||||||
MediaServerConfig mediaInfo = storager.getMediaInfo();
|
MediaServerConfig mediaInfo = storager.getMediaInfo();
|
||||||
String ssrc = streamSession.createPlayBackSsrc();
|
String ssrc = streamSession.createPlayBackSsrc();
|
||||||
|
String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
|
||||||
|
// 添加订阅
|
||||||
|
JSONObject subscribeKey = new JSONObject();
|
||||||
|
subscribeKey.put("app", "rtp");
|
||||||
|
subscribeKey.put("id", streamId);
|
||||||
|
|
||||||
|
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event);
|
||||||
|
|
||||||
//
|
//
|
||||||
StringBuffer content = new StringBuffer(200);
|
StringBuffer content = new StringBuffer(200);
|
||||||
content.append("v=0\r\n");
|
content.append("v=0\r\n");
|
||||||
|
@ -362,16 +384,22 @@ public class SIPCommander implements ISIPCommander {
|
||||||
}
|
}
|
||||||
String streamMode = device.getStreamMode().toUpperCase();
|
String streamMode = device.getStreamMode().toUpperCase();
|
||||||
if("TCP-PASSIVE".equals(streamMode)) {
|
if("TCP-PASSIVE".equals(streamMode)) {
|
||||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n");
|
||||||
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
||||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n");
|
||||||
}else if("UDP".equals(streamMode)) {
|
}else if("UDP".equals(streamMode)) {
|
||||||
content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
|
content.append("m=video "+ mediaPort +" RTP/AVP 126 125 99 34 98 97 96\r\n");
|
||||||
}
|
}
|
||||||
content.append("a=recvonly\r\n");
|
content.append("a=recvonly\r\n");
|
||||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
|
||||||
content.append("a=rtpmap:98 H264/90000\r\n");
|
content.append("a=rtpmap:126 H264/90000\r\n");
|
||||||
content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
content.append("a=rtpmap:125 H264S/90000\r\n");
|
||||||
|
content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
|
||||||
|
content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
|
||||||
|
content.append("a=fmtp:99 profile-level-id=3\r\n");
|
||||||
|
content.append("a=rtpmap:98 H264/90000\r\n");
|
||||||
|
content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
||||||
|
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||||
if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
||||||
content.append("a=setup:passive\r\n");
|
content.append("a=setup:passive\r\n");
|
||||||
content.append("a=connection:new\r\n");
|
content.append("a=connection:new\r\n");
|
||||||
|
@ -386,16 +414,8 @@ public class SIPCommander implements ISIPCommander {
|
||||||
ClientTransaction transaction = transmitRequest(device, request);
|
ClientTransaction transaction = transmitRequest(device, request);
|
||||||
streamSession.put(ssrc, transaction);
|
streamSession.put(ssrc, transaction);
|
||||||
|
|
||||||
StreamInfo streamInfo = new StreamInfo();
|
|
||||||
streamInfo.setSsrc(ssrc);
|
|
||||||
streamInfo.setCahnnelId(channelId);
|
|
||||||
streamInfo.setDeviceID(device.getDeviceId());
|
|
||||||
boolean b = storager.startPlayback(streamInfo);
|
|
||||||
return streamInfo;
|
|
||||||
|
|
||||||
} catch ( SipException | ParseException | InvalidArgumentException e) {
|
} catch ( SipException | ParseException | InvalidArgumentException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,6 +453,7 @@ public class SIPCommander implements ISIPCommander {
|
||||||
clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
|
clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
|
||||||
}
|
}
|
||||||
dialog.sendRequest(clientTransaction);
|
dialog.sendRequest(clientTransaction);
|
||||||
|
streamSession.remove(ssrc);
|
||||||
} catch (TransactionDoesNotExistException e) {
|
} catch (TransactionDoesNotExistException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (SipException e) {
|
} catch (SipException e) {
|
||||||
|
|
|
@ -21,14 +21,12 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor {
|
||||||
* 处理 ACK请求
|
* 处理 ACK请求
|
||||||
*
|
*
|
||||||
* @param evt
|
* @param evt
|
||||||
* @param layer
|
|
||||||
* @param transaction
|
|
||||||
* @param config
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void process(RequestEvent evt) {
|
public void process(RequestEvent evt) {
|
||||||
Request request = evt.getRequest();
|
Request request = evt.getRequest();
|
||||||
Dialog dialog = evt.getDialog();
|
Dialog dialog = evt.getDialog();
|
||||||
|
if (dialog == null) return;
|
||||||
try {
|
try {
|
||||||
Request ackRequest = null;
|
Request ackRequest = null;
|
||||||
CSeq csReq = (CSeq) request.getHeader(CSeq.NAME);
|
CSeq csReq = (CSeq) request.getHeader(CSeq.NAME);
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
|
package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
|
||||||
|
|
||||||
|
import javax.sip.InvalidArgumentException;
|
||||||
import javax.sip.RequestEvent;
|
import javax.sip.RequestEvent;
|
||||||
|
import javax.sip.SipException;
|
||||||
|
import javax.sip.message.Response;
|
||||||
|
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
|
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Description: BYE请求处理器
|
* @Description: BYE请求处理器
|
||||||
* @author: swwheihei
|
* @author: swwheihei
|
||||||
|
@ -13,16 +18,33 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理BYE请求
|
* 处理BYE请求
|
||||||
*
|
|
||||||
* @param evt
|
* @param evt
|
||||||
* @param layer
|
|
||||||
* @param transaction
|
|
||||||
* @param config
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void process(RequestEvent evt) {
|
public void process(RequestEvent evt) {
|
||||||
|
try {
|
||||||
|
responseAck(evt);
|
||||||
|
} catch (SipException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
// TODO 优先级99 Bye Request消息实现,此消息一般为级联消息,上级给下级发送视频停止指令
|
// TODO 优先级99 Bye Request消息实现,此消息一般为级联消息,上级给下级发送视频停止指令
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 回复200 OK
|
||||||
|
* @param evt
|
||||||
|
* @throws SipException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
* @throws ParseException
|
||||||
|
*/
|
||||||
|
private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
|
||||||
|
Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
|
||||||
|
getServerTransaction(evt).sendResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,10 +184,11 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
|
||||||
DeviceChannel deviceChannel = new DeviceChannel();
|
DeviceChannel deviceChannel = new DeviceChannel();
|
||||||
deviceChannel.setName(channelName);
|
deviceChannel.setName(channelName);
|
||||||
deviceChannel.setChannelId(channelDeviceId);
|
deviceChannel.setChannelId(channelDeviceId);
|
||||||
if (status.equals("ON") || status.equals("On")) {
|
// ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
|
||||||
|
if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
|
||||||
deviceChannel.setStatus(1);
|
deviceChannel.setStatus(1);
|
||||||
}
|
}
|
||||||
if (status.equals("OFF") || status.equals("Off")) {
|
if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
|
||||||
deviceChannel.setStatus(0);
|
deviceChannel.setStatus(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,9 @@ public class ZLMHttpHookListener {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ZLMHttpHookSubscribe subscribe;
|
||||||
|
|
||||||
@Value("${media.ip}")
|
@Value("${media.ip}")
|
||||||
private String mediaIp;
|
private String mediaIp;
|
||||||
|
|
||||||
|
@ -128,30 +131,38 @@ public class ZLMHttpHookListener {
|
||||||
}
|
}
|
||||||
String app = json.getString("app");
|
String app = json.getString("app");
|
||||||
String streamId = json.getString("id");
|
String streamId = json.getString("id");
|
||||||
if ("rtp".equals(app)) {
|
|
||||||
String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16));
|
|
||||||
StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc);
|
|
||||||
if ("rtp".equals(app) && streamInfoForPlay != null ) {
|
|
||||||
MediaServerConfig mediaInfo = storager.getMediaInfo();
|
|
||||||
streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
|
||||||
streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
|
||||||
streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId));
|
|
||||||
streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
|
||||||
streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId));
|
|
||||||
storager.startPlay(streamInfoForPlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc);
|
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
|
||||||
if ("rtp".equals(app) && streamInfoForPlayBack != null ) {
|
if (subscribe != null) subscribe.response(json);
|
||||||
MediaServerConfig mediaInfo = storager.getMediaInfo();
|
|
||||||
streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
// if ("rtp".equals(app)) {
|
||||||
streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
// String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16));
|
||||||
streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId));
|
// StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc);
|
||||||
streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
// if ("rtp".equals(app) && streamInfoForPlay != null ) {
|
||||||
streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId));
|
// MediaServerConfig mediaInfo = storager.getMediaInfo();
|
||||||
storager.startPlayback(streamInfoForPlayBack);
|
// streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
}
|
// streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
}
|
// streamInfoForPlay.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
// streamInfoForPlay.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
// streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId));
|
||||||
|
// streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
// streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId));
|
||||||
|
// storager.startPlay(streamInfoForPlay);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc);
|
||||||
|
// if ("rtp".equals(app) && streamInfoForPlayBack != null ) {
|
||||||
|
// MediaServerConfig mediaInfo = storager.getMediaInfo();
|
||||||
|
// streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
// streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
// streamInfoForPlayBack.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
// streamInfoForPlayBack.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
// streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId));
|
||||||
|
// streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
// streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId));
|
||||||
|
// storager.startPlayback(streamInfoForPlayBack);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.genersoft.iot.vmp.media.zlm;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
|
import com.genersoft.iot.vmp.conf.MediaServerConfig;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||||
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description:针对 ZLMediaServer的hook事件订阅
|
||||||
|
* @author: pan
|
||||||
|
* @date: 2020年12月2日 21:17:32
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ZLMHttpHookSubscribe {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookSubscribe.class);
|
||||||
|
|
||||||
|
public enum HookType{
|
||||||
|
on_flow_report,
|
||||||
|
on_http_access,
|
||||||
|
on_play,
|
||||||
|
on_publish,
|
||||||
|
on_record_mp4,
|
||||||
|
on_rtsp_auth,
|
||||||
|
on_rtsp_realm,
|
||||||
|
on_shell_login,
|
||||||
|
on_stream_changed,
|
||||||
|
on_stream_none_reader,
|
||||||
|
on_stream_not_found,
|
||||||
|
on_server_started
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Event{
|
||||||
|
void response(JSONObject response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void addSubscribe(HookType type, JSONObject hookResponse, ZLMHttpHookSubscribe.Event event) {
|
||||||
|
Map<JSONObject, Event> eventMap = allSubscribes.get(type);
|
||||||
|
if (eventMap == null) {
|
||||||
|
eventMap = new HashMap<JSONObject, Event>();
|
||||||
|
allSubscribes.put(type,eventMap);
|
||||||
|
}
|
||||||
|
eventMap.put(hookResponse, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZLMHttpHookSubscribe.Event getSubscribe(HookType type, JSONObject hookResponse) {
|
||||||
|
ZLMHttpHookSubscribe.Event event= null;
|
||||||
|
Map<JSONObject, Event> eventMap = allSubscribes.get(type);
|
||||||
|
if (eventMap == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (JSONObject key : eventMap.keySet()) {
|
||||||
|
Boolean result = null;
|
||||||
|
for (String s : key.keySet()) {
|
||||||
|
String string = hookResponse.getString(s);
|
||||||
|
String string1 = key.getString(s);
|
||||||
|
if (result == null) {
|
||||||
|
result = key.getString(s).equals(hookResponse.getString(s));
|
||||||
|
}else {
|
||||||
|
result = result && key.getString(s).equals(hookResponse.getString(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
event = eventMap.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,9 @@ public class ZLMRunner implements CommandLineRunner {
|
||||||
@Value("${media.wanIp}")
|
@Value("${media.wanIp}")
|
||||||
private String mediaWanIp;
|
private String mediaWanIp;
|
||||||
|
|
||||||
|
@Value("${media.hookIp}")
|
||||||
|
private String mediaHookIp;
|
||||||
|
|
||||||
@Value("${media.port}")
|
@Value("${media.port}")
|
||||||
private int mediaPort;
|
private int mediaPort;
|
||||||
|
|
||||||
|
@ -51,6 +54,9 @@ public class ZLMRunner implements CommandLineRunner {
|
||||||
@Value("${server.port}")
|
@Value("${server.port}")
|
||||||
private String serverPort;
|
private String serverPort;
|
||||||
|
|
||||||
|
@Value("${media.autoConfig}")
|
||||||
|
private boolean autoConfig;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||||
|
|
||||||
|
@ -61,8 +67,7 @@ public class ZLMRunner implements CommandLineRunner {
|
||||||
MediaServerConfig mediaServerConfig = getMediaServerConfig();
|
MediaServerConfig mediaServerConfig = getMediaServerConfig();
|
||||||
if (mediaServerConfig != null) {
|
if (mediaServerConfig != null) {
|
||||||
logger.info("zlm接入成功...");
|
logger.info("zlm接入成功...");
|
||||||
logger.info("设置zlm...");
|
if (autoConfig) saveZLMConfig();
|
||||||
saveZLMConfig();
|
|
||||||
mediaServerConfig = getMediaServerConfig();
|
mediaServerConfig = getMediaServerConfig();
|
||||||
storager.updateMediaInfo(mediaServerConfig);
|
storager.updateMediaInfo(mediaServerConfig);
|
||||||
}
|
}
|
||||||
|
@ -91,12 +96,12 @@ public class ZLMRunner implements CommandLineRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveZLMConfig() {
|
private void saveZLMConfig() {
|
||||||
String hookIP = sipIP;
|
logger.info("设置zlm...");
|
||||||
if (mediaIp.equals(sipIP)) {
|
if (StringUtils.isEmpty(mediaHookIp)) {
|
||||||
hookIP = "127.0.0.1";
|
mediaHookIp = sipIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
String hookPrex = String.format("http://%s:%s/index/hook", hookIP, serverPort);
|
String hookPrex = String.format("http://%s:%s/index/hook", mediaHookIp, serverPort);
|
||||||
Map<String, Object> param = new HashMap<>();
|
Map<String, Object> param = new HashMap<>();
|
||||||
param.put("api.secret",mediaSecret); // -profile:v Baseline
|
param.put("api.secret",mediaSecret); // -profile:v Baseline
|
||||||
param.put("ffmpeg.cmd","%s -fflags nobuffer -rtsp_transport tcp -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s");
|
param.put("ffmpeg.cmd","%s -fflags nobuffer -rtsp_transport tcp -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s");
|
||||||
|
|
|
@ -555,6 +555,10 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
|
||||||
List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
|
List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
|
||||||
deviceId,
|
deviceId,
|
||||||
code));
|
code));
|
||||||
|
if (playLeys == null || playLeys.size() == 0) {
|
||||||
|
playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
|
||||||
|
deviceId));
|
||||||
|
}
|
||||||
if (playLeys == null || playLeys.size() == 0) return null;
|
if (playLeys == null || playLeys.size() == 0) return null;
|
||||||
return (StreamInfo)redis.get(playLeys.get(0).toString());
|
return (StreamInfo)redis.get(playLeys.get(0).toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,10 @@ import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.conf.MediaServerConfig;
|
import com.genersoft.iot.vmp.conf.MediaServerConfig;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
||||||
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -22,6 +25,10 @@ import com.alibaba.fastjson.JSONObject;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
|
import org.springframework.web.context.request.async.DeferredResult;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RestController
|
@RestController
|
||||||
|
@ -39,94 +46,57 @@ public class PlayController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||||
|
|
||||||
@Value("${media.closeWaitRTPInfo}")
|
@Autowired
|
||||||
private boolean closeWaitRTPInfo;
|
private DeferredResultHolder resultHolder;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IPlayService playService;
|
||||||
|
|
||||||
@GetMapping("/play/{deviceId}/{channelId}")
|
@GetMapping("/play/{deviceId}/{channelId}")
|
||||||
public ResponseEntity<String> play(@PathVariable String deviceId, @PathVariable String channelId,
|
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId,
|
||||||
Integer getEncoding) {
|
@PathVariable String channelId) {
|
||||||
|
|
||||||
|
|
||||||
if (getEncoding == null) getEncoding = 0;
|
|
||||||
getEncoding = closeWaitRTPInfo ? 0 : getEncoding;
|
|
||||||
Device device = storager.queryVideoDevice(deviceId);
|
Device device = storager.queryVideoDevice(deviceId);
|
||||||
StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId);
|
StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId);
|
||||||
|
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
|
||||||
|
// 超时处理
|
||||||
|
result.onTimeout(()->{
|
||||||
|
logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId));
|
||||||
|
RequestMessage msg = new RequestMessage();
|
||||||
|
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
|
||||||
|
msg.setData("Timeout");
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
|
});
|
||||||
|
// 录像查询以channelId作为deviceId查询
|
||||||
|
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
|
||||||
|
|
||||||
if (streamInfo == null) {
|
if (streamInfo == null) {
|
||||||
streamInfo = cmder.playStreamCmd(device, channelId);
|
// 发送点播消息
|
||||||
|
cmder.playStreamCmd(device, channelId, (JSONObject response) -> {
|
||||||
|
logger.info("收到订阅消息: " + response.toJSONString());
|
||||||
|
playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString());
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
|
String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
|
||||||
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
|
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
|
||||||
if (rtpInfo.getBoolean("exist")) {
|
if (rtpInfo.getBoolean("exist")) {
|
||||||
return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK);
|
RequestMessage msg = new RequestMessage();
|
||||||
|
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
|
||||||
|
msg.setData(JSON.toJSONString(streamInfo));
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
} else {
|
} else {
|
||||||
storager.stopPlay(streamInfo);
|
storager.stopPlay(streamInfo);
|
||||||
streamInfo = cmder.playStreamCmd(device, channelId);
|
// TODO playStreamCmd 超时处理
|
||||||
|
cmder.playStreamCmd(device, channelId, (JSONObject response) -> {
|
||||||
|
logger.info("收到订阅消息: " + response.toJSONString());
|
||||||
|
playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
|
return result;
|
||||||
// 等待推流, TODO 默认超时30s
|
|
||||||
boolean lockFlag = true;
|
|
||||||
boolean rtpPushed = false;
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
JSONObject rtpInfo = null;
|
|
||||||
|
|
||||||
if (getEncoding == 1) {
|
|
||||||
while (lockFlag) {
|
|
||||||
try {
|
|
||||||
if (System.currentTimeMillis() - startTime > 60 * 1000) {
|
|
||||||
storager.stopPlay(streamInfo);
|
|
||||||
logger.info("播放等待超时");
|
|
||||||
return new ResponseEntity<String>("timeout", HttpStatus.OK);
|
|
||||||
} else {
|
|
||||||
streamInfo = storager.queryPlayByDevice(deviceId, channelId);
|
|
||||||
if (!rtpPushed) {
|
|
||||||
logger.info("查询RTP推流信息...");
|
|
||||||
rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
|
|
||||||
}
|
|
||||||
if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null
|
|
||||||
&& streamInfo.getFlv() != null) {
|
|
||||||
logger.info("查询流编码信息:" + streamInfo.getFlv());
|
|
||||||
rtpPushed = true;
|
|
||||||
Thread.sleep(2000);
|
|
||||||
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId);
|
|
||||||
if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) {
|
|
||||||
lockFlag = false;
|
|
||||||
logger.info("流编码信息已获取");
|
|
||||||
JSONArray tracks = mediaInfo.getJSONArray("tracks");
|
|
||||||
streamInfo.setTracks(tracks);
|
|
||||||
storager.startPlay(streamInfo);
|
|
||||||
} else {
|
|
||||||
logger.info("流编码信息未获取,2秒后重试...");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String flv = storager.getMediaInfo().getWanIp() + ":" + storager.getMediaInfo().getHttpPort() + "/rtp/"
|
|
||||||
+ streamId + ".flv";
|
|
||||||
streamInfo.setFlv("http://" + flv);
|
|
||||||
streamInfo.setWs_flv("ws://" + flv);
|
|
||||||
storager.startPlay(streamInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug(String.format("设备预览 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
|
|
||||||
logger.debug("设备预览 API调用,ssrc:" + streamInfo.getSsrc() + ",ZLMedia streamId:"
|
|
||||||
+ Integer.toHexString(Integer.parseInt(streamInfo.getSsrc())));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (streamInfo != null) {
|
|
||||||
return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK);
|
|
||||||
} else {
|
|
||||||
logger.warn("设备预览API调用失败!");
|
|
||||||
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/play/{ssrc}/stop")
|
@PostMapping("/play/{ssrc}/stop")
|
||||||
|
@ -172,17 +142,28 @@ public class PlayController {
|
||||||
MediaServerConfig mediaInfo = storager.getMediaInfo();
|
MediaServerConfig mediaInfo = storager.getMediaInfo();
|
||||||
String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(),
|
String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(),
|
||||||
streamId );
|
streamId );
|
||||||
JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(streamInfo.getRtsp(), dstUrl, "1000000");
|
String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId);
|
||||||
|
JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(srcUrl, dstUrl, "1000000");
|
||||||
System.out.println(jsonObject);
|
System.out.println(jsonObject);
|
||||||
JSONObject result = new JSONObject();
|
JSONObject result = new JSONObject();
|
||||||
if (jsonObject != null && jsonObject.getInteger("code") == 0) {
|
if (jsonObject != null && jsonObject.getInteger("code") == 0) {
|
||||||
result.put("code", 0);
|
result.put("code", 0);
|
||||||
JSONObject data = jsonObject.getJSONObject("data");
|
JSONObject data = jsonObject.getJSONObject("data");
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
result.put("key", data.getString("key"));
|
result.put("key", data.getString("key"));
|
||||||
result.put("rtmp", dstUrl);
|
StreamInfo streamInfoResult = new StreamInfo();
|
||||||
result.put("flv", String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
streamInfoResult.setRtmp(dstUrl);
|
||||||
result.put("ws_flv", String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId));
|
||||||
|
streamInfoResult.setStreamId(streamId);
|
||||||
|
streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
|
||||||
|
result.put("data", streamInfoResult);
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
result.put("code", 1);
|
result.put("code", 1);
|
||||||
|
|
|
@ -3,7 +3,10 @@ package com.genersoft.iot.vmp.vmanager.playback;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
||||||
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -22,6 +25,9 @@ import com.alibaba.fastjson.JSONObject;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
|
import org.springframework.web.context.request.async.DeferredResult;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RestController
|
@RestController
|
||||||
|
@ -39,105 +45,42 @@ public class PlaybackController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||||
|
|
||||||
@Value("${media.closeWaitRTPInfo}")
|
@Autowired
|
||||||
private boolean closeWaitRTPInfo;
|
private IPlayService playService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DeferredResultHolder resultHolder;
|
||||||
|
|
||||||
@GetMapping("/playback/{deviceId}/{channelId}")
|
@GetMapping("/playback/{deviceId}/{channelId}")
|
||||||
public ResponseEntity<String> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime,
|
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime,
|
||||||
String endTime) {
|
String endTime) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
|
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
|
||||||
}
|
}
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
if (StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(channelId)) {
|
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
|
||||||
String log = String.format("设备回放 API调用失败,deviceId:%s ,channelId:%s", deviceId, channelId);
|
// 超时处理
|
||||||
logger.warn(log);
|
result.onTimeout(()->{
|
||||||
return new ResponseEntity<String>(log, HttpStatus.BAD_REQUEST);
|
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
|
||||||
}
|
RequestMessage msg = new RequestMessage();
|
||||||
|
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
|
||||||
|
msg.setData("Timeout");
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
|
});
|
||||||
Device device = storager.queryVideoDevice(deviceId);
|
Device device = storager.queryVideoDevice(deviceId);
|
||||||
StreamInfo streamInfo = storager.queryPlaybackByDevice(deviceId, channelId);
|
StreamInfo streamInfo = storager.queryPlaybackByDevice(deviceId, channelId);
|
||||||
|
|
||||||
if (streamInfo != null) {
|
if (streamInfo != null) {
|
||||||
|
// 停止之前的回放
|
||||||
cmder.streamByeCmd(streamInfo.getSsrc());
|
cmder.streamByeCmd(streamInfo.getSsrc());
|
||||||
}
|
}
|
||||||
|
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
|
||||||
|
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> {
|
||||||
|
logger.info("收到订阅消息: " + response.toJSONString());
|
||||||
|
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString());
|
||||||
|
});
|
||||||
|
|
||||||
// }else {
|
return result;
|
||||||
// String streamId = String.format("%08x",
|
|
||||||
// Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
|
|
||||||
// JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
|
|
||||||
// if (rtpInfo.getBoolean("exist")) {
|
|
||||||
// return new
|
|
||||||
// ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK);
|
|
||||||
// }else {
|
|
||||||
// storager.stopPlayback(streamInfo);
|
|
||||||
// streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime);
|
|
||||||
|
|
||||||
String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("设备回放 API调用,ssrc:" + streamInfo.getSsrc() + ",ZLMedia streamId:" + streamId);
|
|
||||||
}
|
|
||||||
// 等待推流, TODO 默认超时15s
|
|
||||||
boolean lockFlag = true;
|
|
||||||
boolean rtpPushed = false;
|
|
||||||
long lockStartTime = System.currentTimeMillis();
|
|
||||||
JSONObject rtpInfo = null;
|
|
||||||
|
|
||||||
if (closeWaitRTPInfo) {
|
|
||||||
String flv = storager.getMediaInfo().getWanIp() + ":" + storager.getMediaInfo().getHttpPort() + "/rtp/"
|
|
||||||
+ streamId + ".flv";
|
|
||||||
streamInfo.setFlv("http://" + flv);
|
|
||||||
streamInfo.setWs_flv("ws://" + flv);
|
|
||||||
storager.startPlayback(streamInfo);
|
|
||||||
} else {
|
|
||||||
while (lockFlag) {
|
|
||||||
try {
|
|
||||||
if (System.currentTimeMillis() - lockStartTime > 75 * 1000) {
|
|
||||||
storager.stopPlayback(streamInfo);
|
|
||||||
logger.info("播放等待超时");
|
|
||||||
return new ResponseEntity<String>("timeout", HttpStatus.OK);
|
|
||||||
} else {
|
|
||||||
streamInfo = storager.queryPlaybackByDevice(deviceId, channelId);
|
|
||||||
if (!rtpPushed) {
|
|
||||||
logger.info("查询RTP推流信息...");
|
|
||||||
rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
|
|
||||||
}
|
|
||||||
if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null
|
|
||||||
&& streamInfo.getFlv() != null) {
|
|
||||||
logger.info("查询流编码信息:" + streamInfo.getFlv());
|
|
||||||
rtpPushed = true;
|
|
||||||
Thread.sleep(2000);
|
|
||||||
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId);
|
|
||||||
if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) {
|
|
||||||
lockFlag = false;
|
|
||||||
logger.info("流编码信息已获取");
|
|
||||||
JSONArray tracks = mediaInfo.getJSONArray("tracks");
|
|
||||||
streamInfo.setTracks(tracks);
|
|
||||||
storager.startPlayback(streamInfo);
|
|
||||||
} else {
|
|
||||||
logger.info("流编码信息未获取,2秒后重试...");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (streamInfo != null) {
|
|
||||||
return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK);
|
|
||||||
} else {
|
|
||||||
logger.warn("设备回放API调用失败!");
|
|
||||||
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/playback/{ssrc}/stop")
|
@RequestMapping("/playback/{ssrc}/stop")
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.genersoft.iot.vmp.vmanager.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点播处理
|
||||||
|
*/
|
||||||
|
public interface IPlayService {
|
||||||
|
|
||||||
|
void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid);
|
||||||
|
void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid);
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.genersoft.iot.vmp.vmanager.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
|
import com.genersoft.iot.vmp.conf.MediaServerConfig;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
||||||
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.play.PlayController;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class PlayServiceImpl implements IPlayService {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IVideoManagerStorager storager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DeferredResultHolder resultHolder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid) {
|
||||||
|
RequestMessage msg = new RequestMessage();
|
||||||
|
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
|
||||||
|
StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid);
|
||||||
|
if (streamInfo != null) {
|
||||||
|
storager.startPlay(streamInfo);
|
||||||
|
msg.setData(JSON.toJSONString(streamInfo));
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
|
} else {
|
||||||
|
logger.warn("设备预览API调用失败!");
|
||||||
|
msg.setData("设备预览API调用失败!");
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid) {
|
||||||
|
RequestMessage msg = new RequestMessage();
|
||||||
|
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
|
||||||
|
StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid);
|
||||||
|
if (streamInfo != null) {
|
||||||
|
storager.startPlayback(streamInfo);
|
||||||
|
msg.setData(JSON.toJSONString(streamInfo));
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
|
} else {
|
||||||
|
logger.warn("设备预览API调用失败!");
|
||||||
|
msg.setData("设备预览API调用失败!");
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) {
|
||||||
|
String streamId = resonse.getString("id");
|
||||||
|
String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16));
|
||||||
|
StreamInfo streamInfo = new StreamInfo();
|
||||||
|
streamInfo.setSsrc(ssrc);
|
||||||
|
streamInfo.setStreamId(streamId);
|
||||||
|
streamInfo.setDeviceID(deviceId);
|
||||||
|
streamInfo.setCahnnelId(channelId);
|
||||||
|
MediaServerConfig mediaServerConfig = storager.getMediaInfo();
|
||||||
|
|
||||||
|
streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
|
||||||
|
streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
|
||||||
|
|
||||||
|
streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
|
||||||
|
streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
|
||||||
|
|
||||||
|
streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
|
||||||
|
streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
|
||||||
|
|
||||||
|
streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
|
||||||
|
streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
|
||||||
|
|
||||||
|
streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtmpPort(), streamId));
|
||||||
|
streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtspPort(), streamId));
|
||||||
|
|
||||||
|
return streamInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -34,8 +34,7 @@ public class ApiStreamController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IVideoManagerStorager storager;
|
private IVideoManagerStorager storager;
|
||||||
|
|
||||||
@Value("${media.closeWaitRTPInfo}")
|
private boolean closeWaitRTPInfo = false;
|
||||||
private boolean closeWaitRTPInfo;
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -94,7 +93,7 @@ public class ApiStreamController {
|
||||||
StreamInfo streamInfo = storager.queryPlayByDevice(device.getDeviceId(), code);
|
StreamInfo streamInfo = storager.queryPlayByDevice(device.getDeviceId(), code);
|
||||||
if (streamInfo == null) {
|
if (streamInfo == null) {
|
||||||
logger.debug("streamInfo 等于null, 重新点播");
|
logger.debug("streamInfo 等于null, 重新点播");
|
||||||
streamInfo = cmder.playStreamCmd(device, code);
|
// streamInfo = cmder.playStreamCmd(device, code);
|
||||||
}else {
|
}else {
|
||||||
logger.debug("streamInfo 不等于null, 向流媒体查询是否正在推流");
|
logger.debug("streamInfo 不等于null, 向流媒体查询是否正在推流");
|
||||||
String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
|
String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
|
||||||
|
@ -136,7 +135,7 @@ public class ApiStreamController {
|
||||||
} else {
|
} else {
|
||||||
logger.debug("向流媒体查询没有推流, 重新点播");
|
logger.debug("向流媒体查询没有推流, 重新点播");
|
||||||
storager.stopPlay(streamInfo);
|
storager.stopPlay(streamInfo);
|
||||||
streamInfo = cmder.playStreamCmd(device, code);
|
// streamInfo = cmder.playStreamCmd(device, code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
spring:
|
||||||
|
# [不需要改]
|
||||||
|
application:
|
||||||
|
name: iot-vmp-vmanager
|
||||||
|
# [不需要改] 影子数据存储方式,支持redis、jdbc,暂不支持mysql,
|
||||||
|
database: redis
|
||||||
|
# [不需要改] 通信方式,支持kafka、http
|
||||||
|
communicate: http
|
||||||
|
# REDIS数据库配置
|
||||||
|
redis:
|
||||||
|
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
|
||||||
|
host: 127.0.0.1
|
||||||
|
# [必须修改] 端口号
|
||||||
|
port: 6379
|
||||||
|
# [可选] 数据库 DB
|
||||||
|
database: 6
|
||||||
|
# [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
|
||||||
|
password:
|
||||||
|
# [可选] 超时时间
|
||||||
|
timeout: 10000
|
||||||
|
# [不可用] jdbc数据库配置, 暂不支持
|
||||||
|
datasource:
|
||||||
|
name: eiot
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
|
||||||
|
username:
|
||||||
|
password:
|
||||||
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
|
driver-class-name: com.mysql.jdbc.Driver
|
||||||
|
|
||||||
|
# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
|
||||||
|
server:
|
||||||
|
port: 18080
|
||||||
|
|
||||||
|
# 作为28181服务器的配置
|
||||||
|
sip:
|
||||||
|
# [必须修改] 本机的IP, 必须是网卡上的IP
|
||||||
|
ip: 192.168.0.100
|
||||||
|
# [可选] 28181服务监听的端口
|
||||||
|
port: 5060
|
||||||
|
# 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)
|
||||||
|
# 后两位为行业编码,定义参照附录D.3
|
||||||
|
# 3701020049标识山东济南历下区 信息行业接入
|
||||||
|
# [可选]
|
||||||
|
domain: 4401020049
|
||||||
|
# [可选]
|
||||||
|
id: 44010200492000000001
|
||||||
|
# [可选] 默认设备认证密码,后续扩展使用设备单独密码
|
||||||
|
password: admin123
|
||||||
|
|
||||||
|
# 登陆的用户名密码
|
||||||
|
auth:
|
||||||
|
# [可选] 用户名
|
||||||
|
username: admin
|
||||||
|
# [可选] 密码, 默认为admin
|
||||||
|
password: 21232f297a57a5a743894a0e4a801fc3
|
||||||
|
|
||||||
|
#zlm服务器配置
|
||||||
|
media:
|
||||||
|
# [必须修改] zlm服务器的内网IP
|
||||||
|
ip: 192.168.0.100
|
||||||
|
# [可选] zlm服务器的公网IP, 内网部署置空即可
|
||||||
|
wanIp:
|
||||||
|
# [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip
|
||||||
|
hookIp:
|
||||||
|
# [必须修改] zlm服务器的http.port
|
||||||
|
port: 80
|
||||||
|
# [可选] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修改
|
||||||
|
autoConfig: true
|
||||||
|
# [可选] zlm服务器的hook.admin_params=secret
|
||||||
|
secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
|
||||||
|
# [可选] zlm服务器的general.streamNoneReaderDelayMS
|
||||||
|
streamNoneReaderDelayMS: 18000 # 无人观看多久自动关闭流
|
||||||
|
# [可选] 关闭等待收到流编码信息后在返回,
|
||||||
|
# 设为false可以获得更好的兼容性,保证返回后流就可以播放,
|
||||||
|
# 设为true可以快速打开播放窗口,可以获得更好的体验
|
||||||
|
closeWaitRTPInfo: false
|
||||||
|
# 启用udp多端口模式
|
||||||
|
rtp:
|
||||||
|
# [可选] 是否启用udp多端口模式, 开启后会在udpPortRange范围内选择端口用于媒体流传输
|
||||||
|
enable: true
|
||||||
|
# [可选] 在此范围内选择端口用于媒体流传输, 不只是udp, 使用TCP被动传输模式时,也是从这个范围内选择端口
|
||||||
|
udpPortRange: 30000,30500 # 端口范围
|
||||||
|
|
||||||
|
# [可选] 日志配置, 一般不需要改
|
||||||
|
logging:
|
||||||
|
file:
|
||||||
|
name: logs/wvp.log
|
||||||
|
max-history: 30
|
||||||
|
max-size: 10MB
|
||||||
|
total-size-cap: 300MB
|
||||||
|
level:
|
||||||
|
com:
|
||||||
|
genersoft:
|
||||||
|
iot: debug
|
|
@ -1,64 +1,3 @@
|
||||||
spring:
|
spring:
|
||||||
application:
|
profiles:
|
||||||
name: iot-vmp-vmanager
|
active: dev
|
||||||
# 影子数据存储方式,支持redis、jdbc,暂不支持mysql
|
|
||||||
database: redis
|
|
||||||
# 通信方式,支持kafka、http
|
|
||||||
communicate: http
|
|
||||||
redis:
|
|
||||||
# Redis服务器IP
|
|
||||||
host: 192.168.1.141
|
|
||||||
#端口号
|
|
||||||
port: 6379
|
|
||||||
database: 6
|
|
||||||
#访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
|
|
||||||
password: 4767cb971b40a1300fa09b7f87b09d1c
|
|
||||||
#超时时间
|
|
||||||
timeout: 10000
|
|
||||||
datasource:
|
|
||||||
name: eiot
|
|
||||||
url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
|
|
||||||
username:
|
|
||||||
password:
|
|
||||||
type: com.alibaba.druid.pool.DruidDataSource
|
|
||||||
driver-class-name: com.mysql.jdbc.Driver
|
|
||||||
server:
|
|
||||||
port: 18080
|
|
||||||
sip:
|
|
||||||
ip: 192.168.1.20
|
|
||||||
port: 5060
|
|
||||||
# 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)
|
|
||||||
# 后两位为行业编码,定义参照附录D.3
|
|
||||||
# 3701020049标识山东济南历下区 信息行业接入
|
|
||||||
domain: 3402000000
|
|
||||||
id: 34020000002000000001
|
|
||||||
# 默认设备认证密码,后续扩展使用设备单独密码
|
|
||||||
password: 12345678
|
|
||||||
|
|
||||||
auth: #32位小写md5加密(默认密码为admin)
|
|
||||||
username: admin
|
|
||||||
password: 21232f297a57a5a743894a0e4a801fc3
|
|
||||||
|
|
||||||
media: #zlm服务器的ip与http端口, 重点: 这是http端口
|
|
||||||
ip: 127.0.0.1
|
|
||||||
wanIp: 192.168.1.20
|
|
||||||
port: 6080
|
|
||||||
secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
|
|
||||||
streamNoneReaderDelayMS: 600000 # 无人观看多久自动关闭流
|
|
||||||
# 关闭等待收到流编码信息后在返回,
|
|
||||||
# 设为false可以获得更好的兼容性,保证返回后流就可以播放,
|
|
||||||
# 设为true可以快速打开播放窗口,可以获得更好的体验
|
|
||||||
closeWaitRTPInfo: true
|
|
||||||
rtp: # 启用udp多端口模式
|
|
||||||
enable: true
|
|
||||||
udpPortRange: 30000,30500 # 端口范围
|
|
||||||
logging:
|
|
||||||
file:
|
|
||||||
name: logs/wvp.log
|
|
||||||
max-history: 30
|
|
||||||
max-size: 10MB
|
|
||||||
total-size-cap: 300MB
|
|
||||||
level:
|
|
||||||
com:
|
|
||||||
genersoft:
|
|
||||||
iot: debug
|
|
|
@ -64,9 +64,8 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||||
to: config.dev.assetsSubDirectory,
|
to: config.dev.assetsSubDirectory,
|
||||||
ignore: ['.*']
|
ignore: ['.*']
|
||||||
},
|
},
|
||||||
{ from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'},
|
{ from: 'node_modules/@easydarwin/easywasmplayer/libDecoder.wasm'},
|
||||||
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'},
|
{ from: 'node_modules/@easydarwin/easywasmplayer/EasyWasmPlayer.js', to: 'js/'}
|
||||||
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/'}
|
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -115,9 +115,8 @@ const webpackConfig = merge(baseWebpackConfig, {
|
||||||
to: config.build.assetsSubDirectory,
|
to: config.build.assetsSubDirectory,
|
||||||
ignore: ['.*']
|
ignore: ['.*']
|
||||||
},
|
},
|
||||||
{ from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'},
|
{ from: 'node_modules/@easydarwin/easywasmplayer/libDecoder.wasm'},
|
||||||
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'},
|
{ from: 'node_modules/@easydarwin/easywasmplayer/EasyWasmPlayer.js', to: 'js/'}
|
||||||
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/'}
|
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<title>国标28181</title>
|
<title>国标28181</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="text/javascript" src="./js/liveplayer-lib.min.js"></script>
|
<script type="text/javascript" src="./js/EasyWasmPlayer.js"></script>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!-- built files will be auto injected -->
|
<!-- built files will be auto injected -->
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@liveqing/liveplayer": {
|
"@easydarwin/easywasmplayer": {
|
||||||
"version": "1.9.9",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npm.taobao.org/@liveqing/liveplayer/download/@liveqing/liveplayer-1.9.9.tgz",
|
"resolved": "https://registry.npm.taobao.org/@easydarwin/easywasmplayer/download/@easydarwin/easywasmplayer-4.0.7.tgz",
|
||||||
"integrity": "sha1-K7wiab+BiY5qe1/nTpKVyeGdIGo="
|
"integrity": "sha1-FNtIUXbdwIWdalvIMEaH0+zUGx4="
|
||||||
},
|
},
|
||||||
"@types/q": {
|
"@types/q": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.4",
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"build": "node build/build.js"
|
"build": "node build/build.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@liveqing/liveplayer": "^1.9.6",
|
"@easydarwin/easywasmplayer": "^4.0.7",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"core-js": "^2.6.5",
|
"core-js": "^2.6.5",
|
||||||
"echarts": "^4.7.0",
|
"echarts": "^4.7.0",
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="devicePlayer" v-loading="isLoging">
|
<div id="devicePlayer" v-loading="isLoging">
|
||||||
|
|
||||||
<el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
|
<el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
|
||||||
<LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer>
|
<!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> -->
|
||||||
|
<player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></player>
|
||||||
<div id="shared" style="text-align: right; margin-top: 1rem;">
|
<div id="shared" style="text-align: right; margin-top: 1rem;">
|
||||||
<el-tabs v-model="tabActiveName">
|
<el-tabs v-model="tabActiveName" @tab-click="tabHandleClick">
|
||||||
<el-tab-pane label="实时视频" name="media">
|
<el-tab-pane label="实时视频" name="media">
|
||||||
<div style="margin-bottom: 0.5rem;">
|
<div style="margin-bottom: 0.5rem;">
|
||||||
<!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>-->
|
<!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>-->
|
||||||
|
@ -97,6 +99,32 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="编码信息" name="codec" v-loading="tracksLoading">
|
||||||
|
<p>
|
||||||
|
无法播放或者没有声音?   试一试
|
||||||
|
<el-button size="mini" type="primary" v-if="!coverPlaying" @click="coverPlay">转码播放</el-button>
|
||||||
|
<el-button size="mini" type="danger" v-if="coverPlaying" @click="convertStopClick">停止转码</el-button>
|
||||||
|
</p>
|
||||||
|
<div class="trank" >
|
||||||
|
<div v-for="(item, index) in tracks">
|
||||||
|
<span >流 {{index}}</span>
|
||||||
|
<div class="trankInfo" v-if="item.codec_type == 0">
|
||||||
|
<p>格式: {{item.codec_id_name}}</p>
|
||||||
|
<p>类型: 视频</p>
|
||||||
|
<p>分辨率: {{item.width}} x {{item.height}}</p>
|
||||||
|
<p>帧率: {{item.fps}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="trankInfo" v-if="item.codec_type == 1">
|
||||||
|
<p>格式: {{item.codec_id_name}}</p>
|
||||||
|
<p>类型: 音频</p>
|
||||||
|
<p>采样位数: {{item.sample_bit}}</p>
|
||||||
|
<p>采样率: {{item.sample_rate}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
@ -104,12 +132,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LivePlayer from '@liveqing/liveplayer'
|
import player from './player.vue'
|
||||||
export default {
|
export default {
|
||||||
name: 'devicePlayer',
|
name: 'devicePlayer',
|
||||||
props: {},
|
props: {},
|
||||||
components: {
|
components: {
|
||||||
LivePlayer
|
player,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getPlayerShared: function () {
|
getPlayerShared: function () {
|
||||||
|
@ -131,6 +159,7 @@ export default {
|
||||||
},
|
},
|
||||||
showVideoDialog: false,
|
showVideoDialog: false,
|
||||||
ssrc: '',
|
ssrc: '',
|
||||||
|
streamId: '',
|
||||||
convertKey: '',
|
convertKey: '',
|
||||||
deviceId: '',
|
deviceId: '',
|
||||||
channelId: '',
|
channelId: '',
|
||||||
|
@ -148,20 +177,45 @@ export default {
|
||||||
cruisingGroup: 0,
|
cruisingGroup: 0,
|
||||||
scanSpeed: 100,
|
scanSpeed: 100,
|
||||||
scanGroup: 0,
|
scanGroup: 0,
|
||||||
|
tracks: [],
|
||||||
|
coverPlaying:false,
|
||||||
|
tracksLoading: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
tabHandleClick: function(tab, event) {
|
||||||
|
console.log(tab)
|
||||||
|
var that = this;
|
||||||
|
that.tracks = [];
|
||||||
|
that.tracksLoading = true;
|
||||||
|
if (tab.name == "codec") {
|
||||||
|
this.$axios({
|
||||||
|
method: 'get',
|
||||||
|
url: '/zlm/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app=rtp&stream='+ this.streamId
|
||||||
|
}).then(function (res) {
|
||||||
|
that.tracksLoading = false;
|
||||||
|
if (res.data.code == 0 && res.data.online) {
|
||||||
|
that.tracks = res.data.tracks;
|
||||||
|
}else{
|
||||||
|
that.$message({
|
||||||
|
showClose: true,
|
||||||
|
message: '获取编码信息失败,',
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(function (e) {});
|
||||||
|
}
|
||||||
|
},
|
||||||
openDialog: function (tab, deviceId, channelId, param) {
|
openDialog: function (tab, deviceId, channelId, param) {
|
||||||
this.tabActiveName = tab;
|
this.tabActiveName = tab;
|
||||||
this.channelId = channelId;
|
this.channelId = channelId;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.ssrc = "";
|
this.ssrc = "";
|
||||||
|
this.streamId = "";
|
||||||
this.videoUrl = ""
|
this.videoUrl = ""
|
||||||
if (!!this.$refs.videoPlayer) {
|
if (!!this.$refs.videoPlayer) {
|
||||||
this.$refs.videoPlayer.pause();
|
this.$refs.videoPlayer.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (tab) {
|
switch (tab) {
|
||||||
case "media":
|
case "media":
|
||||||
this.play(param.streamInfo, param.hasAudio)
|
this.play(param.streamInfo, param.hasAudio)
|
||||||
|
@ -180,85 +234,56 @@ export default {
|
||||||
console.log(val)
|
console.log(val)
|
||||||
},
|
},
|
||||||
play: function (streamInfo, hasAudio) {
|
play: function (streamInfo, hasAudio) {
|
||||||
this.hasaudio = hasAudio;
|
|
||||||
var that = this;
|
|
||||||
that.isLoging = false;
|
|
||||||
if (!!streamInfo.tracks && streamInfo.tracks.length > 0 ) {
|
|
||||||
for (let i = 0; i < streamInfo.tracks.length; i++) {
|
|
||||||
if (streamInfo.tracks[i].codec_type == 0 && streamInfo.tracks[i].codec_id_name != "CodecH264") { // 判断为H265视频
|
|
||||||
that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{
|
|
||||||
that.close();
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
}else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name != "CodecAAC") {
|
|
||||||
that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{
|
|
||||||
that.playFromStreamInfo(false. streamInfo)
|
|
||||||
})
|
|
||||||
}else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name == "CodecAAC") {
|
|
||||||
that.playFromStreamInfo(true, streamInfo)
|
|
||||||
}else {
|
|
||||||
that.playFromStreamInfo(false, streamInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
that.playFromStreamInfo(false, streamInfo)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
coverPlay: function (streamInfo, codec_id_name, catchcallback) {
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
that.$confirm(codec_id_name + ' 编码格式不支持播放, 是否转码播放?', '提示', {
|
this.hasaudio = hasAudio;
|
||||||
confirmButtonText: '确定',
|
this.isLoging = false;
|
||||||
cancelButtonText: '取消',
|
this.videoUrl = streamInfo.ws_flv;
|
||||||
type: 'warning'
|
this.ssrc = streamInfo.ssrc;
|
||||||
}).then(() => {
|
this.streamId = streamInfo.streamId;
|
||||||
that.isLoging = true;
|
this.playFromStreamInfo(false, streamInfo)
|
||||||
that.$axios({
|
},
|
||||||
|
coverPlay: function () {
|
||||||
|
var that = this;
|
||||||
|
this.coverPlaying = true;
|
||||||
|
this.$refs.videoPlayer.pause()
|
||||||
|
that.$axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/api/play/' + streamInfo.ssrc + '/convert'
|
url: '/api/play/' + that.ssrc + '/convert'
|
||||||
}).then(function (res) {
|
}).then(function (res) {
|
||||||
if (res.data.code == 0) {
|
if (res.data.code == 0) {
|
||||||
streamInfo.ws_flv = res.data.ws_flv;
|
that.convertKey = res.data.key;
|
||||||
that.convertKey = res.data.key;
|
setTimeout(()=>{
|
||||||
setTimeout(()=>{
|
that.isLoging = false;
|
||||||
that.isLoging = false;
|
that.playFromStreamInfo(false, res.data.data);
|
||||||
that.playFromStreamInfo(false, streamInfo);
|
}, 2000)
|
||||||
}, 2000)
|
} else {
|
||||||
} else {
|
that.isLoging = false;
|
||||||
that.isLoging = false;
|
that.coverPlaying = false;
|
||||||
that.$message({
|
that.$message({
|
||||||
showClose: true,
|
showClose: true,
|
||||||
message: '转码失败',
|
message: '转码失败',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).catch(function (e) {
|
}).catch(function (e) {
|
||||||
that.$message({
|
console.log(e)
|
||||||
showClose: true,
|
that.coverPlaying = false;
|
||||||
message: '播放错误',
|
that.$message({
|
||||||
type: 'error'
|
showClose: true,
|
||||||
|
message: '播放错误',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
}).catch(function (e) {
|
convertStopClick: function() {
|
||||||
if (catchcallback)catchcallback()
|
this.convertStop(()=>{
|
||||||
|
this.$refs.videoPlayer.play(this.videoUrl)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
playFromStreamInfo: function (realHasAudio, streamInfo) {
|
convertStop: function(callback) {
|
||||||
this.videoUrl = streamInfo.ws_flv;
|
var that = this;
|
||||||
this.showVideoDialog = true;
|
that.$refs.videoPlayer.pause()
|
||||||
this.hasaudio = realHasAudio && this.hasaudio;
|
this.$axios({
|
||||||
this.ssrc = streamInfo.ssrc;
|
|
||||||
console.log(this.ssrc);
|
|
||||||
},
|
|
||||||
close: function () {
|
|
||||||
console.log('关闭视频');
|
|
||||||
if (!this.$refs.videoPlayer){
|
|
||||||
this.$refs.videoPlayer.pause();
|
|
||||||
}
|
|
||||||
this.videoUrl = '';
|
|
||||||
this.showVideoDialog = false;
|
|
||||||
if (this.convertKey != '') {
|
|
||||||
this.$axios({
|
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/api/play/convert/stop/' + this.convertKey
|
url: '/api/play/convert/stop/' + this.convertKey
|
||||||
}).then(function (res) {
|
}).then(function (res) {
|
||||||
|
@ -267,12 +292,36 @@ export default {
|
||||||
}else {
|
}else {
|
||||||
console.error(res.data.msg)
|
console.error(res.data.msg)
|
||||||
}
|
}
|
||||||
|
if (callback )callback();
|
||||||
}).catch(function (e) {});
|
}).catch(function (e) {});
|
||||||
}
|
that.coverPlaying = false;
|
||||||
this.convertKey = ''
|
that.convertKey = "";
|
||||||
|
// if (callback )callback();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
playFromStreamInfo: function (realHasAudio, streamInfo) {
|
||||||
|
this.showVideoDialog = true;
|
||||||
|
this.hasaudio = realHasAudio && this.hasaudio;
|
||||||
|
this.$refs.videoPlayer.play(streamInfo.ws_flv)
|
||||||
|
},
|
||||||
|
close: function () {
|
||||||
|
console.log('关闭视频');
|
||||||
|
if (!!this.$refs.videoPlayer){
|
||||||
|
this.$refs.videoPlayer.pause();
|
||||||
|
}
|
||||||
|
this.videoUrl = '';
|
||||||
|
this.coverPlaying = false;
|
||||||
|
this.showVideoDialog = false;
|
||||||
|
if (this.convertKey != '') {
|
||||||
|
this.convertStop();
|
||||||
|
}
|
||||||
|
this.convertKey = ''
|
||||||
|
},
|
||||||
|
|
||||||
copySharedInfo: function (data) {
|
copySharedInfo: function (data) {
|
||||||
console.log('复制内容:' + data);
|
console.log('复制内容:' + data);
|
||||||
|
this.coverPlaying = false;
|
||||||
|
this.tracks = []
|
||||||
let _this = this;
|
let _this = this;
|
||||||
this.$copyText(data).then(
|
this.$copyText(data).then(
|
||||||
function (e) {
|
function (e) {
|
||||||
|
@ -602,4 +651,15 @@ export default {
|
||||||
.control-bottom .fa {
|
.control-bottom .fa {
|
||||||
transform: rotate(-45deg) translateY(7px);
|
transform: rotate(-45deg) translateY(7px);
|
||||||
}
|
}
|
||||||
|
.trank {
|
||||||
|
width: 80%;
|
||||||
|
height: 180px;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0 10%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.trankInfo {
|
||||||
|
width: 80%;
|
||||||
|
padding: 0 10%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<template>
|
||||||
|
<div id="player">
|
||||||
|
<div id="easyplayer"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'player',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
easyPlayer: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: ['videoUrl', 'error', 'hasaudio'],
|
||||||
|
mounted () {
|
||||||
|
this.$nextTick(() =>{
|
||||||
|
console.log("初始化时的地址为: " + this.videoUrl)
|
||||||
|
this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK)
|
||||||
|
this.easyPlayer.play(this.videoUrl, 1)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
videoUrl(newData, oldData){
|
||||||
|
this.easyPlayer.destroy()
|
||||||
|
this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK)
|
||||||
|
this.easyPlayer.play(newData, 1)
|
||||||
|
},
|
||||||
|
immediate:true
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
play: function (url) {
|
||||||
|
this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK)
|
||||||
|
this.easyPlayer.play(url, 1)
|
||||||
|
},
|
||||||
|
pause: function () {
|
||||||
|
this.easyPlayer.destroy();
|
||||||
|
},
|
||||||
|
eventcallbacK: function(type, message) {
|
||||||
|
console.log("player 事件回调")
|
||||||
|
console.log(type)
|
||||||
|
console.log(message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.LodingTitle {
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
/* 隐藏logo */
|
||||||
|
/* .iconqingxiLOGO {
|
||||||
|
display: none !important;
|
||||||
|
} */
|
||||||
|
|
||||||
|
</style>
|
|
@ -73,12 +73,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import devicePlayer from './gb28181/devicePlayer.vue'
|
|
||||||
import uiHeader from './UiHeader.vue'
|
import uiHeader from './UiHeader.vue'
|
||||||
export default {
|
export default {
|
||||||
name: 'app',
|
name: 'app',
|
||||||
components: {
|
components: {
|
||||||
devicePlayer,
|
|
||||||
uiHeader
|
uiHeader
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default new VueRouter({
|
||||||
path: '/channelList/:deviceId/:parentChannelId/:count/:page',
|
path: '/channelList/:deviceId/:parentChannelId/:count/:page',
|
||||||
name: 'channelList',
|
name: 'channelList',
|
||||||
component: channelList,
|
component: channelList,
|
||||||
},,
|
},
|
||||||
{
|
{
|
||||||
path: '/parentPlatformList/:count/:page',
|
path: '/parentPlatformList/:count/:page',
|
||||||
name: 'parentPlatformList',
|
name: 'parentPlatformList',
|
||||||
|
|
Loading…
Reference in New Issue