Browse Source

使用异步接口, 更好的并发, 对hook使用订阅机制

替换前段播放器, 支持h265的播放
放弃循环获取编码信息,
pull/10/head
648540858 4 years ago
parent
commit
f75b3e6cda
  1. 56
      src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
  2. 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
  3. 49
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  4. 91
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  5. 34
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
  6. 57
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  7. 88
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
  8. 134
      src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
  9. 116
      src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
  10. 13
      src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java
  11. 90
      src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
  12. 7
      src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java
  13. 5
      web_src/build/webpack.dev.conf.js
  14. 5
      web_src/build/webpack.prod.conf.js
  15. 2
      web_src/index.html
  16. 8
      web_src/package-lock.json
  17. 2
      web_src/package.json
  18. 210
      web_src/src/components/gb28181/devicePlayer.vue
  19. 57
      web_src/src/components/gb28181/player.vue
  20. 2
      web_src/src/components/videoList.vue

56
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java

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

2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java

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

49
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@ -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;
/** /**
* @Description:设备能力接口用于定义设备的控制查询能力 * @Description:设备能力接口用于定义设备的控制查询能力
@ -19,7 +20,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);
/** /**
* 云台方向放控制 * 云台方向放控制
@ -30,7 +31,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);
/** /**
* 云台缩放控制使用配置文件中的默认镜头缩放速度 * 云台缩放控制使用配置文件中的默认镜头缩放速度
@ -39,7 +40,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);
/** /**
* 云台缩放控制 * 云台缩放控制
@ -49,7 +50,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);
/** /**
* 云台控制支持方向与缩放控制 * 云台控制支持方向与缩放控制
@ -62,7 +63,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指令预置位指令巡航指令扫描指令和辅助开关指令
@ -74,7 +75,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);
/** /**
* 请求预览视频流 * 请求预览视频流
@ -82,7 +83,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);
/** /**
* 请求回放视频流 * 请求回放视频流
@ -92,14 +93,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);
/** /**
* 语音广播 * 语音广播
@ -107,7 +108,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);
/** /**
* 音视频录像控制 * 音视频录像控制
@ -115,21 +116,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帧
@ -137,21 +138,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);
/** /**
@ -159,7 +160,7 @@ public interface ISIPCommander {
* *
* @param device 视频设备 * @param device 视频设备
*/ */
public boolean deviceStatusQuery(Device device); boolean deviceStatusQuery(Device device);
/** /**
* 查询设备信息 * 查询设备信息
@ -167,14 +168,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);
/** /**
* 查询录像信息 * 查询录像信息
@ -183,33 +184,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);
} }

91
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@ -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;
/** /**
@ -264,12 +268,12 @@ public class SIPCommander implements ISIPCommander {
} }
/** /**
* 请求预览视频流 * 请求预览视频流
* *
* @param device 视频设备 * @param device 视频设备
* @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,53 +286,57 @@ 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 96 98 97\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 96 98 97\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 96 98 97\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=rtpmap:96 PS/90000\r\n");
content.append("a=rtpmap:98 H264/90000\r\n"); content.append("a=rtpmap:98 H264/90000\r\n");
content.append("a=rtpmap:97 MPEG4/90000\r\n"); content.append("a=rtpmap:97 MPEG4/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");
}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 +348,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");
@ -386,16 +402,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 +441,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) {

34
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java

@ -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
@ -11,18 +16,35 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcesso
*/ */
public class ByeRequestProcessor extends SIPRequestAbstractProcessor { 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);
}
} }

57
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

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

88
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java

