|  | @@ -10,11 +10,12 @@ import com.genersoft.iot.vmp.gb28181.dao.DeviceMapper;
 | 
	
		
			
				|  |  |  import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService;
 | 
	
		
			
				|  |  |  import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
 | 
	
		
			
				|  |  |  import com.genersoft.iot.vmp.service.bean.ErrorCallback;
 | 
	
		
			
				|  |  | -import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 | 
	
		
			
				|  |  |  import lombok.extern.slf4j.Slf4j;
 | 
	
		
			
				|  |  |  import org.springframework.beans.factory.annotation.Autowired;
 | 
	
		
			
				|  |  |  import org.springframework.context.event.EventListener;
 | 
	
		
			
				|  |  | +import org.springframework.data.redis.core.Cursor;
 | 
	
		
			
				|  |  |  import org.springframework.data.redis.core.RedisTemplate;
 | 
	
		
			
				|  |  | +import org.springframework.data.redis.core.ScanOptions;
 | 
	
		
			
				|  |  |  import org.springframework.scheduling.annotation.Async;
 | 
	
		
			
				|  |  |  import org.springframework.stereotype.Service;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -23,7 +24,6 @@ import java.util.List;
 | 
	
		
			
				|  |  |  import java.util.Map;
 | 
	
		
			
				|  |  |  import java.util.concurrent.ConcurrentHashMap;
 | 
	
		
			
				|  |  |  import java.util.concurrent.CopyOnWriteArrayList;
 | 
	
		
			
				|  |  | -import java.util.concurrent.TimeUnit;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  @Slf4j
 | 
	
		
			
				|  |  |  @Service
 | 
	
	
		
			
				|  | @@ -61,11 +61,12 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public void updateInviteInfo(InviteInfo inviteInfo) {
 | 
	
		
			
				|  |  |          if (InviteSessionStatus.ready == inviteInfo.getStatus()) {
 | 
	
		
			
				|  |  |              updateInviteInfo(inviteInfo, Long.valueOf(userSetting.getPlayTimeout()) * 2);
 | 
	
		
			
				|  |  | -        }else {
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  |              updateInviteInfo(inviteInfo, null);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -114,16 +115,15 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |              inviteInfoForUpdate = inviteInfoInRedis;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        String key = VideoManagerConstants.INVITE_PREFIX +
 | 
	
		
			
				|  |  | -                ":" + inviteInfoForUpdate.getType() +
 | 
	
		
			
				|  |  | +        String key = VideoManagerConstants.INVITE_PREFIX;
 | 
	
		
			
				|  |  | +        String objectKey = inviteInfoForUpdate.getType() +
 | 
	
		
			
				|  |  |                  ":" + inviteInfoForUpdate.getChannelId() +
 | 
	
		
			
				|  |  | -                ":" + inviteInfoForUpdate.getStream()+
 | 
	
		
			
				|  |  | +                ":" + inviteInfoForUpdate.getStream() +
 | 
	
		
			
				|  |  |                  ":" + inviteInfoForUpdate.getSsrcInfo().getSsrc();
 | 
	
		
			
				|  |  |          if (time != null && time > 0) {
 | 
	
		
			
				|  |  | -            redisTemplate.opsForValue().set(key, inviteInfoForUpdate, time, TimeUnit.SECONDS);
 | 
	
		
			
				|  |  | -        }else {
 | 
	
		
			
				|  |  | -            redisTemplate.opsForValue().set(key, inviteInfoForUpdate);
 | 
	
		
			
				|  |  | +            inviteInfoForUpdate.setExpirationTime(time);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        redisTemplate.opsForHash().put(key, objectKey, inviteInfoForUpdate);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
	
		
			
				|  | @@ -134,8 +134,8 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |              return null;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          removeInviteInfo(inviteInfoInDb);
 | 
	
		
			
				|  |  | -        String key = VideoManagerConstants.INVITE_PREFIX +
 | 
	
		
			
				|  |  | -                ":" + inviteInfo.getType() +
 | 
	
		
			
				|  |  | +        String key = VideoManagerConstants.INVITE_PREFIX;
 | 
	
		
			
				|  |  | +        String objectKey = inviteInfo.getType() +
 | 
	
		
			
				|  |  |                  ":" + inviteInfo.getChannelId() +
 | 
	
		
			
				|  |  |                  ":" + stream +
 | 
	
		
			
				|  |  |                  ":" + inviteInfo.getSsrcInfo().getSsrc();
 | 
	
	
		
			
				|  | @@ -144,46 +144,43 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |              inviteInfoInDb.getSsrcInfo().setStream(stream);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          if (InviteSessionStatus.ready == inviteInfo.getStatus()) {
 | 
	
		
			
				|  |  | -            redisTemplate.opsForValue().set(key, inviteInfoInDb, userSetting.getPlayTimeout() * 2, TimeUnit.SECONDS);
 | 
	
		
			
				|  |  | -        }else {
 | 
	
		
			
				|  |  | -            redisTemplate.opsForValue().set(key, inviteInfoInDb);
 | 
	
		
			
				|  |  | +            inviteInfoInDb.setExpirationTime((long) (userSetting.getPlayTimeout() * 2));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +        redisTemplate.opsForHash().put(key, objectKey, inviteInfoInDb);
 | 
	
		
			
				|  |  |          return inviteInfoInDb;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public InviteInfo getInviteInfo(InviteSessionType type, Integer channelId, String stream) {
 | 
	
		
			
				|  |  | -        String key = VideoManagerConstants.INVITE_PREFIX +
 | 
	
		
			
				|  |  | -                ":" + (type != null ? type : "*") +
 | 
	
		
			
				|  |  | +        String key = VideoManagerConstants.INVITE_PREFIX;
 | 
	
		
			
				|  |  | +        String keyPattern = (type != null ? type : "*") +
 | 
	
		
			
				|  |  |                  ":" + (channelId != null ? channelId : "*") +
 | 
	
		
			
				|  |  |                  ":" + (stream != null ? stream : "*")
 | 
	
		
			
				|  |  |                  + ":*";
 | 
	
		
			
				|  |  | -        List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
 | 
	
		
			
				|  |  | -        if (scanResult.isEmpty()) {
 | 
	
		
			
				|  |  | -            return null;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        if (scanResult.size() != 1) {
 | 
	
		
			
				|  |  | -            log.warn("[获取InviteInfo] 发现 key: {}存在多条", key);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +        ScanOptions options = ScanOptions.scanOptions().match(keyPattern).count(20).build();
 | 
	
		
			
				|  |  | +        try (Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, options)) {
 | 
	
		
			
				|  |  | +            if (cursor.hasNext()) {
 | 
	
		
			
				|  |  | +                InviteInfo inviteInfo = (InviteInfo) cursor.next().getValue();
 | 
	
		
			
				|  |  | +                cursor.close();
 | 
	
		
			
				|  |  | +                return inviteInfo;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        } catch (Exception e) {
 | 
	
		
			
				|  |  | +            log.error("[Redis-InviteInfo] 查询异常: ", e);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return null;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | -    public List<InviteInfo> getAllInviteInfo(InviteSessionType type, Integer channelId, String stream) {
 | 
	
		
			
				|  |  | -        String key = VideoManagerConstants.INVITE_PREFIX +
 | 
	
		
			
				|  |  | -                ":" + (type != null ? type : "*") +
 | 
	
		
			
				|  |  | -                ":" + (channelId != null ? channelId : "*") +
 | 
	
		
			
				|  |  | -                ":" + (stream != null ? stream : "*")
 | 
	
		
			
				|  |  | -                + ":*";
 | 
	
		
			
				|  |  | -        List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
 | 
	
		
			
				|  |  | -        if (scanResult.isEmpty()) {
 | 
	
		
			
				|  |  | -            return new ArrayList<>();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +    public List<InviteInfo> getAllInviteInfo() {
 | 
	
		
			
				|  |  |          List<InviteInfo> result = new ArrayList<>();
 | 
	
		
			
				|  |  | -        for (Object keyObj : scanResult) {
 | 
	
		
			
				|  |  | -            result.add((InviteInfo) redisTemplate.opsForValue().get(keyObj));
 | 
	
		
			
				|  |  | +        String key = VideoManagerConstants.INVITE_PREFIX;
 | 
	
		
			
				|  |  | +        List<Object> values = redisTemplate.opsForHash().values(key);
 | 
	
		
			
				|  |  | +        if(values.isEmpty()) {
 | 
	
		
			
				|  |  | +            return result;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        for (Object value : values) {
 | 
	
		
			
				|  |  | +            result.add((InviteInfo)value);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          return result;
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -199,23 +196,22 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | -    public void removeInviteInfo(InviteSessionType type,  Integer channelId, String stream) {
 | 
	
		
			
				|  |  | -        String scanKey = VideoManagerConstants.INVITE_PREFIX +
 | 
	
		
			
				|  |  | -                ":" + (type != null ? type : "*") +
 | 
	
		
			
				|  |  | -                ":" + (channelId != null ? channelId : "*") +
 | 
	
		
			
				|  |  | -                ":" + (stream != null ? stream : "*") +
 | 
	
		
			
				|  |  | -                ":*";
 | 
	
		
			
				|  |  | -        List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey);
 | 
	
		
			
				|  |  | -        if (!scanResult.isEmpty()) {
 | 
	
		
			
				|  |  | -            for (Object keyObj : scanResult) {
 | 
	
		
			
				|  |  | -                String key = (String) keyObj;
 | 
	
		
			
				|  |  | -                InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key);
 | 
	
		
			
				|  |  | -                if (inviteInfo == null) {
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                redisTemplate.delete(key);
 | 
	
		
			
				|  |  | -                inviteErrorCallbackMap.remove(buildKey(type,channelId, inviteInfo.getStream()));
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +    public void removeInviteInfo(InviteSessionType type, Integer channelId, String stream) {
 | 
	
		
			
				|  |  | +        String key = VideoManagerConstants.INVITE_PREFIX;
 | 
	
		
			
				|  |  | +        if (type == null && channelId == null && stream == null) {
 | 
	
		
			
				|  |  | +            redisTemplate.opsForHash().delete(key);
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        InviteInfo inviteInfo = getInviteInfo(type, channelId, stream);
 | 
	
		
			
				|  |  | +        if (inviteInfo != null) {
 | 
	
		
			
				|  |  | +            String objectKey = inviteInfo.getType() +
 | 
	
		
			
				|  |  | +                    ":" + inviteInfo.getChannelId() +
 | 
	
		
			
				|  |  | +                    ":" + stream +
 | 
	
		
			
				|  |  | +                    ":" + inviteInfo.getSsrcInfo().getSsrc();
 | 
	
		
			
				|  |  | +            redisTemplate.opsForHash().delete(key, objectKey);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (redisTemplate.opsForHash().size(key) == 0) {
 | 
	
		
			
				|  |  | +            redisTemplate.opsForHash().delete(key);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -230,14 +226,14 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | -    public void once(InviteSessionType type,  Integer channelId, String stream, ErrorCallback<StreamInfo> callback) {
 | 
	
		
			
				|  |  | +    public void once(InviteSessionType type, Integer channelId, String stream, ErrorCallback<StreamInfo> callback) {
 | 
	
		
			
				|  |  |          String key = buildKey(type, channelId, stream);
 | 
	
		
			
				|  |  |          List<ErrorCallback<StreamInfo>> callbacks = inviteErrorCallbackMap.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>());
 | 
	
		
			
				|  |  |          callbacks.add(callback);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private String buildKey(InviteSessionType type,  Integer channelId, String stream) {
 | 
	
		
			
				|  |  | +    private String buildKey(InviteSessionType type, Integer channelId, String stream) {
 | 
	
		
			
				|  |  |          String key = type + ":" + channelId;
 | 
	
		
			
				|  |  |          // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite
 | 
	
		
			
				|  |  |          if (stream != null) {
 | 
	
	
		
			
				|  | @@ -249,7 +245,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public void clearInviteInfo(String deviceId) {
 | 
	
		
			
				|  |  | -        List<InviteInfo> inviteInfoList = getAllInviteInfo(null, null, null);
 | 
	
		
			
				|  |  | +        List<InviteInfo> inviteInfoList = getAllInviteInfo();
 | 
	
		
			
				|  |  |          for (InviteInfo inviteInfo : inviteInfoList) {
 | 
	
		
			
				|  |  |              if (inviteInfo.getDeviceId().equals(deviceId)) {
 | 
	
		
			
				|  |  |                  removeInviteInfo(inviteInfo);
 | 
	
	
		
			
				|  | @@ -260,23 +256,21 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public int getStreamInfoCount(String mediaServerId) {
 | 
	
		
			
				|  |  |          int count = 0;
 | 
	
		
			
				|  |  | -        String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*";
 | 
	
		
			
				|  |  | -        List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
 | 
	
		
			
				|  |  | -        if (scanResult.isEmpty()) {
 | 
	
		
			
				|  |  | -            return 0;
 | 
	
		
			
				|  |  | -        }else {
 | 
	
		
			
				|  |  | -            for (Object keyObj : scanResult) {
 | 
	
		
			
				|  |  | -                String keyStr = (String) keyObj;
 | 
	
		
			
				|  |  | -                InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(keyStr);
 | 
	
		
			
				|  |  | -                if (inviteInfo != null
 | 
	
		
			
				|  |  | -                        && inviteInfo.getStreamInfo() != null
 | 
	
		
			
				|  |  | -                        && inviteInfo.getStreamInfo().getMediaServer() != null
 | 
	
		
			
				|  |  | -                        && inviteInfo.getStreamInfo().getMediaServer().getId().equals(mediaServerId)) {
 | 
	
		
			
				|  |  | -                    if (inviteInfo.getType().equals(InviteSessionType.DOWNLOAD) && inviteInfo.getStreamInfo().getProgress() == 1) {
 | 
	
		
			
				|  |  | -                        continue;
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    count++;
 | 
	
		
			
				|  |  | +        String key = VideoManagerConstants.INVITE_PREFIX;
 | 
	
		
			
				|  |  | +        List<Object> values = redisTemplate.opsForHash().values(key);
 | 
	
		
			
				|  |  | +        if (values.isEmpty()) {
 | 
	
		
			
				|  |  | +            return count;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        for (Object value : values) {
 | 
	
		
			
				|  |  | +            InviteInfo inviteInfo = (InviteInfo)value;
 | 
	
		
			
				|  |  | +            if (inviteInfo != null
 | 
	
		
			
				|  |  | +                    && inviteInfo.getStreamInfo() != null
 | 
	
		
			
				|  |  | +                    && inviteInfo.getStreamInfo().getMediaServer() != null
 | 
	
		
			
				|  |  | +                    && inviteInfo.getStreamInfo().getMediaServer().getId().equals(mediaServerId)) {
 | 
	
		
			
				|  |  | +                if (inviteInfo.getType().equals(InviteSessionType.DOWNLOAD) && inviteInfo.getStreamInfo().getProgress() == 1) {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | +                count++;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          return count;
 | 
	
	
		
			
				|  | @@ -309,13 +303,16 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public InviteInfo getInviteInfoBySSRC(String ssrc) {
 | 
	
		
			
				|  |  | -        String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:" + ssrc;
 | 
	
		
			
				|  |  | -        List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
 | 
	
		
			
				|  |  | -        if (scanResult.size() != 1) {
 | 
	
		
			
				|  |  | +        List<InviteInfo> inviteInfoList = getAllInviteInfo();
 | 
	
		
			
				|  |  | +        if (inviteInfoList.isEmpty()) {
 | 
	
		
			
				|  |  |              return null;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0));
 | 
	
		
			
				|  |  | +        for (InviteInfo inviteInfo : inviteInfoList) {
 | 
	
		
			
				|  |  | +            if (inviteInfo.getSsrcInfo() != null && ssrc.equals(inviteInfo.getSsrcInfo().getSsrc())) {
 | 
	
		
			
				|  |  | +                return inviteInfo;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return null;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
	
		
			
				|  | @@ -325,15 +322,15 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
 | 
	
		
			
				|  |  |              return null;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          removeInviteInfo(inviteInfoInDb);
 | 
	
		
			
				|  |  | -        String key = VideoManagerConstants.INVITE_PREFIX +
 | 
	
		
			
				|  |  | -                ":" + inviteInfo.getType() +
 | 
	
		
			
				|  |  | +        String key = VideoManagerConstants.INVITE_PREFIX;
 | 
	
		
			
				|  |  | +        String objectKey = inviteInfo.getType() +
 | 
	
		
			
				|  |  |                  ":" + inviteInfo.getChannelId() +
 | 
	
		
			
				|  |  |                  ":" + inviteInfo.getStream() +
 | 
	
		
			
				|  |  |                  ":" + ssrc;
 | 
	
		
			
				|  |  |          if (inviteInfoInDb.getSsrcInfo() != null) {
 | 
	
		
			
				|  |  |              inviteInfoInDb.getSsrcInfo().setSsrc(ssrc);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        redisTemplate.opsForValue().set(key, inviteInfoInDb);
 | 
	
		
			
				|  |  | +        redisTemplate.opsForHash().put(key, objectKey, inviteInfoInDb);
 | 
	
		
			
				|  |  |          return inviteInfoInDb;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 |