Commit 44d216100b45c3337c593ee82ee68e7e0f35d24b
1 parent
ecaf8750
与master分支同步
Showing
30 changed files
with
824 additions
and
445 deletions
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
| @@ -5,12 +5,18 @@ import com.alibaba.fastjson.JSONArray; | @@ -5,12 +5,18 @@ import com.alibaba.fastjson.JSONArray; | ||
| 5 | public class StreamInfo { | 5 | public class StreamInfo { |
| 6 | 6 | ||
| 7 | private String ssrc; | 7 | private String ssrc; |
| 8 | + private String streamId; | ||
| 8 | private String deviceID; | 9 | private String deviceID; |
| 9 | private String cahnnelId; | 10 | private String cahnnelId; |
| 10 | private String flv; | 11 | private String flv; |
| 11 | private String ws_flv; | 12 | private String ws_flv; |
| 12 | - private String rtmp; | 13 | + private String fmp4; |
| 14 | + private String ws_fmp4; | ||
| 13 | private String hls; | 15 | private String hls; |
| 16 | + private String ws_hls; | ||
| 17 | + private String ts; | ||
| 18 | + private String ws_ts; | ||
| 19 | + private String rtmp; | ||
| 14 | private String rtsp; | 20 | private String rtsp; |
| 15 | private JSONArray tracks; | 21 | private JSONArray tracks; |
| 16 | 22 | ||
| @@ -85,4 +91,52 @@ public class StreamInfo { | @@ -85,4 +91,52 @@ public class StreamInfo { | ||
| 85 | public void setTracks(JSONArray tracks) { | 91 | public void setTracks(JSONArray tracks) { |
| 86 | this.tracks = tracks; | 92 | this.tracks = tracks; |
| 87 | } | 93 | } |
| 94 | + | ||
| 95 | + public String getFmp4() { | ||
| 96 | + return fmp4; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + public void setFmp4(String fmp4) { | ||
| 100 | + this.fmp4 = fmp4; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + public String getWs_fmp4() { | ||
| 104 | + return ws_fmp4; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + public void setWs_fmp4(String ws_fmp4) { | ||
| 108 | + this.ws_fmp4 = ws_fmp4; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + public String getWs_hls() { | ||
| 112 | + return ws_hls; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + public void setWs_hls(String ws_hls) { | ||
| 116 | + this.ws_hls = ws_hls; | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + public String getTs() { | ||
| 120 | + return ts; | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + public void setTs(String ts) { | ||
| 124 | + this.ts = ts; | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + public String getWs_ts() { | ||
| 128 | + return ws_ts; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + public void setWs_ts(String ws_ts) { | ||
| 132 | + this.ws_ts = ws_ts; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + public String getStreamId() { | ||
| 136 | + return streamId; | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + public void setStreamId(String streamId) { | ||
| 140 | + this.streamId = streamId; | ||
| 141 | + } | ||
| 88 | } | 142 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
| @@ -113,6 +113,7 @@ public class SipLayer implements SipListener { | @@ -113,6 +113,7 @@ public class SipLayer implements SipListener { | ||
| 113 | */ | 113 | */ |
| 114 | @Override | 114 | @Override |
| 115 | public void processRequest(RequestEvent evt) { | 115 | public void processRequest(RequestEvent evt) { |
| 116 | + logger.debug(evt.getRequest().toString()); | ||
| 116 | // 由于jainsip是单线程程序,为提高性能并发处理 | 117 | // 由于jainsip是单线程程序,为提高性能并发处理 |
| 117 | processThreadPool.execute(() -> { | 118 | processThreadPool.execute(() -> { |
| 118 | processorFactory.createRequestProcessor(evt).process(); | 119 | processorFactory.createRequestProcessor(evt).process(); |
| @@ -122,6 +123,7 @@ public class SipLayer implements SipListener { | @@ -122,6 +123,7 @@ public class SipLayer implements SipListener { | ||
| 122 | @Override | 123 | @Override |
| 123 | public void processResponse(ResponseEvent evt) { | 124 | public void processResponse(ResponseEvent evt) { |
| 124 | Response response = evt.getResponse(); | 125 | Response response = evt.getResponse(); |
| 126 | + logger.debug(evt.getResponse().toString()); | ||
| 125 | int status = response.getStatusCode(); | 127 | int status = response.getStatusCode(); |
| 126 | if (((status >= 200) && (status < 300)) || status == 401) { // Success! | 128 | if (((status >= 200) && (status < 300)) || status == 401) { // Success! |
| 127 | ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); | 129 | ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); |
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java
| @@ -32,7 +32,7 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> { | @@ -32,7 +32,7 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> { | ||
| 32 | public void onApplicationEvent(OnlineEvent event) { | 32 | public void onApplicationEvent(OnlineEvent event) { |
| 33 | 33 | ||
| 34 | if (logger.isDebugEnabled()) { | 34 | if (logger.isDebugEnabled()) { |
| 35 | - logger.debug("设备离线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom()); | 35 | + logger.debug("设备上线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom()); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId(); | 38 | String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId(); |
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
| @@ -17,13 +17,11 @@ public class VideoStreamSessionManager { | @@ -17,13 +17,11 @@ public class VideoStreamSessionManager { | ||
| 17 | private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>(); | 17 | private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>(); |
| 18 | 18 | ||
| 19 | public String createPlaySsrc(){ | 19 | public String createPlaySsrc(){ |
| 20 | - String ssrc = SsrcUtil.getPlaySsrc(); | ||
| 21 | - return ssrc; | 20 | + return SsrcUtil.getPlaySsrc(); |
| 22 | } | 21 | } |
| 23 | 22 | ||
| 24 | public String createPlayBackSsrc(){ | 23 | public String createPlayBackSsrc(){ |
| 25 | - String ssrc = SsrcUtil.getPlayBackSsrc(); | ||
| 26 | - return ssrc; | 24 | + return SsrcUtil.getPlayBackSsrc(); |
| 27 | } | 25 | } |
| 28 | 26 | ||
| 29 | public void put(String ssrc,ClientTransaction transaction){ | 27 | public void put(String ssrc,ClientTransaction transaction){ |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
| @@ -22,6 +22,8 @@ public class DeferredResultHolder { | @@ -22,6 +22,8 @@ public class DeferredResultHolder { | ||
| 22 | 22 | ||
| 23 | public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; | 23 | public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; |
| 24 | 24 | ||
| 25 | + public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; | ||
| 26 | + | ||
| 25 | private Map<String, DeferredResult> map = new HashMap<String, DeferredResult>(); | 27 | private Map<String, DeferredResult> map = new HashMap<String, DeferredResult>(); |
| 26 | 28 | ||
| 27 | public void put(String key, DeferredResult result) { | 29 | public void put(String key, DeferredResult result) { |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; | @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; | ||
| 2 | 2 | ||
| 3 | import com.genersoft.iot.vmp.common.StreamInfo; | 3 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 4 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 4 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 5 | +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | ||
| 5 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | 6 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 6 | 7 | ||
| 7 | /** | 8 | /** |
| @@ -20,7 +21,7 @@ public interface ISIPCommander { | @@ -20,7 +21,7 @@ public interface ISIPCommander { | ||
| 20 | * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 | 21 | * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 |
| 21 | * @param moveSpeed 镜头移动速度 | 22 | * @param moveSpeed 镜头移动速度 |
| 22 | */ | 23 | */ |
| 23 | - public boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); | 24 | + boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); |
| 24 | 25 | ||
| 25 | /** | 26 | /** |
| 26 | * 云台方向放控制 | 27 | * 云台方向放控制 |
| @@ -31,7 +32,7 @@ public interface ISIPCommander { | @@ -31,7 +32,7 @@ public interface ISIPCommander { | ||
| 31 | * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 | 32 | * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 |
| 32 | * @param moveSpeed 镜头移动速度 | 33 | * @param moveSpeed 镜头移动速度 |
| 33 | */ | 34 | */ |
| 34 | - public boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); | 35 | + boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * 云台缩放控制,使用配置文件中的默认镜头缩放速度 | 38 | * 云台缩放控制,使用配置文件中的默认镜头缩放速度 |
| @@ -40,7 +41,7 @@ public interface ISIPCommander { | @@ -40,7 +41,7 @@ public interface ISIPCommander { | ||
| 40 | * @param channelId 预览通道 | 41 | * @param channelId 预览通道 |
| 41 | * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 | 42 | * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 |
| 42 | */ | 43 | */ |
| 43 | - public boolean ptzZoomCmd(Device device,String channelId,int inOut); | 44 | + boolean ptzZoomCmd(Device device,String channelId,int inOut); |
| 44 | 45 | ||
| 45 | /** | 46 | /** |
| 46 | * 云台缩放控制 | 47 | * 云台缩放控制 |
| @@ -50,7 +51,7 @@ public interface ISIPCommander { | @@ -50,7 +51,7 @@ public interface ISIPCommander { | ||
| 50 | * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 | 51 | * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 |
| 51 | * @param zoomSpeed 镜头缩放速度 | 52 | * @param zoomSpeed 镜头缩放速度 |
| 52 | */ | 53 | */ |
| 53 | - public boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); | 54 | + boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); |
| 54 | 55 | ||
| 55 | /** | 56 | /** |
| 56 | * 云台控制,支持方向与缩放控制 | 57 | * 云台控制,支持方向与缩放控制 |
| @@ -63,7 +64,7 @@ public interface ISIPCommander { | @@ -63,7 +64,7 @@ public interface ISIPCommander { | ||
| 63 | * @param moveSpeed 镜头移动速度 | 64 | * @param moveSpeed 镜头移动速度 |
| 64 | * @param zoomSpeed 镜头缩放速度 | 65 | * @param zoomSpeed 镜头缩放速度 |
| 65 | */ | 66 | */ |
| 66 | - public boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); | 67 | + boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); |
| 67 | 68 | ||
| 68 | /** | 69 | /** |
| 69 | * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 | 70 | * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 |
| @@ -75,7 +76,7 @@ public interface ISIPCommander { | @@ -75,7 +76,7 @@ public interface ISIPCommander { | ||
| 75 | * @param parameter2 数据2 | 76 | * @param parameter2 数据2 |
| 76 | * @param combineCode2 组合码2 | 77 | * @param combineCode2 组合码2 |
| 77 | */ | 78 | */ |
| 78 | - public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); | 79 | + boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); |
| 79 | 80 | ||
| 80 | /** | 81 | /** |
| 81 | * 请求预览视频流 | 82 | * 请求预览视频流 |
| @@ -83,7 +84,7 @@ public interface ISIPCommander { | @@ -83,7 +84,7 @@ public interface ISIPCommander { | ||
| 83 | * @param device 视频设备 | 84 | * @param device 视频设备 |
| 84 | * @param channelId 预览通道 | 85 | * @param channelId 预览通道 |
| 85 | */ | 86 | */ |
| 86 | - public StreamInfo playStreamCmd(Device device, String channelId); | 87 | + void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event); |
| 87 | 88 | ||
| 88 | /** | 89 | /** |
| 89 | * 请求回放视频流 | 90 | * 请求回放视频流 |
| @@ -93,14 +94,14 @@ public interface ISIPCommander { | @@ -93,14 +94,14 @@ public interface ISIPCommander { | ||
| 93 | * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss | 94 | * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss |
| 94 | * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss | 95 | * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
| 95 | */ | 96 | */ |
| 96 | - public StreamInfo playbackStreamCmd(Device device,String channelId, String startTime, String endTime); | 97 | + void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event); |
| 97 | 98 | ||
| 98 | /** | 99 | /** |
| 99 | * 视频流停止 | 100 | * 视频流停止 |
| 100 | * | 101 | * |
| 101 | * @param ssrc ssrc | 102 | * @param ssrc ssrc |
| 102 | */ | 103 | */ |
| 103 | - public void streamByeCmd(String ssrc); | 104 | + void streamByeCmd(String ssrc); |
| 104 | 105 | ||
| 105 | /** | 106 | /** |
| 106 | * 语音广播 | 107 | * 语音广播 |
| @@ -108,7 +109,7 @@ public interface ISIPCommander { | @@ -108,7 +109,7 @@ public interface ISIPCommander { | ||
| 108 | * @param device 视频设备 | 109 | * @param device 视频设备 |
| 109 | * @param channelId 预览通道 | 110 | * @param channelId 预览通道 |
| 110 | */ | 111 | */ |
| 111 | - public boolean audioBroadcastCmd(Device device,String channelId); | 112 | + boolean audioBroadcastCmd(Device device,String channelId); |
| 112 | 113 | ||
| 113 | /** | 114 | /** |
| 114 | * 音视频录像控制 | 115 | * 音视频录像控制 |
| @@ -116,21 +117,21 @@ public interface ISIPCommander { | @@ -116,21 +117,21 @@ public interface ISIPCommander { | ||
| 116 | * @param device 视频设备 | 117 | * @param device 视频设备 |
| 117 | * @param channelId 预览通道 | 118 | * @param channelId 预览通道 |
| 118 | */ | 119 | */ |
| 119 | - public boolean recordCmd(Device device,String channelId); | 120 | + boolean recordCmd(Device device,String channelId); |
| 120 | 121 | ||
| 121 | /** | 122 | /** |
| 122 | * 报警布防/撤防命令 | 123 | * 报警布防/撤防命令 |
| 123 | * | 124 | * |
| 124 | * @param device 视频设备 | 125 | * @param device 视频设备 |
| 125 | */ | 126 | */ |
| 126 | - public boolean guardCmd(Device device); | 127 | + boolean guardCmd(Device device); |
| 127 | 128 | ||
| 128 | /** | 129 | /** |
| 129 | * 报警复位命令 | 130 | * 报警复位命令 |
| 130 | * | 131 | * |
| 131 | * @param device 视频设备 | 132 | * @param device 视频设备 |
| 132 | */ | 133 | */ |
| 133 | - public boolean alarmCmd(Device device); | 134 | + boolean alarmCmd(Device device); |
| 134 | 135 | ||
| 135 | /** | 136 | /** |
| 136 | * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 | 137 | * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 |
| @@ -138,21 +139,21 @@ public interface ISIPCommander { | @@ -138,21 +139,21 @@ public interface ISIPCommander { | ||
| 138 | * @param device 视频设备 | 139 | * @param device 视频设备 |
| 139 | * @param channelId 预览通道 | 140 | * @param channelId 预览通道 |
| 140 | */ | 141 | */ |
| 141 | - public boolean iFameCmd(Device device,String channelId); | 142 | + boolean iFameCmd(Device device,String channelId); |
| 142 | 143 | ||
| 143 | /** | 144 | /** |
| 144 | * 看守位控制命令 | 145 | * 看守位控制命令 |
| 145 | * | 146 | * |
| 146 | * @param device 视频设备 | 147 | * @param device 视频设备 |
| 147 | */ | 148 | */ |
| 148 | - public boolean homePositionCmd(Device device); | 149 | + boolean homePositionCmd(Device device); |
| 149 | 150 | ||
| 150 | /** | 151 | /** |
| 151 | * 设备配置命令 | 152 | * 设备配置命令 |
| 152 | * | 153 | * |
| 153 | * @param device 视频设备 | 154 | * @param device 视频设备 |
| 154 | */ | 155 | */ |
| 155 | - public boolean deviceConfigCmd(Device device); | 156 | + boolean deviceConfigCmd(Device device); |
| 156 | 157 | ||
| 157 | 158 | ||
| 158 | /** | 159 | /** |
| @@ -160,7 +161,7 @@ public interface ISIPCommander { | @@ -160,7 +161,7 @@ public interface ISIPCommander { | ||
| 160 | * | 161 | * |
| 161 | * @param device 视频设备 | 162 | * @param device 视频设备 |
| 162 | */ | 163 | */ |
| 163 | - public boolean deviceStatusQuery(Device device); | 164 | + boolean deviceStatusQuery(Device device); |
| 164 | 165 | ||
| 165 | /** | 166 | /** |
| 166 | * 查询设备信息 | 167 | * 查询设备信息 |
| @@ -168,14 +169,14 @@ public interface ISIPCommander { | @@ -168,14 +169,14 @@ public interface ISIPCommander { | ||
| 168 | * @param device 视频设备 | 169 | * @param device 视频设备 |
| 169 | * @return | 170 | * @return |
| 170 | */ | 171 | */ |
| 171 | - public boolean deviceInfoQuery(Device device); | 172 | + boolean deviceInfoQuery(Device device); |
| 172 | 173 | ||
| 173 | /** | 174 | /** |
| 174 | * 查询目录列表 | 175 | * 查询目录列表 |
| 175 | * | 176 | * |
| 176 | * @param device 视频设备 | 177 | * @param device 视频设备 |
| 177 | */ | 178 | */ |
| 178 | - public boolean catalogQuery(Device device); | 179 | + boolean catalogQuery(Device device); |
| 179 | 180 | ||
| 180 | /** | 181 | /** |
| 181 | * 查询录像信息 | 182 | * 查询录像信息 |
| @@ -184,35 +185,33 @@ public interface ISIPCommander { | @@ -184,35 +185,33 @@ public interface ISIPCommander { | ||
| 184 | * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss | 185 | * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss |
| 185 | * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss | 186 | * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
| 186 | */ | 187 | */ |
| 187 | - public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); | 188 | + boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); |
| 188 | 189 | ||
| 189 | /** | 190 | /** |
| 190 | * 查询报警信息 | 191 | * 查询报警信息 |
| 191 | * | 192 | * |
| 192 | * @param device 视频设备 | 193 | * @param device 视频设备 |
| 193 | */ | 194 | */ |
| 194 | - public boolean alarmInfoQuery(Device device); | 195 | + boolean alarmInfoQuery(Device device); |
| 195 | 196 | ||
| 196 | /** | 197 | /** |
| 197 | * 查询设备配置 | 198 | * 查询设备配置 |
| 198 | * | 199 | * |
| 199 | * @param device 视频设备 | 200 | * @param device 视频设备 |
| 200 | */ | 201 | */ |
| 201 | - public boolean configQuery(Device device); | 202 | + boolean configQuery(Device device); |
| 202 | 203 | ||
| 203 | /** | 204 | /** |
| 204 | * 查询设备预置位置 | 205 | * 查询设备预置位置 |
| 205 | * | 206 | * |
| 206 | * @param device 视频设备 | 207 | * @param device 视频设备 |
| 207 | */ | 208 | */ |
| 208 | - public boolean presetQuery(Device device); | 209 | + boolean presetQuery(Device device); |
| 209 | 210 | ||
| 210 | /** | 211 | /** |
| 211 | * 查询移动设备位置数据 | 212 | * 查询移动设备位置数据 |
| 212 | * | 213 | * |
| 213 | * @param device 视频设备 | 214 | * @param device 视频设备 |
| 214 | */ | 215 | */ |
| 215 | - public boolean mobilePostitionQuery(Device device); | ||
| 216 | - | ||
| 217 | - | 216 | + boolean mobilePostitionQuery(Device device); |
| 218 | } | 217 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| @@ -19,6 +19,7 @@ import com.alibaba.fastjson.JSONObject; | @@ -19,6 +19,7 @@ import com.alibaba.fastjson.JSONObject; | ||
| 19 | import com.genersoft.iot.vmp.common.StreamInfo; | 19 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 20 | import com.genersoft.iot.vmp.conf.MediaServerConfig; | 20 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 21 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | 21 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 22 | +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | ||
| 22 | import com.genersoft.iot.vmp.media.zlm.ZLMUtils; | 23 | import com.genersoft.iot.vmp.media.zlm.ZLMUtils; |
| 23 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | 24 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 24 | import org.springframework.beans.factory.annotation.Autowired; | 25 | import org.springframework.beans.factory.annotation.Autowired; |
| @@ -67,6 +68,9 @@ public class SIPCommander implements ISIPCommander { | @@ -67,6 +68,9 @@ public class SIPCommander implements ISIPCommander { | ||
| 67 | @Value("${media.rtp.enable}") | 68 | @Value("${media.rtp.enable}") |
| 68 | private boolean rtpEnable; | 69 | private boolean rtpEnable; |
| 69 | 70 | ||
| 71 | + @Autowired | ||
| 72 | + private ZLMHttpHookSubscribe subscribe; | ||
| 73 | + | ||
| 70 | 74 | ||
| 71 | 75 | ||
| 72 | /** | 76 | /** |
| @@ -264,12 +268,12 @@ public class SIPCommander implements ISIPCommander { | @@ -264,12 +268,12 @@ public class SIPCommander implements ISIPCommander { | ||
| 264 | } | 268 | } |
| 265 | /** | 269 | /** |
| 266 | * 请求预览视频流 | 270 | * 请求预览视频流 |
| 267 | - * | 271 | + * |
| 268 | * @param device 视频设备 | 272 | * @param device 视频设备 |
| 269 | * @param channelId 预览通道 | 273 | * @param channelId 预览通道 |
| 270 | - */ | 274 | + */ |
| 271 | @Override | 275 | @Override |
| 272 | - public StreamInfo playStreamCmd(Device device, String channelId) { | 276 | + public void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event) { |
| 273 | try { | 277 | try { |
| 274 | 278 | ||
| 275 | String ssrc = streamSession.createPlaySsrc(); | 279 | String ssrc = streamSession.createPlaySsrc(); |
| @@ -282,53 +286,63 @@ public class SIPCommander implements ISIPCommander { | @@ -282,53 +286,63 @@ public class SIPCommander implements ISIPCommander { | ||
| 282 | }else { | 286 | }else { |
| 283 | mediaPort = mediaInfo.getRtpProxyPort(); | 287 | mediaPort = mediaInfo.getRtpProxyPort(); |
| 284 | } | 288 | } |
| 289 | + | ||
| 290 | + String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); | ||
| 291 | + // 添加订阅 | ||
| 292 | + JSONObject subscribeKey = new JSONObject(); | ||
| 293 | + subscribeKey.put("app", "rtp"); | ||
| 294 | + subscribeKey.put("id", streamId); | ||
| 295 | + | ||
| 296 | + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); | ||
| 285 | // | 297 | // |
| 286 | StringBuffer content = new StringBuffer(200); | 298 | StringBuffer content = new StringBuffer(200); |
| 287 | - content.append("v=0\r\n"); | ||
| 288 | - content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | ||
| 289 | - content.append("s=Play\r\n"); | ||
| 290 | - content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | ||
| 291 | - content.append("t=0 0\r\n"); | ||
| 292 | - if("TCP-PASSIVE".equals(streamMode)) { | ||
| 293 | - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); | 299 | + content.append("v=0\r\n"); |
| 300 | + content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | ||
| 301 | + content.append("s=Play\r\n"); | ||
| 302 | + content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | ||
| 303 | + content.append("t=0 0\r\n"); | ||
| 304 | + if("TCP-PASSIVE".equals(streamMode)) { | ||
| 305 | + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); | ||
| 294 | }else if ("TCP-ACTIVE".equals(streamMode)) { | 306 | }else if ("TCP-ACTIVE".equals(streamMode)) { |
| 295 | - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); | 307 | + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); |
| 296 | }else if("UDP".equals(streamMode)) { | 308 | }else if("UDP".equals(streamMode)) { |
| 297 | - content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n"); | 309 | + content.append("m=video "+ mediaPort +" RTP/AVP 126 125 99 34 98 97 96\r\n"); |
| 298 | } | 310 | } |
| 299 | - content.append("a=recvonly\r\n"); | ||
| 300 | - content.append("a=rtpmap:96 PS/90000\r\n"); | ||
| 301 | - content.append("a=rtpmap:98 H264/90000\r\n"); | ||
| 302 | - content.append("a=rtpmap:97 MPEG4/90000\r\n"); | ||
| 303 | - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 | ||
| 304 | - content.append("a=setup:passive\r\n"); | 311 | + content.append("a=recvonly\r\n"); |
| 312 | + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); | ||
| 313 | + content.append("a=rtpmap:126 H264/90000\r\n"); | ||
| 314 | + content.append("a=rtpmap:125 H264S/90000\r\n"); | ||
| 315 | + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); | ||
| 316 | + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); | ||
| 317 | + content.append("a=fmtp:99 profile-level-id=3\r\n"); | ||
| 318 | + content.append("a=rtpmap:98 H264/90000\r\n"); | ||
| 319 | + content.append("a=rtpmap:97 MPEG4/90000\r\n"); | ||
| 320 | + content.append("a=rtpmap:96 PS/90000\r\n"); | ||
| 321 | + if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 | ||
| 322 | + content.append("a=setup:passive\r\n"); | ||
| 305 | content.append("a=connection:new\r\n"); | 323 | content.append("a=connection:new\r\n"); |
| 306 | - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 | 324 | + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 |
| 307 | content.append("a=setup:active\r\n"); | 325 | content.append("a=setup:active\r\n"); |
| 308 | content.append("a=connection:new\r\n"); | 326 | content.append("a=connection:new\r\n"); |
| 309 | } | 327 | } |
| 310 | - content.append("y="+ssrc+"\r\n");//ssrc | ||
| 311 | - | ||
| 312 | - Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null, ssrc); | ||
| 313 | - | ||
| 314 | - ClientTransaction transaction = transmitRequest(device, request); | ||
| 315 | - streamSession.put(ssrc, transaction); | 328 | + content.append("y="+ssrc+"\r\n");//ssrc |
| 329 | + | ||
| 330 | + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null, ssrc); | ||
| 331 | + | ||
| 332 | + ClientTransaction transaction = transmitRequest(device, request); | ||
| 333 | + streamSession.put(ssrc, transaction); | ||
| 316 | DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); | 334 | DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); |
| 317 | if (deviceChannel != null) { | 335 | if (deviceChannel != null) { |
| 318 | deviceChannel.setSsrc(ssrc); | 336 | deviceChannel.setSsrc(ssrc); |
| 319 | storager.updateChannel(device.getDeviceId(), deviceChannel); | 337 | storager.updateChannel(device.getDeviceId(), deviceChannel); |
| 320 | } | 338 | } |
| 321 | 339 | ||
| 322 | - StreamInfo streamInfo = new StreamInfo(); | ||
| 323 | - streamInfo.setSsrc(ssrc); | ||
| 324 | - streamInfo.setCahnnelId(channelId); | ||
| 325 | - streamInfo.setDeviceID(device.getDeviceId()); | ||
| 326 | - storager.startPlay(streamInfo); | ||
| 327 | - return streamInfo; | 340 | + // TODO 订阅SIP response,处理对方的错误返回 |
| 341 | + | ||
| 342 | + | ||
| 328 | } catch ( SipException | ParseException | InvalidArgumentException e) { | 343 | } catch ( SipException | ParseException | InvalidArgumentException e) { |
| 329 | e.printStackTrace(); | 344 | e.printStackTrace(); |
| 330 | - return null; | ||
| 331 | - } | 345 | + } |
| 332 | } | 346 | } |
| 333 | 347 | ||
| 334 | /** | 348 | /** |
| @@ -340,10 +354,18 @@ public class SIPCommander implements ISIPCommander { | @@ -340,10 +354,18 @@ public class SIPCommander implements ISIPCommander { | ||
| 340 | * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss | 354 | * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
| 341 | */ | 355 | */ |
| 342 | @Override | 356 | @Override |
| 343 | - public StreamInfo playbackStreamCmd(Device device, String channelId, String startTime, String endTime) { | 357 | + public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event) { |
| 344 | try { | 358 | try { |
| 345 | MediaServerConfig mediaInfo = storager.getMediaInfo(); | 359 | MediaServerConfig mediaInfo = storager.getMediaInfo(); |
| 346 | String ssrc = streamSession.createPlayBackSsrc(); | 360 | String ssrc = streamSession.createPlayBackSsrc(); |
| 361 | + String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); | ||
| 362 | + // 添加订阅 | ||
| 363 | + JSONObject subscribeKey = new JSONObject(); | ||
| 364 | + subscribeKey.put("app", "rtp"); | ||
| 365 | + subscribeKey.put("id", streamId); | ||
| 366 | + | ||
| 367 | + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); | ||
| 368 | + | ||
| 347 | // | 369 | // |
| 348 | StringBuffer content = new StringBuffer(200); | 370 | StringBuffer content = new StringBuffer(200); |
| 349 | content.append("v=0\r\n"); | 371 | content.append("v=0\r\n"); |
| @@ -362,16 +384,22 @@ public class SIPCommander implements ISIPCommander { | @@ -362,16 +384,22 @@ public class SIPCommander implements ISIPCommander { | ||
| 362 | } | 384 | } |
| 363 | String streamMode = device.getStreamMode().toUpperCase(); | 385 | String streamMode = device.getStreamMode().toUpperCase(); |
| 364 | if("TCP-PASSIVE".equals(streamMode)) { | 386 | if("TCP-PASSIVE".equals(streamMode)) { |
| 365 | - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); | 387 | + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); |
| 366 | }else if ("TCP-ACTIVE".equals(streamMode)) { | 388 | }else if ("TCP-ACTIVE".equals(streamMode)) { |
| 367 | - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); | 389 | + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); |
| 368 | }else if("UDP".equals(streamMode)) { | 390 | }else if("UDP".equals(streamMode)) { |
| 369 | - content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n"); | 391 | + content.append("m=video "+ mediaPort +" RTP/AVP 126 125 99 34 98 97 96\r\n"); |
| 370 | } | 392 | } |
| 371 | - content.append("a=recvonly\r\n"); | ||
| 372 | - content.append("a=rtpmap:96 PS/90000\r\n"); | ||
| 373 | - content.append("a=rtpmap:98 H264/90000\r\n"); | ||
| 374 | - content.append("a=rtpmap:97 MPEG4/90000\r\n"); | 393 | + content.append("a=recvonly\r\n"); |
| 394 | + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); | ||
| 395 | + content.append("a=rtpmap:126 H264/90000\r\n"); | ||
| 396 | + content.append("a=rtpmap:125 H264S/90000\r\n"); | ||
| 397 | + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); | ||
| 398 | + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); | ||
| 399 | + content.append("a=fmtp:99 profile-level-id=3\r\n"); | ||
| 400 | + content.append("a=rtpmap:98 H264/90000\r\n"); | ||
| 401 | + content.append("a=rtpmap:97 MPEG4/90000\r\n"); | ||
| 402 | + content.append("a=rtpmap:96 PS/90000\r\n"); | ||
| 375 | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 | 403 | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 |
| 376 | content.append("a=setup:passive\r\n"); | 404 | content.append("a=setup:passive\r\n"); |
| 377 | content.append("a=connection:new\r\n"); | 405 | content.append("a=connection:new\r\n"); |
| @@ -386,16 +414,8 @@ public class SIPCommander implements ISIPCommander { | @@ -386,16 +414,8 @@ public class SIPCommander implements ISIPCommander { | ||
| 386 | ClientTransaction transaction = transmitRequest(device, request); | 414 | ClientTransaction transaction = transmitRequest(device, request); |
| 387 | streamSession.put(ssrc, transaction); | 415 | streamSession.put(ssrc, transaction); |
| 388 | 416 | ||
| 389 | - StreamInfo streamInfo = new StreamInfo(); | ||
| 390 | - streamInfo.setSsrc(ssrc); | ||
| 391 | - streamInfo.setCahnnelId(channelId); | ||
| 392 | - streamInfo.setDeviceID(device.getDeviceId()); | ||
| 393 | - boolean b = storager.startPlayback(streamInfo); | ||
| 394 | - return streamInfo; | ||
| 395 | - | ||
| 396 | } catch ( SipException | ParseException | InvalidArgumentException e) { | 417 | } catch ( SipException | ParseException | InvalidArgumentException e) { |
| 397 | e.printStackTrace(); | 418 | e.printStackTrace(); |
| 398 | - return null; | ||
| 399 | } | 419 | } |
| 400 | } | 420 | } |
| 401 | 421 | ||
| @@ -433,6 +453,7 @@ public class SIPCommander implements ISIPCommander { | @@ -433,6 +453,7 @@ public class SIPCommander implements ISIPCommander { | ||
| 433 | clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); | 453 | clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); |
| 434 | } | 454 | } |
| 435 | dialog.sendRequest(clientTransaction); | 455 | dialog.sendRequest(clientTransaction); |
| 456 | + streamSession.remove(ssrc); | ||
| 436 | } catch (TransactionDoesNotExistException e) { | 457 | } catch (TransactionDoesNotExistException e) { |
| 437 | e.printStackTrace(); | 458 | e.printStackTrace(); |
| 438 | } catch (SipException e) { | 459 | } catch (SipException e) { |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java
| @@ -21,14 +21,12 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor { | @@ -21,14 +21,12 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 21 | * 处理 ACK请求 | 21 | * 处理 ACK请求 |
| 22 | * | 22 | * |
| 23 | * @param evt | 23 | * @param evt |
| 24 | - * @param layer | ||
| 25 | - * @param transaction | ||
| 26 | - * @param config | ||
| 27 | - */ | 24 | + */ |
| 28 | @Override | 25 | @Override |
| 29 | public void process(RequestEvent evt) { | 26 | public void process(RequestEvent evt) { |
| 30 | Request request = evt.getRequest(); | 27 | Request request = evt.getRequest(); |
| 31 | Dialog dialog = evt.getDialog(); | 28 | Dialog dialog = evt.getDialog(); |
| 29 | + if (dialog == null) return; | ||
| 32 | try { | 30 | try { |
| 33 | Request ackRequest = null; | 31 | Request ackRequest = null; |
| 34 | CSeq csReq = (CSeq) request.getHeader(CSeq.NAME); | 32 | CSeq csReq = (CSeq) request.getHeader(CSeq.NAME); |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
| 1 | package com.genersoft.iot.vmp.gb28181.transmit.request.impl; | 1 | package com.genersoft.iot.vmp.gb28181.transmit.request.impl; |
| 2 | 2 | ||
| 3 | +import javax.sip.InvalidArgumentException; | ||
| 3 | import javax.sip.RequestEvent; | 4 | import javax.sip.RequestEvent; |
| 5 | +import javax.sip.SipException; | ||
| 6 | +import javax.sip.message.Response; | ||
| 4 | 7 | ||
| 5 | import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; | 8 | import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; |
| 6 | 9 | ||
| 10 | +import java.text.ParseException; | ||
| 11 | + | ||
| 7 | /** | 12 | /** |
| 8 | * @Description: BYE请求处理器 | 13 | * @Description: BYE请求处理器 |
| 9 | * @author: swwheihei | 14 | * @author: swwheihei |
| @@ -11,18 +16,35 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcesso | @@ -11,18 +16,35 @@ import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcesso | ||
| 11 | */ | 16 | */ |
| 12 | public class ByeRequestProcessor extends SIPRequestAbstractProcessor { | 17 | public class ByeRequestProcessor extends SIPRequestAbstractProcessor { |
| 13 | 18 | ||
| 14 | - /** | 19 | + /** |
| 15 | * 处理BYE请求 | 20 | * 处理BYE请求 |
| 16 | - * | ||
| 17 | * @param evt | 21 | * @param evt |
| 18 | - * @param layer | ||
| 19 | - * @param transaction | ||
| 20 | - * @param config | ||
| 21 | - */ | 22 | + */ |
| 22 | @Override | 23 | @Override |
| 23 | public void process(RequestEvent evt) { | 24 | public void process(RequestEvent evt) { |
| 25 | + try { | ||
| 26 | + responseAck(evt); | ||
| 27 | + } catch (SipException e) { | ||
| 28 | + e.printStackTrace(); | ||
| 29 | + } catch (InvalidArgumentException e) { | ||
| 30 | + e.printStackTrace(); | ||
| 31 | + } catch (ParseException e) { | ||
| 32 | + e.printStackTrace(); | ||
| 33 | + } | ||
| 24 | // TODO 优先级99 Bye Request消息实现,此消息一般为级联消息,上级给下级发送视频停止指令 | 34 | // TODO 优先级99 Bye Request消息实现,此消息一般为级联消息,上级给下级发送视频停止指令 |
| 25 | 35 | ||
| 26 | } | 36 | } |
| 27 | 37 | ||
| 38 | + /*** | ||
| 39 | + * 回复200 OK | ||
| 40 | + * @param evt | ||
| 41 | + * @throws SipException | ||
| 42 | + * @throws InvalidArgumentException | ||
| 43 | + * @throws ParseException | ||
| 44 | + */ | ||
| 45 | + private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { | ||
| 46 | + Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); | ||
| 47 | + getServerTransaction(evt).sendResponse(response); | ||
| 48 | + } | ||
| 49 | + | ||
| 28 | } | 50 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
| @@ -184,10 +184,11 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | @@ -184,10 +184,11 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 184 | DeviceChannel deviceChannel = new DeviceChannel(); | 184 | DeviceChannel deviceChannel = new DeviceChannel(); |
| 185 | deviceChannel.setName(channelName); | 185 | deviceChannel.setName(channelName); |
| 186 | deviceChannel.setChannelId(channelDeviceId); | 186 | deviceChannel.setChannelId(channelDeviceId); |
| 187 | - if (status.equals("ON") || status.equals("On")) { | 187 | + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 |
| 188 | + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) { | ||
| 188 | deviceChannel.setStatus(1); | 189 | deviceChannel.setStatus(1); |
| 189 | } | 190 | } |
| 190 | - if (status.equals("OFF") || status.equals("Off")) { | 191 | + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { |
| 191 | deviceChannel.setStatus(0); | 192 | deviceChannel.setStatus(0); |
| 192 | } | 193 | } |
| 193 | 194 |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| @@ -49,6 +49,9 @@ public class ZLMHttpHookListener { | @@ -49,6 +49,9 @@ public class ZLMHttpHookListener { | ||
| 49 | @Autowired | 49 | @Autowired |
| 50 | private ZLMRESTfulUtils zlmresTfulUtils; | 50 | private ZLMRESTfulUtils zlmresTfulUtils; |
| 51 | 51 | ||
| 52 | + @Autowired | ||
| 53 | + private ZLMHttpHookSubscribe subscribe; | ||
| 54 | + | ||
| 52 | @Value("${media.ip}") | 55 | @Value("${media.ip}") |
| 53 | private String mediaIp; | 56 | private String mediaIp; |
| 54 | 57 | ||
| @@ -128,30 +131,38 @@ public class ZLMHttpHookListener { | @@ -128,30 +131,38 @@ public class ZLMHttpHookListener { | ||
| 128 | } | 131 | } |
| 129 | String app = json.getString("app"); | 132 | String app = json.getString("app"); |
| 130 | String streamId = json.getString("id"); | 133 | String streamId = json.getString("id"); |
| 131 | - if ("rtp".equals(app)) { | ||
| 132 | - String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); | ||
| 133 | - StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc); | ||
| 134 | - if ("rtp".equals(app) && streamInfoForPlay != null ) { | ||
| 135 | - MediaServerConfig mediaInfo = storager.getMediaInfo(); | ||
| 136 | - streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 137 | - streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 138 | - streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); | ||
| 139 | - streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 140 | - streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); | ||
| 141 | - storager.startPlay(streamInfoForPlay); | ||
| 142 | - } | ||
| 143 | 134 | ||
| 144 | - StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc); | ||
| 145 | - if ("rtp".equals(app) && streamInfoForPlayBack != null ) { | ||
| 146 | - MediaServerConfig mediaInfo = storager.getMediaInfo(); | ||
| 147 | - streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 148 | - streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 149 | - streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); | ||
| 150 | - streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 151 | - streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); | ||
| 152 | - storager.startPlayback(streamInfoForPlayBack); | ||
| 153 | - } | ||
| 154 | - } | 135 | + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); |
| 136 | + if (subscribe != null) subscribe.response(json); | ||
| 137 | + | ||
| 138 | +// if ("rtp".equals(app)) { | ||
| 139 | +// String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); | ||
| 140 | +// StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc); | ||
| 141 | +// if ("rtp".equals(app) && streamInfoForPlay != null ) { | ||
| 142 | +// MediaServerConfig mediaInfo = storager.getMediaInfo(); | ||
| 143 | +// streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 144 | +// streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 145 | +// streamInfoForPlay.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 146 | +// streamInfoForPlay.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 147 | +// streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); | ||
| 148 | +// streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 149 | +// streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); | ||
| 150 | +// storager.startPlay(streamInfoForPlay); | ||
| 151 | +// } | ||
| 152 | +// | ||
| 153 | +// StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc); | ||
| 154 | +// if ("rtp".equals(app) && streamInfoForPlayBack != null ) { | ||
| 155 | +// MediaServerConfig mediaInfo = storager.getMediaInfo(); | ||
| 156 | +// streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 157 | +// streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 158 | +// streamInfoForPlayBack.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 159 | +// streamInfoForPlayBack.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 160 | +// streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); | ||
| 161 | +// streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 162 | +// streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); | ||
| 163 | +// storager.startPlayback(streamInfoForPlayBack); | ||
| 164 | +// } | ||
| 165 | +// } | ||
| 155 | 166 | ||
| 156 | // TODO Auto-generated method stub | 167 | // TODO Auto-generated method stub |
| 157 | 168 |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.media.zlm; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSON; | ||
| 4 | +import com.alibaba.fastjson.JSONObject; | ||
| 5 | +import com.genersoft.iot.vmp.common.StreamInfo; | ||
| 6 | +import com.genersoft.iot.vmp.conf.MediaServerConfig; | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | ||
| 8 | +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | ||
| 9 | +import org.slf4j.Logger; | ||
| 10 | +import org.slf4j.LoggerFactory; | ||
| 11 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 12 | +import org.springframework.beans.factory.annotation.Value; | ||
| 13 | +import org.springframework.http.HttpStatus; | ||
| 14 | +import org.springframework.http.ResponseEntity; | ||
| 15 | +import org.springframework.stereotype.Component; | ||
| 16 | +import org.springframework.util.ConcurrentReferenceHashMap; | ||
| 17 | +import org.springframework.web.bind.annotation.*; | ||
| 18 | + | ||
| 19 | +import javax.servlet.http.HttpServletRequest; | ||
| 20 | +import java.math.BigInteger; | ||
| 21 | +import java.text.DecimalFormat; | ||
| 22 | +import java.util.HashMap; | ||
| 23 | +import java.util.Map; | ||
| 24 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * @Description:针对 ZLMediaServer的hook事件订阅 | ||
| 28 | + * @author: pan | ||
| 29 | + * @date: 2020年12月2日 21:17:32 | ||
| 30 | + */ | ||
| 31 | +@Component | ||
| 32 | +public class ZLMHttpHookSubscribe { | ||
| 33 | + | ||
| 34 | + private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookSubscribe.class); | ||
| 35 | + | ||
| 36 | + public enum HookType{ | ||
| 37 | + on_flow_report, | ||
| 38 | + on_http_access, | ||
| 39 | + on_play, | ||
| 40 | + on_publish, | ||
| 41 | + on_record_mp4, | ||
| 42 | + on_rtsp_auth, | ||
| 43 | + on_rtsp_realm, | ||
| 44 | + on_shell_login, | ||
| 45 | + on_stream_changed, | ||
| 46 | + on_stream_none_reader, | ||
| 47 | + on_stream_not_found, | ||
| 48 | + on_server_started | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + public interface Event{ | ||
| 52 | + void response(JSONObject response); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>(); | ||
| 56 | + | ||
| 57 | + public void addSubscribe(HookType type, JSONObject hookResponse, ZLMHttpHookSubscribe.Event event) { | ||
| 58 | + Map<JSONObject, Event> eventMap = allSubscribes.get(type); | ||
| 59 | + if (eventMap == null) { | ||
| 60 | + eventMap = new HashMap<JSONObject, Event>(); | ||
| 61 | + allSubscribes.put(type,eventMap); | ||
| 62 | + } | ||
| 63 | + eventMap.put(hookResponse, event); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + public ZLMHttpHookSubscribe.Event getSubscribe(HookType type, JSONObject hookResponse) { | ||
| 67 | + ZLMHttpHookSubscribe.Event event= null; | ||
| 68 | + Map<JSONObject, Event> eventMap = allSubscribes.get(type); | ||
| 69 | + if (eventMap == null) { | ||
| 70 | + return null; | ||
| 71 | + } | ||
| 72 | + for (JSONObject key : eventMap.keySet()) { | ||
| 73 | + Boolean result = null; | ||
| 74 | + for (String s : key.keySet()) { | ||
| 75 | + String string = hookResponse.getString(s); | ||
| 76 | + String string1 = key.getString(s); | ||
| 77 | + if (result == null) { | ||
| 78 | + result = key.getString(s).equals(hookResponse.getString(s)); | ||
| 79 | + }else { | ||
| 80 | + result = result && key.getString(s).equals(hookResponse.getString(s)); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + } | ||
| 84 | + if (result) { | ||
| 85 | + event = eventMap.get(key); | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + return event; | ||
| 89 | + } | ||
| 90 | +} |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
| @@ -36,6 +36,9 @@ public class ZLMRunner implements CommandLineRunner { | @@ -36,6 +36,9 @@ public class ZLMRunner implements CommandLineRunner { | ||
| 36 | @Value("${media.wanIp}") | 36 | @Value("${media.wanIp}") |
| 37 | private String mediaWanIp; | 37 | private String mediaWanIp; |
| 38 | 38 | ||
| 39 | + @Value("${media.hookIp}") | ||
| 40 | + private String mediaHookIp; | ||
| 41 | + | ||
| 39 | @Value("${media.port}") | 42 | @Value("${media.port}") |
| 40 | private int mediaPort; | 43 | private int mediaPort; |
| 41 | 44 | ||
| @@ -51,6 +54,9 @@ public class ZLMRunner implements CommandLineRunner { | @@ -51,6 +54,9 @@ public class ZLMRunner implements CommandLineRunner { | ||
| 51 | @Value("${server.port}") | 54 | @Value("${server.port}") |
| 52 | private String serverPort; | 55 | private String serverPort; |
| 53 | 56 | ||
| 57 | + @Value("${media.autoConfig}") | ||
| 58 | + private boolean autoConfig; | ||
| 59 | + | ||
| 54 | @Autowired | 60 | @Autowired |
| 55 | private ZLMRESTfulUtils zlmresTfulUtils; | 61 | private ZLMRESTfulUtils zlmresTfulUtils; |
| 56 | 62 | ||
| @@ -61,8 +67,7 @@ public class ZLMRunner implements CommandLineRunner { | @@ -61,8 +67,7 @@ public class ZLMRunner implements CommandLineRunner { | ||
| 61 | MediaServerConfig mediaServerConfig = getMediaServerConfig(); | 67 | MediaServerConfig mediaServerConfig = getMediaServerConfig(); |
| 62 | if (mediaServerConfig != null) { | 68 | if (mediaServerConfig != null) { |
| 63 | logger.info("zlm接入成功..."); | 69 | logger.info("zlm接入成功..."); |
| 64 | - logger.info("设置zlm..."); | ||
| 65 | - saveZLMConfig(); | 70 | + if (autoConfig) saveZLMConfig(); |
| 66 | mediaServerConfig = getMediaServerConfig(); | 71 | mediaServerConfig = getMediaServerConfig(); |
| 67 | storager.updateMediaInfo(mediaServerConfig); | 72 | storager.updateMediaInfo(mediaServerConfig); |
| 68 | } | 73 | } |
| @@ -91,12 +96,12 @@ public class ZLMRunner implements CommandLineRunner { | @@ -91,12 +96,12 @@ public class ZLMRunner implements CommandLineRunner { | ||
| 91 | } | 96 | } |
| 92 | 97 | ||
| 93 | private void saveZLMConfig() { | 98 | private void saveZLMConfig() { |
| 94 | - String hookIP = sipIP; | ||
| 95 | - if (mediaIp.equals(sipIP)) { | ||
| 96 | - hookIP = "127.0.0.1"; | 99 | + logger.info("设置zlm..."); |
| 100 | + if (StringUtils.isEmpty(mediaHookIp)) { | ||
| 101 | + mediaHookIp = sipIP; | ||
| 97 | } | 102 | } |
| 98 | 103 | ||
| 99 | - String hookPrex = String.format("http://%s:%s/index/hook", hookIP, serverPort); | 104 | + String hookPrex = String.format("http://%s:%s/index/hook", mediaHookIp, serverPort); |
| 100 | Map<String, Object> param = new HashMap<>(); | 105 | Map<String, Object> param = new HashMap<>(); |
| 101 | param.put("api.secret",mediaSecret); // -profile:v Baseline | 106 | param.put("api.secret",mediaSecret); // -profile:v Baseline |
| 102 | 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"); | 107 | 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"); |
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java
| @@ -555,6 +555,10 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { | @@ -555,6 +555,10 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { | ||
| 555 | List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | 555 | List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, |
| 556 | deviceId, | 556 | deviceId, |
| 557 | code)); | 557 | code)); |
| 558 | + if (playLeys == null || playLeys.size() == 0) { | ||
| 559 | + playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | ||
| 560 | + deviceId)); | ||
| 561 | + } | ||
| 558 | if (playLeys == null || playLeys.size() == 0) return null; | 562 | if (playLeys == null || playLeys.size() == 0) return null; |
| 559 | return (StreamInfo)redis.get(playLeys.get(0).toString()); | 563 | return (StreamInfo)redis.get(playLeys.get(0).toString()); |
| 560 | } | 564 | } |
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
| @@ -4,7 +4,10 @@ import com.alibaba.fastjson.JSON; | @@ -4,7 +4,10 @@ import com.alibaba.fastjson.JSON; | ||
| 4 | import com.alibaba.fastjson.JSONArray; | 4 | import com.alibaba.fastjson.JSONArray; |
| 5 | import com.genersoft.iot.vmp.common.StreamInfo; | 5 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 6 | import com.genersoft.iot.vmp.conf.MediaServerConfig; | 6 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 7 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | ||
| 8 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | ||
| 7 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | 9 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| 10 | +import com.genersoft.iot.vmp.vmanager.service.IPlayService; | ||
| 8 | import org.slf4j.Logger; | 11 | import org.slf4j.Logger; |
| 9 | import org.slf4j.LoggerFactory; | 12 | import org.slf4j.LoggerFactory; |
| 10 | import org.springframework.beans.factory.annotation.Autowired; | 13 | import org.springframework.beans.factory.annotation.Autowired; |
| @@ -22,6 +25,10 @@ import com.alibaba.fastjson.JSONObject; | @@ -22,6 +25,10 @@ import com.alibaba.fastjson.JSONObject; | ||
| 22 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 25 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 23 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | 26 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 24 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | 27 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 28 | +import org.springframework.web.context.request.async.DeferredResult; | ||
| 29 | + | ||
| 30 | +import java.text.DecimalFormat; | ||
| 31 | +import java.util.UUID; | ||
| 25 | 32 | ||
| 26 | @CrossOrigin | 33 | @CrossOrigin |
| 27 | @RestController | 34 | @RestController |
| @@ -39,94 +46,57 @@ public class PlayController { | @@ -39,94 +46,57 @@ public class PlayController { | ||
| 39 | @Autowired | 46 | @Autowired |
| 40 | private ZLMRESTfulUtils zlmresTfulUtils; | 47 | private ZLMRESTfulUtils zlmresTfulUtils; |
| 41 | 48 | ||
| 42 | - @Value("${media.closeWaitRTPInfo}") | ||
| 43 | - private boolean closeWaitRTPInfo; | 49 | + @Autowired |
| 50 | + private DeferredResultHolder resultHolder; | ||
| 51 | + | ||
| 52 | + @Autowired | ||
| 53 | + private IPlayService playService; | ||
| 44 | 54 | ||
| 45 | @GetMapping("/play/{deviceId}/{channelId}") | 55 | @GetMapping("/play/{deviceId}/{channelId}") |
| 46 | - public ResponseEntity<String> play(@PathVariable String deviceId, @PathVariable String channelId, | ||
| 47 | - Integer getEncoding) { | 56 | + public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, |
| 57 | + @PathVariable String channelId) { | ||
| 58 | + | ||
| 48 | 59 | ||
| 49 | - if (getEncoding == null) getEncoding = 0; | ||
| 50 | - getEncoding = closeWaitRTPInfo ? 0 : getEncoding; | ||
| 51 | Device device = storager.queryVideoDevice(deviceId); | 60 | Device device = storager.queryVideoDevice(deviceId); |
| 52 | StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId); | 61 | StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId); |
| 53 | 62 | ||
| 63 | + UUID uuid = UUID.randomUUID(); | ||
| 64 | + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); | ||
| 65 | + // 超时处理 | ||
| 66 | + result.onTimeout(()->{ | ||
| 67 | + logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); | ||
| 68 | + RequestMessage msg = new RequestMessage(); | ||
| 69 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | ||
| 70 | + msg.setData("Timeout"); | ||
| 71 | + resultHolder.invokeResult(msg); | ||
| 72 | + }); | ||
| 73 | + // 录像查询以channelId作为deviceId查询 | ||
| 74 | + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); | ||
| 75 | + | ||
| 54 | if (streamInfo == null) { | 76 | if (streamInfo == null) { |
| 55 | - streamInfo = cmder.playStreamCmd(device, channelId); | 77 | + // 发送点播消息 |
| 78 | + cmder.playStreamCmd(device, channelId, (JSONObject response) -> { | ||
| 79 | + logger.info("收到订阅消息: " + response.toJSONString()); | ||
| 80 | + playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); | ||
| 81 | + }); | ||
| 56 | } else { | 82 | } else { |
| 57 | String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); | 83 | String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); |
| 58 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); | 84 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); |
| 59 | if (rtpInfo.getBoolean("exist")) { | 85 | if (rtpInfo.getBoolean("exist")) { |
| 60 | - return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK); | 86 | + RequestMessage msg = new RequestMessage(); |
| 87 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | ||
| 88 | + msg.setData(JSON.toJSONString(streamInfo)); | ||
| 89 | + resultHolder.invokeResult(msg); | ||
| 61 | } else { | 90 | } else { |
| 62 | storager.stopPlay(streamInfo); | 91 | storager.stopPlay(streamInfo); |
| 63 | - streamInfo = cmder.playStreamCmd(device, channelId); | ||
| 64 | - } | ||
| 65 | - } | ||
| 66 | - String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); | ||
| 67 | - // 等待推流, TODO 默认超时30s | ||
| 68 | - boolean lockFlag = true; | ||
| 69 | - boolean rtpPushed = false; | ||
| 70 | - long startTime = System.currentTimeMillis(); | ||
| 71 | - JSONObject rtpInfo = null; | ||
| 72 | - | ||
| 73 | - if (getEncoding == 1) { | ||
| 74 | - while (lockFlag) { | ||
| 75 | - try { | ||
| 76 | - if (System.currentTimeMillis() - startTime > 60 * 1000) { | ||
| 77 | - storager.stopPlay(streamInfo); | ||
| 78 | - logger.info("播放等待超时"); | ||
| 79 | - return new ResponseEntity<String>("timeout", HttpStatus.OK); | ||
| 80 | - } else { | ||
| 81 | - streamInfo = storager.queryPlayByDevice(deviceId, channelId); | ||
| 82 | - if (!rtpPushed) { | ||
| 83 | - logger.info("查询RTP推流信息..."); | ||
| 84 | - rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); | ||
| 85 | - } | ||
| 86 | - if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null | ||
| 87 | - && streamInfo.getFlv() != null) { | ||
| 88 | - logger.info("查询流编码信息:" + streamInfo.getFlv()); | ||
| 89 | - rtpPushed = true; | ||
| 90 | - Thread.sleep(2000); | ||
| 91 | - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); | ||
| 92 | - if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) { | ||
| 93 | - lockFlag = false; | ||
| 94 | - logger.info("流编码信息已获取"); | ||
| 95 | - JSONArray tracks = mediaInfo.getJSONArray("tracks"); | ||
| 96 | - streamInfo.setTracks(tracks); | ||
| 97 | - storager.startPlay(streamInfo); | ||
| 98 | - } else { | ||
| 99 | - logger.info("流编码信息未获取,2秒后重试..."); | ||
| 100 | - } | ||
| 101 | - } else { | ||
| 102 | - Thread.sleep(2000); | ||
| 103 | - continue; | ||
| 104 | - } | ||
| 105 | - } | ||
| 106 | - } catch (InterruptedException e) { | ||
| 107 | - e.printStackTrace(); | ||
| 108 | - } | 92 | + // TODO playStreamCmd 超时处理 |
| 93 | + cmder.playStreamCmd(device, channelId, (JSONObject response) -> { | ||
| 94 | + logger.info("收到订阅消息: " + response.toJSONString()); | ||
| 95 | + playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); | ||
| 96 | + }); | ||
| 109 | } | 97 | } |
| 110 | - } else { | ||
| 111 | - String flv = storager.getMediaInfo().getWanIp() + ":" + storager.getMediaInfo().getHttpPort() + "/rtp/" | ||
| 112 | - + streamId + ".flv"; | ||
| 113 | - streamInfo.setFlv("http://" + flv); | ||
| 114 | - streamInfo.setWs_flv("ws://" + flv); | ||
| 115 | - storager.startPlay(streamInfo); | ||
| 116 | - } | ||
| 117 | - | ||
| 118 | - if (logger.isDebugEnabled()) { | ||
| 119 | - logger.debug(String.format("设备预览 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); | ||
| 120 | - logger.debug("设备预览 API调用,ssrc:" + streamInfo.getSsrc() + ",ZLMedia streamId:" | ||
| 121 | - + Integer.toHexString(Integer.parseInt(streamInfo.getSsrc()))); | ||
| 122 | - } | ||
| 123 | - | ||
| 124 | - if (streamInfo != null) { | ||
| 125 | - return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK); | ||
| 126 | - } else { | ||
| 127 | - logger.warn("设备预览API调用失败!"); | ||
| 128 | - return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); | ||
| 129 | } | 98 | } |
| 99 | + return result; | ||
| 130 | } | 100 | } |
| 131 | 101 | ||
| 132 | @PostMapping("/play/{ssrc}/stop") | 102 | @PostMapping("/play/{ssrc}/stop") |
| @@ -172,17 +142,28 @@ public class PlayController { | @@ -172,17 +142,28 @@ public class PlayController { | ||
| 172 | MediaServerConfig mediaInfo = storager.getMediaInfo(); | 142 | MediaServerConfig mediaInfo = storager.getMediaInfo(); |
| 173 | String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), | 143 | String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), |
| 174 | streamId ); | 144 | streamId ); |
| 175 | - JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(streamInfo.getRtsp(), dstUrl, "1000000"); | 145 | + String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId); |
| 146 | + JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(srcUrl, dstUrl, "1000000"); | ||
| 176 | System.out.println(jsonObject); | 147 | System.out.println(jsonObject); |
| 177 | JSONObject result = new JSONObject(); | 148 | JSONObject result = new JSONObject(); |
| 178 | if (jsonObject != null && jsonObject.getInteger("code") == 0) { | 149 | if (jsonObject != null && jsonObject.getInteger("code") == 0) { |
| 179 | result.put("code", 0); | 150 | result.put("code", 0); |
| 180 | JSONObject data = jsonObject.getJSONObject("data"); | 151 | JSONObject data = jsonObject.getJSONObject("data"); |
| 181 | if (data != null) { | 152 | if (data != null) { |
| 182 | - result.put("key", data.getString("key")); | ||
| 183 | - result.put("rtmp", dstUrl); | ||
| 184 | - result.put("flv", String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 185 | - result.put("ws_flv", String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | 153 | + result.put("key", data.getString("key")); |
| 154 | + StreamInfo streamInfoResult = new StreamInfo(); | ||
| 155 | + streamInfoResult.setRtmp(dstUrl); | ||
| 156 | + streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); | ||
| 157 | + streamInfoResult.setStreamId(streamId); | ||
| 158 | + streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 159 | + streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 160 | + streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 161 | + streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 162 | + streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 163 | + streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 164 | + streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 165 | + streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | ||
| 166 | + result.put("data", streamInfoResult); | ||
| 186 | } | 167 | } |
| 187 | }else { | 168 | }else { |
| 188 | result.put("code", 1); | 169 | result.put("code", 1); |
src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
| @@ -3,7 +3,10 @@ package com.genersoft.iot.vmp.vmanager.playback; | @@ -3,7 +3,10 @@ package com.genersoft.iot.vmp.vmanager.playback; | ||
| 3 | import com.alibaba.fastjson.JSON; | 3 | import com.alibaba.fastjson.JSON; |
| 4 | import com.alibaba.fastjson.JSONArray; | 4 | import com.alibaba.fastjson.JSONArray; |
| 5 | import com.genersoft.iot.vmp.common.StreamInfo; | 5 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 6 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | ||
| 6 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | 8 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| 9 | +import com.genersoft.iot.vmp.vmanager.service.IPlayService; | ||
| 7 | import org.slf4j.Logger; | 10 | import org.slf4j.Logger; |
| 8 | import org.slf4j.LoggerFactory; | 11 | import org.slf4j.LoggerFactory; |
| 9 | import org.springframework.beans.factory.annotation.Autowired; | 12 | import org.springframework.beans.factory.annotation.Autowired; |
| @@ -22,6 +25,9 @@ import com.alibaba.fastjson.JSONObject; | @@ -22,6 +25,9 @@ import com.alibaba.fastjson.JSONObject; | ||
| 22 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 25 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 23 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | 26 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 24 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | 27 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 28 | +import org.springframework.web.context.request.async.DeferredResult; | ||
| 29 | + | ||
| 30 | +import java.util.UUID; | ||
| 25 | 31 | ||
| 26 | @CrossOrigin | 32 | @CrossOrigin |
| 27 | @RestController | 33 | @RestController |
| @@ -39,105 +45,42 @@ public class PlaybackController { | @@ -39,105 +45,42 @@ public class PlaybackController { | ||
| 39 | @Autowired | 45 | @Autowired |
| 40 | private ZLMRESTfulUtils zlmresTfulUtils; | 46 | private ZLMRESTfulUtils zlmresTfulUtils; |
| 41 | 47 | ||
| 42 | - @Value("${media.closeWaitRTPInfo}") | ||
| 43 | - private boolean closeWaitRTPInfo; | 48 | + @Autowired |
| 49 | + private IPlayService playService; | ||
| 50 | + | ||
| 51 | + @Autowired | ||
| 52 | + private DeferredResultHolder resultHolder; | ||
| 44 | 53 | ||
| 45 | @GetMapping("/playback/{deviceId}/{channelId}") | 54 | @GetMapping("/playback/{deviceId}/{channelId}") |
| 46 | - public ResponseEntity<String> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, | ||
| 47 | - String endTime) { | 55 | + public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, |
| 56 | + String endTime) { | ||
| 48 | 57 | ||
| 49 | if (logger.isDebugEnabled()) { | 58 | if (logger.isDebugEnabled()) { |
| 50 | logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); | 59 | logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); |
| 51 | } | 60 | } |
| 52 | - | ||
| 53 | - if (StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(channelId)) { | ||
| 54 | - String log = String.format("设备回放 API调用失败,deviceId:%s ,channelId:%s", deviceId, channelId); | ||
| 55 | - logger.warn(log); | ||
| 56 | - return new ResponseEntity<String>(log, HttpStatus.BAD_REQUEST); | ||
| 57 | - } | ||
| 58 | - | 61 | + UUID uuid = UUID.randomUUID(); |
| 62 | + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); | ||
| 63 | + // 超时处理 | ||
| 64 | + result.onTimeout(()->{ | ||
| 65 | + logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); | ||
| 66 | + RequestMessage msg = new RequestMessage(); | ||
| 67 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | ||
| 68 | + msg.setData("Timeout"); | ||
| 69 | + resultHolder.invokeResult(msg); | ||
| 70 | + }); | ||
| 59 | Device device = storager.queryVideoDevice(deviceId); | 71 | Device device = storager.queryVideoDevice(deviceId); |
| 60 | StreamInfo streamInfo = storager.queryPlaybackByDevice(deviceId, channelId); | 72 | StreamInfo streamInfo = storager.queryPlaybackByDevice(deviceId, channelId); |
| 61 | - | ||
| 62 | if (streamInfo != null) { | 73 | if (streamInfo != null) { |
| 74 | + // 停止之前的回放 | ||
| 63 | cmder.streamByeCmd(streamInfo.getSsrc()); | 75 | cmder.streamByeCmd(streamInfo.getSsrc()); |
| 64 | } | 76 | } |
| 77 | + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); | ||
| 78 | + cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { | ||
| 79 | + logger.info("收到订阅消息: " + response.toJSONString()); | ||
| 80 | + playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); | ||
| 81 | + }); | ||
| 65 | 82 | ||
| 66 | - // }else { | ||
| 67 | - // String streamId = String.format("%08x", | ||
| 68 | - // Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); | ||
| 69 | - // JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); | ||
| 70 | - // if (rtpInfo.getBoolean("exist")) { | ||
| 71 | - // return new | ||
| 72 | - // ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK); | ||
| 73 | - // }else { | ||
| 74 | - // storager.stopPlayback(streamInfo); | ||
| 75 | - // streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime); | ||
| 76 | - // } | ||
| 77 | - // } | ||
| 78 | - streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime); | ||
| 79 | - | ||
| 80 | - String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); | ||
| 81 | - | ||
| 82 | - if (logger.isDebugEnabled()) { | ||
| 83 | - logger.debug("设备回放 API调用,ssrc:" + streamInfo.getSsrc() + ",ZLMedia streamId:" + streamId); | ||
| 84 | - } | ||
| 85 | - // 等待推流, TODO 默认超时15s | ||
| 86 | - boolean lockFlag = true; | ||
| 87 | - boolean rtpPushed = false; | ||
| 88 | - long lockStartTime = System.currentTimeMillis(); | ||
| 89 | - JSONObject rtpInfo = null; | ||
| 90 | - | ||
| 91 | - if (closeWaitRTPInfo) { | ||
| 92 | - String flv = storager.getMediaInfo().getWanIp() + ":" + storager.getMediaInfo().getHttpPort() + "/rtp/" | ||
| 93 | - + streamId + ".flv"; | ||
| 94 | - streamInfo.setFlv("http://" + flv); | ||
| 95 | - streamInfo.setWs_flv("ws://" + flv); | ||
| 96 | - storager.startPlayback(streamInfo); | ||
| 97 | - } else { | ||
| 98 | - while (lockFlag) { | ||
| 99 | - try { | ||
| 100 | - if (System.currentTimeMillis() - lockStartTime > 75 * 1000) { | ||
| 101 | - storager.stopPlayback(streamInfo); | ||
| 102 | - logger.info("播放等待超时"); | ||
| 103 | - return new ResponseEntity<String>("timeout", HttpStatus.OK); | ||
| 104 | - } else { | ||
| 105 | - streamInfo = storager.queryPlaybackByDevice(deviceId, channelId); | ||
| 106 | - if (!rtpPushed) { | ||
| 107 | - logger.info("查询RTP推流信息..."); | ||
| 108 | - rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); | ||
| 109 | - } | ||
| 110 | - if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null | ||
| 111 | - && streamInfo.getFlv() != null) { | ||
| 112 | - logger.info("查询流编码信息:" + streamInfo.getFlv()); | ||
| 113 | - rtpPushed = true; | ||
| 114 | - Thread.sleep(2000); | ||
| 115 | - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); | ||
| 116 | - if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) { | ||
| 117 | - lockFlag = false; | ||
| 118 | - logger.info("流编码信息已获取"); | ||
| 119 | - JSONArray tracks = mediaInfo.getJSONArray("tracks"); | ||
| 120 | - streamInfo.setTracks(tracks); | ||
| 121 | - storager.startPlayback(streamInfo); | ||
| 122 | - } else { | ||
| 123 | - logger.info("流编码信息未获取,2秒后重试..."); | ||
| 124 | - } | ||
| 125 | - } else { | ||
| 126 | - Thread.sleep(2000); | ||
| 127 | - continue; | ||
| 128 | - } | ||
| 129 | - } | ||
| 130 | - } catch (InterruptedException e) { | ||
| 131 | - e.printStackTrace(); | ||
| 132 | - } | ||
| 133 | - } | ||
| 134 | - } | ||
| 135 | - if (streamInfo != null) { | ||
| 136 | - return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK); | ||
| 137 | - } else { | ||
| 138 | - logger.warn("设备回放API调用失败!"); | ||
| 139 | - return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); | ||
| 140 | - } | 83 | + return result; |
| 141 | } | 84 | } |
| 142 | 85 | ||
| 143 | @RequestMapping("/playback/{ssrc}/stop") | 86 | @RequestMapping("/playback/{ssrc}/stop") |
src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.service; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.genersoft.iot.vmp.common.StreamInfo; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 点播处理 | ||
| 8 | + */ | ||
| 9 | +public interface IPlayService { | ||
| 10 | + | ||
| 11 | + void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid); | ||
| 12 | + void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid); | ||
| 13 | +} |
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.service.impl; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSON; | ||
| 4 | +import com.alibaba.fastjson.JSONObject; | ||
| 5 | +import com.genersoft.iot.vmp.common.StreamInfo; | ||
| 6 | +import com.genersoft.iot.vmp.conf.MediaServerConfig; | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | ||
| 8 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | ||
| 9 | +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | ||
| 10 | +import com.genersoft.iot.vmp.vmanager.play.PlayController; | ||
| 11 | +import com.genersoft.iot.vmp.vmanager.service.IPlayService; | ||
| 12 | +import org.slf4j.Logger; | ||
| 13 | +import org.slf4j.LoggerFactory; | ||
| 14 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 15 | +import org.springframework.stereotype.Service; | ||
| 16 | + | ||
| 17 | +import java.text.DecimalFormat; | ||
| 18 | + | ||
| 19 | +@Service | ||
| 20 | +public class PlayServiceImpl implements IPlayService { | ||
| 21 | + | ||
| 22 | + private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class); | ||
| 23 | + | ||
| 24 | + @Autowired | ||
| 25 | + private IVideoManagerStorager storager; | ||
| 26 | + | ||
| 27 | + @Autowired | ||
| 28 | + private DeferredResultHolder resultHolder; | ||
| 29 | + | ||
| 30 | + @Override | ||
| 31 | + public void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid) { | ||
| 32 | + RequestMessage msg = new RequestMessage(); | ||
| 33 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | ||
| 34 | + StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); | ||
| 35 | + if (streamInfo != null) { | ||
| 36 | + storager.startPlay(streamInfo); | ||
| 37 | + msg.setData(JSON.toJSONString(streamInfo)); | ||
| 38 | + resultHolder.invokeResult(msg); | ||
| 39 | + } else { | ||
| 40 | + logger.warn("设备预览API调用失败!"); | ||
| 41 | + msg.setData("设备预览API调用失败!"); | ||
| 42 | + resultHolder.invokeResult(msg); | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + @Override | ||
| 47 | + public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid) { | ||
| 48 | + RequestMessage msg = new RequestMessage(); | ||
| 49 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | ||
| 50 | + StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); | ||
| 51 | + if (streamInfo != null) { | ||
| 52 | + storager.startPlayback(streamInfo); | ||
| 53 | + msg.setData(JSON.toJSONString(streamInfo)); | ||
| 54 | + resultHolder.invokeResult(msg); | ||
| 55 | + } else { | ||
| 56 | + logger.warn("设备预览API调用失败!"); | ||
| 57 | + msg.setData("设备预览API调用失败!"); | ||
| 58 | + resultHolder.invokeResult(msg); | ||
| 59 | + } | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + public StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) { | ||
| 63 | + String streamId = resonse.getString("id"); | ||
| 64 | + String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); | ||
| 65 | + StreamInfo streamInfo = new StreamInfo(); | ||
| 66 | + streamInfo.setSsrc(ssrc); | ||
| 67 | + streamInfo.setStreamId(streamId); | ||
| 68 | + streamInfo.setDeviceID(deviceId); | ||
| 69 | + streamInfo.setCahnnelId(channelId); | ||
| 70 | + MediaServerConfig mediaServerConfig = storager.getMediaInfo(); | ||
| 71 | + | ||
| 72 | + streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ||
| 73 | + streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ||
| 74 | + | ||
| 75 | + streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ||
| 76 | + streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ||
| 77 | + | ||
| 78 | + streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ||
| 79 | + streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ||
| 80 | + | ||
| 81 | + streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ||
| 82 | + streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ||
| 83 | + | ||
| 84 | + streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtmpPort(), streamId)); | ||
| 85 | + streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtspPort(), streamId)); | ||
| 86 | + | ||
| 87 | + return streamInfo; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | +} |
src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java
| @@ -34,8 +34,7 @@ public class ApiStreamController { | @@ -34,8 +34,7 @@ public class ApiStreamController { | ||
| 34 | @Autowired | 34 | @Autowired |
| 35 | private IVideoManagerStorager storager; | 35 | private IVideoManagerStorager storager; |
| 36 | 36 | ||
| 37 | - @Value("${media.closeWaitRTPInfo}") | ||
| 38 | - private boolean closeWaitRTPInfo; | 37 | + private boolean closeWaitRTPInfo = false; |
| 39 | 38 | ||
| 40 | 39 | ||
| 41 | @Autowired | 40 | @Autowired |
| @@ -94,7 +93,7 @@ public class ApiStreamController { | @@ -94,7 +93,7 @@ public class ApiStreamController { | ||
| 94 | StreamInfo streamInfo = storager.queryPlayByDevice(device.getDeviceId(), code); | 93 | StreamInfo streamInfo = storager.queryPlayByDevice(device.getDeviceId(), code); |
| 95 | if (streamInfo == null) { | 94 | if (streamInfo == null) { |
| 96 | logger.debug("streamInfo 等于null, 重新点播"); | 95 | logger.debug("streamInfo 等于null, 重新点播"); |
| 97 | - streamInfo = cmder.playStreamCmd(device, code); | 96 | +// streamInfo = cmder.playStreamCmd(device, code); |
| 98 | }else { | 97 | }else { |
| 99 | logger.debug("streamInfo 不等于null, 向流媒体查询是否正在推流"); | 98 | logger.debug("streamInfo 不等于null, 向流媒体查询是否正在推流"); |
| 100 | String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); | 99 | String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); |
| @@ -136,7 +135,7 @@ public class ApiStreamController { | @@ -136,7 +135,7 @@ public class ApiStreamController { | ||
| 136 | } else { | 135 | } else { |
| 137 | logger.debug("向流媒体查询没有推流, 重新点播"); | 136 | logger.debug("向流媒体查询没有推流, 重新点播"); |
| 138 | storager.stopPlay(streamInfo); | 137 | storager.stopPlay(streamInfo); |
| 139 | - streamInfo = cmder.playStreamCmd(device, code); | 138 | +// streamInfo = cmder.playStreamCmd(device, code); |
| 140 | } | 139 | } |
| 141 | } | 140 | } |
| 142 | 141 |
src/main/resources/application-dev.yml
0 → 100644
| 1 | +spring: | ||
| 2 | + # [不需要改] | ||
| 3 | + application: | ||
| 4 | + name: iot-vmp-vmanager | ||
| 5 | + # [不需要改] 影子数据存储方式,支持redis、jdbc,暂不支持mysql, | ||
| 6 | + database: redis | ||
| 7 | + # [不需要改] 通信方式,支持kafka、http | ||
| 8 | + communicate: http | ||
| 9 | + # REDIS数据库配置 | ||
| 10 | + redis: | ||
| 11 | + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 | ||
| 12 | + host: 127.0.0.1 | ||
| 13 | + # [必须修改] 端口号 | ||
| 14 | + port: 6379 | ||
| 15 | + # [可选] 数据库 DB | ||
| 16 | + database: 6 | ||
| 17 | + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 | ||
| 18 | + password: | ||
| 19 | + # [可选] 超时时间 | ||
| 20 | + timeout: 10000 | ||
| 21 | + # [不可用] jdbc数据库配置, 暂不支持 | ||
| 22 | + datasource: | ||
| 23 | + name: eiot | ||
| 24 | + url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true | ||
| 25 | + username: | ||
| 26 | + password: | ||
| 27 | + type: com.alibaba.druid.pool.DruidDataSource | ||
| 28 | + driver-class-name: com.mysql.jdbc.Driver | ||
| 29 | + | ||
| 30 | +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 | ||
| 31 | +server: | ||
| 32 | + port: 18080 | ||
| 33 | + | ||
| 34 | +# 作为28181服务器的配置 | ||
| 35 | +sip: | ||
| 36 | + # [必须修改] 本机的IP, 必须是网卡上的IP | ||
| 37 | + ip: 192.168.0.100 | ||
| 38 | + # [可选] 28181服务监听的端口 | ||
| 39 | + port: 5060 | ||
| 40 | + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) | ||
| 41 | + # 后两位为行业编码,定义参照附录D.3 | ||
| 42 | + # 3701020049标识山东济南历下区 信息行业接入 | ||
| 43 | + # [可选] | ||
| 44 | + domain: 4401020049 | ||
| 45 | + # [可选] | ||
| 46 | + id: 44010200492000000001 | ||
| 47 | + # [可选] 默认设备认证密码,后续扩展使用设备单独密码 | ||
| 48 | + password: admin123 | ||
| 49 | + | ||
| 50 | +# 登陆的用户名密码 | ||
| 51 | +auth: | ||
| 52 | + # [可选] 用户名 | ||
| 53 | + username: admin | ||
| 54 | + # [可选] 密码, 默认为admin | ||
| 55 | + password: 21232f297a57a5a743894a0e4a801fc3 | ||
| 56 | + | ||
| 57 | +#zlm服务器配置 | ||
| 58 | +media: | ||
| 59 | + # [必须修改] zlm服务器的内网IP | ||
| 60 | + ip: 192.168.0.100 | ||
| 61 | + # [可选] zlm服务器的公网IP, 内网部署置空即可 | ||
| 62 | + wanIp: | ||
| 63 | + # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip | ||
| 64 | + hookIp: | ||
| 65 | + # [必须修改] zlm服务器的http.port | ||
| 66 | + port: 80 | ||
| 67 | + # [可选] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修改 | ||
| 68 | + autoConfig: true | ||
| 69 | + # [可选] zlm服务器的hook.admin_params=secret | ||
| 70 | + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc | ||
| 71 | + # [可选] zlm服务器的general.streamNoneReaderDelayMS | ||
| 72 | + streamNoneReaderDelayMS: 18000 # 无人观看多久自动关闭流 | ||
| 73 | + # [可选] 关闭等待收到流编码信息后在返回, | ||
| 74 | + # 设为false可以获得更好的兼容性,保证返回后流就可以播放, | ||
| 75 | + # 设为true可以快速打开播放窗口,可以获得更好的体验 | ||
| 76 | + closeWaitRTPInfo: false | ||
| 77 | + # 启用udp多端口模式 | ||
| 78 | + rtp: | ||
| 79 | + # [可选] 是否启用udp多端口模式, 开启后会在udpPortRange范围内选择端口用于媒体流传输 | ||
| 80 | + enable: true | ||
| 81 | + # [可选] 在此范围内选择端口用于媒体流传输, 不只是udp, 使用TCP被动传输模式时,也是从这个范围内选择端口 | ||
| 82 | + udpPortRange: 30000,30500 # 端口范围 | ||
| 83 | + | ||
| 84 | +# [可选] 日志配置, 一般不需要改 | ||
| 85 | +logging: | ||
| 86 | + file: | ||
| 87 | + name: logs/wvp.log | ||
| 88 | + max-history: 30 | ||
| 89 | + max-size: 10MB | ||
| 90 | + total-size-cap: 300MB | ||
| 91 | + level: | ||
| 92 | + com: | ||
| 93 | + genersoft: | ||
| 94 | + iot: debug | ||
| 0 | \ No newline at end of file | 95 | \ No newline at end of file |
src/main/resources/application.yml
| 1 | spring: | 1 | spring: |
| 2 | - application: | ||
| 3 | - name: iot-vmp-vmanager | ||
| 4 | - # 影子数据存储方式,支持redis、jdbc,暂不支持mysql | ||
| 5 | - database: redis | ||
| 6 | - # 通信方式,支持kafka、http | ||
| 7 | - communicate: http | ||
| 8 | - redis: | ||
| 9 | - # Redis服务器IP | ||
| 10 | - host: 192.168.1.141 | ||
| 11 | - #端口号 | ||
| 12 | - port: 6379 | ||
| 13 | - database: 6 | ||
| 14 | - #访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 | ||
| 15 | - password: 4767cb971b40a1300fa09b7f87b09d1c | ||
| 16 | - #超时时间 | ||
| 17 | - timeout: 10000 | ||
| 18 | - datasource: | ||
| 19 | - name: eiot | ||
| 20 | - url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true | ||
| 21 | - username: | ||
| 22 | - password: | ||
| 23 | - type: com.alibaba.druid.pool.DruidDataSource | ||
| 24 | - driver-class-name: com.mysql.jdbc.Driver | ||
| 25 | -server: | ||
| 26 | - port: 18080 | ||
| 27 | -sip: | ||
| 28 | - ip: 192.168.1.20 | ||
| 29 | - port: 5060 | ||
| 30 | - # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) | ||
| 31 | - # 后两位为行业编码,定义参照附录D.3 | ||
| 32 | - # 3701020049标识山东济南历下区 信息行业接入 | ||
| 33 | - domain: 3402000000 | ||
| 34 | - id: 34020000002000000001 | ||
| 35 | - # 默认设备认证密码,后续扩展使用设备单独密码 | ||
| 36 | - password: 12345678 | ||
| 37 | - | ||
| 38 | -auth: #32位小写md5加密(默认密码为admin) | ||
| 39 | - username: admin | ||
| 40 | - password: 21232f297a57a5a743894a0e4a801fc3 | ||
| 41 | - | ||
| 42 | -media: #zlm服务器的ip与http端口, 重点: 这是http端口 | ||
| 43 | - ip: 127.0.0.1 | ||
| 44 | - wanIp: 192.168.1.20 | ||
| 45 | - port: 6080 | ||
| 46 | - secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc | ||
| 47 | - streamNoneReaderDelayMS: 600000 # 无人观看多久自动关闭流 | ||
| 48 | - # 关闭等待收到流编码信息后在返回, | ||
| 49 | - # 设为false可以获得更好的兼容性,保证返回后流就可以播放, | ||
| 50 | - # 设为true可以快速打开播放窗口,可以获得更好的体验 | ||
| 51 | - closeWaitRTPInfo: true | ||
| 52 | - rtp: # 启用udp多端口模式 | ||
| 53 | - enable: true | ||
| 54 | - udpPortRange: 30000,30500 # 端口范围 | ||
| 55 | -logging: | ||
| 56 | - file: | ||
| 57 | - name: logs/wvp.log | ||
| 58 | - max-history: 30 | ||
| 59 | - max-size: 10MB | ||
| 60 | - total-size-cap: 300MB | ||
| 61 | - level: | ||
| 62 | - com: | ||
| 63 | - genersoft: | ||
| 64 | - iot: debug | ||
| 65 | \ No newline at end of file | 2 | \ No newline at end of file |
| 3 | + profiles: | ||
| 4 | + active: dev | ||
| 66 | \ No newline at end of file | 5 | \ No newline at end of file |
web_src/build/webpack.dev.conf.js
| @@ -64,9 +64,8 @@ const devWebpackConfig = merge(baseWebpackConfig, { | @@ -64,9 +64,8 @@ const devWebpackConfig = merge(baseWebpackConfig, { | ||
| 64 | to: config.dev.assetsSubDirectory, | 64 | to: config.dev.assetsSubDirectory, |
| 65 | ignore: ['.*'] | 65 | ignore: ['.*'] |
| 66 | }, | 66 | }, |
| 67 | - { from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'}, | ||
| 68 | - { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'}, | ||
| 69 | - { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/'} | 67 | + { from: 'node_modules/@easydarwin/easywasmplayer/libDecoder.wasm'}, |
| 68 | + { from: 'node_modules/@easydarwin/easywasmplayer/EasyWasmPlayer.js', to: 'js/'} | ||
| 70 | ]) | 69 | ]) |
| 71 | ] | 70 | ] |
| 72 | }) | 71 | }) |
web_src/build/webpack.prod.conf.js
| @@ -115,9 +115,8 @@ const webpackConfig = merge(baseWebpackConfig, { | @@ -115,9 +115,8 @@ const webpackConfig = merge(baseWebpackConfig, { | ||
| 115 | to: config.build.assetsSubDirectory, | 115 | to: config.build.assetsSubDirectory, |
| 116 | ignore: ['.*'] | 116 | ignore: ['.*'] |
| 117 | }, | 117 | }, |
| 118 | - { from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'}, | ||
| 119 | - { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'}, | ||
| 120 | - { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/'} | 118 | + { from: 'node_modules/@easydarwin/easywasmplayer/libDecoder.wasm'}, |
| 119 | + { from: 'node_modules/@easydarwin/easywasmplayer/EasyWasmPlayer.js', to: 'js/'} | ||
| 121 | ]) | 120 | ]) |
| 122 | ] | 121 | ] |
| 123 | }) | 122 | }) |
web_src/index.html
| @@ -6,7 +6,7 @@ | @@ -6,7 +6,7 @@ | ||
| 6 | <title>国标28181</title> | 6 | <title>国标28181</title> |
| 7 | </head> | 7 | </head> |
| 8 | <body> | 8 | <body> |
| 9 | - <script type="text/javascript" src="./js/liveplayer-lib.min.js"></script> | 9 | + <script type="text/javascript" src="./js/EasyWasmPlayer.js"></script> |
| 10 | <div id="app"></div> | 10 | <div id="app"></div> |
| 11 | <!-- built files will be auto injected --> | 11 | <!-- built files will be auto injected --> |
| 12 | </body> | 12 | </body> |
web_src/package-lock.json
| @@ -4,10 +4,10 @@ | @@ -4,10 +4,10 @@ | ||
| 4 | "lockfileVersion": 1, | 4 | "lockfileVersion": 1, |
| 5 | "requires": true, | 5 | "requires": true, |
| 6 | "dependencies": { | 6 | "dependencies": { |
| 7 | - "@liveqing/liveplayer": { | ||
| 8 | - "version": "1.9.9", | ||
| 9 | - "resolved": "https://registry.npm.taobao.org/@liveqing/liveplayer/download/@liveqing/liveplayer-1.9.9.tgz", | ||
| 10 | - "integrity": "sha1-K7wiab+BiY5qe1/nTpKVyeGdIGo=" | 7 | + "@easydarwin/easywasmplayer": { |
| 8 | + "version": "4.0.7", | ||
| 9 | + "resolved": "https://registry.npm.taobao.org/@easydarwin/easywasmplayer/download/@easydarwin/easywasmplayer-4.0.7.tgz", | ||
| 10 | + "integrity": "sha1-FNtIUXbdwIWdalvIMEaH0+zUGx4=" | ||
| 11 | }, | 11 | }, |
| 12 | "@types/q": { | 12 | "@types/q": { |
| 13 | "version": "1.5.4", | 13 | "version": "1.5.4", |
web_src/package.json
| @@ -10,7 +10,7 @@ | @@ -10,7 +10,7 @@ | ||
| 10 | "build": "node build/build.js" | 10 | "build": "node build/build.js" |
| 11 | }, | 11 | }, |
| 12 | "dependencies": { | 12 | "dependencies": { |
| 13 | - "@liveqing/liveplayer": "^1.9.6", | 13 | + "@easydarwin/easywasmplayer": "^4.0.7", |
| 14 | "axios": "^0.19.2", | 14 | "axios": "^0.19.2", |
| 15 | "core-js": "^2.6.5", | 15 | "core-js": "^2.6.5", |
| 16 | "echarts": "^4.7.0", | 16 | "echarts": "^4.7.0", |
web_src/src/components/gb28181/devicePlayer.vue
| 1 | <template> | 1 | <template> |
| 2 | <div id="devicePlayer" v-loading="isLoging"> | 2 | <div id="devicePlayer" v-loading="isLoging"> |
| 3 | + | ||
| 3 | <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> | 4 | <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> |
| 4 | - <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> | 5 | + <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> --> |
| 6 | + <player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></player> | ||
| 5 | <div id="shared" style="text-align: right; margin-top: 1rem;"> | 7 | <div id="shared" style="text-align: right; margin-top: 1rem;"> |
| 6 | - <el-tabs v-model="tabActiveName"> | 8 | + <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick"> |
| 7 | <el-tab-pane label="实时视频" name="media"> | 9 | <el-tab-pane label="实时视频" name="media"> |
| 8 | <div style="margin-bottom: 0.5rem;"> | 10 | <div style="margin-bottom: 0.5rem;"> |
| 9 | <!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>--> | 11 | <!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>--> |
| @@ -97,6 +99,32 @@ | @@ -97,6 +99,32 @@ | ||
| 97 | </div> | 99 | </div> |
| 98 | </div> | 100 | </div> |
| 99 | </el-tab-pane> | 101 | </el-tab-pane> |
| 102 | + <el-tab-pane label="编码信息" name="codec" v-loading="tracksLoading"> | ||
| 103 | + <p> | ||
| 104 | + 无法播放或者没有声音?   试一试 | ||
| 105 | + <el-button size="mini" type="primary" v-if="!coverPlaying" @click="coverPlay">转码播放</el-button> | ||
| 106 | + <el-button size="mini" type="danger" v-if="coverPlaying" @click="convertStopClick">停止转码</el-button> | ||
| 107 | + </p> | ||
| 108 | + <div class="trank" > | ||
| 109 | + <div v-for="(item, index) in tracks"> | ||
| 110 | + <span >流 {{index}}</span> | ||
| 111 | + <div class="trankInfo" v-if="item.codec_type == 0"> | ||
| 112 | + <p>格式: {{item.codec_id_name}}</p> | ||
| 113 | + <p>类型: 视频</p> | ||
| 114 | + <p>分辨率: {{item.width}} x {{item.height}}</p> | ||
| 115 | + <p>帧率: {{item.fps}}</p> | ||
| 116 | + </div> | ||
| 117 | + <div class="trankInfo" v-if="item.codec_type == 1"> | ||
| 118 | + <p>格式: {{item.codec_id_name}}</p> | ||
| 119 | + <p>类型: 音频</p> | ||
| 120 | + <p>采样位数: {{item.sample_bit}}</p> | ||
| 121 | + <p>采样率: {{item.sample_rate}}</p> | ||
| 122 | + </div> | ||
| 123 | + </div> | ||
| 124 | + | ||
| 125 | + </div> | ||
| 126 | + | ||
| 127 | + </el-tab-pane> | ||
| 100 | </el-tabs> | 128 | </el-tabs> |
| 101 | </div> | 129 | </div> |
| 102 | </el-dialog> | 130 | </el-dialog> |
| @@ -104,12 +132,12 @@ | @@ -104,12 +132,12 @@ | ||
| 104 | </template> | 132 | </template> |
| 105 | 133 | ||
| 106 | <script> | 134 | <script> |
| 107 | -import LivePlayer from '@liveqing/liveplayer' | 135 | +import player from './player.vue' |
| 108 | export default { | 136 | export default { |
| 109 | name: 'devicePlayer', | 137 | name: 'devicePlayer', |
| 110 | props: {}, | 138 | props: {}, |
| 111 | components: { | 139 | components: { |
| 112 | - LivePlayer | 140 | + player, |
| 113 | }, | 141 | }, |
| 114 | computed: { | 142 | computed: { |
| 115 | getPlayerShared: function () { | 143 | getPlayerShared: function () { |
| @@ -131,6 +159,7 @@ export default { | @@ -131,6 +159,7 @@ export default { | ||
| 131 | }, | 159 | }, |
| 132 | showVideoDialog: false, | 160 | showVideoDialog: false, |
| 133 | ssrc: '', | 161 | ssrc: '', |
| 162 | + streamId: '', | ||
| 134 | convertKey: '', | 163 | convertKey: '', |
| 135 | deviceId: '', | 164 | deviceId: '', |
| 136 | channelId: '', | 165 | channelId: '', |
| @@ -148,20 +177,45 @@ export default { | @@ -148,20 +177,45 @@ export default { | ||
| 148 | cruisingGroup: 0, | 177 | cruisingGroup: 0, |
| 149 | scanSpeed: 100, | 178 | scanSpeed: 100, |
| 150 | scanGroup: 0, | 179 | scanGroup: 0, |
| 180 | + tracks: [], | ||
| 181 | + coverPlaying:false, | ||
| 182 | + tracksLoading: false | ||
| 151 | }; | 183 | }; |
| 152 | }, | 184 | }, |
| 153 | methods: { | 185 | methods: { |
| 186 | + tabHandleClick: function(tab, event) { | ||
| 187 | + console.log(tab) | ||
| 188 | + var that = this; | ||
| 189 | + that.tracks = []; | ||
| 190 | + that.tracksLoading = true; | ||
| 191 | + if (tab.name == "codec") { | ||
| 192 | + this.$axios({ | ||
| 193 | + method: 'get', | ||
| 194 | + url: '/zlm/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app=rtp&stream='+ this.streamId | ||
| 195 | + }).then(function (res) { | ||
| 196 | + that.tracksLoading = false; | ||
| 197 | + if (res.data.code == 0 && res.data.online) { | ||
| 198 | + that.tracks = res.data.tracks; | ||
| 199 | + }else{ | ||
| 200 | + that.$message({ | ||
| 201 | + showClose: true, | ||
| 202 | + message: '获取编码信息失败,', | ||
| 203 | + type: 'warning' | ||
| 204 | + }); | ||
| 205 | + } | ||
| 206 | + }).catch(function (e) {}); | ||
| 207 | + } | ||
| 208 | + }, | ||
| 154 | openDialog: function (tab, deviceId, channelId, param) { | 209 | openDialog: function (tab, deviceId, channelId, param) { |
| 155 | this.tabActiveName = tab; | 210 | this.tabActiveName = tab; |
| 156 | this.channelId = channelId; | 211 | this.channelId = channelId; |
| 157 | this.deviceId = deviceId; | 212 | this.deviceId = deviceId; |
| 158 | this.ssrc = ""; | 213 | this.ssrc = ""; |
| 214 | + this.streamId = ""; | ||
| 159 | this.videoUrl = "" | 215 | this.videoUrl = "" |
| 160 | if (!!this.$refs.videoPlayer) { | 216 | if (!!this.$refs.videoPlayer) { |
| 161 | this.$refs.videoPlayer.pause(); | 217 | this.$refs.videoPlayer.pause(); |
| 162 | } | 218 | } |
| 163 | - | ||
| 164 | - | ||
| 165 | switch (tab) { | 219 | switch (tab) { |
| 166 | case "media": | 220 | case "media": |
| 167 | this.play(param.streamInfo, param.hasAudio) | 221 | this.play(param.streamInfo, param.hasAudio) |
| @@ -180,99 +234,94 @@ export default { | @@ -180,99 +234,94 @@ export default { | ||
| 180 | console.log(val) | 234 | console.log(val) |
| 181 | }, | 235 | }, |
| 182 | play: function (streamInfo, hasAudio) { | 236 | play: function (streamInfo, hasAudio) { |
| 237 | + | ||
| 183 | this.hasaudio = hasAudio; | 238 | this.hasaudio = hasAudio; |
| 239 | + this.isLoging = false; | ||
| 240 | + this.videoUrl = streamInfo.ws_flv; | ||
| 241 | + this.ssrc = streamInfo.ssrc; | ||
| 242 | + this.streamId = streamInfo.streamId; | ||
| 243 | + this.playFromStreamInfo(false, streamInfo) | ||
| 244 | + }, | ||
| 245 | + coverPlay: function () { | ||
| 184 | var that = this; | 246 | var that = this; |
| 185 | - that.isLoging = false; | ||
| 186 | - if (!!streamInfo.tracks && streamInfo.tracks.length > 0 ) { | ||
| 187 | - for (let i = 0; i < streamInfo.tracks.length; i++) { | ||
| 188 | - if (streamInfo.tracks[i].codec_type == 0 && streamInfo.tracks[i].codec_id_name != "CodecH264") { // 判断为H265视频 | ||
| 189 | - that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ | ||
| 190 | - that.close(); | ||
| 191 | - return; | ||
| 192 | - }) | ||
| 193 | - }else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name != "CodecAAC") { | ||
| 194 | - that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ | ||
| 195 | - that.playFromStreamInfo(false. streamInfo) | ||
| 196 | - }) | ||
| 197 | - }else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name == "CodecAAC") { | ||
| 198 | - that.playFromStreamInfo(true, streamInfo) | ||
| 199 | - }else { | ||
| 200 | - that.playFromStreamInfo(false, streamInfo) | ||
| 201 | - } | ||
| 202 | - } | ||
| 203 | - }else { | ||
| 204 | - that.playFromStreamInfo(false, streamInfo) | ||
| 205 | - } | 247 | + this.coverPlaying = true; |
| 248 | + this.$refs.videoPlayer.pause() | ||
| 249 | + that.$axios({ | ||
| 250 | + method: 'post', | ||
| 251 | + url: '/api/play/' + that.ssrc + '/convert' | ||
| 252 | + }).then(function (res) { | ||
| 253 | + if (res.data.code == 0) { | ||
| 254 | + that.convertKey = res.data.key; | ||
| 255 | + setTimeout(()=>{ | ||
| 256 | + that.isLoging = false; | ||
| 257 | + that.playFromStreamInfo(false, res.data.data); | ||
| 258 | + }, 2000) | ||
| 259 | + } else { | ||
| 260 | + that.isLoging = false; | ||
| 261 | + that.coverPlaying = false; | ||
| 262 | + that.$message({ | ||
| 263 | + showClose: true, | ||
| 264 | + message: '转码失败', | ||
| 265 | + type: 'error' | ||
| 266 | + }); | ||
| 267 | + } | ||
| 268 | + }).catch(function (e) { | ||
| 269 | + console.log(e) | ||
| 270 | + that.coverPlaying = false; | ||
| 271 | + that.$message({ | ||
| 272 | + showClose: true, | ||
| 273 | + message: '播放错误', | ||
| 274 | + type: 'error' | ||
| 275 | + }); | ||
| 276 | + }); | ||
| 206 | }, | 277 | }, |
| 207 | - coverPlay: function (streamInfo, codec_id_name, catchcallback) { | ||
| 208 | - var that = this; | ||
| 209 | - | ||
| 210 | - that.$confirm(codec_id_name + ' 编码格式不支持播放, 是否转码播放?', '提示', { | ||
| 211 | - confirmButtonText: '确定', | ||
| 212 | - cancelButtonText: '取消', | ||
| 213 | - type: 'warning' | ||
| 214 | - }).then(() => { | ||
| 215 | - that.isLoging = true; | ||
| 216 | - that.$axios({ | 278 | + convertStopClick: function() { |
| 279 | + this.convertStop(()=>{ | ||
| 280 | + this.$refs.videoPlayer.play(this.videoUrl) | ||
| 281 | + }); | ||
| 282 | + }, | ||
| 283 | + convertStop: function(callback) { | ||
| 284 | + var that = this; | ||
| 285 | + that.$refs.videoPlayer.pause() | ||
| 286 | + this.$axios({ | ||
| 217 | method: 'post', | 287 | method: 'post', |
| 218 | - url: '/api/play/' + streamInfo.ssrc + '/convert' | 288 | + url: '/api/play/convert/stop/' + this.convertKey |
| 219 | }).then(function (res) { | 289 | }).then(function (res) { |
| 220 | if (res.data.code == 0) { | 290 | if (res.data.code == 0) { |
| 221 | - streamInfo.ws_flv = res.data.ws_flv; | ||
| 222 | - that.convertKey = res.data.key; | ||
| 223 | - setTimeout(()=>{ | ||
| 224 | - that.isLoging = false; | ||
| 225 | - that.playFromStreamInfo(false, streamInfo); | ||
| 226 | - }, 2000) | ||
| 227 | - } else { | ||
| 228 | - that.isLoging = false; | ||
| 229 | - that.$message({ | ||
| 230 | - showClose: true, | ||
| 231 | - message: '转码失败', | ||
| 232 | - type: 'error' | ||
| 233 | - }); | 291 | + console.log(res.data.msg) |
| 292 | + }else { | ||
| 293 | + console.error(res.data.msg) | ||
| 234 | } | 294 | } |
| 235 | - }).catch(function (e) { | ||
| 236 | - that.$message({ | ||
| 237 | - showClose: true, | ||
| 238 | - message: '播放错误', | ||
| 239 | - type: 'error' | ||
| 240 | - }); | ||
| 241 | - }); | ||
| 242 | - }).catch(function (e) { | ||
| 243 | - if (catchcallback)catchcallback() | ||
| 244 | - }); | 295 | + if (callback )callback(); |
| 296 | + }).catch(function (e) {}); | ||
| 297 | + that.coverPlaying = false; | ||
| 298 | + that.convertKey = ""; | ||
| 299 | + // if (callback )callback(); | ||
| 245 | }, | 300 | }, |
| 301 | + | ||
| 246 | playFromStreamInfo: function (realHasAudio, streamInfo) { | 302 | playFromStreamInfo: function (realHasAudio, streamInfo) { |
| 247 | - this.videoUrl = streamInfo.ws_flv; | ||
| 248 | this.showVideoDialog = true; | 303 | this.showVideoDialog = true; |
| 249 | this.hasaudio = realHasAudio && this.hasaudio; | 304 | this.hasaudio = realHasAudio && this.hasaudio; |
| 250 | - this.ssrc = streamInfo.ssrc; | ||
| 251 | - console.log(this.ssrc); | 305 | + this.$refs.videoPlayer.play(streamInfo.ws_flv) |
| 252 | }, | 306 | }, |
| 253 | close: function () { | 307 | close: function () { |
| 254 | console.log('关闭视频'); | 308 | console.log('关闭视频'); |
| 255 | - if (!this.$refs.videoPlayer){ | 309 | + if (!!this.$refs.videoPlayer){ |
| 256 | this.$refs.videoPlayer.pause(); | 310 | this.$refs.videoPlayer.pause(); |
| 257 | } | 311 | } |
| 258 | this.videoUrl = ''; | 312 | this.videoUrl = ''; |
| 313 | + this.coverPlaying = false; | ||
| 259 | this.showVideoDialog = false; | 314 | this.showVideoDialog = false; |
| 260 | if (this.convertKey != '') { | 315 | if (this.convertKey != '') { |
| 261 | - this.$axios({ | ||
| 262 | - method: 'post', | ||
| 263 | - url: '/api/play/convert/stop/' + this.convertKey | ||
| 264 | - }).then(function (res) { | ||
| 265 | - if (res.data.code == 0) { | ||
| 266 | - console.log(res.data.msg) | ||
| 267 | - }else { | ||
| 268 | - console.error(res.data.msg) | ||
| 269 | - } | ||
| 270 | - }).catch(function (e) {}); | 316 | + this.convertStop(); |
| 271 | } | 317 | } |
| 272 | - this.convertKey = '' | 318 | + this.convertKey = '' |
| 273 | }, | 319 | }, |
| 320 | + | ||
| 274 | copySharedInfo: function (data) { | 321 | copySharedInfo: function (data) { |
| 275 | console.log('复制内容:' + data); | 322 | console.log('复制内容:' + data); |
| 323 | + this.coverPlaying = false; | ||
| 324 | + this.tracks = [] | ||
| 276 | let _this = this; | 325 | let _this = this; |
| 277 | this.$copyText(data).then( | 326 | this.$copyText(data).then( |
| 278 | function (e) { | 327 | function (e) { |
| @@ -602,4 +651,15 @@ export default { | @@ -602,4 +651,15 @@ export default { | ||
| 602 | .control-bottom .fa { | 651 | .control-bottom .fa { |
| 603 | transform: rotate(-45deg) translateY(7px); | 652 | transform: rotate(-45deg) translateY(7px); |
| 604 | } | 653 | } |
| 654 | +.trank { | ||
| 655 | + width: 80%; | ||
| 656 | + height: 180px; | ||
| 657 | + text-align: left; | ||
| 658 | + padding: 0 10%; | ||
| 659 | + overflow: auto; | ||
| 660 | +} | ||
| 661 | +.trankInfo { | ||
| 662 | + width: 80%; | ||
| 663 | + padding: 0 10%; | ||
| 664 | +} | ||
| 605 | </style> | 665 | </style> |
web_src/src/components/gb28181/player.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div id="player"> | ||
| 3 | + <div id="easyplayer"></div> | ||
| 4 | + </div> | ||
| 5 | +</template> | ||
| 6 | + | ||
| 7 | +<script> | ||
| 8 | +export default { | ||
| 9 | + name: 'player', | ||
| 10 | + data() { | ||
| 11 | + return { | ||
| 12 | + easyPlayer: null | ||
| 13 | + }; | ||
| 14 | + }, | ||
| 15 | + props: ['videoUrl', 'error', 'hasaudio'], | ||
| 16 | + mounted () { | ||
| 17 | + this.$nextTick(() =>{ | ||
| 18 | + console.log("初始化时的地址为: " + this.videoUrl) | ||
| 19 | + this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK) | ||
| 20 | + this.easyPlayer.play(this.videoUrl, 1) | ||
| 21 | + }) | ||
| 22 | + }, | ||
| 23 | + watch:{ | ||
| 24 | + videoUrl(newData, oldData){ | ||
| 25 | + this.easyPlayer.destroy() | ||
| 26 | + this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK) | ||
| 27 | + this.easyPlayer.play(newData, 1) | ||
| 28 | + }, | ||
| 29 | + immediate:true | ||
| 30 | + }, | ||
| 31 | + methods: { | ||
| 32 | + play: function (url) { | ||
| 33 | + this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK) | ||
| 34 | + this.easyPlayer.play(url, 1) | ||
| 35 | + }, | ||
| 36 | + pause: function () { | ||
| 37 | + this.easyPlayer.destroy(); | ||
| 38 | + }, | ||
| 39 | + eventcallbacK: function(type, message) { | ||
| 40 | + console.log("player 事件回调") | ||
| 41 | + console.log(type) | ||
| 42 | + console.log(message) | ||
| 43 | + } | ||
| 44 | + }, | ||
| 45 | +} | ||
| 46 | +</script> | ||
| 47 | + | ||
| 48 | +<style> | ||
| 49 | + .LodingTitle { | ||
| 50 | + min-width: 70px; | ||
| 51 | + } | ||
| 52 | + /* 隐藏logo */ | ||
| 53 | + /* .iconqingxiLOGO { | ||
| 54 | + display: none !important; | ||
| 55 | + } */ | ||
| 56 | + | ||
| 57 | +</style> | ||
| 0 | \ No newline at end of file | 58 | \ No newline at end of file |
web_src/src/components/videoList.vue
| @@ -73,12 +73,10 @@ | @@ -73,12 +73,10 @@ | ||
| 73 | </template> | 73 | </template> |
| 74 | 74 | ||
| 75 | <script> | 75 | <script> |
| 76 | - import devicePlayer from './gb28181/devicePlayer.vue' | ||
| 77 | import uiHeader from './UiHeader.vue' | 76 | import uiHeader from './UiHeader.vue' |
| 78 | export default { | 77 | export default { |
| 79 | name: 'app', | 78 | name: 'app', |
| 80 | components: { | 79 | components: { |
| 81 | - devicePlayer, | ||
| 82 | uiHeader | 80 | uiHeader |
| 83 | }, | 81 | }, |
| 84 | data() { | 82 | data() { |
web_src/src/router/index.js
| @@ -35,7 +35,7 @@ export default new VueRouter({ | @@ -35,7 +35,7 @@ export default new VueRouter({ | ||
| 35 | path: '/channelList/:deviceId/:parentChannelId/:count/:page', | 35 | path: '/channelList/:deviceId/:parentChannelId/:count/:page', |
| 36 | name: 'channelList', | 36 | name: 'channelList', |
| 37 | component: channelList, | 37 | component: channelList, |
| 38 | - },, | 38 | + }, |
| 39 | { | 39 | { |
| 40 | path: '/parentPlatformList/:count/:page', | 40 | path: '/parentPlatformList/:count/:page', |
| 41 | name: 'parentPlatformList', | 41 | name: 'parentPlatformList', |