liusuyi
2026-02-10 4ac9edd4c0030d2daafd712cd14ef9080b2f1e43
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
package com.ard.device.channel.service.impl;
 
import com.ard.common.constant.CacheConstants;
import com.ard.common.constant.UserConstants;
import com.ard.common.core.redis.RedisCache;
import com.ard.common.utils.uuid.IdUtils;
import com.ard.device.camera.domain.ArdCamera;
import com.ard.device.camera.service.IArdCameraService;
import com.ard.device.channel.domain.ArdChannel;
import com.ard.device.channel.mapper.ArdChannelMapper;
import com.ard.device.channel.service.IArdChannelService;
import com.ard.vtdu.config.MediamtxConfig;
import com.ard.vtdu.domain.ArdVtdu;
import com.ard.vtdu.service.IArdVtduService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
 
/**
 * 通道管理Service业务层处理
 *
 * @author ard
 * @date 2023-08-19
 */
@Slf4j
@Service
public class ArdChannelServiceImpl implements IArdChannelService {
 
    @Resource
    private ArdChannelMapper ardChannelMapper;
    @Resource
    private RedisCache redisCache;
    @Resource
    private IArdVtduService ardVtduService;
    @Resource
    private MediamtxConfig mediamtxConfig;
 
    /**
     * 查询通道管理
     *
     * @param id 通道管理主键
     * @return 通道管理
     */
    @Override
    public ArdChannel selectArdChannelById(String id) {
        return ardChannelMapper.selectArdChannelById(id);
    }
 
    /**
     * 查询通道管理列表
     *
     * @param ardChannel 通道管理
     * @return 通道管理
     */
    @Override
    public List<ArdChannel> selectArdChannelList(ArdChannel ardChannel) {
        return ardChannelMapper.selectArdChannelList(ardChannel);
    }
 
    /**
     * 新增通道管理
     *
     * @param ardChannel 通道管理
     * @return 结果
     */
    @Override
    public int insertArdChannel(ArdChannel ardChannel) {
        ardChannel.setId(IdUtils.simpleUUID());
        return ardChannelMapper.insertArdChannel(ardChannel);
    }
 
    /**
     * 修改通道管理
     *
     * @param ardChannel 通道管理
     * @return 结果
     */
    @Override
    public int updateArdChannel(ArdChannel ardChannel) {
        return ardChannelMapper.updateArdChannel(ardChannel);
    }
 
    /**
     * 批量删除通道管理
     *
     * @param ids 需要删除的通道管理主键
     * @return 结果
     */
    @Override
    public int deleteArdChannelByIds(String[] ids) {
        return ardChannelMapper.deleteArdChannelByIds(ids);
    }
 
    /**
     * 删除通道管理信息
     *
     * @param id 通道管理主键
     * @return 结果
     */
    @Override
    public int deleteArdChannelById(String id) {
        return ardChannelMapper.deleteArdChannelById(id);
    }
 
    /**
     * 清空通道
     *
     * @author 刘苏义
     * @date 2024/8/10 11:18
     */
    @Override
    public int clearArdChannel() {
        return ardChannelMapper.clearArdChannel();
    }
 
    /**
     * 删除通道管理信息
     *
     * @param deviceId 所属设备ID
     * @return 结果
     */
    @Override
    public int deleteArdChannelByDeviceId(String deviceId) {
        return ardChannelMapper.deleteArdChannelByDeviceId(deviceId);
    }
 
