优化编码信息的展示
parent
36f79d798b
commit
2060a2117c
|
@ -33,6 +33,10 @@ public class MediaInfo {
|
||||||
private Integer width;
|
private Integer width;
|
||||||
@Schema(description = "视频高度")
|
@Schema(description = "视频高度")
|
||||||
private Integer height;
|
private Integer height;
|
||||||
|
@Schema(description = "FPS")
|
||||||
|
private Integer fps;
|
||||||
|
@Schema(description = "丢包率")
|
||||||
|
private Integer loss;
|
||||||
@Schema(description = "音频编码类型")
|
@Schema(description = "音频编码类型")
|
||||||
private String audioCodec;
|
private String audioCodec;
|
||||||
@Schema(description = "音频通道数")
|
@Schema(description = "音频通道数")
|
||||||
|
@ -58,6 +62,7 @@ public class MediaInfo {
|
||||||
@Schema(description = "服务ID")
|
@Schema(description = "服务ID")
|
||||||
private String serverId;
|
private String serverId;
|
||||||
|
|
||||||
|
|
||||||
public static MediaInfo getInstance(JSONObject jsonObject, MediaServer mediaServer, String serverId) {
|
public static MediaInfo getInstance(JSONObject jsonObject, MediaServer mediaServer, String serverId) {
|
||||||
MediaInfo mediaInfo = new MediaInfo();
|
MediaInfo mediaInfo = new MediaInfo();
|
||||||
mediaInfo.setMediaServer(mediaServer);
|
mediaInfo.setMediaServer(mediaServer);
|
||||||
|
@ -112,6 +117,13 @@ public class MediaInfo {
|
||||||
Integer sampleRate = trackJson.getInteger("sample_rate");
|
Integer sampleRate = trackJson.getInteger("sample_rate");
|
||||||
Integer height = trackJson.getInteger("height");
|
Integer height = trackJson.getInteger("height");
|
||||||
Integer width = trackJson.getInteger("height");
|
Integer width = trackJson.getInteger("height");
|
||||||
|
Integer fps = trackJson.getInteger("fps");
|
||||||
|
Integer loss = trackJson.getInteger("loss");
|
||||||
|
Integer frames = trackJson.getInteger("frames");
|
||||||
|
Long keyFrames = trackJson.getLongValue("key_frames");
|
||||||
|
Integer gop_interval_ms = trackJson.getInteger("gop_interval_ms");
|
||||||
|
Long gop_size = trackJson.getLongValue("gop_size");
|
||||||
|
|
||||||
Long duration = trackJson.getLongValue("duration");
|
Long duration = trackJson.getLongValue("duration");
|
||||||
if (channels != null) {
|
if (channels != null) {
|
||||||
mediaInfo.setAudioChannels(channels);
|
mediaInfo.setAudioChannels(channels);
|
||||||
|
@ -125,6 +137,12 @@ public class MediaInfo {
|
||||||
if (width != null) {
|
if (width != null) {
|
||||||
mediaInfo.setWidth(width);
|
mediaInfo.setWidth(width);
|
||||||
}
|
}
|
||||||
|
if (fps != null) {
|
||||||
|
mediaInfo.setFps(fps);
|
||||||
|
}
|
||||||
|
if (loss != null) {
|
||||||
|
mediaInfo.setLoss(loss);
|
||||||
|
}
|
||||||
if (duration > 0L) {
|
if (duration > 0L) {
|
||||||
mediaInfo.setDuration(duration);
|
mediaInfo.setDuration(duration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,15 +118,6 @@ public class ZLMHttpHookListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
|
|
||||||
*/
|
|
||||||
// @ResponseBody
|
|
||||||
// @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
|
|
||||||
// public HookResult onStreamChanged(@RequestBody JSONObject param) {
|
|
||||||
// System.out.println(11);
|
|
||||||
// return HookResult.SUCCESS();
|
|
||||||
// }
|
|
||||||
/**
|
/**
|
||||||
* rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
|
* rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||||
import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
|
import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
|
||||||
import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
|
import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
|
||||||
|
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||||
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
|
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
|
||||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||||
|
@ -162,6 +163,20 @@ public class ServerController {
|
||||||
mediaServerService.delete(mediaServer);
|
mediaServerService.delete(mediaServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取流信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||||
|
@Parameter(name = "app", description = "应用名", required = true)
|
||||||
|
@Parameter(name = "stream", description = "流ID", required = true)
|
||||||
|
@Parameter(name = "mediaServerId", description = "流媒体ID", required = true)
|
||||||
|
@GetMapping(value = "/media_server/media_info")
|
||||||
|
@ResponseBody
|
||||||
|
public MediaInfo getMediaInfo(String app, String stream, String mediaServerId) {
|
||||||
|
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||||
|
if (mediaServer == null) {
|
||||||
|
throw new ControllerException(ErrorCode.ERROR100.getCode(), "流媒体不存在");
|
||||||
|
}
|
||||||
|
return mediaServerService.getMediaInfo(mediaServer, app, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Operation(summary = "重启服务", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
@Operation(summary = "重启服务", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||||
@GetMapping(value = "/restart")
|
@GetMapping(value = "/restart")
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
<template>
|
||||||
|
<div id="mediaInfo" >
|
||||||
|
<el-button style="position: absolute; right: 1rem;" icon="el-icon-refresh-right" circle size="mini" @click="getMediaInfo"></el-button>
|
||||||
|
<el-descriptions size="mini" :column="3" title="概况">
|
||||||
|
<el-descriptions-item label="观看人数">{{ info.readerCount }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="网络">{{ formatByteSpeed() }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="持续时间">{{info.aliveSecond}}秒</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<div style="display: grid; grid-template-columns: 1fr 1fr">
|
||||||
|
<el-descriptions size="mini" v-if="info.videoCodec" :column="2" title="视频信息">
|
||||||
|
<el-descriptions-item label="编码">{{ info.videoCodec }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="分辨率"
|
||||||
|
>{{ info.width }}x{{ info.height }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="FPS">{{ info.fps }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="丢包率">{{ info.loss }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<el-descriptions size="mini" v-if="info.audioCodec" :column="2" title="音频信息">
|
||||||
|
<el-descriptions-item label="编码">
|
||||||
|
{{ info.audioCodec }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="采样率">{{ info.audioSampleRate }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "mediaInfo",
|
||||||
|
props: [ 'app', 'stream', 'mediaServerId'],
|
||||||
|
components: {},
|
||||||
|
created() {
|
||||||
|
this.getMediaInfo()
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
info: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getMediaInfo: function () {
|
||||||
|
this.$axios({
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/server/media_server/media_info`,
|
||||||
|
params: {
|
||||||
|
app: this.app,
|
||||||
|
stream: this.stream,
|
||||||
|
mediaServerId: this.mediaServerId,
|
||||||
|
}
|
||||||
|
}).then((res)=> {
|
||||||
|
console.log(res.data.data);
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
this.info = res.data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
}).catch((error)=> {
|
||||||
|
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatByteSpeed: function (){
|
||||||
|
let bytesSpeed = this.info.bytesSpeed
|
||||||
|
let num = 1024.0 //byte
|
||||||
|
if (bytesSpeed < num) return bytesSpeed + ' B/S'
|
||||||
|
if (bytesSpeed < Math.pow(num, 2)) return (bytesSpeed / num).toFixed(2) + ' KB/S' //kb
|
||||||
|
if (bytesSpeed < Math.pow(num, 3))
|
||||||
|
return (bytesSpeed / Math.pow(num, 2)).toFixed(2) + ' MB/S' //M
|
||||||
|
if (bytesSpeed < Math.pow(num, 4))
|
||||||
|
return (bytesSpeed / Math.pow(num, 3)).toFixed(2) + ' G/S' //G
|
||||||
|
return (bytesSpeed / Math.pow(num, 4)).toFixed(2) + ' T/S' //T
|
||||||
|
},
|
||||||
|
formatAliveSecond: function (){
|
||||||
|
let aliveSecond = this.info.aliveSecond
|
||||||
|
const h = parseInt(aliveSecond.value / 3600)
|
||||||
|
const minute = parseInt((aliveSecond.value / 60) % 60)
|
||||||
|
const second = Math.ceil(aliveSecond.value % 60)
|
||||||
|
|
||||||
|
const hours = h < 10 ? '0' + h : h
|
||||||
|
const formatSecond = second > 59 ? 59 : second
|
||||||
|
return `${hours > 0 ? `${hours}小时` : ''}${minute < 10 ? '0' + minute : minute}分${
|
||||||
|
formatSecond < 10 ? '0' + formatSecond : formatSecond
|
||||||
|
}秒`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.channel-form {
|
||||||
|
display: grid;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
padding: 1rem 2rem 0 2rem;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -232,31 +232,7 @@
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="编码信息" name="codec" v-loading="tracksLoading">
|
<el-tab-pane label="编码信息" name="codec" v-loading="tracksLoading">
|
||||||
<p>
|
<mediaInfo :app="app" :stream="streamId" :mediaServerId="mediaServerId"></mediaInfo>
|
||||||
无法播放或者没有声音?   试一试 
|
|
||||||
<el-button size="mini" type="primary" v-if="!coverPlaying" @click="coverPlay">转码播放</el-button>
|
|
||||||
<el-button size="mini" type="danger" v-if="coverPlaying" @click="convertStopClick">停止转码</el-button>
|
|
||||||
</p>
|
|
||||||
<div class="trank">
|
|
||||||
<p v-if="tracksNotLoaded" style="text-align: center;padding-top: 3rem;">暂无数据</p>
|
|
||||||
<div v-for="(item, index) in tracks" style="width: 50%; float: left" loading>
|
|
||||||
<span>流 {{ index }}</span>
|
|
||||||
<div class="trankInfo" v-if="item.codec_type == 0">
|
|
||||||
<p>格式: {{ item.codec_id_name }}</p>
|
|
||||||
<p>类型: 视频</p>
|
|
||||||
<p>分辨率: {{ item.width }} x {{ item.height }}</p>
|
|
||||||
<p>帧率: {{ item.fps }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="trankInfo" v-if="item.codec_type == 1">
|
|
||||||
<p>格式: {{ item.codec_id_name }}</p>
|
|
||||||
<p>类型: 音频</p>
|
|
||||||
<p>采样位数: {{ item.sample_bit }}</p>
|
|
||||||
<p>采样率: {{ item.sample_rate }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="语音对讲" name="broadcast">
|
<el-tab-pane label="语音对讲" name="broadcast">
|
||||||
<div style="padding: 0 10px">
|
<div style="padding: 0 10px">
|
||||||
|
@ -293,12 +269,13 @@ import PtzCruising from "../common/ptzCruising.vue";
|
||||||
import ptzScan from "../common/ptzScan.vue";
|
import ptzScan from "../common/ptzScan.vue";
|
||||||
import ptzWiper from "../common/ptzWiper.vue";
|
import ptzWiper from "../common/ptzWiper.vue";
|
||||||
import ptzSwitch from "../common/ptzSwitch.vue";
|
import ptzSwitch from "../common/ptzSwitch.vue";
|
||||||
|
import mediaInfo from "../common/mediaInfo.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'devicePlayer',
|
name: 'devicePlayer',
|
||||||
props: {},
|
props: {},
|
||||||
components: {
|
components: {
|
||||||
PtzPreset,PtzCruising,ptzScan,ptzWiper,ptzSwitch,
|
PtzPreset,PtzCruising,ptzScan,ptzWiper,ptzSwitch,mediaInfo,
|
||||||
LivePlayer, jessibucaPlayer, rtcPlayer,
|
LivePlayer, jessibucaPlayer, rtcPlayer,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -334,7 +311,6 @@ export default {
|
||||||
ptzPresetId: '',
|
ptzPresetId: '',
|
||||||
app: '',
|
app: '',
|
||||||
mediaServerId: '',
|
mediaServerId: '',
|
||||||
convertKey: '',
|
|
||||||
deviceId: '',
|
deviceId: '',
|
||||||
channelId: '',
|
channelId: '',
|
||||||
tabActiveName: 'media',
|
tabActiveName: 'media',
|
||||||
|
@ -353,7 +329,6 @@ export default {
|
||||||
scanSpeed: 100,
|
scanSpeed: 100,
|
||||||
scanGroup: 0,
|
scanGroup: 0,
|
||||||
tracks: [],
|
tracks: [],
|
||||||
coverPlaying: false,
|
|
||||||
tracksLoading: false,
|
tracksLoading: false,
|
||||||
showPtz: true,
|
showPtz: true,
|
||||||
showRrecord: true,
|
showRrecord: true,
|
||||||
|
@ -453,63 +428,6 @@ export default {
|
||||||
}
|
}
|
||||||
return this.videoUrl;
|
return this.videoUrl;
|
||||||
|
|
||||||
},
|
|
||||||
coverPlay: function () {
|
|
||||||
var that = this;
|
|
||||||
this.coverPlaying = true;
|
|
||||||
this.$refs[this.activePlayer].pause()
|
|
||||||
that.$axios({
|
|
||||||
method: 'post',
|
|
||||||
url: '/api/play/convert/' + that.streamId
|
|
||||||
}).then(function (res) {
|
|
||||||
if (res.data.code === 0) {
|
|
||||||
that.convertKey = res.data.key;
|
|
||||||
setTimeout(() => {
|
|
||||||
that.isLoging = false;
|
|
||||||
that.playFromStreamInfo(false, res.data.data);
|
|
||||||
}, 2000)
|
|
||||||
} else {
|
|
||||||
that.isLoging = false;
|
|
||||||
that.coverPlaying = false;
|
|
||||||
that.$message({
|
|
||||||
showClose: true,
|
|
||||||
message: '转码失败',
|
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).catch(function (e) {
|
|
||||||
console.log(e)
|
|
||||||
that.coverPlaying = false;
|
|
||||||
that.$message({
|
|
||||||
showClose: true,
|
|
||||||
message: '播放错误',
|
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
convertStopClick: function () {
|
|
||||||
this.convertStop(() => {
|
|
||||||
this.$refs[this.activePlayer].play(this.videoUrl)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
convertStop: function (callback) {
|
|
||||||
var that = this;
|
|
||||||
that.$refs.videoPlayer.pause()
|
|
||||||
this.$axios({
|
|
||||||
method: 'post',
|
|
||||||
url: '/api/play/convertStop/' + this.convertKey
|
|
||||||
}).then(function (res) {
|
|
||||||
if (res.data.code == 0) {
|
|
||||||
console.log(res.data.msg)
|
|
||||||
} else {
|
|
||||||
console.error(res.data.msg)
|
|
||||||
}
|
|
||||||
if (callback) callback();
|
|
||||||
}).catch(function (e) {
|
|
||||||
});
|
|
||||||
that.coverPlaying = false;
|
|
||||||
that.convertKey = "";
|
|
||||||
// if (callback )callback();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
playFromStreamInfo: function (realHasAudio, streamInfo) {
|
playFromStreamInfo: function (realHasAudio, streamInfo) {
|
||||||
|
@ -531,10 +449,6 @@ export default {
|
||||||
this.videoUrl = '';
|
this.videoUrl = '';
|
||||||
this.coverPlaying = false;
|
this.coverPlaying = false;
|
||||||
this.showVideoDialog = false;
|
this.showVideoDialog = false;
|
||||||
if (this.convertKey != '') {
|
|
||||||
this.convertStop();
|
|
||||||
}
|
|
||||||
this.convertKey = ''
|
|
||||||
this.stopBroadcast()
|
this.stopBroadcast()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue