[1078] 支持通用通道的点播

This commit is contained in:
lin
2025-07-29 14:46:16 +08:00
parent 4c97022c78
commit 0ed395ff2e
8 changed files with 134 additions and 10 deletions

View File

@@ -307,7 +307,6 @@ public class CommonChannelController {
wvpResult.setCode(code);
wvpResult.setMsg(msg);
}
result.setResult(wvpResult);
}else {
result.setResult(WVPResult.fail(code, msg));

View File

@@ -21,12 +21,16 @@ public interface IGbChannelPlayService {
void playProxy(CommonGBChannel channel, Boolean record, ErrorCallback<StreamInfo> callback);
void playJt1078(CommonGBChannel channel, Boolean record, ErrorCallback<StreamInfo> callback);
void stopPlayProxy(CommonGBChannel channel);
void playPush(CommonGBChannel channel, String platformDeviceId, String platformName, ErrorCallback<StreamInfo> callback);
void stopPlayPush(CommonGBChannel channel);
void stopPlayJt1078(CommonGBChannel channel);
void pauseRtp(String streamId);
void resumeRtp(String streamId);

View File

@@ -12,6 +12,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.PlayException;
import com.genersoft.iot.vmp.gb28181.service.IGbChannelPlayService;
import com.genersoft.iot.vmp.gb28181.service.IPlayService;
import com.genersoft.iot.vmp.jt1078.service.Ijt1078PlayService;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyPlayService;
import com.genersoft.iot.vmp.streamPush.service.IStreamPushPlayService;
@@ -34,6 +35,9 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
@Autowired
private IStreamProxyPlayService streamProxyPlayService;
@Autowired
private Ijt1078PlayService jt1078PlayService;
@Autowired
private IStreamPushPlayService streamPushPlayService;
@@ -54,6 +58,9 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
if (channel.getDataType() == ChannelDataType.GB28181.value) {
// 国标通道
playbackGbDeviceChannel(channel, inviteInfo.getStartTime(), inviteInfo.getStopTime(), callback);
} else if (channel.getDataType() == ChannelDataType.JT_1078.value) {
// 部标通道
playbackJtDeviceChannel(channel, inviteInfo.getStartTime(), inviteInfo.getStopTime(), callback);
} else if (channel.getDataType() == ChannelDataType.STREAM_PROXY.value) {
// 拉流代理
log.warn("[回放通用通道] 不支持回放拉流代理的录像: {}({})", channel.getGbName(), channel.getGbDeviceId());
@@ -78,6 +85,10 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
// 国标通道
downloadGbDeviceChannel(channel, inviteInfo.getStartTime(), inviteInfo.getStopTime(), downloadSpeed, callback);
} else if (channel.getDataType() == ChannelDataType.JT_1078.value) {
// 部标录像下载
log.warn("[下载通用通道录像] 不支持下载部标的录像: {}({})", channel.getGbName(), channel.getGbDeviceId());
throw new PlayException(Response.FORBIDDEN, "forbidden");
} else if (channel.getDataType() == ChannelDataType.STREAM_PROXY.value) {
// 拉流代理
log.warn("[下载通用通道录像] 不支持下载拉流代理的录像: {}({})", channel.getGbName(), channel.getGbDeviceId());
@@ -109,6 +120,9 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
} else if (channel.getDataType() == ChannelDataType.STREAM_PUSH.value) {
// 推流
stopPlayPush(channel);
} else if (channel.getDataType() == ChannelDataType.JT_1078.value) {
// 推流
stopPlayJt1078(channel);
} else {
// 通道数据异常
log.error("[点播通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId());
@@ -116,6 +130,8 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
}
}
@Override
public void play(CommonGBChannel channel, Platform platform, Boolean record, ErrorCallback<StreamInfo> callback) {
log.info("[通用通道] 播放, 类型: {} 编号:{}", channel.getDataType(), channel.getGbDeviceId());
@@ -133,6 +149,9 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
// 推流
playPush(channel, null, null, callback);
}
} else if (channel.getDataType() == ChannelDataType.JT_1078.value) {
// 部标设备
playJt1078(channel, record, callback);
} else {
// 通道数据异常
log.error("[点播通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId());
@@ -172,6 +191,18 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
try {
streamProxyPlayService.start(channel.getDataDeviceId(), record, callback);
}catch (Exception e) {
log.info("[通用通道] 拉流代理点播异常 {}", e.getMessage());
callback.run(Response.BUSY_HERE, "busy here", null);
}
}
@Override
public void playJt1078(CommonGBChannel channel, Boolean record, ErrorCallback<StreamInfo> callback){
// 部标设备通道
try {
jt1078PlayService.start(channel.getDataDeviceId(), record, callback);
}catch (Exception e) {
log.info("[通用通道] 部标设备点播异常 {}", e.getMessage());
callback.run(Response.BUSY_HERE, "busy here", null);
}
}
@@ -209,6 +240,16 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
}
}
@Override
public void stopPlayJt1078(CommonGBChannel channel) {
// 推流
try {
jt1078PlayService.stop(channel.getDataDeviceId());
}catch (Exception e) {
log.error("[停止点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e);
}
}
private void playbackGbDeviceChannel(CommonGBChannel channel, Long startTime, Long stopTime, ErrorCallback<StreamInfo> callback){
try {
deviceChannelPlayService.playBack(channel, startTime, stopTime, callback);
@@ -219,6 +260,16 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
}
}
private void playbackJtDeviceChannel(CommonGBChannel channel, Long startTime, Long stopTime, ErrorCallback<StreamInfo> callback){
try {
jt1078PlayService.playBack(channel.getDataDeviceId(), startTime, stopTime, callback);
} catch (PlayException e) {
callback.run(e.getCode(), e.getMsg(), null);
} catch (Exception e) {
callback.run(Response.BUSY_HERE, "busy here", null);
}
}
@Override
public void pauseRtp(String streamId) {
try {
@@ -243,6 +294,4 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
callback.run(Response.BUSY_HERE, "busy here", null);
}
}
}