@ -0,0 +1,88 @@
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.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
/**
* @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 HashMap<>();
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;
}
}

134
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java

@ -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,95 +46,56 @@ 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(()->{
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); // TODO playStreamCmd 超时处理
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());
String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString());
// 等待推流, 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");
logger.info(tracks.toJSONString());
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);
} }
return result;
} }
@PostMapping("/play/{ssrc}/stop") @PostMapping("/play/{ssrc}/stop")
@ -180,10 +148,20 @@ public class PlayController {
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);

116
src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java

@ -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,41 @@ 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); 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")

13
src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java

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

90
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java

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

7
src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java

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

5
web_src/build/webpack.dev.conf.js

@ -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/'}
]) ])
] ]
}) })

5
web_src/build/webpack.prod.conf.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/'}
]) ])
] ]
}) })

2
web_src/index.html

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

8
web_src/package-lock.json

@ -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",

2
web_src/package.json

@ -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",

210
web_src/src/components/gb28181/devicePlayer.vue

@ -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>
无法播放或者没有声音?&nbsp&nbsp&nbsp试一试
<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,75 +234,75 @@ export default {
console.log(val) console.log(val)
}, },
play: function (streamInfo, hasAudio) { play: function (streamInfo, hasAudio) {
this.hasaudio = hasAudio; this.hasaudio = hasAudio;
this.isLoging = false;
this.videoUrl = streamInfo.ws_flv;
this.ssrc = streamInfo.ssrc;
this.streamId = streamInfo.streamId;
this.playFromStreamInfo(false, streamInfo)
},
coverPlay: function () {
var that = this; var that = this;
that.isLoging = false; this.coverPlaying = true;
if (!!streamInfo.tracks && streamInfo.tracks.length > 0 ) { this.$refs.videoPlayer.pause()
for (let i = 0; i < streamInfo.tracks.length; i++) { that.$axios({
if (streamInfo.tracks[i].codec_type == 0 && streamInfo.tracks[i].codec_id_name != "CodecH264") { // H265 method: 'post',
that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ url: '/api/play/' + that.ssrc + '/convert'
that.close(); }).then(function (res) {
return; if (res.data.code == 0) {
}) that.convertKey = res.data.key;
}else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name != "CodecAAC") { setTimeout(()=>{
that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ that.isLoging = false;
that.playFromStreamInfo(false. streamInfo) that.playFromStreamInfo(false, res.data.data);
}) }, 2000)
}else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name == "CodecAAC") { } else {
that.playFromStreamInfo(true, streamInfo) that.isLoging = false;
}else { that.coverPlaying = false;
that.playFromStreamInfo(false, streamInfo) that.$message({
} showClose: true,
} message: '转码失败',
}else { type: 'error'
that.playFromStreamInfo(false, streamInfo) });
} }
}).catch(function (e) {
console.log(e)
that.coverPlaying = false;
that.$message({
showClose: true,
message: '播放错误',
type: 'error'
});
});
}, },
coverPlay: function (streamInfo, codec_id_name, catchcallback) { convertStopClick: function() {
var that = this; this.convertStop(()=>{
this.$refs.videoPlayer.play(this.videoUrl)
that.$confirm(codec_id_name + ' 编码格式不支持播放, 是否转码播放?', '提示', { });
confirmButtonText: '确定', },
cancelButtonText: '取消', convertStop: function(callback) {
type: 'warning' var that = this;
}).then(() => { that.$refs.videoPlayer.pause()
that.isLoging = true; this.$axios({
that.$axios({
method: 'post', method: 'post',
url: '/api/play/' + streamInfo.ssrc + '/convert' url: '/api/play/convert/stop/' + this.convertKey
}).then(function (res) { }).then(function (res) {
if (res.data.code == 0) { if (res.data.code == 0) {
streamInfo.ws_flv = res.data.ws_flv; console.log(res.data.msg)
that.convertKey = res.data.key; }else {
setTimeout(()=>{ console.error(res.data.msg)
that.isLoging = false;
that.playFromStreamInfo(false, streamInfo);
}, 2000)
} else {
that.isLoging = false;
that.$message({
showClose: true,
message: '转码失败',
type: 'error'
});
} }
}).catch(function (e) { if (callback )callback();
that.$message({ }).catch(function (e) {});
showClose: true, that.coverPlaying = false;
message: '播放错误', that.convertKey = "";
type: 'error' if (callback )callback();
});
});
}).catch(function (e) {
if (catchcallback)catchcallback()
});
}, },
playFromStreamInfo: function (realHasAudio, streamInfo) { playFromStreamInfo: function (realHasAudio, streamInfo) {
this.videoUrl = streamInfo.ws_flv;
this.showVideoDialog = true; this.showVideoDialog = true;
this.hasaudio = realHasAudio && this.hasaudio; this.hasaudio = realHasAudio && this.hasaudio;
this.ssrc = streamInfo.ssrc; this.$refs.videoPlayer.play(streamInfo.ws_flv)
console.log(this.ssrc);
}, },
close: function () { close: function () {
console.log('关闭视频'); console.log('关闭视频');
@ -256,23 +310,18 @@ export default {
this.$refs.videoPlayer.pause(); this.$refs.videoPlayer.pause();
} }
this.videoUrl = ''; this.videoUrl = '';
this.coverPlaying = false;
this.showVideoDialog = false; this.showVideoDialog = false;
if (this.convertKey != '') { if (this.convertKey != '') {
this.$axios({ this.convertStop();
method: 'post',
url: '/api/play/convert/stop/' + this.convertKey
}).then(function (res) {
if (res.data.code == 0) {
console.log(res.data.msg)
}else {
console.error(res.data.msg)
}
}).catch(function (e) {});
} }
this.convertKey = '' 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>

57
web_src/src/components/gb28181/player.vue

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

2
web_src/src/components/videoList.vue

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

Loading…
Cancel
Save