优化录像下载。 支持八倍速(设备支持的最高速度)下载

master
lin 2025-03-13 16:48:15 +08:00
parent 1869945ff1
commit 298fb05378
9 changed files with 65 additions and 29 deletions

View File

@ -37,6 +37,10 @@ public class InviteInfo {
private Boolean record; private Boolean record;
private String startTime;
private String endTime;
public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, String mediaServerId, public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, String mediaServerId,
String receiveIp, Integer receivePort, String streamMode, String receiveIp, Integer receivePort, String streamMode,

View File

@ -15,7 +15,6 @@ import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
@ -210,6 +209,13 @@ public class DeviceQuery {
public void updateChannelStreamIdentification(DeviceChannel channel){ public void updateChannelStreamIdentification(DeviceChannel channel){
deviceChannelService.updateChannelStreamIdentification(channel); deviceChannelService.updateChannelStreamIdentification(channel);
} }
@Operation(summary = "获取单个通道详情", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备的国标编码", required = true)
@Parameter(name = "channelDeviceId", description = "通道的国标编码", required = true)
@GetMapping("/channel/one")
public DeviceChannel getChannel(String deviceId, String channelDeviceId){
return deviceChannelService.getOne(deviceId, channelDeviceId);
}
@Operation(summary = "修改数据流传输模式", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Operation(summary = "修改数据流传输模式", security = @SecurityRequirement(name = JwtUtils.HEADER))

View File

@ -390,6 +390,7 @@ public class PlayServiceImpl implements IPlayService {
rtpServerParam.setTcpMode(tcpMode); rtpServerParam.setTcpMode(tcpMode);
rtpServerParam.setOnlyAuto(false); rtpServerParam.setOnlyAuto(false);
rtpServerParam.setDisableAudio(!channel.isHasAudio()); rtpServerParam.setDisableAudio(!channel.isHasAudio());
SSRCInfo ssrcInfo = receiveRtpServerService.openRTPServer(rtpServerParam, (code, msg, result) -> { SSRCInfo ssrcInfo = receiveRtpServerService.openRTPServer(rtpServerParam, (code, msg, result) -> {
if (code == InviteErrorCode.SUCCESS.getCode() && result != null && result.getHookData() != null) { if (code == InviteErrorCode.SUCCESS.getCode() && result != null && result.getHookData() != null) {
@ -1055,6 +1056,8 @@ public class PlayServiceImpl implements IPlayService {
InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(),
mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD, mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD,
InviteSessionStatus.ready, true); InviteSessionStatus.ready, true);
inviteInfo.setStartTime(startTime);
inviteInfo.setEndTime(endTime);
inviteStreamService.updateInviteInfo(inviteInfo); inviteStreamService.updateInviteInfo(inviteInfo);
try { try {

View File

@ -14,7 +14,6 @@ import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.hook.HookType; import com.genersoft.iot.vmp.media.event.hook.HookType;
import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.ISendRtpServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element; import org.dom4j.Element;
@ -101,23 +100,25 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
// 去除监听流注销自动停止下载的监听 // 去除监听流注销自动停止下载的监听
Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcTransaction.getStream(), ssrcTransaction.getMediaServerId()); Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcTransaction.getStream(), ssrcTransaction.getMediaServerId());
subscribe.removeSubscribe(hook); subscribe.removeSubscribe(hook);
// 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题需要将点播CallId进行上下级绑定 if (ssrcTransaction.getPlatformId() != null) {
SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(ssrcTransaction.getChannelId(), ssrcTransaction.getPlatformId()); // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题需要将点播CallId进行上下级绑定
if (sendRtpInfo != null) { SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(ssrcTransaction.getChannelId(), ssrcTransaction.getPlatformId());
Platform parentPlatform = platformService.queryPlatformByServerGBId(sendRtpInfo.getTargetId()); if (sendRtpInfo != null) {
if (parentPlatform == null) { Platform parentPlatform = platformService.queryPlatformByServerGBId(sendRtpInfo.getTargetId());
log.warn("[级联消息发送]发送MediaStatus发现上级平台{}不存在", sendRtpInfo.getTargetId()); if (parentPlatform == null) {
return; log.warn("[级联消息发送]发送MediaStatus发现上级平台{}不存在", sendRtpInfo.getTargetId());
} return;
CommonGBChannel channel = platformChannelService.queryChannelByPlatformIdAndChannelId(parentPlatform.getId(), sendRtpInfo.getChannelId()); }
if (channel == null) { CommonGBChannel channel = platformChannelService.queryChannelByPlatformIdAndChannelId(parentPlatform.getId(), sendRtpInfo.getChannelId());
log.warn("[级联消息发送]发送MediaStatus发现通道{}不存在", sendRtpInfo.getChannelId()); if (channel == null) {
return; log.warn("[级联消息发送]发送MediaStatus发现通道{}不存在", sendRtpInfo.getChannelId());
} return;
try { }
sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpInfo, channel); try {
} catch (SipException | InvalidArgumentException | ParseException e) { sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpInfo, channel);
log.error("[命令发送失败] 国标级联 录像播放完毕: {}", e.getMessage()); } catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 国标级联 录像播放完毕: {}", e.getMessage());
}
} }
} }
}else { }else {

View File

@ -807,7 +807,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
@Override @Override
public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay) { public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay) {
System.out.println(callId);
StreamInfo streamInfoResult = new StreamInfo(); StreamInfo streamInfoResult = new StreamInfo();
streamInfoResult.setStream(stream); streamInfoResult.setStream(stream);
streamInfoResult.setApp(app); streamInfoResult.setApp(app);

View File

@ -173,9 +173,9 @@ public class MediaServiceImpl implements IMediaService {
if (ssrcTransaction.getType() == InviteSessionType.DOWNLOAD) { if (ssrcTransaction.getType() == InviteSessionType.DOWNLOAD) {
// 获取录像的总时长,然后设置为这个视频的时长 // 获取录像的总时长,然后设置为这个视频的时长
InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, channelId, stream); InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, channelId, stream);
if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) { if (inviteInfoForDownload != null) {
String startTime = inviteInfoForDownload.getStreamInfo().getStartTime(); String startTime = inviteInfoForDownload.getStartTime();
String endTime = inviteInfoForDownload.getStreamInfo().getEndTime(); String endTime = inviteInfoForDownload.getEndTime();
long difference = DateUtil.getDifference(startTime, endTime) / 1000; long difference = DateUtil.getDifference(startTime, endTime) / 1000;
result.setMp4_max_second((int) difference); result.setMp4_max_second((int) difference);
result.setEnable_mp4(true); result.setEnable_mp4(true);

View File

@ -33,6 +33,9 @@ public final class JsonUtil {
} }
public static <T> T redisHashJsonToObject(RedisTemplate<Object, Object> redisTemplate, String key, String objKey, Class<T> clazz) { public static <T> T redisHashJsonToObject(RedisTemplate<Object, Object> redisTemplate, String key, String objKey, Class<T> clazz) {
// if (key == null || objKey == null) {
// return null;
// }
Object jsonObject = redisTemplate.opsForHash().get(key, objKey); Object jsonObject = redisTemplate.opsForHash().get(key, objKey);
if (Objects.isNull(jsonObject)) { if (Objects.isNull(jsonObject)) {
return null; return null;

View File

@ -65,11 +65,7 @@
倍速 <i class="el-icon-arrow-down el-icon--right"></i> 倍速 <i class="el-icon-arrow-down el-icon--right"></i>
</el-button> </el-button>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item command="0.25">0.25倍速</el-dropdown-item> <el-dropdown-item v-for="(item,index) in downloadSpeedArray" :key="index" :command="item">{{item}}</el-dropdown-item>
<el-dropdown-item command="0.5">0.5倍速</el-dropdown-item>
<el-dropdown-item command="1.0">1倍速</el-dropdown-item>
<el-dropdown-item command="2.0">2倍速</el-dropdown-item>
<el-dropdown-item command="4.0">4倍速</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<el-button size="mini" class="iconfont icon-xiazai1" title="下载选定录像" @click="downloadRecord()"></el-button> <el-button size="mini" class="iconfont icon-xiazai1" title="下载选定录像" @click="downloadRecord()"></el-button>
@ -115,6 +111,7 @@
return { return {
deviceId: this.$route.params.deviceId, deviceId: this.$route.params.deviceId,
channelId: this.$route.params.channelId, channelId: this.$route.params.channelId,
downloadSpeedArray: [0.25, 0.5, 1, 2, 4],
recordsLoading: false, recordsLoading: false,
streamId: "", streamId: "",
hasAudio: false, hasAudio: false,
@ -184,6 +181,7 @@
this.playerBoxStyle["height"] = this.winHeight + "px"; this.playerBoxStyle["height"] = this.winHeight + "px";
this.chooseDate = moment().format('YYYY-MM-DD') this.chooseDate = moment().format('YYYY-MM-DD')
this.dateChange(); this.dateChange();
this.getDownloadSpeedArray()
window.addEventListener('beforeunload', this.stopPlayRecord) window.addEventListener('beforeunload', this.stopPlayRecord)
}, },
destroyed() { destroyed() {
@ -274,6 +272,27 @@
}); });
} }
}, },
getDownloadSpeedArray(){
this.$axios({
method: 'get',
url: '/api/device/query/channel/one',
params: {
deviceId: this.deviceId,
channelDeviceId: this.channelId,
}
}).then((res)=> {
if (res.data.code === 0 && res.data.data.downloadSpeed) {
let speedArray = res.data.data.downloadSpeed.split('/');
speedArray.forEach(item => {
if (parseInt(item) > 4) {
this.downloadSpeedArray.push(parseInt(item));
console.log(this.downloadSpeedArray);
}
})
}
})
},
gbPlay(){ gbPlay(){
console.log('前端控制:播放'); console.log('前端控制:播放');
this.$axios({ this.$axios({
@ -317,7 +336,7 @@
this.$axios({ this.$axios({
method: 'get', method: 'get',
url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' + url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
row.endTime + '&downloadSpeed=4' row.endTime + '&downloadSpeed='+ this.downloadSpeedArray[this.downloadSpeedArray.length - 1]
}).then( (res)=> { }).then( (res)=> {
if (res.data.code === 0) { if (res.data.code === 0) {
let streamInfo = res.data.data; let streamInfo = res.data.data;

View File

@ -53,6 +53,7 @@ export default {
this.showDialog = true; this.showDialog = true;
this.getProgressRun = true; this.getProgressRun = true;
this.percentage = 0.0; this.percentage = 0.0;
this.downloadFile = null;
this.getProgressTimer() this.getProgressTimer()
}, },
getProgressTimer: function (){ getProgressTimer: function (){