View File

@@ -26,7 +26,7 @@ public class J0200 extends Re {
@Override
protected Rs decode0(ByteBuf buf, Header header, Session session) {
positionInfo = JTPositionBaseInfo.decode(buf);
log.info("[JT-位置汇报]: phoneNumber={} {}", header.getPhoneNumber(), positionInfo.toSimpleString());
log.debug("[JT-位置汇报]: phoneNumber={} {}", header.getPhoneNumber(), positionInfo.toSimpleString());
// 读取附加信息
// JTPositionAdditionalInfo positionAdditionalInfo = new JTPositionAdditionalInfo();
// Map<Integer, byte[]> additionalMsg = new HashMap<>();

View File

@@ -2,8 +2,10 @@ package com.genersoft.iot.vmp.jt1078.service;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.jt1078.bean.*;
import com.genersoft.iot.vmp.jt1078.proc.request.J1205;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import java.util.List;
@@ -33,4 +35,9 @@ public interface Ijt1078PlayService {
void playbackControl(String phoneNumber, Integer channelId, Integer command, Integer playbackSpeed, String time);
void start(Integer channelId, Boolean record, ErrorCallback<StreamInfo> callback);
void stop(Integer channelId);
void playBack(Integer channelId, Long startTime, Long stopTime, ErrorCallback<StreamInfo> callback);
}

View File

@@ -7,9 +7,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.conf.ftpServer.FtpSetting;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.jt1078.bean.*;
import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template;
import com.genersoft.iot.vmp.jt1078.event.FtpUploadEvent;
@@ -29,6 +27,7 @@ import com.genersoft.iot.vmp.media.event.media.MediaNotFoundEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaSendRtpStoppedEvent;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.ISendRtpServerService;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -45,6 +44,7 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.sip.message.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -191,7 +191,12 @@ public class jt1078PlayServiceImpl implements Ijt1078PlayService {
if (channel == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "通道不存在");
}
play(device, channel, type, callback);
}
private void play(JTDevice device, JTChannel channel, int type, CommonCallback<WVPResult<StreamInfo>> callback) {
String phoneNumber = device.getPhoneNumber();
int channelId = channel.getId();
String app = "1078";
String stream = phoneNumber + "_" + channelId;
// 检查流是否已经存在,存在则返回
@@ -375,6 +380,8 @@ public class jt1078PlayServiceImpl implements Ijt1078PlayService {
return JRecordItemList;
}
@Override
public void playback(String phoneNumber, Integer channelId, String startTime, String endTime, Integer type,
Integer rate, Integer playbackType, Integer playbackSpeed, CommonCallback<WVPResult<StreamInfo>> callback) {
@@ -387,7 +394,27 @@ public class jt1078PlayServiceImpl implements Ijt1078PlayService {
if (channel == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "通道不存在");
}
playback(device, channel, startTime, endTime, type, rate, playbackType, playbackSpeed, callback);
}
/**
* 回放
* @param device 设备
* @param channel 通道
* @param startTime 开始时间
* @param endTime 结束时间
* @param type 音视频资源类型0.音视频 1.音频 2.视频 3.视频或音视频
* @param rate 码流类型0.所有码流 1.主码流 2.子码流(如果此通道只传输音频,此字段置0)
* @param playbackType 回放方式0.正常回放 1.快进回放 2.关键帧快退回放 3.关键帧播放 4.单帧上传
* @param playbackSpeed 快进或快退倍数0.无效 1.1倍 2.2倍 3.4倍 4.8倍 5.16倍 (回放控制为1和2时,此字段内容有效,否则置0)
* @param callback 结束回调
*/
private void playback(JTDevice device, JTChannel channel, String startTime, String endTime, Integer type,
Integer rate, Integer playbackType, Integer playbackSpeed, CommonCallback<WVPResult<StreamInfo>> callback) {
String phoneNumber = device.getPhoneNumber();
Integer channelId = channel.getChannelId();
log.info("[JT-回放] 回放,设备:{} 通道: {} 开始时间: {} 结束时间: {} 音视频类型: {} 码流类型: {} " +
"回放方式: {} 快进或快退倍数: {}", phoneNumber, channelId, startTime, endTime, type, rate, playbackType, playbackSpeed);
// 检查流是否已经存在,存在则返回
@@ -646,4 +673,40 @@ public class jt1078PlayServiceImpl implements Ijt1078PlayService {
}
}
}
@Override
public void start(Integer channelId, Boolean record, ErrorCallback<StreamInfo> callback) {
JTChannel channel = jt1078Service.getChannelByDbId(channelId);
Assert.notNull(channel, "通道不存在");
JTDevice device = jt1078Service.getDeviceById(channel.getDataDeviceId());
Assert.notNull(device, "设备不存在");
jt1078Template.checkTerminalStatus(device.getPhoneNumber());
play(device, channel, 0,
result -> callback.run(result.getCode(), result.getMsg(), result.getData()));
}
@Override
public void stop(Integer channelId) {
JTChannel channel = jt1078Service.getChannelByDbId(channelId);
Assert.notNull(channel, "通道不存在");
JTDevice device = jt1078Service.getDeviceById(channel.getDataDeviceId());
Assert.notNull(device, "设备不存在");
stopPlay(device.getPhoneNumber(), channel.getChannelId());
}
@Override
public void playBack(Integer channelId, Long startTime, Long stopTime, ErrorCallback<StreamInfo> callback) {
if (startTime == null || stopTime == null) {
throw new PlayException(Response.BAD_REQUEST, "bad request");
}
JTChannel channel = jt1078Service.getChannelByDbId(channelId);
Assert.notNull(channel, "通道不存在");
JTDevice device = jt1078Service.getDeviceById(channel.getDataDeviceId());
Assert.notNull(device, "设备不存在");
jt1078Template.checkTerminalStatus(device.getPhoneNumber());
String startTimeStr = DateUtil.timestampTo_yyyy_MM_dd_HH_mm_ss(startTime);
String stopTimeStr = DateUtil.timestampTo_yyyy_MM_dd_HH_mm_ss(stopTime);
playback(device, channel, startTimeStr, stopTimeStr, 0, 1, 0, 0,
result -> callback.run(result.getCode(), result.getMsg(), result.getData()));
}
}

View File

@@ -40,9 +40,10 @@ Vue.use(VueClipboard)
Vue.config.productionTip = false
Vue.prototype.$channelTypeList = {
1: { id: 1, name: '国标设备', style: { color: '#409eff', borderColor: '#b3d8ff' }},
2: { id: 2, name: '推流设备', style: { color: '#67c23a', borderColor: '#c2e7b0' }},
3: { id: 3, name: '拉流代理', style: { color: '#e6a23c', borderColor: '#f5dab1' }}
1: { id: 1, name: '国标设备', style: { color: '#409eff', borderColor: '#b3d8ff' } },
2: { id: 2, name: '推流设备', style: { color: '#67c23a', borderColor: '#c2e7b0' } },
3: { id: 3, name: '拉流代理', style: { color: '#e6a23c', borderColor: '#f5dab1' } },
200: { id: 200, name: '部标设备', style: { color: '#36c6fa', borderColor: '#90e3fb' } }
}
new Vue({

View File

@@ -218,6 +218,7 @@ export default {
this.getChannelList()
},
getChannelList: function() {
this.channelList = []
this.$store.dispatch('commonChanel/getList', {
page: this.currentPage,
count: this.count,