添加网页自动转码,以支持h265 g.711的播放
| | |
| | | }
|
| | | 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
|
| | |
| | | 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); |
| | | } |
| | |
| | | |
| | | 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",""); |
| | |
| | | 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); |
| | |
| | | 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; |
| | |
| | | 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); |
| | | |
| | |
| | | return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将不是h264的视频通过ffmpeg 转码为h264 + 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); |
| | | } |
| | | } |
| | | |
| | |
| | | wanIp: |
| | | port: 80 |
| | | secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc |
| | | streamNoneReaderDelayMS: 1800000 # 无人观看多久自动关闭流 |
| | | closeWaitRTPInfo: false # 强制关闭等待收到流编码信息后在返回, 设为true可以快速打开播放窗口, 设为false保证返回后流就可以播放 |
| | | streamNoneReaderDelayMS: 600000 # 无人观看多久自动关闭流 |
| | | # 关闭等待收到流编码信息后在返回, |
| | | # 设为false可以获得更好的兼容性,保证返回后流就可以播放, |
| | | # 设为true可以快速打开播放窗口,可以获得更好的体验 |
| | | closeWaitRTPInfo: true |
| | | rtp: # 启用udp多端口模式 |
| | | enable: true |
| | | udpPortRange: 30000,30500 # 端口范围 |
| | |
| | | <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"> |
| | |
| | | <!--{"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> |
| | |
| | | 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, |
| | |
| | | this.$refs.videoPlayer.pause(); |
| | | } |
| | | |
| | | |
| | | switch (tab) { |
| | | case "media": |
| | | this.play(param.streamInfo, param.hasAudio) |
| | |
| | | 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") { // 判断为AAC音频 |
| | | realHasAudio = true; |
| | | } |
| | | if (streamInfo.tracks[i].codec_type == 0 && streamInfo.tracks[i].codec_id_name != "CodecH264") { // 判断为H265视频 |
| | | that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ |
| | | that.close(); |
| | | return; |
| | | }) |
| | | }else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name != "CodecAAC") { |
| | | that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ |
| | | that.playFromStreamInfo(false. streamInfo) |
| | | }) |
| | | }else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name == "CodecAAC") { |
| | | that.playFromStreamInfo(true, streamInfo) |
| | | }else { |
| | | that.playFromStreamInfo(false, streamInfo) |
| | | } |
| | | } |
| | | }else { |
| | | that.playFromStreamInfo(false, streamInfo) |
| | | } |
| | | 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); |