    /**
     * 校验相机通道是否唯一
     *
     * @param channel 通道信息
     * @return 结果
     */
    @Override
    public boolean checkArdChannelUnique(ArdChannel channel) {
        ArdChannel info = ardChannelMapper.checkArdChannelUnique(channel);
        if (info != null && !Objects.equals(info.getId(), channel.getId())) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }
 
 
    /**
     * 同步通道流媒体
     */
    @Scheduled(cron = "0/10 * * * * ?")
    public void syncVtdu() {
        log.debug("定时同步Vtdu任务");
        try {
            ArdChannel ardChannel = new ArdChannel();
            ardChannel.setUseDataScope(false);
            List<ArdChannel> channels = this.selectArdChannelList(ardChannel);
//            if (channels == null || channels.isEmpty()) {
//                // log.warn("没有获取到通道数据,退出同步");
//                return;
//            }
            List<ArdVtdu> ardVtduList = ardVtduService.selectArdVtduList(new ArdVtdu());
 
            Set<String> validNames = channels.stream()
                    .map(channel -> channel.getDeviceId() + "_" + channel.getChanNo())
                    .collect(Collectors.toSet());
            // 2. 合并操作:增、删、改
            channels.forEach(channel -> {
                String name = channel.getDeviceId() + "_" + channel.getChanNo();  // 生成媒体表的 name
                String cameraId = channel.getDeviceId();  // cameraId 就是通道的 deviceId
                String rtspSourceUrl = getRtspSource(channel);
                String rtspUrl =
                        "rtsp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtspPort() + "/" + name;  //
                // 拼接 webrtcUrl
                String rtmpUrl =
                        "rtmp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtmpPort() + "/" + name;
                // 拼接
                // webrtcUrl
                String protocol = mediamtxConfig.isHttps() ? "https://" : "http://";
                String webrtcUrl =
                        protocol + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getWebrtcPort() + "/" + name;
                // 拼接 webrtcUrl
 
                // 先检查媒体表是否已有此记录
                Optional<ArdVtdu> existingMedia = ardVtduList.stream()
                        .filter(media -> media.getName().equals(name))  // 查找是否已有对应的媒体记录
                        .findFirst();
 
                if (existingMedia.isPresent()) {
                    // 如果已有,更新现有记录
                    ArdVtdu vtdu = existingMedia.get();
                    // 更新前后内容相同则不进行更新
                    if (!vtdu.getRtspSource().equals(rtspSourceUrl) ||
                            !vtdu.getWebrtcUrl().equals(webrtcUrl) ||
                            !vtdu.getRtspUrl().equals(rtspUrl) ||
                            !vtdu.getRtmpUrl().equals(rtmpUrl)) {
                        vtdu.setRtspUrl(rtspUrl);
                        vtdu.setRtmpUrl(rtmpUrl);
                        vtdu.setWebrtcUrl(webrtcUrl);
                        ardVtduService.updateArdVtdu(vtdu);
                        log.info("更新媒体记录:{}", vtdu.getName());
                    }
                } else {
                    // 如果没有,插入新的记录
                    ArdVtdu vtdu = new ArdVtdu();
                    vtdu.setName(name);
                    vtdu.setRtspSource(rtspSourceUrl);
                    vtdu.setCameraId(cameraId);
                    vtdu.setRtspUrl(rtspUrl);
                    vtdu.setRtmpUrl(rtmpUrl);
                    vtdu.setWebrtcUrl(webrtcUrl);
                    ardVtduService.insertArdVtdu(vtdu);  // 保存到媒体表
                    log.info("插入新媒体记录:{}", vtdu.getName());
                }
            });
            // 3. 删除操作:删除媒体表中多余的记录
            List<String> invalidNames = ardVtduList.stream()
                    .filter(vtdu -> !validNames.contains(vtdu.getName()))
                    .filter(vtdu -> !"publisher".equalsIgnoreCase(vtdu.getRtspSource())) // 不是机场流
                    .map(ArdVtdu::getName)
                    .collect(Collectors.toList());
 
            if (!invalidNames.isEmpty()) {
                // 将 List<String> 转换为 String[]
                String[] namesArray = invalidNames.toArray(new String[0]);
                ardVtduService.deleteArdVtduByNames(namesArray); // 传递数组
                log.info("删除不再存在的媒体记录:{}", Arrays.toString(namesArray));
            }
        } catch (Exception e) {
            log.error("定时同步Vtdu任务异常:{}", e.getMessage());
        }
 
    }
 
    // 根据通道信息获取rtsp源地址
    private String getRtspSource(ArdChannel channel) {
        String rtspSource = "";
        ArdCamera ardCamera = redisCache.getCacheMapValue(CacheConstants.CAMERA_LIST, channel.getDeviceId());
        if (ardCamera != null) {
            switch (ardCamera.getFactory()) {
                case "1":
                    rtspSource =
                            "rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main/av_stream";
                    break;
                case "2":
                    rtspSource =
                            "rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/cam/realmonitor?channel=" + channel.getChanNo() + "&subtype=0";
                    break;
            }
        }
        return rtspSource;
    }
}