liusuyi
2025-02-21 8c5435101924d68588448a4c981417df4b4dab1d
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
package com.ard.tianyu.service.impl;
 
import com.ard.tianyu.model.receiver.BoxStreamUrlReceiver;
import com.ard.tianyu.service.ITyService;
import com.ard.tianyu.websocket.utils.WebSocketUtil;
import com.ard.vtdu.domain.ArdVtdu;
import com.ard.vtdu.service.IMediaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
 
@Service
@Slf4j
public class UavStreamManager {
 
    private static final String STREAM_LOG_PREFIX = "【stream】";
    private static final String STREAM_NOT_FOUND_MSG = STREAM_LOG_PREFIX + "Box stream not found: ";
    private static final String STREAM_CLOSED_MSG = STREAM_LOG_PREFIX + "Stream closed due to no active users: ";
    private static final String STREAM_IN_USE_MSG = STREAM_LOG_PREFIX + "Stream is still in use, remaining users: ";
 
    @Resource
    private ITyService tyService;
    @Resource
    private IMediaService mediaService;
 
    // 盒子拉流地址缓存 key:boxSn value: webrtcUrl
    public static final ConcurrentMap<String, String> BOX_STREAM_PULL_MAP = new ConcurrentHashMap<>();
    // 盒子拉流用户集合缓存 key:boxSn value:Set<UserId>
    public static final ConcurrentMap<String, Set<String>> STREAM_USER_MAP = new ConcurrentHashMap<>();
 
    /**
     * 开始拉流
     *
     * @param boxSn  盒子序列号
     * @param userId 用户ID
     */
    public void startPullStream(String boxSn, String userId) {
        STREAM_USER_MAP.computeIfAbsent(boxSn, k -> ConcurrentHashMap.newKeySet()).add(userId);
 
        String webrtcUrl = BOX_STREAM_PULL_MAP.get(boxSn);
        if (webrtcUrl != null) {
            log.info("{}Already pulled stream: {}", STREAM_LOG_PREFIX, webrtcUrl);
            sendMessageToUser(userId, webrtcUrl);
            return;
        }
 
        try {
            BoxStreamUrlReceiver boxLiveUrl = tyService.getBoxLiveUrl(boxSn);
            if (boxLiveUrl == null) {
                log.warn("{}{}{}", STREAM_LOG_PREFIX, STREAM_NOT_FOUND_MSG, boxSn);
                sendMessageToUser(userId, STREAM_NOT_FOUND_MSG + boxSn);
                return;
            }
 
            ArdVtdu vtdu = ArdVtdu.builder()
                    .name(boxSn)
                    .mode(0)
                    .isCode(0)
                    .rtspSource(boxLiveUrl.getRtsp())
                    .build();
            ArdVtdu ardVtdu = mediaService.addPath(vtdu);
            webrtcUrl = ardVtdu.getWebrtcUrl();
            BOX_STREAM_PULL_MAP.put(boxSn, webrtcUrl);
 
            log.info("{}First pulled stream: {}", STREAM_LOG_PREFIX, webrtcUrl);
            sendMessageToUser(userId, webrtcUrl);
        } catch (Exception e) {
            log.error("{}Failed to start pull stream for box: {}", STREAM_LOG_PREFIX, boxSn, e);
            sendMessageToUser(userId, "Failed to start pull stream due to an internal error.");
        }
    }
 
    /**
     * 停止拉流
     *
     * @param boxSn  盒子序列号
     * @param userId 用户ID
     */
    public void stopPullStream(String boxSn, String userId) {
        synchronized (this) {
            Set<String> users = STREAM_USER_MAP.get(boxSn);
            if (users == null) {
                log.warn("{}No active users for box: {}", STREAM_LOG_PREFIX, boxSn);
                sendMessageToUser(userId, "No active users for box: " + boxSn);
                return;
            }
 
            users.remove(userId);
            if (users.isEmpty()) {
                mediaService.removePath(boxSn);
                BOX_STREAM_PULL_MAP.remove(boxSn);
                STREAM_USER_MAP.remove(boxSn);
                log.info("{}{}{}", STREAM_LOG_PREFIX, STREAM_CLOSED_MSG, boxSn);
                sendMessageToUser(userId, STREAM_CLOSED_MSG + boxSn);
            } else {
                log.info("{}{}{}", STREAM_LOG_PREFIX, STREAM_IN_USE_MSG, users);
                sendMessageToUser(userId, STREAM_IN_USE_MSG + users);
            }
        }
    }
    /**
     * 停止拉流
     *
     * @param userId 用户ID
     */
    public void stopAllPullStreamByUser(String userId) {
        synchronized (this) {
            STREAM_USER_MAP.forEach((boxSn, users) -> stopPullStream(boxSn, userId));
        }
    }
 
    /**
     * 发送消息给指定用户
     *
     * @param userId  用户ID
     * @param message 消息内容
     */
    private void sendMessageToUser(String userId, String message) {
        try {
            WebSocketUtil.sendMessage(userId, message);
        } catch (Exception e) {
            log.error("{}Failed to send message to user: {}", STREAM_LOG_PREFIX, userId, e);
        }
    }
}