Commit 36cd31d69d451072e00ff1dd1928a61753aa5a97
1 parent
04af6de3
修复ui开启音频无法播放的bug
修复可能导致录象查看的bug 修复开启openRTPServer时的bug
Showing
9 changed files
with
63 additions
and
49 deletions
README.md
| 1 | # wvp | 1 | # wvp |
| 2 | WEB VIDEO PLATFORM譏ッ荳荳ェ蝓コ莠雑B28181-2016譬螳樒鴫逧ス醍サ懆ァ「大ケウ蜿ー瑚エ溯エ」螳樒鴫譬ク蠢ソ。莉、荳手ョセ螟ョ。逅錘蜿ー驛ィ蛻シ梧髪謖¨AT遨ソ騾擾シ梧髪謖∵オキ蠎キ縲∝、ァ蜊弱∝ョァュ牙刀迚檎噪IPC縲¨VR縲.VR謗・蜈・縲 | 2 | WEB VIDEO PLATFORM譏ッ荳荳ェ蝓コ莠雑B28181-2016譬螳樒鴫逧ス醍サ懆ァ「大ケウ蜿ー瑚エ溯エ」螳樒鴫譬ク蠢ソ。莉、荳手ョセ螟ョ。逅錘蜿ー驛ィ蛻シ梧髪謖¨AT遨ソ騾擾シ梧髪謖∵オキ蠎キ縲∝、ァ蜊弱∝ョァュ牙刀迚檎噪IPC縲¨VR縲.VR謗・蜈・縲 |
| 3 | -豬∝ェ剃ス捺恪蜉。蝓コ莠算LMediaKit-https://github.com/xiongziliang/ZLMediaKit | 3 | +豬∝ェ剃ス捺恪蜉。蝓コ莠算LMediaKit-https://github.com/xiongziliang/ZLMediaKit |
| 4 | 蜑肴ョオ鬘オ髱「蝓コ莠皿ediaServerUI霑幄。御ソョ謾ケ. | 4 | 蜑肴ョオ鬘オ髱「蝓コ莠皿ediaServerUI霑幄。御ソョ謾ケ. |
| 5 | 5 | ||
| 6 | # 蠎皮畑蝨コ譎ッ | 6 | # 蠎皮畑蝨コ譎ッ |
| @@ -16,24 +16,25 @@ WEB VIDEO PLATFORM譏ッ荳荳ェ蝓コ莠雑B28181-2016譬螳樒鴫逧ス醍サ懆ァ「大ケウ蜿ー | @@ -16,24 +16,25 @@ WEB VIDEO PLATFORM譏ッ荳荳ェ蝓コ莠雑B28181-2016譬螳樒鴫逧ス醍サ懆ァ「大ケウ蜿ー | ||
| 16 |  | 16 |  |
| 17 | 17 | ||
| 18 | # 蜴溽沿迚ケ諤ァ | 18 | # 蜴溽沿迚ケ諤ァ |
| 19 | -1. 隗「鷹「ァ | ||
| 20 | -2. 莠大床謗ァ蛻カ域婿蜷代∫シゥ謾セ謗ァ蛻カ | ||
| 21 | -3. 隗「題ョセ螟ソ。諱ッ蜷梧ュ・ | ||
| 22 | -4. 遖サ蝨ィ郤ソ逶第而 | ||
| 23 | -5. 蠖募ワ譟・隸「荳主屓謾セ亥渕莠晒VR\DVR梧嘯荳肴髪謖∝ソォ霑帙《eek謫堺ス懶シ | ||
| 24 | -6. 譌莠コ隗ら恚閾ェ蜉ィ譁ュ豬 | ||
| 25 | - | ||
| 26 | - | ||
| 27 | -# 譁ー謾ッ謖∫音諤ァ | ||
| 28 | -1. 髮web逡碁擇, 荳埼怙隕∝黒迢ャ驛ィ鄂イ蜑咲ォッ譛榊苅, 逶エ謗・蛻ゥ逕ィwvp蜀スョ譁サカ譛榊苅驛ィ鄂イ. | ||
| 29 | -2. 謾ッ謖∝ケウ蜿ー謗・蜈・, 髓亥ッケ螟ァ蟷ウ蜿ー螟ァ驥剰ョセ螟噪諠霑幄。御シ伜喧. | ||
| 30 | -3. 謾ッ謖∵」邏「,騾夐%遲幃. | ||
| 31 | -4. 謾ッ謖∬蜉ィ驟咲スョZLM蟐剃ス捺恪蜉。, 蜃丞ー大屏驟咲スョ髣ョ鬚俶園蜃コ邇ー逧琉鬚. | ||
| 32 | -5. 謾ッ謖∝星逕ィudp螟夂ォッ蜿」讓。蠑, 謠宣ォdp讓。蠑丈ク句ェ剃ス謎シ霎捺ァ閭ス. | ||
| 33 | -6. 謾ッ謖夐%譏ッ蜷ヲ蜷ォ譛蛾浹鬚醍噪隶セ鄂ョ | ||
| 34 | -7. 謾ッ謖夐%蟄千岼蠖墓衍隸「 | ||
| 35 | -8. 謾ッ謖「dp/tcp,荳、遘肴ィ。蠑丈シ霎楢ァ「第オ | ||
| 36 | - | 19 | +1. 隗「鷹「ァ; |
| 20 | +2. 莠大床謗ァ蛻カ域婿蜷代∫シゥ謾セ謗ァ蛻カ; | ||
| 21 | +3. 隗「題ョセ螟ソ。諱ッ蜷梧ュ・; | ||
| 22 | +4. 遖サ蝨ィ郤ソ逶第而; | ||
| 23 | +5. 蠖募ワ譟・隸「荳主屓謾セ亥渕莠晒VR\DVR梧嘯荳肴髪謖∝ソォ霑帙《eek謫堺ス懶シ; | ||
| 24 | +6. 譌莠コ隗ら恚閾ェ蜉ィ譁ュ豬; | ||
| 25 | +7. 謾ッ謖ゞDP蜥卦CP荳、遘榊嵜譬ソ。莉、莨霎捺ィ。蠑; | ||
| 26 | + | ||
| 27 | +# 譁ー謾ッ謖∫音諤ァ | ||
| 28 | +1. 髮web逡碁擇, 荳埼怙隕∝黒迢ャ驛ィ鄂イ蜑咲ォッ譛榊苅, 逶エ謗・蛻ゥ逕ィwvp蜀スョ譁サカ譛榊苅驛ィ鄂イ, 髫舜vp荳襍キ驛ィ鄂イ; | ||
| 29 | +2. 謾ッ謖∝ケウ蜿ー謗・蜈・, 髓亥ッケ螟ァ蟷ウ蜿ー螟ァ驥剰ョセ螟噪諠霑幄。御シ伜喧; | ||
| 30 | +3. 謾ッ謖∵」邏「,騾夐%遲幃; | ||
| 31 | +4. 謾ッ謖∬蜉ィ驟咲スョZLM蟐剃ス捺恪蜉。, 蜃丞ー大屏驟咲スョ髣ョ鬚俶園蜃コ邇ー逧琉鬚; | ||
| 32 | +5. 謾ッ謖∝星逕ィudp螟夂ォッ蜿」讓。蠑, 謠宣ォdp讓。蠑丈ク句ェ剃ス謎シ霎捺ァ閭ス; | ||
| 33 | +6. 謾ッ謖夐%譏ッ蜷ヲ蜷ォ譛蛾浹鬚醍噪隶セ鄂ョ; | ||
| 34 | +7. 謾ッ謖夐%蟄千岼蠖墓衍隸「; | ||
| 35 | +8. 謾ッ謖「dp/tcp蝗ス譬オ∽シ霎捺ィ。蠑; | ||
| 36 | +9. 謾ッ謖∫峩謗・霎灘RTSP縲ヽTMP縲?TTP-FLV縲仝ebsocket-FLV縲?LS螟夂ァ榊刻隶ョ豬∝慍蝮 | ||
| 37 | +10. | ||
| 37 | # 蠕ョ樒鴫 | 38 | # 蠕ョ樒鴫 |
| 38 | 荳顔コァ郤ァ閨 | 39 | 荳顔コァ郤ァ閨 |
| 39 | 謗ィ豬∝陦ィ | 40 | 謗ィ豬∝陦ィ |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| @@ -538,7 +538,7 @@ public class SIPCommander implements ISIPCommander { | @@ -538,7 +538,7 @@ public class SIPCommander implements ISIPCommander { | ||
| 538 | recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); | 538 | recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); |
| 539 | recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n"); | 539 | recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n"); |
| 540 | recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n"); | 540 | recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n"); |
| 541 | - recordInfoXml.append("<Secrecy>0</Secrecy>\\r\n"); | 541 | + recordInfoXml.append("<Secrecy>0</Secrecy>\r\n"); |
| 542 | // 大华NVR要求必须增加一个值为all的文本元素节点Type | 542 | // 大华NVR要求必须增加一个值为all的文本元素节点Type |
| 543 | recordInfoXml.append("<Type>all</Type>\r\n"); | 543 | recordInfoXml.append("<Type>all</Type>\r\n"); |
| 544 | recordInfoXml.append("</Query>\r\n"); | 544 | recordInfoXml.append("</Query>\r\n"); |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| @@ -140,8 +140,6 @@ public class ZLMHttpHookListener { | @@ -140,8 +140,6 @@ public class ZLMHttpHookListener { | ||
| 140 | streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId)); | 140 | streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId)); |
| 141 | streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); | 141 | streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); |
| 142 | streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId)); | 142 | streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId)); |
| 143 | - | ||
| 144 | - | ||
| 145 | storager.startPlay(streamInfo); | 143 | storager.startPlay(streamInfo); |
| 146 | } | 144 | } |
| 147 | 145 |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
| @@ -74,6 +74,15 @@ public class ZLMRESTfulUtils { | @@ -74,6 +74,15 @@ public class ZLMRESTfulUtils { | ||
| 74 | return sendPost("getMediaList",param); | 74 | return sendPost("getMediaList",param); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | + public JSONObject getMediaInfo(String app, String schema, String stream){ | ||
| 78 | + Map<String, Object> param = new HashMap<>(); | ||
| 79 | + param.put("app",app); | ||
| 80 | + param.put("schema",schema); | ||
| 81 | + param.put("stream",stream); | ||
| 82 | + param.put("vhost","__defaultVhost__"); | ||
| 83 | + return sendPost("getMediaInfo",param); | ||
| 84 | + } | ||
| 85 | + | ||
| 77 | public JSONObject getRtpInfo(String stream_id){ | 86 | public JSONObject getRtpInfo(String stream_id){ |
| 78 | Map<String, Object> param = new HashMap<>(); | 87 | Map<String, Object> param = new HashMap<>(); |
| 79 | param.put("stream_id",stream_id); | 88 | param.put("stream_id",stream_id); |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java
| @@ -37,7 +37,7 @@ public class ZLMUtils { | @@ -37,7 +37,7 @@ public class ZLMUtils { | ||
| 37 | System.out.println(jsonObject.toJSONString()); | 37 | System.out.println(jsonObject.toJSONString()); |
| 38 | return newPort; | 38 | return newPort; |
| 39 | }else { | 39 | }else { |
| 40 | - return getNewRTPPort(streamId); | 40 | + return getNewRTPPort(ssrc); |
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| 43 | 43 |
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java
| @@ -165,7 +165,7 @@ public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager { | @@ -165,7 +165,7 @@ public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager { | ||
| 165 | 165 | ||
| 166 | @Override | 166 | @Override |
| 167 | public void updateCatch() { | 167 | public void updateCatch() { |
| 168 | - | 168 | + System.out.println("##################"); |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | @Override | 171 | @Override |
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
| @@ -64,18 +64,25 @@ public class PlayController { | @@ -64,18 +64,25 @@ public class PlayController { | ||
| 64 | 64 | ||
| 65 | while (lockFlag) { | 65 | while (lockFlag) { |
| 66 | try { | 66 | try { |
| 67 | - | ||
| 68 | if (System.currentTimeMillis() - startTime > 15 * 1000) { | 67 | if (System.currentTimeMillis() - startTime > 15 * 1000) { |
| 69 | storager.stopPlay(streamInfo); | 68 | storager.stopPlay(streamInfo); |
| 70 | return new ResponseEntity<String>("timeout",HttpStatus.OK); | 69 | return new ResponseEntity<String>("timeout",HttpStatus.OK); |
| 71 | }else { | 70 | }else { |
| 71 | + streamInfo = storager.queryPlayByDevice(deviceId, channelId); | ||
| 72 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); | 72 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); |
| 73 | - if (rtpInfo == null || !rtpInfo.getBoolean("exist") || storager.queryPlayByDevice(deviceId, channelId).getFlv() == null){ | 73 | + if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo.getFlv() != null){ |
| 74 | + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); | ||
| 75 | + if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) { | ||
| 76 | + lockFlag = false; | ||
| 77 | + JSONArray tracks = mediaInfo.getJSONArray("tracks"); | ||
| 78 | + streamInfo.setTracks(tracks); | ||
| 79 | + storager.startPlay(streamInfo); | ||
| 80 | + }else { | ||
| 81 | + | ||
| 82 | + } | ||
| 83 | + }else { | ||
| 74 | Thread.sleep(2000); | 84 | Thread.sleep(2000); |
| 75 | continue; | 85 | continue; |
| 76 | - }else { | ||
| 77 | - lockFlag = false; | ||
| 78 | - streamInfo = storager.queryPlay(streamInfo); | ||
| 79 | }; | 86 | }; |
| 80 | } | 87 | } |
| 81 | } catch (InterruptedException e) { | 88 | } catch (InterruptedException e) { |
web_src/src/components/channelList.vue
| @@ -54,12 +54,15 @@ | @@ -54,12 +54,15 @@ | ||
| 54 | </el-table-column> | 54 | </el-table-column> |
| 55 | <el-table-column prop="ptztypeText" label="云台类型"> | 55 | <el-table-column prop="ptztypeText" label="云台类型"> |
| 56 | </el-table-column> | 56 | </el-table-column> |
| 57 | - <el-table-column label="操作" width="240" align="center" fixed="right"> | 57 | + <el-table-column label="操作" width="280" align="center" fixed="right"> |
| 58 | <template slot-scope="scope"> | 58 | <template slot-scope="scope"> |
| 59 | - <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> | ||
| 60 | - <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="scope.row.play" @click="stopDevicePush(scope.row)">停止</el-button> | ||
| 61 | - <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看子目录</el-button> | ||
| 62 | - <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> --> | 59 | + <el-button-group> |
| 60 | + <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> | ||
| 61 | + <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="scope.row.play" @click="stopDevicePush(scope.row)">停止</el-button> | ||
| 62 | + <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看</el-button> | ||
| 63 | +<!-- <el-button size="mini" icon="el-icon-video-camera" type="primary" >设备录象</el-button>--> | ||
| 64 | + <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> --> | ||
| 65 | + </el-button-group> | ||
| 63 | </template> | 66 | </template> |
| 64 | </el-table-column> | 67 | </el-table-column> |
| 65 | </el-table> | 68 | </el-table> |
web_src/src/components/gb28181/devicePlayer.vue
| 1 | <template> | 1 | <template> |
| 2 | <div id="devicePlayer"> | 2 | <div id="devicePlayer"> |
| 3 | <el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> | 3 | <el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> |
| 4 | - <LivePlayer v-if="showVideoDialog && hasaudio" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" hasaudio fluent autoplay live ></LivePlayer> | ||
| 5 | - <LivePlayer v-if="showVideoDialog && !hasaudio" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" fluent autoplay live ></LivePlayer> | 4 | + <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live ></LivePlayer> |
| 6 | <div id="shared" style="text-align: right; margin-top: 1rem;"> | 5 | <div id="shared" style="text-align: right; margin-top: 1rem;"> |
| 7 | <el-tabs v-model="tabActiveName"> | 6 | <el-tabs v-model="tabActiveName"> |
| 8 | <el-tab-pane label="媒体流信息" name="media"> | 7 | <el-tab-pane label="媒体流信息" name="media"> |
| @@ -123,20 +122,17 @@ | @@ -123,20 +122,17 @@ | ||
| 123 | methods: { | 122 | methods: { |
| 124 | 123 | ||
| 125 | play: function(streamInfo, deviceId, channelId, hasAudio) { | 124 | play: function(streamInfo, deviceId, channelId, hasAudio) { |
| 126 | - // this.hasaudio = hasAudio; | ||
| 127 | - if (!hasAudio) { // hasaudio == false时设置播放器hasaudio false, 否则不设置 | ||
| 128 | - this.hasaudio = hasAudio; | ||
| 129 | - } | 125 | + this.hasaudio = hasAudio; |
| 130 | // 根据媒体流信息二次判断 | 126 | // 根据媒体流信息二次判断 |
| 131 | - // if( this.hasaudio && !!streamInfo.tracks && streamInfo.tracks.length > 0) { | ||
| 132 | - // var realHasAudio = false; | ||
| 133 | - // for (let i = 0; i < streamInfo.tracks; i++) { | ||
| 134 | - // if (streamInfo.tracks[i].codec_type == 1) { // 判断为音频 | ||
| 135 | - // realHasAudio = true; | ||
| 136 | - // } | ||
| 137 | - // } | ||
| 138 | - // this.hasaudio = realHasAudio && this.hasaudio; | ||
| 139 | - // } | 127 | + if( this.hasaudio && !!streamInfo.tracks && streamInfo.tracks.length > 0) { |
| 128 | + var realHasAudio = false; | ||
| 129 | + for (let i = 0; i < streamInfo.tracks; i++) { | ||
| 130 | + if (streamInfo.tracks[i].codec_type == 1) { // 判断为音频 | ||
| 131 | + realHasAudio = true; | ||
| 132 | + } | ||
| 133 | + } | ||
| 134 | + this.hasaudio = realHasAudio && this.hasaudio; | ||
| 135 | + } | ||
| 140 | this.ssrc = streamInfo.ssrc; | 136 | this.ssrc = streamInfo.ssrc; |
| 141 | this.deviceId = deviceId; | 137 | this.deviceId = deviceId; |
| 142 | this.channelId = channelId; | 138 | this.channelId = channelId; |