From 09470bef96d48373219ac03315a4391c24accf98 Mon Sep 17 00:00:00 2001 From: 648540858 <648540858@qq.com> Date: 星期四, 19 十一月 2020 16:00:02 +0800 Subject: [PATCH] 添加网页自动转码,以支持h265 g.711的播放 --- src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java | 3 src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java | 71 ++++++++++++++ src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java | 44 ++++---- src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java | 16 +++ src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java | 2 web_src/src/components/gb28181/devicePlayer.vue | 146 +++++++++++++++++------------ src/main/resources/application.yml | 7 + 7 files changed, 202 insertions(+), 87 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index 0c450a7..b9c05e4 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -128,29 +128,29 @@ } String app = json.getString("app"); String streamId = json.getString("id"); + if ("rtp".equals(app)) { + String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); + StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc); + if ("rtp".equals(app) && streamInfoForPlay != null ) { + MediaServerConfig mediaInfo = storager.getMediaInfo(); + streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); + streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); + storager.startPlay(streamInfoForPlay); + } - - String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); - StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc); - if ("rtp".equals(app) && streamInfoForPlay != null ) { - MediaServerConfig mediaInfo = storager.getMediaInfo(); - streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); - streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); - storager.startPlay(streamInfoForPlay); - } - - StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc); - if ("rtp".equals(app) && streamInfoForPlayBack != null ) { - MediaServerConfig mediaInfo = storager.getMediaInfo(); - streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); - streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); - storager.startPlayback(streamInfoForPlayBack); + StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc); + if ("rtp".equals(app) && streamInfoForPlayBack != null ) { + MediaServerConfig mediaInfo = storager.getMediaInfo(); + streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); + streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); + storager.startPlayback(streamInfoForPlayBack); + } } // TODO Auto-generated method stub diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java index 86f05da..1e38bdc 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java @@ -89,6 +89,22 @@ return sendPost("getRtpInfo",param); } + public JSONObject addFFmpegSource(String src_url, String dst_url, String timeout_ms){ + System.out.println(src_url); + System.out.println(dst_url); + Map<String, Object> param = new HashMap<>(); + param.put("src_url", src_url); + param.put("dst_url", dst_url); + param.put("timeout_ms", timeout_ms); + return sendPost("addFFmpegSource",param); + } + + public JSONObject delFFmpegSource(String key){ + Map<String, Object> param = new HashMap<>(); + param.put("key", key); + return sendPost("delFFmpegSource",param); + } + public JSONObject getMediaServerConfig(){ return sendPost("getServerConfig",null); } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index fc519da..e7cfdb2 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java @@ -101,7 +101,8 @@ String hookPrex = String.format("http://%s:%s/index/hook", hookIP, serverPort); Map<String, Object> param = new HashMap<>(); - param.put("secret",mediaSecret); + param.put("api.secret",mediaSecret); // -profile:v Baseline + param.put("ffmpeg.cmd","%s -fflags nobuffer -rtsp_transport tcp -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"); param.put("hook.enable","1"); param.put("hook.on_flow_report",""); param.put("hook.on_play",""); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java index f44e7df..8195b65 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java @@ -29,7 +29,7 @@ param.put("enable_tcp", 1); param.put("stream_id", streamId); JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param); - if (jsonObject.getInteger("code") == 0) { + if (jsonObject != null && jsonObject.getInteger("code") == 0) { return newPort; } else { return getNewRTPPort(ssrc); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java index a01748e..300ffc3 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java @@ -3,6 +3,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +47,7 @@ Integer getEncoding) { if (getEncoding == null) getEncoding = 0; - getEncoding = closeWaitRTPInfo ? 0: getEncoding; + getEncoding = closeWaitRTPInfo ? 0 : getEncoding; Device device = storager.queryVideoDevice(deviceId); StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId); @@ -149,5 +150,73 @@ return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); } } + + /** + * 灏嗕笉鏄痟264鐨勮棰戦�氳繃ffmpeg 杞爜涓篽264 + aac + * @param ssrc + * @return + */ + @PostMapping("/play/{ssrc}/convert") + public ResponseEntity<String> playConvert(@PathVariable String ssrc) { + StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc); + if (streamInfo == null) { + logger.warn("瑙嗛杞爜API璋冪敤澶辫触锛�, 瑙嗛娴佸凡缁忓仠姝�!"); + return new ResponseEntity<String>("鏈壘鍒拌棰戞祦淇℃伅, 瑙嗛娴佸彲鑳藉凡缁忓仠姝�", HttpStatus.OK); + } + String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); + if (!rtpInfo.getBoolean("exist")) { + logger.warn("瑙嗛杞爜API璋冪敤澶辫触锛�, 瑙嗛娴佸凡鍋滄鎺ㄦ祦!"); + return new ResponseEntity<String>("鎺ㄦ祦淇℃伅鍦ㄦ祦濯掍綋涓笉瀛樺湪, 瑙嗛娴佸彲鑳藉凡鍋滄鎺ㄦ祦", HttpStatus.OK); + } else { + MediaServerConfig mediaInfo = storager.getMediaInfo(); + String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), + streamId ); + JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(streamInfo.getRtsp(), dstUrl, "1000000"); + System.out.println(jsonObject); + JSONObject result = new JSONObject(); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + result.put("code", 0); + JSONObject data = jsonObject.getJSONObject("data"); + if (data != null) { + result.put("key", data.getString("key")); + result.put("rtmp", dstUrl); + result.put("flv", String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + result.put("ws_flv", String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + } + }else { + result.put("code", 1); + result.put("msg", "cover fail"); + } + return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK); + } + } + + /** + * 缁撴潫杞爜 + * @param key + * @return + */ + @PostMapping("/play/convert/stop/{key}") + public ResponseEntity<String> playConvertStop(@PathVariable String key) { + + JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(key); + System.out.println(jsonObject); + JSONObject result = new JSONObject(); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + result.put("code", 0); + JSONObject data = jsonObject.getJSONObject("data"); + if (data != null && data.getBoolean("flag")) { + result.put("code", "0"); + result.put("msg", "success"); + }else { + + } + }else { + result.put("code", 1); + result.put("msg", "delFFmpegSource fail"); + } + return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 49bd03f..463548a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -44,8 +44,11 @@ wanIp: port: 80 secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc - streamNoneReaderDelayMS: 1800000 # 鏃犱汉瑙傜湅澶氫箙鑷姩鍏抽棴娴� - closeWaitRTPInfo: false # 寮哄埗鍏抽棴绛夊緟鏀跺埌娴佺紪鐮佷俊鎭悗鍦ㄨ繑鍥�, 璁句负true鍙互蹇�熸墦寮�鎾斁绐楀彛, 璁句负false淇濊瘉杩斿洖鍚庢祦灏卞彲浠ユ挱鏀� + streamNoneReaderDelayMS: 600000 # 鏃犱汉瑙傜湅澶氫箙鑷姩鍏抽棴娴� + # 鍏抽棴绛夊緟鏀跺埌娴佺紪鐮佷俊鎭悗鍦ㄨ繑鍥�, + # 璁句负false鍙互鑾峰緱鏇村ソ鐨勫吋瀹规��,淇濊瘉杩斿洖鍚庢祦灏卞彲浠ユ挱鏀�, + # 璁句负true鍙互蹇�熸墦寮�鎾斁绐楀彛,鍙互鑾峰緱鏇村ソ鐨勪綋楠� + closeWaitRTPInfo: true rtp: # 鍚敤udp澶氱鍙fā寮� enable: true udpPortRange: 30000,30500 # 绔彛鑼冨洿 diff --git a/web_src/src/components/gb28181/devicePlayer.vue b/web_src/src/components/gb28181/devicePlayer.vue index 780d03e..c87d0c4 100644 --- a/web_src/src/components/gb28181/devicePlayer.vue +++ b/web_src/src/components/gb28181/devicePlayer.vue @@ -1,7 +1,7 @@ <template> -<div id="devicePlayer"> +<div id="devicePlayer" v-loading="isLoging"> <el-dialog title="瑙嗛鎾斁" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> - <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> + <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> <div id="shared" style="text-align: right; margin-top: 1rem;"> <el-tabs v-model="tabActiveName"> <el-tab-pane label="瀹炴椂瑙嗛" name="media"> @@ -26,20 +26,6 @@ <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}--> <el-tab-pane label="褰曞儚鏌ヨ" name="record"> <el-date-picker size="mini" v-model="videoHistory.date" type="date" value-format="yyyy-MM-dd" placeholder="鏃ユ湡" @change="queryRecords()"></el-date-picker> - <!-- <el-slider style="margin: 0 1rem 1rem 1rem;"--> - <!-- v-model="timeVal"--> - <!-- :min="timeMin"--> - <!-- :max="timeMax"--> - <!-- :step="5"--> - <!-- :marks="getTimeMakrs()"--> - <!-- :format-tooltip="formatTooltip">--> - <!-- </el-slider>--> - <!-- <range-slider :min="timeMin"--> - <!-- :max="timeMax"--> - <!-- :step="5"></range-slider>--> - - <!-- <el-date-picker v-model="videoHistory.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="缁撴潫鏃堕棿"--> - <!-- @change="recordList()"></el-date-picker>--> <el-table :data="videoHistory.searchHistoryResult" height="150" v-loading="recordsLoading"> <el-table-column label="鍚嶇О" prop="name"></el-table-column> <el-table-column label="鏂囦欢" prop="filePath"></el-table-column> @@ -143,41 +129,16 @@ date: '', searchHistoryResult: [] //濯掍綋娴佸巻鍙茶褰曟悳绱㈢粨鏋� }, - timeMakrs: { - // 0 : "0:00", - // // 60 : "1:00", - // 120 : "2:00", - // // 180 : "3:00", - // 240 : "4:00", - // // 300 : "5:00", - // 360 : "6:00", - // // 420 : "7:00", - // 480 : "8:00", - // 540 : "9:00", - 600: "10:00", - // 660 : "11:00", - 720: "12:00", - // 780 : "13:00", - 840: "14:00", - // 900 : "15:00", - 960: "16:00", - // 1020 : "17:00", - 1080: "18:00", - // 1140 : "19:00", - // 1200 : "20:00", - // // 1260 : "21:00", - // 1320 : "22:00", - // // 1380 : "23:00", - // 1440 : "24:00" - }, showVideoDialog: false, ssrc: '', + convertKey: '', deviceId: '', channelId: '', tabActiveName: 'media', hasaudio: false, loadingRecords: false, recordsLoading: false, + isLoging: false, timeVal: 0, timeMin: 0, timeMax: 1440, @@ -200,6 +161,7 @@ this.$refs.videoPlayer.pause(); } + switch (tab) { case "media": this.play(param.streamInfo, param.hasAudio) @@ -217,33 +179,97 @@ timeAxisSelTime: function (val) { console.log(val) }, - getTimeMakrs() { - return this.timeMakrs; - }, play: function (streamInfo, hasAudio) { this.hasaudio = hasAudio; - // 鏍规嵁濯掍綋娴佷俊鎭簩娆″垽鏂� - var realHasAudio = false; - if (!!streamInfo.tracks && streamInfo.tracks.length > 0 && hasAudio) { + var that = this; + that.isLoging = false; + if (!!streamInfo.tracks && streamInfo.tracks.length > 0 ) { for (let i = 0; i < streamInfo.tracks.length; i++) { - if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name == "CodecAAC") { // 鍒ゆ柇涓篈AC闊抽 - realHasAudio = true; - } + if (streamInfo.tracks[i].codec_type == 0 && streamInfo.tracks[i].codec_id_name != "CodecH264") { // 鍒ゆ柇涓篐265瑙嗛 + that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ + that.close(); + return; + }) + }else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name != "CodecAAC") { + that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ + that.playFromStreamInfo(false. streamInfo) + }) + }else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name == "CodecAAC") { + that.playFromStreamInfo(true, streamInfo) + }else { + that.playFromStreamInfo(false, streamInfo) + } } + }else { + that.playFromStreamInfo(false, streamInfo) } - this.hasaudio = realHasAudio && this.hasaudio; - this.ssrc = streamInfo.ssrc; - // this.$refs.videoPlayer.hasaudio = hasAudio; - // this.videoUrl = streamInfo.flv + "?" + new Date().getTime(); - this.videoUrl = streamInfo.ws_flv; - this.showVideoDialog = true; - console.log(this.ssrc); + }, + coverPlay: function (streamInfo, codec_id_name, catchcallback) { + var that = this; + + that.$confirm(codec_id_name + ' 缂栫爜鏍煎紡涓嶆敮鎸佹挱鏀�, 鏄惁杞爜鎾斁?', '鎻愮ず', { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: 'warning' + }).then(() => { + that.isLoging = true; + that.$axios({ + method: 'post', + url: '/api/play/' + streamInfo.ssrc + '/convert' + }).then(function (res) { + if (res.data.code == 0) { + streamInfo.ws_flv = res.data.ws_flv; + that.convertKey = res.data.key; + setTimeout(()=>{ + that.isLoging = false; + that.playFromStreamInfo(false, streamInfo); + }, 2000) + } else { + that.isLoging = false; + that.$message({ + showClose: true, + message: '杞爜澶辫触', + type: 'error' + }); + } + }).catch(function (e) { + that.$message({ + showClose: true, + message: '鎾斁閿欒', + type: 'error' + }); + }); + }).catch(function (e) { + if (catchcallback)catchcallback() + }); + }, + playFromStreamInfo: function (realHasAudio, streamInfo) { + this.videoUrl = streamInfo.ws_flv; + this.showVideoDialog = true; + this.hasaudio = realHasAudio && this.hasaudio; + this.ssrc = streamInfo.ssrc; + console.log(this.ssrc); }, close: function () { console.log('鍏抽棴瑙嗛'); - this.$refs.videoPlayer.pause(); + if (!this.$refs.videoPlayer){ + this.$refs.videoPlayer.pause(); + } this.videoUrl = ''; this.showVideoDialog = false; + if (this.convertKey != '') { + this.$axios({ + method: 'post', + url: '/api/play/convert/stop/' + this.convertKey + }).then(function (res) { + if (res.data.code == 0) { + console.log(res.data.msg) + }else { + console.error(res.data.msg) + } + }).catch(function (e) {}); + } + this.convertKey = '' }, copySharedInfo: function (data) { console.log('澶嶅埗鍐呭锛�' + data); -- Gitblit v1.8.0