优化编码信息的展示

pull/1694/head
648540858 2024-11-14 17:05:12 +08:00
parent 36f79d798b
commit 2060a2117c
5 changed files with 134 additions and 98 deletions

View File

@ -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);
} }

View File

@ -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
*/ */

View File

@ -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")

View File

@ -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>

View File

@ -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>
无法播放或者没有声音?&nbsp&nbsp&nbsp试一试&nbsp
<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()
}, },